Runs OpenCode inside a Docker container pre-configured with the XQAI provider. Any local project directory is bind-mounted into the container at /workspace. A Flask management server provides a dark-themed web UI for monitoring and managing sessions.
soc [path]
│
├─ Ensures Flask management server is running (auto-starts it)
├─ Builds the Docker image on first run
├─ Mounts [path] → /workspace inside a new container
├─ Opens http://localhost:5000 in the browser
└─ Attaches to OpenCode interactively (docker exec -it)
Containers stay alive after OpenCode exits so you can reconnect or inspect them from the web UI. Each container is tracked via Docker labels (opencode.managed=true, opencode.host_path=<path>). Running soc on a directory that already has a running container reuses it instead of creating a new one.
| Requirement | Notes |
|---|---|
| Docker Desktop | https://docker.com — must be running |
| Python 3.8+ | Windows: 3.10+ recommended, resolved via py launcher |
| Admin rights | Not required — user-level PATH only |
install.cmdOpen a new terminal window after installation completes.
bash install.shOpen a new shell (or source ~/.bashrc) after installation completes.
- Adds the project directory to the user PATH (Windows registry
HKCU, no admin required) or creates~/.local/bin/socsymlink (Unix). - Creates a Python virtual environment at
.venv/. - Installs
Flask==3.0.3anddocker==7.1.0into the venv. - Builds the
secure-opencodeDocker image (first time, ~1 minute).
soc # mounts the current directory
soc /path/to/project # mounts the specified directory
soc C:\path\to\project # WindowsWorkflow:
socauto-starts the Flask management server if it is not already running.- On first run, it builds the Docker image.
- A container named
soc-<dirname>-<suffix>is started with the directory bind-mounted at/workspace. - The browser opens at
http://localhost:5000. - OpenCode launches interactively in the container working directory.
- Typing
exitin OpenCode detaches but leaves the container running. - Running
socagain on the same directory reattaches to the existing container.
docker exec -it -w /workspace <container-name> opencode --continueThe connect command is shown on every session card in the web UI with a copy button. The --continue flag resumes the last active session — conversation history and context are preserved.
Either click Stop in the web UI, or:
docker stop <container-name> && docker rm <container-name>Start the server standalone (without launching OpenCode):
start-server.cmd # Windows
bash start-server.sh # Unix / Git BashThen open http://localhost:5000.
| Feature | Details |
|---|---|
| Docker / Image status | Navbar badges — live connection and image state |
| Active Sessions | Card per running container, auto-refreshes every 5 seconds |
| Session card | Container name, status, live uptime, started datetime, container ID, mount path, connect command with copy button |
| New Session | Create a session by entering an absolute directory path |
| Stop / Remove | Stop a running container or permanently remove a stopped one |
| Build Image | Shown when the secure-opencode image is missing; triggers a build |
| Global Mappings | Bind-mount additional host directories into every container — changes recreate all containers automatically |
| Per-session Mappings | Extra bind mounts scoped to a single container, shown in the collapsible Extra Mounts section of each session card |
| Folder Browser | Native server-side directory picker for selecting host paths — remembers the last visited directory across sessions, walks up to the nearest valid ancestor if a stored path no longer exists |
| Session continuity | OpenCode conversation history and context survive container recreations (mapping changes); each new terminal window resumes the last session via --continue |
The session list uses DOM diffing — cards update in-place without flickering. Uptime ticks every 10 seconds independently of the 5-second data refresh.
SecureOpenCode/
├── docker/
│ ├── Dockerfile # Ubuntu 24.04 + OpenCode
│ └── opencode.jsonc # OpenCode provider configuration
├── templates/
│ └── index.html # Flask template — dark SPA
├── app.py # Flask management server
├── requirements.txt # Flask==3.0.3, docker==7.1.0
├── soc.cmd # Windows CLI entry point
├── soc.sh # Unix / Git Bash CLI entry point
├── start-server.cmd # Start management server only (Windows)
├── start-server.sh # Start management server only (Unix)
├── install.cmd # Windows installer
└── install.sh # Unix installer
Base image: ubuntu:24.04
Installed:
curl,git,ca-certificates,nodejs,npm- OpenCode via
curl -fsSL https://opencode.ai/install | bash(installs to/root/.opencode/bin/)
Configuration: /root/.config/opencode/opencode.jsonc
opencode.jsonc is copied into the base image at build time (/root/.config/opencode/opencode.jsonc), so every container has a working configuration even when a different project folder is mounted as the workspace.
At container startup, entrypoint.sh checks for a workspace-local config and promotes it if present (workspace root → docker/ subfolder → image default), so project-specific overrides are still supported without rebuilding the image.
The web UI status bar continuously monitors whether the on-disk opencode.jsonc matches the version baked into the running image:
When a mismatch is detected the status bar highlights the change. You can then choose to:
- Rebuild base image & restart all — rebuilds
secure-opencodewith the updated config and recreates every managed container. - Restart selected containers — recreates only the containers you choose, picking up the workspace config override via
entrypoint.shwithout a full image rebuild.
Before any recreation starts, a warning dialog is shown so you can confirm the operation:
While the base image is being rebuilt, the status bar shows a live progress indicator:
To rebuild the image manually after changing Dockerfile:
docker build -t secure-opencode docker/Or use the Build Image button in the web UI.
GitHub Copilot is a built-in provider — no npm package installation required. Authentication is a one-time OAuth device flow. The token is stored server-side and can be injected into any container automatically.
Since v1.2.0, authentication can be managed entirely from the management UI — no manual docker exec required.
1. Open the GitHub Copilot panel in the web UI and click Authenticate:
A terminal window opens and runs the auth flow inside a temporary container.
2. Select GitHub.com (Public) when prompted. The terminal shows a device code and a URL.
3. Open the URL, enter the device code shown in the terminal, and authorize OpenCode:
4. Once the terminal shows success, click Capture Credentials in the web UI. The credentials are stored and the terminal container is cleaned up.
5. Enable Auto-inject in the Copilot panel to have credentials injected into every new container automatically. For existing containers, use the Inject / Remove buttons on each session card.
You can also authenticate directly inside any running container (requires an interactive TTY — run in cmd/PowerShell, not via a script):
docker exec -it <container-name> opencode auth login -p github-copilot -m "Login with GitHub Copilot"Select GitHub.com (Public) when prompted. The CLI outputs a device code and a URL.

