Skip to content

System Overview

Mental model

Coffer is a local-first AI agent vault: a long-lived local daemon that holds a developer's accumulated AI assets on-device and lets any AI agent (Claude Code, Codex, future ones) read and contribute through one safe interface. The vault spans six resource kinds — mcp_server, agent, skill, knowledge_base, memory, and channel — over a kind-agnostic Resource framework, plus cross-cutting features (in-process and CLI-agent chat, messaging channels, multi-machine sync). The MCP gateway is one of those kinds: register an upstream server once and every MCP client connects to the same daemon and sees the same namespaced tools, with names like filesystem__read_file. All state lives locally in SQLite (secrets as Fernet ciphertext), with a single master key in a 0600 file beside the DB (OS keychain opt-in). The daemon binds to 127.0.0.1 only — nothing is reachable from outside your machine.

System topology

The diagram below shows how the pieces fit together. The daemon owns all vault state across the six resource kinds; for the MCP gateway kind, clients do not connect to upstream servers directly — they connect to the Coffer daemon, which manages upstream connections on their behalf. A separate callback listener process serves signed channel webhooks when a SeaTalk channel is enabled.

Named components and interfaces

ComponentTypeRole
coffer-daemonLong-lived processFastAPI service on 127.0.0.1:<auto-port>. Owns all state across the six resource kinds and the cross-cutting features (chat, channels, sync). Single SQLite writer.
Resource kindsDaemon-hosted abstractionSix kinds over one kind-agnostic Resource framework: mcp_server (gateway upstreams), agent (registered coding agents), skill (master skill bundles delivered into agents), knowledge_base and memory (two faces of the files-as-truth + SQLite-retrieval substrate), channel (Telegram/SeaTalk bindings).
Chat / channels / syncCross-cutting daemon featuresIn-process LangGraph chat agent plus Claude Code/Codex CLI agents (spec 008); messaging channels relaying chat from IM apps (ADR-014); opt-in multi-machine sync over a user-owned git repo (ADR-016). Not kinds — they span the kinds.
coffer callback listenerDaemon-spawned child processServes only signed channel webhooks (POST /seatalk/{channel}) on a loopback port behind a user-run tunnel; verifies the SeaTalk signature and forwards events to the daemon. Runs while a SeaTalk channel is enabled (ADR-014).
coffer-mcp-shimShort-lived process (one per MCP client session)Bridges MCP client stdio ↔ daemon HTTP/SSE. Detects a running daemon or spawns one.
coffer CLIShort-lived child processUser-facing management commands. Calls the daemon over loopback HTTP.
Web UIBrowser processManagement interface. In development, served by the Vite dev server at http://localhost:5173; in production, embedded by the Tauri desktop shell. Calls REST API.
Desktop appNative process (Tauri 2, Rust + WebView)Embeds the Web UI in a native desktop window. Communicates with the daemon via REST.
REST API (/api/v1)HTTP surface on daemonManagement plane: CRUD for resources, audit log, settings. Token + CORS authenticated.
MCP endpoint (/mcp)HTTP/SSE surface on daemonMCP JSON-RPC endpoint. This is what the shim connects to. Forwards namespaced tool calls to upstream subprocesses.
SQLite (~/.coffer/coffer.db)Persistent storeControl-plane state: resource registrations, capability preferences, audit log, retention policies, and secrets as Fernet ciphertext in the credentials table. WAL mode, single writer.
master.key / OS KeychainMaster-key storeThe single Fernet master key. Default: a 0600 ~/.coffer/master.key file beside the DB; OS keychain is an opt-in. Secrets are decrypted with it and materialized into the upstream env / headers at spawn time.
~/.coffer/daemon.jsonDiscovery file (mode 0600)PID + port + token. Written by the daemon on startup; read by shim and CLI to locate a running daemon.

Authentication model

All surfaces that communicate with the daemon carry an X-Coffer-Token header. The token is generated when the daemon starts and written to ~/.coffer/daemon.json (mode 0600). Because the file is owner-readable only and the daemon binds to loopback, the effective trust boundary is the local user account.

All state under ~/.coffer/

Every persistent artifact lives in one directory:

~/.coffer/
├── daemon.json          # discovery: PID + port + token (0600)
├── coffer.db            # SQLite: control-plane state + rebuildable retrieval index
├── master.key           # Fernet master key (0600; keychain opt-in moves it out)
├── knowledge/           # knowledge_base files-as-truth (docs/ + raw/ per KB)
├── memory/              # memory files-as-truth (global/ + projects/<ulid>/)
├── skills/              # canonical master skill store
├── sync/                # git working tree for multi-machine sync (opt-in)
├── backups/             # default destination for vault backups
├── bin/                 # co-located coffer binaries (daemon/shim/CLI)
├── logs/
│   └── daemon.log       # structured JSON logs from the daemon
└── upstream-pids/       # PID files for upstream subprocesses (swept on restart)

This single-directory layout means a complete backup of your Coffer state is one cp -r ~/.coffer/ backup/ away.

Reading map

The pages that follow each explore one slice of the system in depth:

PageTopic
Daemon & processesProcess model, detect-or-spawn, upstream subprocess lifecycle
Resource frameworkThe kind-agnostic abstraction that unifies identity, lifecycle, and audit
Layering & boundariesImport rules, layer responsibilities, enforcement
SurfacesREST API, MCP endpoint, CLI, Web UI, Desktop
Request lifecycleEnd-to-end trace of a tool call from client to upstream
PersistenceSQLite schema, WAL, Alembic, JSON field handling
SecurityToken auth, encrypted credential store, SSRF guard, loopback enforcement
ObservabilityStructured logging, trace IDs, audit log, retention
DistributionPackage layout, install, platform support

See also: Architecture reference