feat(cli): add --foreground flag for systemd and process manager deployments (#695)
* 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.
This commit is contained in:
@@ -134,6 +134,72 @@ OPENCHAMBER_OPENCODE_HOSTNAME=0.0.0.0 openchamber --port 3000
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>systemd service (VPN / LAN access)</summary>
|
||||
|
||||
Run OpenChamber and OpenCode as separate persistent services — useful when you want to access your
|
||||
dev machine over a VPN (e.g. Tailscale) or LAN without a Cloudflare tunnel.
|
||||
|
||||
**How it works:**
|
||||
- OpenCode runs as its own service, binding only to `localhost`.
|
||||
- OpenChamber connects to it via `OPENCODE_HOST` and `--host 0.0.0.0` makes it reachable on your VPN IP.
|
||||
- `--foreground` keeps the CLI process alive so systemd can track and restart it.
|
||||
|
||||
**`~/.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 because the agent socket isn't inherited.
|
||||
> Adjust the `PATH` to match your own tool installation paths.
|
||||
> `%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
|
||||
```
|
||||
|
||||
OpenChamber will be reachable at `http://<your-vpn-hostname>:3000` from any device on your VPN.
|
||||
|
||||
> **Note:** `--host 0.0.0.0` is required to listen on all interfaces. The default
|
||||
> bind address is `127.0.0.1` (localhost only). Use `--host <ip>` or
|
||||
> `OPENCHAMBER_HOST=<ip>` to bind to a specific interface instead.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Docker</summary>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user