Skip to content

Dashboard

vole serve starts the control-plane dashboard — one web server that manages all of your OpenVole agents from a single place in the browser. This is the primary way to run and operate OpenVole.

bash
vole serve

By default it listens on http://localhost:3000. Set VOLE_DASHBOARD_PORT to use a different port.

NOTE

The control-plane dashboard replaces the old "one dashboard per project" model. The legacy @openvole/paw-dashboard is deprecated in its favor — see Infrastructure Paws. The single-engine vole init / vole start / vole run commands have been removed — OpenVole now runs as a server, and every agent is fully isolated.

Agents

An agent is fully isolated — its own vole.config.json, paws, identity files, and data directory. Each running agent is its own engine subprocess, parented to the vole serve process (not detached, so stopping vole serve stops them too).

One vole serve process manages every agent under a single OpenVole root directory. Agents are recorded in an agents.json registry at the root. Agents granted orchestrator authority show an orchestrator badge on their card.

Root Resolution

When you run vole serve, the OpenVole root is resolved in this order:

  1. VOLE_HOME — if set, this explicit override always wins.
  2. The current directory, if it is already a root — it contains an agents.json registry file.
  3. The current directory, if it is empty — ignoring incidental files (.DS_Store, .git, .gitignore). It then becomes a new root.
  4. Otherwise it refuses to start with a clear error. If a legacy ~/.openvole with agents exists, it tells you how to reach it.

On startup it logs the resolved root (with (new) if freshly created) and the tokenized dashboard URL:

OpenVole root: /Users/me/agents  (new)
Manage your agents at http://localhost:3000/?token=3f9c2a…

IMPORTANT

The dashboard is gated by a session token, generated on first run and persisted at <root>/.openvole/dashboard-token (or set VOLE_DASHBOARD_TOKEN). Open the printed URL — the token is required to reach the dashboard. It binds all interfaces by default; set VOLE_DASHBOARD_HOST=127.0.0.1 for localhost-only, and never expose the port raw on a public network. See Security › Dashboard / Control Plane.

TIP

Pick a directory to be your OpenVole root and run vole serve there — for example mkdir ~/agents && cd ~/agents && vole serve. To always serve a fixed root regardless of where you are, set VOLE_HOME:

bash
VOLE_HOME=~/agents vole serve

WARNING

The old behavior — an implicit global ~/.openvole regardless of the current directory — is gone. If your existing agents live at ~/.openvole, reach them with cd ~/.openvole && vole serve or VOLE_HOME=~/.openvole vole serve. The error message prints these for you.

The Dashboard

The header has an agent switcher to create, start, stop, switch between, and delete agents. Each agent has five tabs:

TabWhat it shows
OverviewPaws (with health), tools, skills, tasks, schedules, and a live event log.
ChatTalk to the agent — multiple chat sessions per channel, rendered as markdown.
AppsEmbedded paw panels (see below). Always visible.
ConfigStructured form editor for the entire vole.config.json (see below).
IdentityEdit SOUL.md, USER.md, AGENT.md, HEARTBEAT.md, and BRAIN.md.

Creating an Agent

Click New agent in the header to open the new-agent form. Enter a name, and on successful create an onboarding step suggests the essential paws, pre-checked:

  • @openvole/paw-brain
  • @openvole/paw-session
  • @openvole/paw-memory
  • @openvole/paw-compact
  • @openvole/paw-shell

Whichever you keep selected are installed into the new agent. (You can install more from the Config tab afterwards.)

The CLI equivalent is vole agent create <name> — see CLI Commands.

Renaming an Agent

Each agent card has a Rename button. It changes the display name only — the agent's id stays exactly as it was, and that is deliberate: the id is its folder on disk, VOLE_AGENT_ID inside the running engine, its MCP endpoint (/mcp/<id>), and the key the dashboard files its chat history and unread counts under. A rename therefore needs no restart and a running agent is unaffected.

Names must be unique across the server (compared case-insensitively against both names and ids), because an agent can be addressed by either — agent_submit, the CLI, and the control plane all accept a name where they accept an id.

The CLI equivalent is vole agent rename <name> <new name>.

Deleting an Agent

Deleting an agent from the dashboard permanently deletes its directory on disk — config, identity, installed paws, and data — after a destructive confirmation.

WARNING

Deletion from the dashboard is equivalent to vole agent remove <name> --purge. The CLI's vole agent remove <name> without --purge removes the agent from the registry but keeps its files on disk.

Behind a reverse proxy

