HTTP API
Authentication#
Five surfaces, five different answers, because they carry different risk.
The dashboard API#
Default: loopback, no token. The dashboard binds to 127.0.0.1 and anything
that can reach it is already on the machine.
To require a token:
export CLAWMETRY_API_TOKENS=<token-a>,<token-b>TOKEN=<token-a>
curl -s -H "Authorization: Bearer $TOKEN" localhost:8900/api/local/sessionsComma-separated so you can rotate without downtime: add the new token, move clients over, remove the old one.
Set this whenever anything other than your own browser calls the API, and always when the dashboard is reachable beyond loopback.
The daemon's query server#
Always authenticated, with a token generated at startup and written to a discovery file:
{"port": 51843, "token": "…", "pid": 4711}PORT=$(jq -r .port ~/.clawmetry/local_query.json)
TOK=$(jq -r .token ~/.clawmetry/local_query.json)
curl -s "http://127.0.0.1:$PORT/api/local/query" \
-H "Authorization: Bearer $TOK" \
-H 'content-type: application/json' \
-d '{"shape":"sessions","args":{"limit":5}}'The file is readable only by your user, and the token changes when the daemon restarts. Read it at call time rather than caching the value.
Consumers should also check the pid is alive before trusting the port — a stale file from a crashed daemon would otherwise point at whatever process later took it. The MCP server does exactly this.
The custom-runtime ingest API#
Two modes:
- Loopback only (default). With
CLAWMETRY_INGEST_TOKENunset, only
loopback requests are accepted.
- Token header. Set it, and clients send
X-ClawMetry-Token: <secret>. The
comparison is constant-time.
export CLAWMETRY_INGEST_TOKEN=secret
curl -s localhost:8900/api/v1/runs \
-H "X-ClawMetry-Token: secret" \
-H 'content-type: application/json' \
-d '{"runtime":"my_engine"}'A non-loopback request without a matching token returns 401.
Note the different header. Ingest uses X-ClawMetry-Token and read uses
Authorization: Bearer — they are separate credentials with separate blast
radii, and a write token should not be able to read your transcripts.
OTLP#
The receivers accept untrusted data that lands in your store, so they are authenticated like any other write surface. For a local-only setup:
CLAWMETRY_OTLP_ALLOW_UNAUTH=1 clawmetryDo not set that on anything with a routable address.
Control endpoints#
Pause, stop and kill are origin-checked rather than token-gated. They signal real processes, and the check exists to stop a cross-origin request from a page you happened to have open.
They are not entitlement-gated: you pressed the button.
If you put a reverse proxy in front, forward the origin correctly or those requests are refused — and that refusal is the correct behaviour.
Self-hosted node ingest#
Nodes authenticate to a self-hosted endpoint with node tokens:
export CLAWMETRY_API_TOKENS=node_a,node_bCloud#
Node-to-cloud uses the API key from clawmetry connect, held in
~/.clawmetry/. It authenticates the node; it does not decrypt anything —
session content is encrypted with a separate key that never leaves the machine.
clawmetry status --show-key # prints a credential to your terminalSummary#
| Surface | Credential | Default |
|---|---|---|
| Dashboard API | Authorization: Bearer from CLAWMETRY_API_TOKENS | Loopback, none |
| Daemon query server | Bearer token from local_query.json | Always required |
| Custom ingest | X-ClawMetry-Token from CLAWMETRY_INGEST_TOKEN | Loopback only |
| OTLP | Same as the dashboard API | Required unless explicitly allowed |
| Control endpoints | Origin check | Always |
| Node to cloud | API key from connect | — |
What none of these are#
None of this is user authentication. There are no accounts, roles or per-user permissions on the local dashboard — a token holder can do everything a token holder can do.
If you need per-user access control, put an authenticating proxy in front and manage identity there. RBAC and SSO exist as Enterprise features on the cloud side. → Ports and networking · Plans