Web Dashboard
afk web serves a local browser UI for live agent sessions — streaming output, schedule management, background job inspection, and slash-command dispatch.
The web dashboard is a browser-based surface for Agent AFK. It serves a local HTTP server that streams session output in real time, lets you send prompts and approve tool use from a browser tab, and exposes schedule and background-job management alongside cross-session memory search.
afk webBy default the server binds to 127.0.0.1:4141 and opens a browser tab automatically.
Common options
afk web --port 4242 # listen on a different port
afk web --host 0.0.0.0 # bind all interfaces (requires --token)
afk web --token my-secret-token # fix the bearer token across restarts
afk web --no-open # start the server without opening a browser tab
afk web --model opus # model for sessions started from the browser
afk web --bypass-permissions # skip tool-approval prompts for browser-started sessionsPress Ctrl+C to stop the server. All in-flight sessions are closed before the process exits.
What gets served
afk web is a React single-page app bundled into the package. The server hands the bundle to any browser that presents a valid credential, then acts as the API backend for that page.
Every /api/* request requires a bearer token (sent in the Authorization header, not as a query parameter). The token is minted fresh on each run and printed in the startup URL:
✔ afk web listening on http://127.0.0.1:4141
http://127.0.0.1:4141/?token=<64-hex-chars>The URL you copy and share carries ?token=. The URL the auto-open path passes to your OS launcher carries a one-time handoff nonce instead (?k=), so the real bearer token never appears in your process table or shell history.
Sessions
The sessions view lists every AFK session the server can see — both sessions it started itself (owned) and any sessions discovered on disk from other processes (attached read-only).
Owned sessions are those started by this afk web process. They are fully drivable: you can send prompts, invoke skills, approve elicitations, and interrupt a running turn from the browser.
Attached sessions are sessions from another process (a REPL, daemon, or Telegram bot) whose ledger files exist on disk. The browser can stream their output but cannot send prompts or approve tool use — those operations require an in-process elicitation handler that lives in the other process's memory.
Starting a session
Click New session to start a fresh agent session inside the afk web process. The session runs in the directory you launched the server from, using the model you specified (defaulting to afk.config.json or the AFK_MODEL env var).
Streaming output
Session output is delivered over Server-Sent Events. The browser opens a fetch-based event stream to /api/sessions/:id/stream — using fetch rather than the native EventSource API because EventSource cannot send an Authorization header, and query-string tokens land in browser history. The stream replays the ledger from the beginning on connect, then tails it live. Reconnecting after a network drop resumes from the last-seen event using Last-Event-ID.
Sending prompts
Type a message in the input box and press Enter to send it to the active session. The server returns HTTP 202 immediately and runs the turn asynchronously; output arrives over the SSE stream. If a turn is already in flight the server returns 409 — wait for it to finish or press Stop first.
Interrupting a turn
Stop sends POST /api/sessions/:id/interrupt. The session's current turn is soft-stopped; the session remains open and you can send another prompt afterward.
Tool approvals
When a tool use requires consent, the browser renders an approval card. Clicking Approve or Decline resolves the pending elicitation immediately. The pending list is updated over the same SSE stream that delivers session output.
Only tools running inside THIS afk web process can be approved from the browser. Elicitations raised by a REPL or daemon session in another process are handled there and do not appear in the web UI.
Slash commands
You can type slash commands directly into the prompt input. The server classifies the input before it reaches the model:
| Classification | What happens |
|---|---|
Skill command (e.g. /mint, /diagnose) | Runs the skill via the structured dispatch path — same preflights and payload the REPL uses |
| Unknown command | Returns 422 with a did-you-mean suggestion when one exists |
| REPL-only command | Returns 422 explaining the command cannot run on this surface |
| Everything else | Sent as plain text to the model |
Commands that drive terminal machinery — /clear, /compact, /exit, /font-size, /fork, /keys, /model, /reauth, /resume, /rewind, /sh, /theme, /thinking — are rejected by the server as REPL-only. All skill-based slash commands, including plugin skills, work normally.
Autocomplete is populated by GET /api/commands, which walks the same slash-command registry the REPL uses. On the first request the registry is loaded and memoized for the lifetime of the process.
Schedules
The schedules view reads and writes ~/.afk/config/schedules.json — the same file the create_schedule tool and afk daemon use. Changes made from the browser are live-synced to a running daemon when one is reachable.
From the browser you can:
- View all scheduled tasks and their cron expressions
- Create new schedules
- Edit an existing schedule's cron, command, enabled state, and notify settings
- Enable or disable a schedule with one click
- Delete a schedule
- View execution history for any task
Background jobs
The background jobs view reads the job log that background subagents write to disk. Each entry shows the job id, status (running / completed / failed), the prompt it was dispatched with, and timing. The view is read-only — background jobs cannot be cancelled from the browser.
Memory search
The browser can search cross-session memory via GET /api/memory/search?q=.... Results can be filtered by category (preference, convention, decision, learning) and limited in count. GET /api/memory/hot returns the most-recently-written facts — the same set the agent injects into new sessions as hot context.
Configuration
Environment variables
| Variable | Default | Description |
|---|---|---|
AFK_WEB_PORT | 4141 | Port to listen on. Falls back to an ephemeral port if taken. |
AFK_WEB_HOST | 127.0.0.1 | Bind address. A non-loopback value is refused unless AFK_WEB_TOKEN (or --token) is also set. |
AFK_WEB_TOKEN | (random per run) | Bearer token. Set this to fix the token across restarts. Setting it is also what unlocks non-loopback binds. |
CLI flags
Flags override the corresponding env vars:
afk web -p 4242 # --port
afk web -H 0.0.0.0 # --host (requires --token)
afk web --token <value> # fix bearer token
afk web -m claude-opus-4-5 # --model for browser-started sessionsFixing the token
By default the bearer token is minted fresh on each afk web run, so any saved bookmark or script that embeds the token stops working after a restart. To keep a stable URL:
# In afk.env or your shell profile:
export AFK_WEB_TOKEN=<your-chosen-token>Non-loopback binds
Binding to 0.0.0.0 or any non-loopback address is deliberately refused unless an explicit token is supplied:
# This will fail:
afk web --host 0.0.0.0
# This works:
afk web --host 0.0.0.0 --token <your-token>
# or with AFK_WEB_TOKEN set in the environment.The warning printed on startup reminds you that a non-loopback bind exposes an agent that can run tools and edit files. Prefer a tunnel (e.g. Tailscale, ngrok) over direct port exposure.
Security model
The web surface applies three independent checks on every request:
- Bearer token — every
/api/*call must includeAuthorization: Bearer <token>. Compared in constant time. - Origin validation — every non-GET request must carry an
Originheader that matches this server's own origin. This is defence against CSRF: a page open in your browser on another site can POST tolocalhost, and the bearer token would not be attached by the browser in that case, but Origin validation catches it anyway as a second line of defence. - Bind-time token requirement — a non-loopback bind is rejected at startup unless the operator explicitly provided a token, so
--host 0.0.0.0can never silently publish an agent with a token printed only to your terminal scrollback.
All responses include X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, and a Content-Security-Policy that restricts scripts to same-origin bundles.
Comparing web and REPL
| REPL | Web dashboard | |
|---|---|---|
| Input | Terminal, keyboard-driven | Browser form |
| Output | Streamed markdown to terminal | Streamed SSE to browser |
| Tool approvals | Inline approval prompt | Browser approval card |
| Slash commands | Full set including REPL-only | Skill commands only |
| Sessions | One per REPL process | Multiple per afk web process |
| Background sessions | Ctrl+B to background | Visible as read-only attached sessions |
| Schedule management | create_schedule tool | Schedules view in the browser |
| Memory | memory_search tool | Memory search view |
| Persistence | Session survives terminal close when resumed | Sessions end when the server stops |
| Auth | None (local process) | Bearer token on every API call |