View Markdown ↗

API and realtime transport#

The Go backend serves the embedded SPA, JSON HTTP endpoints, tus uploads, and five WebSocket families from one server.

Request path#

flowchart LR Client["Browser client"] --> Caddy["Caddy HTTPS"] Caddy --> Middleware["Auth and onboarding middleware"] Middleware --> Handler["HTTP or WebSocket handler"] Handler --> Access["Role and project access checks"] Access --> Service["Application service"] Service --> Store["File store"] Service --> Integration["LXD, Git, tmux, host filesystem"]

All /api/* and /ws* requests require a signed session for a registered user. They are also blocked until local-admin setup and at least one provider login are complete, except provider-auth endpoints needed to finish onboarding.

Authentication routes#

MethodRoutePurpose
GET/auth/meCurrent auth and onboarding status
POST/auth/local/claimCreate the local administrator or complete legacy setup
POST/auth/local/loginLocal administrator password login
GET/auth/google/loginStart Google OAuth, optionally preserving a safe return URL
GET/auth/google/callbackValidate OAuth state, authorize invited email, and issue session
GET/auth/logoutClear platform cookies and return to the app
GET/auth/verifyCaddy forward-auth check; preview hosts also check project membership
GET, PUT/api/admin/auth/googleRead or replace Google OAuth configuration; admin only

Users and settings#

MethodRoutePurpose
GET, POST/api/admin/usersList or add registered users; admin only
DELETE/api/admin/users/{email}Remove a user; admin only
PUT/api/admin/users/{email}/rolePromote or demote a user; admin only
GET, PATCH/api/me/settingsRead or update current user's appearance and chat defaults
GET/api/server/infoHost, CPU, memory, storage, network, and process snapshot

Agent authentication and skills#

MethodRoutePurpose
GET/api/{provider}/auth-statusCurrent host credential and login state
POST/api/claude/login/startStart or resume Claude authorization-code login; admin only
POST/api/claude/login/codeSubmit Claude authorization code; admin only
POST/api/claude/login/cancelCancel Claude login; admin only
POST/api/codex/login/deviceStart Codex device login; admin only
POST/api/kimi/login/deviceStart Kimi device login; admin only
GET/api/skills?provider=...&projectId=...List accessible provider and project skills

{provider} is one of claude, codex, or kimi.

Project routes#

MethodRoutePurpose
GET, POST/api/projectsList visible projects or create a project
POST/api/projects/reorderUpdate project ordering
GET, PATCH, DELETE/api/projects/{id}Read, rename, or admin-delete a project
POST/api/projects/{id}/startStart or relaunch a project
POST/api/projects/{id}/stopStop a project
POST/api/projects/{id}/restartForce restart or relaunch a project
GET/api/projects/{id}/containerDetailed container inspection
PUT/api/projects/{id}/limitsSet CPU, memory, and disk overrides; admin only
POST/api/projects/{id}/repair-networkReconfigure container networking and reinspect
GET/api/projects/{id}/appsList externally reachable container listeners
GET/api/projects/{id}/agent-browserGet Agent Browser core/view status and record activity
POST/api/projects/{id}/agent-browser/startEnsure Agent Browser is starting or ready
DELETE/api/projects/{id}/agent-browserStop the complete Agent Browser
DELETE/api/projects/{id}/agent-browser?scope=viewStop only the noVNC view
GET/api/projects/{id}/secretsList project secrets
PUT, DELETE/api/projects/{id}/secrets/{key}Set or delete one secret
GET, POST/api/projects/{id}/accessList members or add a registered email
DELETE/api/projects/{id}/access/{email}Remove a member
GET/internal/tls-ask?domain=...Caddy allow-check for on-demand project certificates

Every {id} project route first requires admin status or project membership. Resource-limit changes and project deletion add an admin-only check.

Chat routes#

MethodRoutePurpose
GET, POST/api/chatsList visible chats or create a chat
GET, PATCH, DELETE/api/chats/{id}Read, update, or delete chat metadata/history
GET/api/chats/{id}/events?limit=&before=Page persisted events backward by sequence
POST/api/chats/{id}/rewindRemove a selected prompt and later events
POST/api/chats/{id}/forkCopy metadata/history and defer provider-session fork
POST/api/chats/{id}/readMark current history read
POST/api/chats/{id}/unreadForce unread state
GET/api/chats/{id}/ide-open?path=...Validate path and redirect to the correct IDE URL
GET/api/chats/{id}/media-open?path=...Serve supported workspace media inline
GET/api/chats/{id}/files?path=...List a workspace directory
GET/api/chats/{id}/files/search?q=...Search workspace filenames
GET/api/chats/{id}/files/download?path=...Download one file
GET/api/chats/{id}/files/download-folder?path=...Stream a folder ZIP
GET/api/chats/{id}/history/reposDiscover workspace Git repositories
GET/api/chats/{id}/history/commits?repo=&limit=List commits
GET/api/chats/{id}/history/diff?repo=&sha=Read one commit patch
POST/api/chats/{id}/history/checkoutOptional checkpoint and detached checkout

All chat routes resolve the caller and enforce the chat's project membership. Loose chats have no project membership check.

Upload and auxiliary routes#

MethodRoutePurpose
POST, HEAD, PATCH, GET, DELETE/api/uploads[/<upload-id>]tus resumable upload lifecycle
GET/__remote_inspectorSame-origin preview inspection wrapper
GET, POST/api/sessionsList or create host tmux sessions
DELETE/api/sessions/{name}Delete tmux session
POST/api/sessions/{name}/sendSend text into tmux session
POST/api/sessions/{name}/uploadMultipart upload into tmux working directory

The upload access check happens when the random upload URL is created. Later chunk requests rely on possession of that URL.

WebSocket routes#

RouteDirectionMessages
/ws/workspaceServer to clientSnapshot, chat upsert/delete, project upsert/delete
/ws/chat/{id}?since=<seq>BothClient prompt or cancel; server chat events and sync
/ws/terminal?chat={id}BothPTY binary data; JSON input and resize control
/ws/{provider}/auth-statusServer to clientProvider credential and login-state snapshots
/ws?session={name}BothAuxiliary tmux PTY binary data and control messages

Realtime channels#

flowchart TD Browser["Browser"] --> WorkspaceWS["Workspace WebSocket"] Browser --> ChatWS["Active chat WebSocket"] Browser --> TerminalWS["Optional terminal WebSocket"] Browser --> AuthWS["Provider auth WebSocket while onboarding"] WorkspaceWS --> WorkspaceHub["Workspace hub"] ChatWS --> RunHub["Per-chat run hub"] TerminalWS --> PTY["lxc exec PTY"] AuthWS --> AuthService["Provider auth subscription"] WorkspaceHub --> Repositories["Repository notifications"] RunHub --> EventStore["Persisted JSONL events"] RunHub --> Prompt["Prompt start and cancel"]

Chat reconnect and replay#

sequenceDiagram participant UI participant HTTP as Events API participant WS as Chat WebSocket participant Store as Event store UI->>HTTP: Load latest event page HTTP->>Store: Read bounded page Store-->>UI: events, lastSeq, nextBefore, hasMore UI->>WS: Connect with since=lastSeq WS->>Store: Read events after sequence Store-->>WS: Missed events WS-->>UI: Replay, then sync state, then live events WS--xUI: Connection drops UI->>WS: Exponential reconnect with latest applied sequence

The workspace and chat streams send ping frames every 25 seconds. The frontend chat socket reconnects from 400 ms up to 5 seconds and requests only unseen sequences.

Chat event shapes#

EventMain fieldsPersisted
usertextYes
assistant_texttext, optional messageIdYes
thinkingtextYes
tool_use_startid, name, inputYes
tool_use_endid, output, isErrorYes
systemsubtype, dataYes
sessionprovider and provider session IDYes
completeusage payloadYes
errormessageUsually yes; lock/contention errors may be transient
syncrunningNo

Common status behavior#

  • 400: invalid IDs, paths, values, or JSON.
  • 401: missing or invalid session.
  • 403: valid user without role or project access.
  • 404: missing chat, project, user, file, or repository target.
  • 409: running-chat conflict, dirty Git state, protected last-admin/member guardrail, or duplicate user.
  • 412: onboarding gate is incomplete.
  • 413: upload or request body is too large.

Code map#

remote.futrx documentation