Skip to content

Persistence

AgentMux persists state across five files, owned by two processes (sidecar + launcher). This page enumerates them and explains the boundary.

FileFormatWriterOwns
objects.dbSQLitesidecarWorkspaces, tabs, blocks, windows, layouts, controllers + the agent-definition / identity / memory / drone catalog — the app-domain reducer state of record
filestore.dbSQLitesidecarPer-block content blobs (agent configs, snapshots, file-pane content)
sagas.dbSQLitesidecarSaga step + lifecycle rows for the srv saga coordinator (crash-resume)
launcher-sagas.dbSQLitelauncherLauncher-side saga state (LSD-1 batch onward)
launcher-events.logJSONLlauncherAppend-only Layer 1 reducer event log (durable, OS-level facts)

All five live under the version-scoped data dir at <data-dir>/data/. For installed/portable builds the data dir is version-scoped — ~/.agentmux/channels/<channel>/versions/<version>/data/ — so two concurrent release versions never share SQLite DBs or caches. Dev builds use ~/.agentmux/dev/<branch>/data/. SQLite files are inside data/db/; the JSONL log is directly in data/. See Data layout for how <channel> and <version> are derived.

Not everything is version-scoped. Alongside data/, the version dir also holds logs/, cef-cache/, and runtime/ (ipc-port, lock). But config/ (settings, keybindings) and agents/ (agent working dirs) live one level up, at the channel-wide root ~/.agentmux/channels/<channel>/ — so settings and agent working dirs survive version upgrades. Path resolution is in data_paths.rs.

Resolution is centralized in agentmux-common::DataPaths — the launcher resolves once and exports AGENTMUX_DATA_DIR; host + sidecar read it from env.

Owned by agentmux-srv. Opened via WaveStore::open(<data-dir>/db/objects.db) in agentmux-srv/src/main.rs. The historical type name WaveStore predates the AgentMux rebrand — the file itself is objects.db.

Stores every:

  • Workspace — top-level container, identity + window membership
  • Tab — per-window tab list with layout root pointer
  • Block — every pane (terminal, browser, agent, file, system metrics, …) including its meta map (controller type, agent id, URL, scroll position, …)
  • Window — OS-window record; the host reads this on launch to restore window geometry
  • Layout — per-tab pane tree (split orientation, focus path, sizes)
  • Client — per-CEF-session client record
  • Controller — block controller registration (shell, command runner, web view)

It also holds the agent domain: agent definitions (the launchable agent catalog — formerly “Forge”), identity and memory bundles, per-launch agent instances, and drone definitions/runs.

The schema is a single flat table set built by run_object_schema (agentmux-srv/src/backend/storage/migrations.rs) — no migration ladder. A PRAGMA user_version stamp acts as a downgrade tripwire.

This is where “I closed the app and reopened it; my tabs are back” comes from. If you delete objects.db, the next launch comes up with a single empty workspace.

At startup the sidecar’s reducer loads its in-memory state from objects.db (bootstrap_state_from_wstore). During the session, HTTP/WS RPC writes still go directly to SQLite via wcore — SQLite stays authoritative even if the reducer’s session-only projection diverges.

A separate persist subscriber (agentmux-srv/src/persist_subscriber.rs) consumes the reducer’s broadcast bus and mirrors lifecycle events back to SQLite for entities the reducer owns. On a Lagged error it does a scoped full-resync (insert/update only, never delete — there’s a code comment explaining the migration-window reason).

Migration status: workspaces are reducer-driven (E.2c.2 onward); tab + block writes are still mostly RPC-direct.

objects.db is authoritative per channel, but user agents are meant to follow you everywhere — an agent created in one channel (or one release version) should appear, fully configured, in all of them. That globalization is implemented by a definition-registry mirror that shadows local objects.db agent mutations into an account-wide store. See SPEC_CROSS_CHANNEL_AGENT_PERSISTENCE and def_registry_mirror.rs.

Both live under ~/.agentmux/shared/agents/ (account-wide, channel- and version-independent), as siblings resolved in registry/paths.rs:

  • Definition registry~/.agentmux/shared/agents/definitions/ — the global copy of each user agent’s definition (the launchable catalog entry).
  • Instances registry~/.agentmux/shared/agents/registry/ — the named cross-channel instances (a running/launched agent’s identity).

A global definition record is not just the row from db_agent_definitions. The mirror (agent_definition_to_record) folds the agent’s content blobs (e.g. instructions, the per-agent ui:zoom) and its skills into the same record, because both live in separate local tables and must travel together for a cross-channel agent to launch with its instructions intact.

  • Local SQLite stays authoritative for writes within its channel. The global store is a cross-channel read view, never a write target that overrides local truth.
  • Reads are global-first for the roster. agent_def_list overlays the global user-agent roster so an agent that exists only in another channel still shows up.
  • Content + skills fall back to the global record when the agent has no local SQLite row (the cross-channel case), so it launches with its instructions.