The dashboard binds 127.0.0.1 when you set VOLE_DASHBOARD_HOST=127.0.0.1 — the right default for a server. To reach it from a browser over TLS, put a reverse proxy in front. It is WebSocket-driven and enforces a same-origin check, so two headers are mandatory:

nginx
server {
    listen 443 ssl;
    server_name agents.example.com;
    ssl_certificate     /etc/letsencrypt/live/agents.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/agents.example.com/privkey.pem;

    # Only you — the dashboard can start, stop, and delete agents.
    allow 203.0.113.7;
    deny all;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;   # required — WebSocket upgrade
        proxy_set_header Connection "upgrade";
        proxy_set_header Host       $host;           # required — the WS same-origin check
        proxy_read_timeout 3600s;                    # long-lived connection
    }
}

WARNING

Anyone with the tokenized URL has full control of every agent on that server — including deleting them. Restrict by IP (as above), a VPN, or an auth layer in front. Rotate the token by deleting <root>/.openvole/dashboard-token and restarting. An SSH tunnel (ssh -L 3000:127.0.0.1:3000 user@host) needs no proxy at all and exposes nothing.

Requires openvole ≥ 4.8.1 — earlier versions hardcoded an insecure ws:// URL that browsers block on an HTTPS page.

Live Events — and the daily log

The Overview tab's Live Events feed opens on today and follows it: it loads the last 300 of today's events for the selected agent, then new ones appear on top as they happen. Three things to know:

  • Click a line to expand it. The collapsed row is a preview; expanding shows the whole payload, pretty-printed and selectable. Nothing is dropped on the way to the UI, so a task result or an error is readable in full.
  • Everything is also written to disk. The control plane appends every event to <root>/.openvole/logs/events-YYYY-MM-DD.jsonl — one JSON object per line, payloads whole, a new file each local day. The in-page feed holds the last 500 lines; the file holds the day.
  • The feed is scoped to the selected agent, like the rest of the tab. The file is not — it covers every agent, with an agentId on each line.

The dropdown offers Live (today) plus earlier days. Today is never listed as history: it is the file being written to right now, and Live is how you read it. Pick an earlier day to read it back (newest first, bounded to the newest 2000 entries — the note tells you when older ones exist), and hit raw for the untouched file of whichever day you are on:

GET /events.jsonl?day=2026-07-26&token=<token>

Each line looks like:

json
{"ts":1785079309840,"time":"2026-07-26 18:24:31.425","agentId":"research-agent","event":"task:completed","data":{"taskId":"262194d2…","sessionId":"dashboard"}}

so it greps and pipes: jq -r 'select(.event=="task:failed") | .data.error' events-2026-07-26.jsonl.

Logs older than 30 days are pruned when the file rotates. Set VOLE_EVENT_LOG_DAYS to change that, or 0 to keep everything.

Chat — including messages the agent starts

The Chat tab is a channel, not just a reply box. The agent can message you first with the built-in chat_send tool — a question from a heartbeat run, a blocker from an overnight job — and it lands in the chat with an unread badge on the tab and on the agent's card, plus a toast.

Unread counts survive a reload and a closed browser: the dashboard keeps a per-chat read watermark and recounts from the agent's session transcripts on reconnect, so a message sent at 3am is still marked unread when you open the page.

Projects Tab

Projects on the left, and for the selected one three sub-tabs:

  • Board — tasks grouped by state with their done-criteria, and buttons for only the moves that are legal from where each task is
  • Chat — a conversation in that project's own context, so you describe what you want and the agent works out the tasks
  • Files — a browser and editor for the project's files, covering both its workspace folder and the root it is attached to

New creates a project, with a directory picker that walks the filesystem of the machine the agent runs on and offers to grant a path that is not yet in security.allowedPaths.

This tab is served from the agent's files rather than over IPC, so it works while the agent is stopped — which is when queueing work up is most useful. See Projects & Tasks for the model behind it.

Config Tab

The Config tab is a fully structured form — no raw-JSON textareas. Edit every section of vole.config.json with typed fields:

  • brain — dropdown of installed brain paws
  • loop — iterations, compaction, tool horizon, context budget, cost tracking
  • heartbeat — enabled, interval, run-on-start
  • security — sandbox toggle and per-paw filesystem paths
  • paw permissions — each paw shows the permissions its manifest requests (network, env, filesystem, child processes) next to what the agent's config actually grants, with toggles to allow or revoke; writes go through the sandbox-weakening guard
  • docker sandbox — image, limits, network mode
  • rate limits — LLM/tool/task throttles
  • tool profiles — per-source allow/deny lists
  • SUB-AGENTS — named sub-agent profiles for spawn_agent (role, instructions, allowTools, denyTools, maxIterations) — config key agents; not to be confused with the server's agents
  • NET (VoleNet) — fully structured with an on/off toggle for enabled, plus peers, share (tools/memory/session), TLS, routing, and the various modes

