Data & storage
The local DuckDB store#
One DuckDB file on your machine holds everything: events, sessions, spans, rollups and Guard state. It is the single data layer every feature reads and writes — there is no second source of truth.
Where it lives#
~/.clawmetry/ ClawMetry's own state directory
local_store.duckdb the store
local_query.json the daemon's query-server discovery file
license.key a self-hosted license, if any
cloud_plan.json the cached cloud plan
hitl/ pause flag files
reports/ Markdown you drop in for the reports browser
evals/ eval suite YAML| Variable | Effect |
|---|---|
CLAWMETRY_HOME | Move the whole state directory |
CLAWMETRY_LOCAL_STORE_PATH | Move just the store file |
One writer, many readers#
The sync daemon owns the writer lock, exclusively. Nothing else opens the file
for writing — the dashboard process marks itself CLAWMETRY_ROLE=dashboard
precisely so the store layer refuses to hand it a writer.
Readers go through the daemon's localhost query server, advertised in a discovery file:
{"port": 51843, "token": "…", "pid": 4711}A reader loads that file, checks the pid is alive, and posts to
http://127.0.0.1:<port>/api/local/query with the bearer token. The dashboard,
the MCP server and the cloud relay all do exactly this.
The pid check matters: a stale discovery file left by a crashed daemon would otherwise point every query at whatever process later took that port.
The rule this implies
A request handler must never read raw agent files. Opening
~/.openclaw/**.jsonl inside a handler works on a laptop and returns nothing in
the cloud, where the container has no agent directories. Everything reads from
the store, through the daemon.
What is in it#
| Table group | Contents |
|---|---|
events | The normalised event stream — messages, tool calls, results, errors |
sessions | One row per session with start, end, totals and status |
spans | OpenTelemetry-shaped spans, for tracing and the agent graph |
| Rollups | Per-session, per-runtime-per-day, per-model-per-day summaries |
| Guard | Session statistics, egress hosts, policy actions, decision log |
| Integrity | The tamper-evident hash chain |
The rollups are why the dashboard is fast on a large store: pages read pre-aggregated rows rather than scanning the event table.
Sizing#
Growth is proportional to agent activity. A busy multi-runtime machine produces a few hundred megabytes over months; retention is what bounds it.
clawmetry status --json | jq '.store'
curl -s localhost:8900/api/local/health | jq| Variable | Effect |
|---|---|
CLAWMETRY_LOCAL_MAX_GB | Soft cap on store size |
CLAWMETRY_AUTO_VACUUM | Automatic compaction |
CLAWMETRY_RETENTION_DAYS | Retention window, within tier limits |
Reported size versus real size
DuckDB's own estimated size counts dead row versions, so a store that reports 1.6 GB may hold a fraction of that in live data. Compaction reclaims it. Judge by the file on disk and by whether compaction has run, not by the estimate.
Writes are idempotent#
Event ids are deterministic. Re-ingesting the same source produces primary-key no-ops rather than duplicates, which is what makes a replay — after a crash, after raising a session limit, after restoring a backup — safe.
Backing it up#
Stop the daemon first, or copy rather than move. DuckDB has WAL-like behaviour and a copy taken mid-write may not be consistent.
clawmetry sync --restart # or stop the service
cp ~/.clawmetry/local_store.duckdb ~/backups/clawmetry-$(date +%F).duckdbFor something you can read elsewhere without a DuckDB dependency, export instead. → Exports
Performance#
Two things dominate, and only one of them is the store:
Memory pressure. DuckDB keeps a buffer pool, and a machine that is swapping will make queries look slow when the store is fine. Before tuning anything, check actual memory pressure on the machine — this has been misdiagnosed as a store problem more than once.
Store size versus retention. A store holding a year of events on a laptop will be slower than one holding a month. If you do not need the history, do not keep it.
| Variable | Effect |
|---|---|
CLAWMETRY_DUCKDB_MEMORY_LIMIT | DuckDB's memory limit |
CLAWMETRY_DUCKDB_THREADS | Thread count |
CLAWMETRY_LOCAL_FLUSH_SECS | Flush cadence |
CLAWMETRY_LOCAL_FLUSH_BATCH | Flush batch size |
If the store will not open#
clawmetry verify-integrity --jsonA store that reports as invalidated after an abrupt kill usually needs compaction rather than deletion. → Troubleshooting