Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/scripts/generate_release_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def main():
args = parser.parse_args()

# Fallback version if version generation fails
FALLBACK_VERSION = "2026.1.4"
FALLBACK_VERSION = "2026.1.5"

try:
base_ver = get_current_date_version()
Expand Down
24 changes: 9 additions & 15 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
workflow_dispatch:

permissions:
contents: read
contents: write
pages: write
id-token: write

Expand Down Expand Up @@ -214,18 +214,12 @@ jobs:
env:
BASE_URL: /

- name: Upload artifact
uses: actions/upload-pages-artifact@v4
- name: Upload to gh-pages
uses: JamesIves/github-pages-deploy-action@v4
with:
path: docs/.vitepress/dist

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
folder: docs/.vitepress/dist
branch: gh-pages
clean: true
# Preserve the pr-preview directory
clean-exclude: |
pr-preview/
59 changes: 57 additions & 2 deletions .github/workflows/docs_preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,56 @@ jobs:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.13'

- name: Install Python Dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
pip install Pillow

- name: Pre-install Addons (Web)
run: |
mkdir -p src/switchcraft/addons
python -c "import zipfile, os, glob; from pathlib import Path; [zipfile.ZipFile(z).extractall(Path('src/switchcraft/addons') / Path(z).stem.replace('switchcraft_', '')) for z in glob.glob('src/switchcraft/assets/addons/*.zip')]"

- name: Build Web Demo
run: |
python scripts/prepare_web_dist.py --bake
mkdir -p build_web
cp -r src/switchcraft build_web/

PR_NUMBER=${{ github.event.pull_request.number }}
cat > build_web/web_entry.py <<EOF
import os
import sys
import flet as ft
# WASM compatibility patches
if sys.platform == "emscripten":
import pyodide_http
pyodide_http.patch_all()
sys.path.insert(0, os.getcwd())
import switchcraft
switchcraft.IS_DEMO = True
import switchcraft.main
if __name__ == "__main__":
ft.run(switchcraft.main.main, assets_dir="assets")
EOF
Comment on lines +54 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine the actual docs_preview.yml file
fd "docs_preview.yml" --type f

Repository: FaserF/SwitchCraft

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Search for workflow files in .github directory
find .github -name "*.yml" -o -name "*.yaml" 2>/dev/null

Repository: FaserF/SwitchCraft

Length of output: 737


🏁 Script executed:

#!/bin/bash
# Read the actual docs_preview.yml file around lines 54-68
cat -n .github/workflows/docs_preview.yml | sed -n '50,75p'

Repository: FaserF/SwitchCraft

Length of output: 1248


🏁 Script executed:

#!/bin/bash
# Test the actual heredoc behavior with the indentation as shown in the workflow
# This simulates what happens in the GitHub Actions environment

cat > /tmp/test_workflow.sh <<'OUTER'
#!/bin/bash
# Simulate the exact workflow structure with indentation
bash -c '
          cat > /tmp/web_entry.py <<EOF
          import os
          import sys
          import flet as ft
          # WASM compatibility patches
          if sys.platform == "emscripten":
              import pyodide_http
              pyodide_http.patch_all()
          sys.path.insert(0, os.getcwd())
          import switchcraft
          switchcraft.IS_DEMO = True
          import switchcraft.main
          if __name__ == "__main__":
              ft.run(switchcraft.main.main, assets_dir="assets")
          EOF
'
OUTER

bash /tmp/test_workflow.sh

# Now check the generated file for syntax errors
echo "=== Checking Python syntax ==="
python -m py_compile /tmp/web_entry.py 2>&1 || true

echo ""
echo "=== First 5 lines of generated file with visible spaces ==="
head -5 /tmp/web_entry.py | cat -A

Repository: FaserF/SwitchCraft

Length of output: 479


Fix heredoc indentation in web_entry.py generation — will cause IndentationError at runtime.

The heredoc content has 10 leading spaces on each line. When the file is generated, these spaces become part of the Python file, causing an IndentationError on line 1. Use <<- instead of << to strip leading tabs, or remove the indentation from the heredoc content:

cat > build_web/web_entry.py <<-EOF
	import os
	import sys
	import flet as ft
	# WASM compatibility patches
	if sys.platform == "emscripten":
	    import pyodide_http
	    pyodide_http.patch_all()
	sys.path.insert(0, os.getcwd())
	import switchcraft
	switchcraft.IS_DEMO = True
	import switchcraft.main
	if __name__ == "__main__":
	    ft.run(switchcraft.main.main, assets_dir="assets")
	EOF
