Repository navigation
Launch Profiles
A profile is a named shell recipe the broker is allowed to spawn. The
browser (and any MCP client) launches a terminal by sending a profile name —
never a command. The broker looks the name up in its allow-list and spawns the
argv it finds there. This is deliberate: a client can only start pre-approved
shells, so an untrusted page or agent can never turn /launch into
"run arbitrary command X" (the RCE-by-design boundary).
Profiles power the right-click + launch picker, the per-host default
terminal profile, and every MCP launch_terminal call.
Each profile is an object under a short name:
-
command(required) — the argv the broker runs, as a list of separate tokens (never one shell string).["bash", "-l"], not"bash -l". This is the whole point of the allow-list: no shell parses it, so there is nothing to quote-inject. -
title(optional) — the window title. Omitted → the profile name. -
cwd(optional) — a starting directory for that shell. Omitted/null→ the agent's default. (A client may also pass acwdat launch time; that is validated as an existing directory separately.) -
color(optional) — a default terminal color as#rrggbb, seeding every new terminal launched from this profile (window frame + taskbar chip). Omitted/null→ no profile color, so the terminal falls back to the per-host default color, then the automatic palette pick. A per-window recolor from the title bar still wins and sticks. Unlike the per-host default (browser- local), this lives in the broker's profile, so every browser/viewer sees it.
Two sources, in order:
-
broker_config.json→agent.profiles— the seed. Read-only at runtime; ships the out-of-the-box defaults for the host OS. -
webterm_profiles.json— the sidecar, written by the Control Panel editor, sitting next to the/statestore (override withprofiles_state_path). Atomic-write, likewebterm_mcp.json/webterm_state.json.
Sidecar-owns-once-written: the first time you save a profile from the UI, the
sidecar is created and from then on it owns the whole set — agent.profiles
becomes just the seed. So if you hand-edit broker_config.json after that and
see no change, that is why (the broker logs it loudly at startup). To go back
to the broker_config.json seed, delete webterm_profiles.json and restart.
A missing, corrupt, or empty sidecar always falls back to the seed — it can never
brick startup or leave zero launchable shells.
Control Panel → (a host tab) → Terminals → Launch profiles. You can:
- Add profile — name, optional title, optional cwd, and the command as a textarea with one argv token per line.
- Default / Edit / Delete an existing profile (the current default shows a default badge, and its Default button is disabled).
-
Detect… — one-click environment scan that seeds the Add dialog:
-
Windows → your installed WSL distros (
wsl.exe -l -q). -
Linux/macOS → the shells actually present (
bash,zsh,fish,sh).
-
Windows → your installed WSL distros (
Every save applies immediately — the live launcher swaps and the next launch
(and the + picker) uses the new set, no broker restart. A profile whose
command[0] isn't found on PATH is flagged ⚠ not found.
A broker with terminals: false (a hub started with --no-terminals)
launches nothing: POST /launch, POST /mcp/launch, POST /profiles/config
and GET /profiles/detect answer 403 terminals_disabled, and its Launch
profiles section says This broker hosts no terminals (terminals: false), so
its launch profiles cannot be edited here. GET /profiles and
GET /profiles/config still answer, so other pages can see what it offers.
A host reached from server has no settings on its tab here, so its Launch profiles section is empty and Add profile / Detect… are disabled. Edit its profiles from that broker's own page, or from a browser that adds it directly (see Hosts-and-Multi-Browser).
Editing profiles means defining commands the host will run, so the editor is
browser-realm only, gated exactly like /file/* and /state — a valid
token, on every interface, no exceptions. The endpoints:
-
GET /profiles/config/POST /profiles/config— the full objects (commands included). Browser realm only. A hub withpeersrelays these under/peer/<id>/on its browser gate, which needs both the hub's token and the peer's — the relay never opens them to an MCP caller. -
GET /profiles/detect— the environment scan. Browser realm only. - On a
terminals: falsebroker,POST /profiles/configandGET /profiles/detectanswer 403terminals_disabled(after the token check, before the body is read). -
GET /profilesandGET /mcp/profiles— names only, unchanged. An MCP/AI agent can list profile names to launch, but can never read a command or define a new profile.
Writing a profile is no more powerful than /file/write, which the same gate
already grants (both let an authenticated caller make the host run code). Since
#142 there is no tokenless broker and no loopback exemption, so a cross-origin
page — which carries no token — cannot reach these endpoints at all. That closes
the old caveat about running a tokenless broker while the same browser visits
untrusted sites.
Copy a command into the Add-profile dialog (one token per line) or into
broker_config.json's agent.profiles.
// Ubuntu (Detect… fills these in for every installed distro)
"ubuntu": { "command": ["wsl.exe", "-d", "Ubuntu", "--cd", "~", "--", "bash", "-l"] },
"debian": { "command": ["wsl.exe", "-d", "Debian", "--cd", "~", "--", "bash", "-l"] },
"kali": { "command": ["wsl.exe", "-d", "kali-linux", "--cd", "~", "--", "bash", "-l"] }Swap Ubuntu/Debian/kali-linux for the exact name from wsl.exe -l -q.
--cd ~ starts in the distro's home; drop it to inherit the broker's cwd.
"pwsh": { "command": ["pwsh", "-NoLogo"] }, // PowerShell 7+
"powershell": { "command": ["powershell.exe", "-NoLogo", "-NoProfile"] },
"cmd": { "command": ["cmd.exe"] },
"git-bash": { "command": ["C:\\Program Files\\Git\\bin\\bash.exe", "-l", "-i"] }-NoProfile skips your PowerShell profile scripts (faster, cleaner). Adjust the
Git-Bash path to your install.
"bash": { "command": ["bash", "-l"] },
"zsh": { "command": ["zsh", "-l"] },
"fish": { "command": ["fish", "-l"] },
"sh": { "command": ["sh", "-l"] }-l makes it a login shell (sources your profile / rc). Detect… proposes each
of these that is actually installed.
Nothing says command has to be a shell. A profile can launch a TUI or an agent
CLI directly, so the window is the app:
// A TUI, straight into the window — no shell in between.
"btop": { "command": ["btop"], "title": "btop" },
"lazygit": { "command": ["wsl.exe", "-d", "Ubuntu", "--cd", "~", "--", "lazygit"] },
// Needs login-shell PATH (nvm / pyenv / ~/.local/bin) before the name resolves:
// -l sources your rc files, -c runs the app, and `exec` keeps the shell out of
// the way so quitting the app ends the session instead of dropping to a prompt.
"claude": { "command": ["wsl.exe", "-d", "Ubuntu", "--cd", "~", "--",
"bash", "-lc", "exec claude"], "color": "#c96442" },
// A remote TUI: -t forces a PTY, without which curses apps refuse to start.
"htop-lab": { "command": ["ssh", "-t", "user@lab", "htop"] }A profile that launches an app behaves differently from one that launches a shell, in ways worth knowing before you wonder what broke:
- There is no prompt to come back to. When the app exits, the session exits with it and the window closes. A shell profile survives the app and leaves you a prompt; this does not.
-
The mouse is usually gone from the first frame. Most full-screen apps turn
on mouse reporting during startup, so drag-select is dead before you ever get a
chance to try it.
Shift-drag (Option-drag on macOS) forces a selection andShift+scrollstill reaches scrollback — see Keyboard Shortcuts. The Mouse mode chip mod puts a 🖱 in the title bar while that is the case. -
No shell means no rc files.
["claude"]only works ifclaudeis already on the broker's ownPATH; anything installed by a login shell needs thebash -lc "exec …"form above. -
Detect…will not propose these. It scans for shells and WSL distros, so app profiles are always hand-added.
See also: Setup-and-Onboarding (config basics) and Technical-Reference (endpoint reference, auth model).
Browserland — a web-based terminal desktop. This wiki is the end-user guide; for the wire protocol, auth model, and HTTP/MCP contract see Technical Reference. Released under the MIT License.
Start here
Building layouts
The desktop
Windows & content
Multi-host & AI
For developers