Every local mutation re-mirrors via registry_def_upsert:

  • agent_content_set and agent_content_delete both re-mirror — so a reset-to-default that deletes a local content row propagates the deletion globally, not just the addition.
  • Skills mirror the same way (insert and delete).

To make that safe, the mirror reads the local row with local-only accessors (agent_content_get_all_local / agent_skill_list_local). If it read through the global-fallback accessors, deleting a local blob would re-read the still-present global copy and re-mirror it straight back, defeating the deletion.

Deleting a user agent calls registry_def_retire, which writes a retired/ tombstone into the definition store rather than just removing the active record. The tombstone is what stops a stale objects.db in another channel from resurrecting a deleted agent on its next registry_def_upsertupsert refuses to revive a tombstoned id. A delete works even for an agent that exists only globally (no local SQLite row): the retire path still tombstones and reports success.

The instances registry carries a session_id (added in schema v1→v2, see registry/schema.rs) so a cross-channel agent’s conversation can resume rather than starting cold when it’s opened from a different channel or version.

Only user agents go global. Seeded template agents stay local (per-channel): they’re manifest-managed and re-seeded into each channel, and their hide flag is per-channel UI state — so the mirror skips any definition with is_seeded != 0.

Same writer (agentmux-srv), opened via FileStore::open(<data-dir>/db/filestore.db). Where blob content lives: agents’ generated config files, session archives, file-pane buffers, captured screenshots. objects.db holds the meta — filestore.db holds the bytes.

You can rebuild filestore.db from scratch (you’ll lose snapshots) without losing your workspace structure. The reverse is not true — objects.db references blobs by hash; deleting filestore.db while keeping objects.db leaves dangling references that the file pane will report as missing.

Per SPEC_PHASE_E_SAGAS_2026-04-30.md (Path A — chosen during Phase E.5), sagas coordinate cross-block side effects (e.g. “open an agent in a new pane and wait for the controller to come up”). sagas.db records each step’s start, completion, retry, and final status.

On crash the saga coordinator reads back from sagas.db and resumes pending sagas from their last durable step — that’s the point of having a durable log instead of in-memory state.

Added by the LSD-1 batch (2026-05-01). Same role as sagas.db but for sagas the launcher coordinates (window-pool warmup, window-detach, monitor-change reconciliation). Owned by agentmux-launcher.

JSONL, append-only. Written by the launcher’s event_log::run_disk_writer task to <data-dir>/launcher-events.log. Holds every command + event the launcher reducer processed: process spawn/exit, window create/destroy, monitor changes, drag operations, tear-off events. Includes WRR (Window Reality Reconciliation) drift records.

In-memory ring buffer (4096 entries) backs disk overflow — recent events are queryable from a running instance without touching disk.

This is the canonical forensic source when something feels off at the OS level: a window vanished, a pane drifted, a monitor disconnect lost focus. Search recipes:

Terminal window
# Windows / Git Bash / muxlog-style
grep -E "HiddenSinceOpen|HwndWithoutBrowser|WRR-DRIFT|wfr:gate|wfr:runner|pending=" \
"$AGENTMUX_DATA_DIR/launcher-events.log"

Each file is independently restorable:

  • Cold-copy the whole <data-dir>/data/ directory while AgentMux is closed — that’s the simplest, most reliable backup.
  • Hot copy of an individual SQLite file while AgentMux is open is supported because the writer holds short transactions; use sqlite3 .backup. JSONL log is append-only and safe to copy at any time.

Re-importing into a fresh install: stop AgentMux, place the files at <data-dir>/data/db/ (and the log at <data-dir>/data/), and launch. Bootstrap reads them.

For completeness, things that are NOT stored in these five files:

  • Settings — JSON at the channel-wide ~/.agentmux/channels/<channel>/config/settings.json (and keybindings.json); not under versions/<version>/, so settings survive version upgrades
  • Agent working directories — per-agent workspace dirs under the channel-wide ~/.agentmux/channels/<channel>/agents/, again one level above the version dir (the agent definitions themselves live in objects.db locally and in the global registry — see below)
  • Cookies / OAuth tokens / dictionary downloads~/.agentmux/shared/ (account-wide, version- and channel-independent)
  • Global agent registry — user-agent definitions + named instances live account-wide under ~/.agentmux/shared/agents/ (see Cross-channel agent persistence)
  • Chromium cache — version-scoped ~/.agentmux/channels/<channel>/versions/<version>/cef-cache/
  • CLI provider configs — auth-config-dir managed by each provider’s CLI