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:
pip install "audittrail[daemon]"
audittrail daemon login # paste a token from Settings → Runner
audittrail daemon startlogin 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.
| State | Meaning |
|---|---|
| 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):
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/runner/allowlist | Effective allowlist + addable-template catalog |
POST | /api/v1/runner/allowlist | Allow 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:
- Server-side at dispatch.
POST /api/v1/runner/dispatchresolves your effective allowlist and rejects anything outside it before the run is ever sent down the socket. - 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, nevershell=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/tracescorrelated to the dispatch, - for a registered agent, the dispatch records the exact immutable
version that ran —
agent_definition_id,agent_version_id, and theagent_versionnumber 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.