GUI index | Getting started | Operations
This page is for operators preparing the static GUI and the optional
gui_server binary. The GUI Cargo feature is not a default feature.
From a new checkout, the setup script can build the export and server, create an independent private extension root, write a config example only when none exists, and create a 0600 Trial token file without printing its value:
./scripts/setup.sh --gui \
--base-path /proxy/commandagent/ \
--extension-root /srv/commandagent/extensions \
--write-config \
--gui-token-file "$HOME/.config/commandagent/gui-token"The final summary prints separate preflight and start commands. Run preflight
first. gui_server --check does not bind a port; it reports ok or ng for
the static export/base path, static GUI / server contract version,
pairwise-disjoint roots, commandagent --version, and token/Origin settings.
Green exits 0, ng exits 1, malformed arguments exit 2, and --check --json
emits the same result as JSON.
The GUI delegates a run to the commandagent CLI and always passes --sandbox required. If the OS isolation cannot be applied, the run still starts, but every Bash
and verifier command is refused with bash_sandbox_unavailable: <reason>. The
run cannot execute commands or verify its work and fails: the GUI never runs a
model's commands without isolation and never does so silently (Issue #502,
decision 1).
This happens where no backend is available, for example inside a nested sandbox
(a sandboxed container or an IDE that already uses Seatbelt), or on a Linux
kernel without Landlock or with it disabled.
commandagent --doctor does not probe the OS sandbox, so it cannot tell you
this in advance. Read the run's bash_sandbox_status event: applied=false
plus reason says the isolation is not in force and why. You can also start a
CLI run outside the GUI, in a scratch workspace, with --sandbox required; for
a run that uses Bash, its commands either run or are refused with
bash_sandbox_unavailable and the reason (a run that never calls Bash cannot
tell you). Fix the
environment (run outside the nested sandbox, or use a kernel with Landlock
enabled) rather than looking for a GUI switch that turns the isolation off;
there is none.
The static export and gui_server carry the same GUI contract marker. Rebuild
both from one checkout whenever either side changes; the server build must keep
the non-default gui feature enabled:
cd gui
GUI_BASE_PATH=/ npm ci
GUI_BASE_PATH=/ npm run build
cd ..
cargo build --bin commandagent
cargo build --features gui --bin gui_server
target/debug/gui_server --check \
--base-path / \
--static-dir gui/out \
--repository-root . \
--commandagent-bin target/debug/commandagentUse the deployment prefix instead of / for both GUI_BASE_PATH and
--base-path. A mismatch makes static.contract_version fail in --check.
If the server is started without preflight, the browser shows
「GUI と gui_server の版が一致していません」 when runtime-status reports a
missing or different marker. Rebuild both artifacts, rerun --check, and only
then restart the server; clearing browser cache is not a substitute for a
matched rebuild.
An existing .commandagent/config.toml or token file is never overwritten.
The setup script displays a proposed config diff and leaves existing files
unchanged.
| Root | Purpose | Write owner |
|---|---|---|
| repository root | Read-only documentation, run reports, contracts, packs, and measurement projections | normal repository workflows, never GUI Trial |
| execution root | Trusted container for isolated sessions/<session-id>/ CLI workspaces and central .commandagent/runs/<session-id>/ GUI run records |
confirmed GUI Trial delegation only |
| extension root | Private packs/, profiles/, pins, retirement markers, and journal.jsonl |
SupplyRoot lifecycle API only |
All three must be pairwise disjoint, including canonicalized symlink aliases. The extension root must not be the Trial execution root.
By default, each confirmed Trial runs with
<execution-root>/sessions/<session-id>/ as both its process working directory
and CLI --cwd. The Trial form can instead select an existing directory below
the execution root by relative path. Gate 1 binds that canonical directory,
and confirmed additional requests and history reconnects reuse it. Absolute
paths, traversal, symlinks, runtime directories, missing directories, and
paths outside the execution root are rejected.
Generated source plus the selected workspace's .commandagent/plans,
evidence, and repairs stay below that working directory. Automatically
generated completion contracts follow the same boundary; these products are
never shared at the execution-root top level.
Confirmations, events.jsonl, summary.md, and directive state remain in the
central <execution-root>/.commandagent/runs/<session-id>/ record used by the
GUI APIs. The legacy .anvil/runs/<session-id>/ location remains read-only
compatibility input for older records.
cd gui
GUI_BASE_PATH=/ npm run build
cd ..
cargo run --features gui --bin gui_server -- \
--port 4173 \
--base-path / \
--static-dir gui/out \
--repository-root . \
--execution-root /path/to/trial-workspace \
--extension-root /path/to/commandagent-extensions \
--trial-token-auth off \
--commandagent-bin target/release/commandagentOpen http://127.0.0.1:4173/. The server binds only to 127.0.0.1. With
authentication off, the token field is hidden, but every POST still requires a
same-host or allowlisted Origin. Read-only recovery documents are also
available without a bearer token in this mode. Use this only for a trusted
loopback session.
If --execution-root is omitted, read-only dashboards remain available and
all Trial APIs fail closed.
The build path and server path must name the same prefix. The build accepts a trailing slash; the server uses the canonical form without it:
cd gui
GUI_BASE_PATH=/proxy/commandagent/ npm run build
cd ..
export GUI_TRIAL_TOKEN="$(openssl rand -hex 32)"
GUI_TRIAL_ALLOWED_ORIGINS='https://admin.example.com' \
cargo run --features gui --bin gui_server -- \
--port 4173 \
--base-path /proxy/commandagent \
--static-dir gui/out \
--repository-root . \
--execution-root /path/to/trial-workspace \
--extension-root /path/to/commandagent-extensions \
--trial-token-auth on \
--commandagent-bin target/release/commandagentThe proxy must preserve the prefix:
location /proxy/commandagent/ {
proxy_pass http://127.0.0.1:4173;
}Do not derive origin or prefix from X-Forwarded-*. Set both explicitly.
CommandMate is external orchestration and is not the GUI server. Keep the three hops distinct:
| Layer | Example public/path value | Destination |
|---|---|---|
| browser / Cloudflare Access | https://admin.example.com/proxy/commandagent/ |
authenticated tunnel route |
| tunnel or reverse proxy | preserve /proxy/commandagent/ |
http://127.0.0.1:4173/proxy/commandagent/ |
gui_server |
--base-path /proxy/commandagent |
static export built with GUI_BASE_PATH=/proxy/commandagent/ |
Require an administrator access policy at Cloudflare or the chosen tunnel. A valid GUI Trial token does not replace upstream authentication. CommandMate may start or supervise a local process only through an explicitly authorized operator workflow; it does not change base-path, Origin, token, or root isolation contracts.
See operations for token and Origin policy and Two-basePath smoke for verification.