source
Ardur Personal Hub HTTP API
The Hub is the local service started by `ardur hub`. It accepts evidence
The Hub is the local service started by ardur hub. It accepts evidence
from the browser extension, desktop observer, native-messaging host, and CLI
adapter, and signs that evidence into the same Execution Receipt chain as the
protocol path. This page is the API reference; for setup see
../guides/ardur-personal-hub.md
.
Source: python/vibap/personal_hub.py
.
Bind Address
Defaults: 127.0.0.1:8765. Loopback only — the Hub is not designed to be
exposed beyond the local machine. CORS is restricted to
chrome-extension://, moz-extension://, and loopback HTTP origins.
Authentication
Every endpoint except GET /health requires the Hub token written by
ardur setup. Provide it one of three ways:
| Where | How |
|---|---|
| Header (preferred) | X-Ardur-Hub-Token: <token> |
| Header (alternate) | Authorization: Bearer <token> |
Query (only for GET / and GET /dashboard) | ?token=<token> |
The token is compared with constant-time secrets.compare_digest. Missing or
incorrect tokens return:
HTTP/1.1 401 Unauthorized
{
"ok": false,
"error": "Ardur Personal Hub token required",
"error_code": "hub_auth_required"
}
Endpoints
GET /health and GET /healthz
Unauthenticated liveness check.
{
"ok": true,
"schema_version": "<hub-schema>",
"version": "<package-version>"
}
GET /, GET /dashboard
HTML dashboard summarising the latest session reviews. Authentication
allowed via header or ?token=. Response is text/html with strict CSP
(default-src 'none', no JS).
GET /v1/status
Returns Hub state suitable for ardur status:
{
"ok": true,
"schema_version": "...",
"version": "...",
"home": "/Users/.../.vibap",
"verifier_id": "...",
"hub_url": "http://127.0.0.1:8765",
"sessions": 0,
"session_reviews": 0,
"latest_receipt": { ... } | null,
"public_key_pem": "-----BEGIN PUBLIC KEY----- ...",
"adapters": {
"browser": "available",
"desktop": "available_with_macos_permissions",
"cli": "available"
}
}
GET /v1/export
Returns the full evidence export — sessions, session reviews, latest
receipts. Used by ardur status --export and the dashboard view.
POST /v1/sessions/start
Idempotent. Creates or returns a Hub-managed session bound to a Mission
Passport. The Hub issues a passport scoped to local-observation tooling
(browser_observe, desktop_observe, cli_command, cli_observe) when the
caller does not supply one.
Request:
{
"source": { "type": "browser" | "desktop" | "cli", ... },
"session": { "title": "..." },
"mission": {
"agent_id": "...",
"mission": "...",
"allowed_tools": [...],
"forbidden_tools": [...],
"resource_scope": [...],
"max_tool_calls": 5000,
"max_duration_s": 86400,
"allowed_side_effect_classes": [
"none", "internal_write", "external_send", "state_change"
]
}
}
Response:
{
"ok": true,
"ardur_session_id": "<jti>",
"agent_id": "...",
"mission": "...",
"source": { ... },
"title": "...",
"started_at": "<iso8601>",
"token": "<JWT>",
"existing": false
}
existing: true is returned if a session with the same (source, session)
key already exists.
POST /v1/events/observe
The main evidence-ingestion endpoint. Implicitly calls start_session if
needed, evaluates the policy, runs the governance proxy, and appends a
receipt to the chain.
Request schema invariants:
source.typeMUST be one ofbrowser,desktop,cli.event.content_digest, when set, MUST matchsha-256:<hex>.raw_content_included: trueis rejected; send digests and consented excerpts only.- A text snapshot in
eventrequiresevent.consent.visible_text == true.
Response includes the policy decision (compliant, violation,
insufficient_evidence), the chained receipt id, and an updated session
review summary.
POST /v1/policy/check
Evaluate the policy for a candidate event without recording a receipt.
Request mirrors observe. Response:
{
"ok": true,
"policy": {
"decision": "compliant" | "violation" | "insufficient_evidence",
"reason": "...",
"tool_name": "...",
"arguments": { ... }
}
}
POST /v1/sessions/{ardur_session_id}/attest
Issue a behavioral attestation summarising the receipt chain for an existing
session. Used by ardur attest and by browser/desktop adapters that want to
end a session cleanly.
Response is the attestation envelope (signed JWT + decoded claims).
Error Format
Every non-success response uses the same shape:
{
"ok": false,
"error": "<human-readable>",
"error_code": "<stable-code>"
}
Stable error codes used today:
| Code | Status | Meaning |
|---|---|---|
hub_auth_required | 401 | Token missing or incorrect |
body_too_large | 413 | Body exceeded MAX_BODY_BYTES |
state_corrupt | 500 | A persisted JSON file failed to parse |
hub_unavailable | — | Used by client helpers when the Hub does not respond |
internal_error | 500 | Catch-all server boundary |
Validation failures from observe use the error field with HTTP 400 and
no specific code (the server returns the parser’s exception message).
Security Notes
- The Hub token is generated locally by
ardur setupand stored under the Ardur home directory. Treat it as a local secret. - The dashboard uses a strict Content-Security-Policy and serves no JS, so
the
?token=query mode is acceptable for browser bookmarking. - CORS is intentionally restricted to extension origins and loopback
origins. Other origins are denied with no
Access-Control-Allow-Originheader. - The Hub does not control hidden provider-side behavior. It governs only
what local adapters expose to it. See
../known-limitations.md.