🤖 Prompt for AI Agents
In @.github/workflows/docs_preview.yml around lines 54 - 68, The heredoc used to
generate build_web/web_entry.py currently preserves the leading spaces causing
an IndentationError when the generated Python file is executed; update the
command that writes the file to strip the leading indentation by changing the
heredoc opener from << to <<- (or by removing the extra indentation inside the
heredoc), so the generated file contains left-aligned Python code (affecting the
block that creates web_entry.py which imports os, sys, flet as ft, the WASM
patch block, sys.path insertion, switchcraft.IS_DEMO assignment, and the ft.run
call).


cd build_web
# Simplified publish for preview - use the PR specific base-url
flet publish web_entry.py --app-name "SwitchCraft Demo PR-$PR_NUMBER" --base-url "/pr-preview/pr-$PR_NUMBER/demo/" --distpath ../dist --assets switchcraft/assets
cd ..

python scripts/prepare_web_dist.py --patch dist
rm -rf docs/public/demo
mkdir -p docs/public/demo
cp -r dist/* docs/public/demo/
shell: bash

- name: Setup Node
uses: actions/setup-node@v6
with:
Expand All @@ -36,8 +86,13 @@ jobs:
cache-dependency-path: docs/package-lock.json

- name: Install dependencies
run: npm ci
working-directory: docs
run: |
cd docs
if [ -f package-lock.json ]; then
npm ci
else
npm install vitepress vue
fi

- name: Build Docs
run: npm run docs:build
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ jobs:
sed -i "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" src/switchcraft_advanced/manifest.json
sed -i "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" src/switchcraft_ai/manifest.json
sed -i "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" src/switchcraft_winget/manifest.json
sed -i "s/\"description\": \".*\"/\"description\": \"SwitchCraft - Enterprise Application Management\"/" src/switchcraft/assets/manifest.json
sed -i "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" src/switchcraft/assets/manifest.json


# Update .iss files (Inno Setup installer scripts)
Expand Down Expand Up @@ -125,7 +127,7 @@ jobs:
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

git add pyproject.toml src/switchcraft/__init__.py file_version_info.txt switchcraft.iss switchcraft_legacy.iss src/switchcraft_advanced/manifest.json src/switchcraft_ai/manifest.json src/switchcraft_winget/manifest.json
git add pyproject.toml src/switchcraft/__init__.py file_version_info.txt switchcraft.iss switchcraft_legacy.iss src/switchcraft_advanced/manifest.json src/switchcraft_ai/manifest.json src/switchcraft_winget/manifest.json src/switchcraft/assets/manifest.json


# Only commit if there are changes
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
if: steps.should_run.outputs.should_run == 'true'
run: |
python -m pip install --upgrade pip
pip install .[test,gui,desktop]
pip install .[test,gui,desktop,web-server]

- name: Run All Python Tests
if: steps.should_run.outputs.should_run == 'true'
Expand Down Expand Up @@ -121,7 +121,7 @@ jobs:
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install .[test,gui,desktop]
pip install .[test,gui,desktop,web-server]
pip install pytest-asyncio

- name: Run E2E Integration Tests
Expand Down
9 changes: 4 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ COPY pyproject.toml .
COPY README.md .
COPY src ./src

# Install dependencies (Modern Flet only)
# Note: We omit 'gui' (Legacy Tkinter) to avoid system package requirements
RUN pip install --no-cache-dir .[modern]
# Install dependencies (Modern Flet + Web Server + Web WASM)
RUN pip install --no-cache-dir .[modern,web-server] flet-web packaging

# Generate Addons (Pre-installed)
RUN python src/generate_addons.py
Expand All @@ -42,5 +41,5 @@ ENV SC_DISABLE_WINGET_INSTALL=1
# Create symlink for assets so Flet can find them at /app/assets
RUN ln -s /app/src/switchcraft/assets /app/assets

# Command to run the application in web mode
CMD ["flet", "run", "--web", "--port", "8080", "--host", "0.0.0.0", "src/switchcraft/main.py"]
# Command to run the application using the Auth Proxy Server
CMD ["uvicorn", "switchcraft.server.app:app", "--host", "0.0.0.0", "--port", "8080"]
24 changes: 24 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
services:
switchcraft-web:
build: .
image: switchcraft-web:latest
container_name: switchcraft-web
ports:
- "8080:8080"
volumes:
- switchcraft_data:/root/.switchcraft
environment:
- SC_CLIENT_ID=${SC_CLIENT_ID}
- SC_CLIENT_SECRET=${SC_CLIENT_SECRET}
- SC_AUTH_PROVIDER=${SC_AUTH_PROVIDER:-entra}
- SC_ENTRA_CLIENT_ID=${SC_ENTRA_CLIENT_ID}
- SC_ENTRA_CLIENT_SECRET=${SC_ENTRA_CLIENT_SECRET}
- SC_ENTRA_TENANT_ID=${SC_ENTRA_TENANT_ID}
- SC_GITHUB_CLIENT_ID=${SC_GITHUB_CLIENT_ID}
- SC_GITHUB_CLIENT_SECRET=${SC_GITHUB_CLIENT_SECRET}
- SC_BASE_URL=${SC_BASE_URL}
- SC_DISABLE_WINGET_INSTALL=1
restart: unless-stopped

volumes:
switchcraft_data:
3 changes: 2 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ export default defineConfig({
{ text: 'OMA-URI Reference', link: '/Intune_Configuration_Guide' },
{ text: 'GPO / ADMX Policies', link: '/PolicyDefinitions/README' },
{ text: 'Registry Settings', link: '/Registry' },
{ text: 'Security Guide', link: '/SECURITY' }
{ text: 'Security Guide', link: '/SECURITY' },
{ text: 'Docker Self-Hosting', link: '/docker_setup' }
]
},
{
Expand Down
149 changes: 107 additions & 42 deletions docs/docker_setup.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,107 @@
# SwitchCraft Web App (Docker) Guide

## Overview
SwitchCraft can be deployed as a Dockerized Web Application using Flet. This provides a web-accessible version of the packaging tool.

## Deployment

### Prerequisites
- Docker Engine
- Git

### Build & Run
1. **Build the Image**
```bash
docker build -t switchcraft-web .
```

2. **Run the Container**
```bash
docker run -d -p 8080:8080 --name switchcraft switchcraft-web
```
Access the app at `http://localhost:8080`.

## Configuration
The application uses environment variables for configuration in Docker.

| Variable | Description | Default |
| :--- | :--- | :--- |
| `SC_AUTH_PROVIDER` | Auth provider (`github`, `entra`, `none`) | `none` |
| `SC_GITHUB_CLIENT_ID` | GitHub App Client ID | - |
| `SC_GITHUB_CLIENT_SECRET` | GitHub App Client Secret | - |
| `SC_SESSION_SECRET` | Secret key for session encryption | (Random) |
| `SC_DISABLE_WINGET_INSTALL` | Set `1` to skip Winget check (Use static fallback) | `0` |

## Limitations vs Desktop
- **Winget Search**: Relies on a static dataset of ~50 popular apps or external APIs. Results may differ from Desktop Winget CLI.
- **Intune Upload**: Requires Azure authentication which may need device code flow.
- **Local Files**: Browser sandbox applies; file upload/download is used instead of direct file system access.

## Troubleshooting
- **No Search Results?** The app uses a fallback static dataset if public APIs are unreachable.
- **Login Fails?** Ensure `SC_GITHUB_CLIENT_ID` and `SECRET` are set correctly if using GitHub auth.
# SwitchCraft Server (Docker) Guide

SwitchCraft enables you to host your own instance of the application using **Docker**. The containerized version allows you to access SwitchCraft from any browser in your network, complete with a built-in authentication system, Single Sign-On (SSO), and administrative controls.

## 🚀 Quick Start

### 1. Build & Run
You can build the image directly from the source:

```bash
# Build the image
docker build -t switchcraft-web .

# Run the container (Mapping port 8080)
# We map a volume to persist configuration AND user database
docker run -d \
-p 8080:8080 \
-v switchcraft_data:/root/.switchcraft \
--name switchcraft \
switchcraft-web
```

Access the application at: `http://localhost:8080`

## 🔐 Authentication & User Management

The Docker container includes a full **User Management System**.

### Default Login
- **Username:** `admin`
- **Password:** `admin` (You should change this immediately in the Admin Panel)
Comment on lines +30 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Strengthen security warning for default credentials.

The note to change the default password should be more emphatic for production deployments. Consider using stronger language and making this a prominent warning block rather than a casual parenthetical note.

🔒 Suggested improvement
 ### Default Login
 - **Username:** `admin`
-- **Password:** `admin` (You should change this immediately in the Admin Panel)
+- **Password:** `admin`
+
+> **🔴 SECURITY WARNING:** The default password `admin` is publicly known. You **must** change it immediately after first login via the Admin Panel (`/admin` → Security → Change Password). Failure to do so leaves your instance vulnerable to unauthorized access.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Username:** `admin`
- **Password:** `admin` (You should change this immediately in the Admin Panel)
### Default Login
- **Username:** `admin`
- **Password:** `admin`
> **🔴 SECURITY WARNING:** The default password `admin` is publicly known. You **must** change it immediately after first login via the Admin Panel (`/admin` → Security → Change Password). Failure to do so leaves your instance vulnerable to unauthorized access.
🤖 Prompt for AI Agents
In `@docs/docker_setup.md` around lines 30 - 31, Replace the casual parenthetical
about the default password with a prominent, emphatic warning block that calls
out the default credentials "**Username:** `admin`" and "**Password:** `admin`"
as insecure for production; explicitly state "DO NOT USE IN PRODUCTION" and
instruct the operator to change the admin password immediately, rotate any
existing secrets, and store new credentials in a secure secrets manager or
environment variables before exposing the service. Ensure the warning block
appears directly below the credentials lines so it's highly visible in the docs.


### Multi-User Support
The server supports multiple users with distinct roles (`admin` or `user`).
- **Admins** have access to the `/admin` dashboard to manage users and server settings.
- **Users** can access the application tools but cannot modify server settings or other users.

### Single Sign-On (SSO) Setup
SwitchCraft supports OAuth2 login via **GitHub** and **Microsoft Entra ID (Office 365)**.
To enable SSO, you must provide the following environment variables when running the Docker container.

#### 1. Microsoft Entra ID (O365)
Register an App in Azure AD -> App Registrations.
* **Redirect URI:** `http://<your-domain>:8080/oauth_callback/entra`
* **API Permissions:** `User.Read` (Delegated)

```bash
docker run -d ... \
-e SC_ENTRA_CLIENT_ID="<your-client-id>" \
-e SC_ENTRA_TENANT_ID="<your-tenant-id>" \
-e SC_ENTRA_CLIENT_SECRET="<your-client-secret>" \
-e SC_BASE_URL="http://myserver.com:8080" \
switchcraft-web
```

*Users logging in via Entra for the first time will be **automatically created** as normal users.*

#### 2. GitHub
Register an OAuth App in GitHub Developer Settings.
* **Callback URL:** `http://<your-domain>:8080/oauth_callback/github`

```bash
docker run -d ... \
-e SC_GITHUB_CLIENT_ID="<your-client-id>" \
-e SC_GITHUB_CLIENT_SECRET="<your-client-secret>" \
switchcraft-web
```
Comment on lines +38 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add guidance on secure secret management for production.

The examples show secrets passed as environment variables directly in docker run commands. While this works for testing, it's not secure for production as secrets may be exposed in shell history, process listings, and container inspect output.

Consider adding a note recommending:

  • Using .env files with docker-compose (with appropriate file permissions)
  • Docker secrets for swarm deployments
  • Secret management systems (Vault, AWS Secrets Manager, etc.) for production
📝 Suggested addition

Add after line 67:

> **🔒 Production Secret Management:**  
> For production deployments, avoid passing secrets via command-line arguments.  
> Use:
> - Docker Compose with `.env` files (set permissions to `600`)
> - Docker Secrets for Swarm mode
> - External secret management systems (HashiCorp Vault, AWS Secrets Manager, etc.)
🤖 Prompt for AI Agents
In `@docs/docker_setup.md` around lines 38 - 67, Add a short "Production Secret
Management" note after the SSO examples (near the "Single Sign-On (SSO) Setup"
section) that warns against passing secrets via command-line `docker run` and
recommends secure alternatives: using Docker Compose with .env files (secure
file permissions), Docker Secrets for Swarm, and external secret managers like
HashiCorp Vault or AWS Secrets Manager; reference the SSO examples that set
SC_ENTRA_* and SC_GITHUB_* environment variables so readers know which secrets
to protect.


## ⚙️ Administration Dashboard

Access the dashboard at `http://localhost:8080/admin` (Link in Footer).

### Features
1. **User Management**:
* **List Users:** See all registered accounts (Local and SSO).
* **Add User:** Manually create accounts with passwords.
* **Delete User:** Remove accounts (except the root admin).
2. **Server Settings**:
* **Demo Mode:** Switch app to Read-Only mode.
* **Disable Auth:** Allow public access without login (Auto-Login as Guest/Admin).
Comment on lines +78 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add security warning for "Disable Auth" feature.

The "Disable Auth" feature that allows auto-login as Admin is extremely dangerous and should have a prominent security warning. This effectively disables all security protections and should only be used in completely isolated, non-production environments.

⚠️ Suggested addition
 2.  **Server Settings**:
     *   **Demo Mode:** Switch app to Read-Only mode.
-    *   **Disable Auth:** Allow public access without login (Auto-Login as Guest/Admin).
+    *   **Disable Auth:** Allow public access without login (Auto-Login as Guest/Admin).
+        > **⚠️ WARNING:** Disabling authentication removes all access controls. Only use this in isolated development/testing environments. Never enable this on internet-accessible instances.
🤖 Prompt for AI Agents
In `@docs/docker_setup.md` around lines 78 - 80, Update the "Disable Auth" bullet
under Server Settings to include a prominent, bold security warning stating that
enabling "Disable Auth" (Auto-Login as Guest/Admin) disables all authentication
and should only be used in isolated, non-production environments; mention the
risk (full public admin access), recommend alternatives (use Demo Mode or enable
proper auth), and advise adding an explicit confirmation step or
environment-only guard around the Disable Auth option to prevent accidental use.

3. **Security**:
* **Change Password:** Update your own password.
* **MFA / 2FA:** Setup Time-based OTP (Google Authenticator) enforcement.

## 💾 Persistence

To keep your users and settings safe, mount the `/root/.switchcraft` volume.

| Path | Purpose |
| :--- | :--- |
| `server/auth_config.json` | Stores Global Server Config (Secret Keys, Demo Mode). |
| `server/users.json` | **User Database** (Usernames, Password Hashes, Roles). |
| `users/<username>/config.json` | (Future) Server-side user profiles. |

## 🌐 Web Architecture Notes

* **Session Storage:** Login sessions use secure, encrypted cookies valid for 24 hours.
* **Client Settings:** Currently, app preferences (Theme, etc.) are stored in the **Browser** (LocalStorage) to ensure fast load times. We are working on server-side roaming profiles for the next release.
* **File Access:** The web version runs in a sandbox. You cannot access the host filesystem directly. Use the Upload/Download features.

## 🛠 Troubleshooting

* **SSO Redirect Error?** Ensure `SC_BASE_URL` matches exactly the URL you access in the browser (including http/https and port).
* **Locked Out?**
* `docker exec -it switchcraft sh`
* Delete `server/users.json` to reset the database (recreating default admin).
* Or manually edit `server/users.json` if you know JSON syntax.
Comment on lines +104 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix list indentation and add safety warning.

The nested list items under "Locked Out?" have inconsistent indentation. Additionally, the instruction to delete users.json should include a safety note about stopping the container first to prevent data corruption.

✍️ Proposed fix
 *   **Locked Out?**
-  *   `docker exec -it switchcraft sh`
-  *   Delete `server/users.json` to reset the database (recreating default admin).
-  *   Or manually edit `server/users.json` if you know JSON syntax.
+    *   `docker exec -it switchcraft sh`
+    *   Stop the container (`docker stop switchcraft`) then delete `server/users.json` to reset the database (recreating default admin).
+    *   Or manually edit `server/users.json` if you know JSON syntax (while container is stopped).

Note: Changed indentation to 4 spaces for nested items and added safety guidance about stopping the container before modifying the database.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

105-105: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


105-105: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


106-106: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


106-106: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


107-107: Inconsistent indentation for list items at the same level
Expected: 0; Actual: 2

(MD005, list-indent)


107-107: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In `@docs/docker_setup.md` around lines 104 - 107, Fix the "Locked Out?" sublist
indentation to use consistent 4-space indentation for the nested bullets under
that heading and add a safety warning telling users to stop the running
container before modifying or deleting server/users.json to avoid data
corruption; update the two nested items that currently read "docker exec -it
switchcraft sh" and "Delete server/users.json" (and the manual-edit note) to be
indented uniformly and prepend a short safety sentence referencing stopping the
container (e.g., stop the 'switchcraft' container) before running those steps.

Loading
Loading