Documentation

Local runner daemon

v3.0.0-rc.1 — dispatch a starter agent from the dashboard to a daemon running on your own machine. v3.1.0 adds a per-user template allowlist so you control exactly which agents can be dispatched.

AuditTrail is a control plane: it orchestrates, versions, and governs agent runs, but it does not host or execute your agent code. The local runner daemon is how the dashboard's "Run locally" button reaches a machine you control. The daemon ships with the Python SDK, connects out to AuditTrail over an authenticated WebSocket, and runs only the work you allow — execution, credentials, and dependencies stay on your side.

Install and connect

Run these on the machine that should execute dispatches:

bash
pip install "audittrail[daemon]"
audittrail daemon login    # paste a token from Settings → Runner
audittrail daemon start

login writes ~/.audittrail/daemon.toml (chmod 600) with the token and API base. start opens the WebSocket and waits for dispatches. When it's connected, Settings → Runner shows a green Connected badge and the assistant's Run locally button lights up.

Tokens

Each daemon authenticates with an sk-atd-… token minted in Settings → Runner → Daemon tokens:

  • The plaintext secret is shown once, at creation. AuditTrail stores only an Argon2 hash plus a 12-char prefix.
  • Revoking a token immediately closes any daemon connected with it (WebSocket close code 4001).
  • Use one token per machine so you can revoke a single host without disturbing the others.

Template allowlist (v3.1.0)

The allowlist controls which agent templates the dashboard may dispatch to your daemon. It lives under Settings → Runner → Dispatchable templates.

StateMeaning
Default (no entries)Every built-in template (quickstart, web-search) is dispatchable. An empty allowlist never implicitly widens the surface — it is the safe default.
Restricted (one or more entries)Only the listed templates can be dispatched. Everything else is rejected at dispatch with 422 template_not_allowed.

You can only allowlist a template the daemon actually knows how to build. The server refuses to add anything else (422 template_not_allowed), which is the invariant that stops a misconfigured — or compromised — server from ever dispatching arbitrary code to your machine.

REST surface (all user-scoped; cross-tenant access returns 404):

MethodPathPurpose
GET/api/v1/runner/allowlistEffective allowlist + addable-template catalog
POST/api/v1/runner/allowlistAllow a template_id (409 if already listed)
DELETE/api/v1/runner/allowlist/{id}Remove an entry; removing the last one returns you to the default

The security model

The allowlist is enforced in two independent places, defense in depth:

  1. Server-side at dispatch. POST /api/v1/runner/dispatch resolves your effective allowlist and rejects anything outside it before the run is ever sent down the socket.
  2. Daemon-side floor. The daemon keeps its own hard-coded set of built-in templates it is willing to run and refuses anything else — even if it somehow received it. The daemon also spawns agents with asyncio.create_subprocess_exec (argv only, never shell=True) and passes your prompt as a single argument.

Together these mean that narrowing the allowlist tightens what you can launch, while the daemon floor guarantees the server can never push code the daemon doesn't already ship.

Dispatching a run

From the Operations Assistant, ask for a starter agent and click Run locally on the resulting tile (visible only when a daemon is connected). The dashboard tracks the run live:

  • stdout/stderr stream back over the socket into a 500-line ring buffer you can watch in the run-output tile,
  • the resulting trace is linked back by correlation_id, so the run shows up in /traces correlated to the dispatch,
  • for a registered agent, the dispatch records the exact immutable version that ran — agent_definition_id, agent_version_id, and the agent_version number appear on the dispatch-status and dispatch-detail responses, so a trace is always tied to the precise bundle that produced it (built-in templates leave these null),
  • Stop sends a best-effort cancel to the daemon.

If no daemon is connected, dispatch returns 404 — start the daemon and try again.