agentafk
Surfaces

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 web

By 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 sessions

Press 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:

ClassificationWhat happens
Skill command (e.g. /mint, /diagnose)Runs the skill via the structured dispatch path — same preflights and payload the REPL uses
Unknown commandReturns 422 with a did-you-mean suggestion when one exists
REPL-only commandReturns 422 explaining the command cannot run on this surface
Everything elseSent 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.

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

VariableDefaultDescription
AFK_WEB_PORT4141Port to listen on. Falls back to an ephemeral port if taken.
AFK_WEB_HOST127.0.0.1Bind 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 sessions

Fixing 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:

  1. Bearer token — every /api/* call must include Authorization: Bearer <token>. Compared in constant time.
  2. Origin validation — every non-GET request must carry an Origin header that matches this server's own origin. This is defence against CSRF: a page open in your browser on another site can POST to localhost, 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.
  3. Bind-time token requirement — a non-loopback bind is rejected at startup unless the operator explicitly provided a token, so --host 0.0.0.0 can 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

REPLWeb dashboard
InputTerminal, keyboard-drivenBrowser form
OutputStreamed markdown to terminalStreamed SSE to browser
Tool approvalsInline approval promptBrowser approval card
Slash commandsFull set including REPL-onlySkill commands only
SessionsOne per REPL processMultiple per afk web process
Background sessionsCtrl+B to backgroundVisible as read-only attached sessions
Schedule managementcreate_schedule toolSchedules view in the browser
Memorymemory_search toolMemory search view
PersistenceSession survives terminal close when resumedSessions end when the server stops
AuthNone (local process)Bearer token on every API call

On this page