--- title: The local DuckDB store description: Where ClawMetry keeps your data — the DuckDB file, the writer-lock model, the localhost query server, sizing and compaction, and how to back it up safely. keywords: DuckDB agent telemetry, local agent database, clawmetry store, agent data storage eyebrow: 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 ```text ~/.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: ```json title="~/.clawmetry/local_query.json" {"port": 51843, "token": "…", "pid": 4711} ``` A reader loads that file, checks the pid is alive, and posts to `http://127.0.0.1:/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. :::warning 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](/docs/data/retention/) is what bounds it. ```bash 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 | :::note 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. ```bash clawmetry sync --restart # or stop the service cp ~/.clawmetry/local_store.duckdb ~/backups/clawmetry-$(date +%F).duckdb ``` For something you can read elsewhere without a DuckDB dependency, export instead. → [Exports](/docs/data/export/) ## 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 ```bash clawmetry verify-integrity --json ``` A store that reports as invalidated after an abrupt kill usually needs compaction rather than deletion. → [Troubleshooting](/docs/ops/troubleshooting/)