See Configuration for what each field means.

Apps — Embedded Paw Panels

Any paw can contribute its own UI to the dashboard. The Apps tab is always visible and shows one entry per panel-contributing paw in a left vertical nav, each rendered as a sandboxed iframe. If an agent has no panel paws, an empty state explains how to add one.

Panels are brain-free — a panel's tool calls go straight to the paw over IPC, with no LLM — and there are no per-paw web servers and no extra ports; everything flows through the single control-plane server.

To add a panel to your own paw, see Build an Embedded App.

Reference example

@openvole/paw-markets is a US-stock tracking paw whose Markets panel embeds this way. Install it into an agent from the Config tab and start the agent — its panel appears under the Apps tab.

Tools over MCP

The control plane exposes each running agent's own tools over the Model Context Protocol at POST /mcp/<agent>, so an MCP client can drive an agent's tools directly:

  • tools/list enumerates the agent's registered tools; tools/call runs one through the agent's normal tool path (the same execution the brain uses).
  • It's a stateless, streamable-HTTP MCP server built on the official @modelcontextprotocol/sdk — no extra ports, no per-paw servers.
  • Token-gated: requests must carry the dashboard session token in the x-vole-token header.

The main consumer is the Claude Code brain: with CLAUDE_CODE_EXPOSE_TOOLS=1, Claude Code calls OpenVole's own tools as mcp__openvole__<tool> through this endpoint. The engine injects VOLE_DASHBOARD_URL, VOLE_AGENT_ID, and the token into each agent, so it wires up automatically under vole serve.

VoleNet Tab

The VoleNet tab is for talking to the humans behind your connected peer nodes. It has two parts:

  • Peer list — every connected peer node, refreshed live (about every 5 seconds) with an online dot so dead peers drop off and new ones appear as they connect.
  • Per-peer chat — pick a peer and chat with the person operating that node. Replies are answered by a human, not the brain, so there is no LLM cost. Each conversation is persisted in its own volenet:<peerId> paw-session (with an in-memory fallback), so transcripts survive restarts.

This is the human-to-human messaging mode. Cross-node chat where the peer's brain replies automatically is a separate, brain-callable tool — the net_message tool — described under VoleNet.

Connecting nodes from the dashboard

The peer list's + Connect button initiates connections without touching a terminal:

  • Pair (a node you operate): enter its URL → the dashboard fetches and shows its fingerprint → confirm → the node is trusted, added to net.peers, and a pair request is filed for the other side's operator to accept (their VoleNet tab shows it under Pair requests). Everything takes effect live — no restarts.
  • Join (public hub): the vole net join flow in-process — registers with the hub, trusts its key, adds the peer entry, connects.

File attachments (VoleDrop)

The 📎 button in the chat composer stages a file (shown as a removable chip); Send ships it, with any typed text riding along as the offer's note — so a file and its message travel together. The browser streams the file to the serve host, which offers it to the peer over the mesh, end-to-end encrypted (details); the peer's operator sees a file bubble (name, size, note) with Accept / Decline (unless their net.files.acceptFrom auto-accepts you). Progress, delivery, and failure states update live in the bubble — and pending offers reappear with their buttons when the chat is reopened later (until net.files.offerTtlMinutes expires them). Accepted files land sha256-verified in the peer's net.files.inboxDir.

Unread badges. Unread chat messages and file offers are counted per peer (badge on the peer row), per agent (badge on the VoleNet tab button and on each agent card — so a message to a non-selected agent still lights up), and survive agent switches and page reloads. Opening a peer's chat clears its count.

Unread badges. A brain reply that lands while you're on another tab or agent raises a badge on the Chat tab and on that agent's card, with a toast naming the agent (and session). Counts are per session and clear when you open that chat. They also survive the browser being closed: the dashboard remembers where you left each chat and recounts from the agent's task list when it reconnects, so an answer that arrived overnight is waiting for you. (The recount covers the selected agent's recent tasks; other agents accumulate while the page is open.)

Config-tab note: the net.encrypt and net.publishNames toggles and net.share.toolAllow previously did not survive a dashboard config save (silently reverted/dropped) — fixed in 4.13.0.