Terminal
An interactive shell on a server, through the browser — authenticated by ticket, capped by profile, bounded by grant.
Fleet → Terminal gives an interactive shell on a server without SSH, a bastion, or an inbound port.
Requirements
Three things, all of which must hold:
Any one missing and the button does nothing useful — the page says which.
Authentication
A browser cannot set an Authorization header on a websocket. So the terminal
uses a single-use ticket, minted by an ordinary authenticated
POST /api/v1/agents/{id}/shell/ticket.
The ticket is what reaches the URL — not a JWT, which would then sit in nginx logs, Cloudflare logs and browser history, valid for hours.
The HTTP fallback
The websocket depends on Connection and Upgrade surviving every proxy hop,
and those are hop-by-hop headers that any hop may drop.
There is therefore a second transport: SSE out, POST in, over ordinary HTTP — the same primitives the rest of the application already runs on. It is ticket-authenticated in exactly the same way.
If the terminal works on the fallback and not on the socket, a proxy is
stripping the upgrade. /api/v1/agent/ws-check will confirm it.
As whom
A session runs as the OS identity on the grant — root or a named user. It is
resolved by the agent and capped by the machine, so a grant naming an
identity the machine will not accept is refused there rather than honoured.
Sessions die with the grant
A grant that expires ends the session. Not at the next reconnect — immediately.
A shell that outlives its authorisation is the whole problem this model exists to prevent. Take a fresh grant rather than expecting an open window to keep working.
Recording
Sessions are recorded and audited: who, which server, which identity, when, and what was run.
Local secrets are redacted unconditionally — including in the transcript.
An agent credential does not become readable because somebody cat-ed a file
in a recorded session.
When you should not use it
The terminal is for investigation. For anything you do more than twice, use a
declared command: it is reviewable, repeatable, works across
many servers at once, and does not need full.
Where this behaviour lives: backend/internal/handler/agent_handler.go, frontend/src/app/agents/terminal/. If the code and this page disagree, the code is right — please fix the page.
Part of Fleet — The agent on each server: what it collects and what it may be asked to do.