* feat(cli): add --foreground flag for systemd and process manager deployments Adds --foreground / --no-daemon to `openchamber serve` which runs the server inline in the CLI process instead of spawning a detached daemon child. Required for systemd Type=simple (and other process managers) that track the direct child — the always-daemon behavior introduced in #640 broke this use case. Also documents OPENCHAMBER_HOST (bind address) in --help, which was implemented but never exposed to users. * docs: add systemd service guide for VPN/LAN deployments Documents how to run OpenCode and OpenChamber as separate systemd user services for persistent access over Tailscale or LAN, using the new --foreground flag and OPENCODE_HOST to wire them together. * fix(cli): address foreground mode parity issues from PR review - Fix Ctrl+C handling: CLI SIGINT handler now defers to server in foreground mode; dedicated signal handlers perform graceful shutdown and clean exit - Restore lifecycle parity: foreground instances write PID/instance files so status, stop, and restart can discover them - Add deterministic --foreground --json output: emits stable startup JSON with port, pid, url, and foreground flag before blocking * fix(cli): tighten inline foreground behavior for restart UX and JSON-only output * fix(cli): pass --host to foreground server, reject --json, add --quiet output - Pass options.host through to startWebUiServer() in foreground mode so the bind address is respected (fixes localhost-only regression from #750) - Reject --foreground --json with a clear usage error; --json is only supported in background (daemon) mode - Emit resolved port on stdout in --quiet foreground mode, matching daemon parity - Update systemd docs to include --host 0.0.0.0 for LAN/VPN access now that the default bind is 127.0.0.1 * fix(cli): remove duplicate OPENCHAMBER_HOST entry from help text * fix(cli): emit restart summary before foreground serve() blocks restart --json (and --quiet / human) with a foreground instance would hang forever without output because serve() blocks and the post-loop summary was unreachable. Emit the final output after stop succeeds but before the blocking serve call — foreground is always sorted last so all daemon results are already collected. * fix(cli): restart stops foreground instances without re-attaching Foreground instances are managed by a process manager (systemd, Docker, etc.) that will restart them automatically. The restart command now just stops the foreground instance, records the result, and exits — no serve() call, no blocking. This makes restart --json and all other output modes work correctly for foreground instances.
194 lines
10 KiB
Markdown
194 lines
10 KiB
Markdown
# <picture><source media="(prefers-color-scheme: dark)" srcset="https://github.com/btriapitsyn/openchamber/raw/HEAD/docs/references/badges/openchamber-logo-dark.svg"><img src="https://github.com/btriapitsyn/openchamber/raw/HEAD/docs/references/badges/openchamber-logo-light.svg" width="32" height="32" align="absmiddle" /></picture> @openchamber/web
|
|
|
|
[](https://github.com/btriapitsyn/openchamber/stargazers)
|
|
[](https://github.com/btriapitsyn/openchamber/releases/latest)
|
|
[](https://discord.gg/ZYRSdnwwKA)
|
|
|
|
Run [OpenCode](https://opencode.ai) in your browser. Install the CLI, open `localhost:3000`, done. Works on desktop browsers, tablets, and phones as a PWA.
|
|
|
|
Full project overview, screenshots, and all features: [github.com/btriapitsyn/openchamber](https://github.com/btriapitsyn/openchamber)
|
|
|
|
## Install
|
|
|
|
```bash
|
|
curl -fsSL https://raw.githubusercontent.com/btriapitsyn/openchamber/main/scripts/install.sh | bash
|
|
```
|
|
|
|
Or install manually: `bun add -g @openchamber/web` (or npm, pnpm, yarn).
|
|
|
|
> **Prerequisites:** [OpenCode CLI](https://opencode.ai) installed, Node.js 20+.
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
openchamber # Start on port 3000
|
|
openchamber --port 8080 # Custom port
|
|
openchamber --ui-password secret # Password-protect UI
|
|
openchamber tunnel help # Tunnel lifecycle commands
|
|
openchamber tunnel providers # Show provider capabilities
|
|
openchamber tunnel profile add --provider cloudflare --mode managed-remote --name prod-main --hostname app.example.com --token <token>
|
|
openchamber tunnel start --profile prod-main
|
|
openchamber tunnel start --provider cloudflare --mode quick --qr
|
|
openchamber tunnel start --provider cloudflare --mode managed-local --config ~/.cloudflared/config.yml
|
|
openchamber tunnel status --all # Show tunnel state across instances
|
|
openchamber tunnel stop --port 3000 # Stop tunnel only (server stays running)
|
|
openchamber logs # Follow latest instance logs
|
|
OPENCODE_PORT=4096 OPENCODE_SKIP_START=true openchamber # Connect to external OpenCode server
|
|
OPENCODE_HOST=https://myhost:4096 OPENCODE_SKIP_START=true openchamber # Connect via custom host/HTTPS
|
|
openchamber stop # Stop server
|
|
openchamber update # Update to latest version
|
|
```
|
|
|
|
### Tunnel behavior notes
|
|
|
|
- One active tunnel per running OpenChamber instance (port).
|
|
- Starting a different tunnel mode/provider on the same instance replaces the active tunnel.
|
|
- Replacing or stopping a tunnel revokes existing connect links and invalidates remote tunnel sessions.
|
|
- Connect links are one-time tokens; generating a new link revokes the previous unused link.
|
|
|
|
<details>
|
|
<summary>Connect to external OpenCode server</summary>
|
|
|
|
```bash
|
|
OPENCODE_PORT=4096 OPENCODE_SKIP_START=true openchamber
|
|
OPENCODE_HOST=https://myhost:4096 OPENCODE_SKIP_START=true openchamber
|
|
```
|
|
|
|
| Variable | Description |
|
|
|----------|-------------|
|
|
| `OPENCODE_HOST` | Full base URL of external server (overrides `OPENCODE_PORT`) |
|
|
| `OPENCODE_PORT` | Port of external server |
|
|
| `OPENCODE_SKIP_START` | Skip starting embedded OpenCode server |
|
|
| `OPENCHAMBER_OPENCODE_HOSTNAME` | Bind hostname for managed OpenCode server (default: `127.0.0.1`, use `0.0.0.0` for LAN/remote access — trusted networks only) |
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Bind managed OpenCode to LAN / Tailscale</summary>
|
|
|
|
```bash
|
|
OPENCHAMBER_OPENCODE_HOSTNAME=0.0.0.0 openchamber --port 3000
|
|
```
|
|
|
|
**Security note:** binding to `0.0.0.0` exposes the server on all network interfaces — use only on trusted networks and protect with firewall rules or `--ui-password`.
|
|
|
|
</details>
|
|
|
|
**Optional env vars:**
|
|
```yaml
|
|
environment:
|
|
UI_PASSWORD: your_secure_password
|
|
OPENCHAMBER_TUNNEL_MODE: quick # quick | managed-remote | managed-local
|
|
OPENCHAMBER_TUNNEL_PROVIDER: cloudflare
|
|
```
|
|
|
|
For `managed-remote` mode, also set:
|
|
|
|
```yaml
|
|
environment:
|
|
OPENCHAMBER_TUNNEL_MODE: managed-remote
|
|
OPENCHAMBER_TUNNEL_HOSTNAME: app.example.com
|
|
OPENCHAMBER_TUNNEL_TOKEN: <token>
|
|
```
|
|
|
|
For `managed-local` mode, you can set:
|
|
|
|
```yaml
|
|
environment:
|
|
OPENCHAMBER_TUNNEL_MODE: managed-local
|
|
OPENCHAMBER_TUNNEL_CONFIG: /home/openchamber/.cloudflared/config.yml
|
|
```
|
|
|
|
Managed-local path note: `OPENCHAMBER_TUNNEL_CONFIG` must use a container path under `/home/openchamber/...`. If the config file references `credentials-file`, ensure that JSON path is also mounted and reachable inside the container.
|
|
|
|
**Data directory:** mount `data/` for persistent storage. Ensure permissions:
|
|
```bash
|
|
mkdir -p data/openchamber data/opencode/share data/opencode/config data/ssh
|
|
chown -R 1000:1000 data/
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>Background & daemon mode</summary>
|
|
|
|
```bash
|
|
openchamber # Runs in background by default
|
|
openchamber stop # Stop background server
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>systemd service (VPN / LAN access)</summary>
|
|
|
|
Use `--foreground` to keep the CLI process alive so systemd (or any other process manager) can track and restart it. Combine with `OPENCODE_HOST` to connect to an OpenCode instance running as a separate service.
|
|
|
|
**`~/.config/systemd/user/opencode.service`**
|
|
```ini
|
|
[Unit]
|
|
Description=OpenCode Server
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=opencode serve --port 4095
|
|
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/home/YOU/.local/bin:/home/YOU/.npm-global/bin:/usr/local/bin:/usr/bin:/bin"
|
|
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
```
|
|
|
|
> **Why set `PATH` and `SSH_AUTH_SOCK`?**
|
|
> systemd user services start with a minimal environment — no shell profile is sourced.
|
|
> Without an explicit `PATH`, OpenCode won't find tools installed via Homebrew, npm, or `~/.local/bin`.
|
|
> Without `SSH_AUTH_SOCK`, git operations over SSH (push, pull, clone) will fail.
|
|
> `%t` expands to `$XDG_RUNTIME_DIR` (e.g. `/run/user/1000`), where most SSH agents write their socket.
|
|
|
|
**`~/.config/systemd/user/openchamber.service`**
|
|
```ini
|
|
[Unit]
|
|
Description=OpenChamber Web Server
|
|
After=opencode.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=openchamber serve --port 3000 --host 0.0.0.0 --ui-password your-password --foreground
|
|
Environment="OPENCODE_HOST=http://localhost:4095"
|
|
Environment="OPENCODE_SKIP_START=true"
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
```
|
|
|
|
```bash
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable --now opencode openchamber
|
|
```
|
|
|
|
`--host 0.0.0.0` is required to listen on all interfaces (the default is `127.0.0.1`). Use `--host <ip>` or `OPENCHAMBER_HOST=<ip>` to bind to a specific interface instead.
|
|
|
|
</details>
|
|
|
|
## What makes the web version special
|
|
|
|
- **Remote access** - Cloudflare tunnel with QR onboarding. Scan from your phone, start coding.
|
|
- **Mobile-first PWA** - optimized chat controls, keyboard-safe layouts, drag-to-reorder projects
|
|
- **Background notifications** - know when your agent finishes, even from another tab
|
|
- **Self-update** - update and restart from the UI, server settings stay intact
|
|
- **Cross-tab tracking** - session activity stays in sync across browser tabs
|
|
|
|
- Cloudflare tunnel access with quick, managed-remote, and managed-local modes
|
|
- One-scan onboarding with tunnel QR + password URL helpers
|
|
- Mobile-first experience: optimized chat controls, keyboard-safe layouts, and attachment-friendly UI
|
|
- Background notifications plus reliable cross-tab session activity tracking
|
|
- Built-in self-update + restart flow that keeps your server settings intact
|
|
|
|
## License
|
|
|
|
MIT
|