Open the URL in a browser and authorize OpenCode:
Confirm authorization — the CLI polls automatically:
The token is stored in the container's state volume (<container>-state → /root/.local/share/opencode/auth.json) and survives container recreations for that container only.
GitHub Copilot models are now available in OpenCode — use /models to open the model selector:
If the token expires, repeat the UI flow above or run the manual docker exec command again. To check current credentials:
docker exec <container-name> opencode auth listThe management server runs on port 5000 by default (override with PORT env var).
| Method | Path | Description |
|---|---|---|
GET |
/ |
Web UI |
GET |
/api/version |
Current version and full changelog |
GET |
/api/status |
Docker connectivity and image state |
POST |
/api/image/build |
Build the secure-opencode image |
GET |
/api/browse?path= |
List subdirectories at a host path (drive list if empty) |
GET |
/api/sessions |
List all managed containers |
POST |
/api/sessions |
Create a new session — body: {"path": "/abs/path"} |
POST |
/api/sessions/<id>/start |
Start a stopped container |
POST |
/api/sessions/<id>/stop |
Stop a running container |
POST |
/api/sessions/<id>/open |
Launch OpenCode in a new terminal window |
DELETE |
/api/sessions/<id> |
Remove a session and its state volume |
GET |
/api/sessions/<id>/mappings |
List per-session extra mounts |
POST |
/api/sessions/<id>/mappings |
Add a per-session mount — body: {"host_path": "...", "container_path": "..."} |
PUT |
/api/sessions/<id>/mappings/<mid> |
Update a per-session mount |
DELETE |
/api/sessions/<id>/mappings/<mid> |
Remove a per-session mount |
GET |
/api/mappings |
List global mappings |
POST |
/api/mappings |
Add a global mapping — body: {"host_path": "...", "container_path": "..."} |
PUT |
/api/mappings/<mid> |
Update a global mapping |
DELETE |
/api/mappings/<mid> |
Remove a global mapping |
Sessions are identified by the Docker label opencode.managed=true. The opencode.host_path label stores the host directory path for container reuse detection. Each session gets a named Docker volume <name>-state mounted at /root/.local/share/opencode to persist OpenCode session data across container recreations.
The full changelog is also accessible inside the web UI — click the version badge in the bottom-right corner.
- Bundle opencode.jsonc into the base image so every container has a sane default when the mounted workspace does not include a project config. The entrypoint now prefers a workspace-local config (workspace root →
docker/subfolder) and falls back to the image-bundled file. Updated entrypoint logging to clarify which source is being used. - Web UI: add config change detection in the status bar — it highlights when the on-disk
opencode.jsoncdiffers from the image-bundled version and exposes actions to either rebuild the base image and restart all managed containers or selectively restart chosen containers to pick up workspace overrides. Added a confirmation warning before recreating containers and a live rebuild progress view with streaming logs. - Docs: README updated with screenshots and guidance for the new config behaviour and UI flows (resources/webui_statusbar.png, resources/webui_warning.png, resources/webui_rebuilding_base_image.png).
- Fix: ensure containers started from arbitrary host folders still have a working OpenCode configuration by bundling a default config into the image at build time.
- GitHub Copilot UI authentication — authenticate once from the web UI, credentials stored server-side
- Auto-inject setting — new containers automatically receive stored Copilot credentials
- Per-session Copilot inject / remove controls on every session card
- Version badge (bottom right of web UI) with clickable changelog popup
- GitHub Copilot provider enabled in
opencode.jsonc - GitHub Copilot authentication guide (manual
docker execflow)
- Initial stable release
- Session persistence via named Docker volumes
- Global and per-session bind-mount mappings
- Folder browser with last-path memory
- DOM-diffing session list (no card flicker)
- Windows CRLF fix and
-ExecutionPolicy Bypassfor fresh Windows 11 machines
soc not found after install
Open a new terminal window. On Unix, run source ~/.bashrc or equivalent. Verify with which soc (Unix) or where soc (Windows).
Typing soc in the File Explorer address bar triggers a web search instead
Explorer's address bar doesn't use the same PATH/PATHEXT resolution as cmd.exe or the Run dialog — unresolved input falls back to a browser search on Windows 11. install.cmd registers soc under HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\soc.exe (no admin required) so Explorer can find it directly. If this was set up before that fix was added, re-run install.cmd.
Docker image build fails Requires internet access during build to download OpenCode. Check Docker Desktop is running. Retry via the web UI Build Image button.
OpenCode does not see the project files
The container must be started with the directory bind-mounted at /workspace. Always use docker exec -it -w /workspace <name> opencode --continue (the -w /workspace flag sets the working directory; --continue resumes the last session).
OpenCode starts a blank session after a mapping change Mapping changes recreate the container but preserve the OpenCode state volume. If you see a blank session, the container was created before session persistence was introduced — remove it via the web UI and create a new one.
Duplicate containers after a mapping change on Windows
On Windows, Docker's named pipe (error 109 / GetOverlappedResult) can drop mid-operation. The server retries with pipe-recovery logic and pre-cleans orphaned temp containers before each recreation. If duplicates appear, remove the extra container manually via docker rm -f <name>.
Port 5000 already in use
PORT=5001 python app.py # Unix
set PORT=5001 && python app.py # Windows CMDPython venv creation fails on Windows
The installer kills any running python.exe psocesses before creating the venv. If it still fails, manually delete .venv\ and re-run install.cmd.
Inkscape Python 3.9 selected instead of Python 3.13
The installer uses the py -3 launcher to resolve Python, which skips non-dev interpreters. If you see version 3.9, ensure the Windows py launcher is installed with your Python 3.13 distribution.











{ "$schema": "https://opencode.ai/config.json", "enabled_providers": ["xqai", "github-copilot"], "disabled_providers": ["opencode"], "provider": { "xqai": { "name": "XQAI", "npm": "@ai-sdk/openai-compatible", "models": { "AiLeonFlash": { "name": "AiLeonFlash" }, "AiLeon": { "name": "AiLeon" } }, "options": { "baseURL": "http://xq-ai.xqueue.int:11434/v1" } } } }