Store

Package: store/. SQLite persistence for all runtime state.

Database settings

The database opens in WAL mode with a 5-second busy timeout. WAL allows concurrent readers while a single writer holds the lock. Multiple daemons (gated, timed, dashd, onbod) open the same messages.db file.

Schema versioning uses PRAGMA user_version. On startup, store.Open reads the current version and applies any pending migration functions in order. Migrations are Go functions (not SQL files at this layer) registered in a slice; the slice index is the version number.

External daemons (timed) run their own migration runner keyed by service name in the shared migrations table, allowing each daemon to apply its own schema additions idempotently.

Schema

TableKey columnsPurpose
messages id, chat_jid, sender, content, timestamp, verb, source, group_folder, is_from_me All messages. Primary poll target. source and group_folder exist for outbound audit trail but are not yet populated.
chats jid (PK), name, channel, is_group, errored Known JIDs. errored flag set on agent failure; cleared on next successful run.
routes id, jid, seq, type, match, target Routing rules evaluated by the gateway. seq controls order within a JID.
registered_groups jid (PK), folder, trigger_word, requires_trigger, container_config (JSON), parent, slink_token Active groups. container_config carries per-group mounts, timeout, sidecars as JSON.
sessions group_folder + topic (composite PK), session_id Current Claude Code session ID. topic is empty string for the default session.
session_log id, group_folder, session_id, started_at, ended_at, result, error Historical session records for audit and debugging.
system_messages id, group_id, origin, event, body XML system events. Flushed and prepended to agent prompt at invocation time.
scheduled_tasks id (PK), owner, chat_jid, prompt, cron, next_run, status, created_at Cron and one-shot tasks. Polled by timed. cron null = one-shot.
router_state key (PK), value Persisted gateway state: lastTimestamp, lastAgentTimestamp.
auth_users sub (unique), username (unique), hash Web auth accounts. hash is argon2id.
auth_sessions token_hash (PK), user_sub, expires_at JWT refresh token hashes. Short-lived access tokens are stateless.
user_groups user_sub + folder (composite PK) Restricts a web user to specific group folders. Absent = operator (unrestricted).
email_threads thread_id (PK), chat_jid, subject Maps IMAP thread IDs to JIDs for reply threading.
onboarding jid (PK), status, world_name, prompted_at Per-JID onboarding state. Owned by onbod.

Migration ownership

Each daemon owns its own tables and applies its schema idempotently. gated owns the core tables via store.Open. timed creates scheduled_tasks via its own migration runner (idempotent; no-ops if the table already exists from gated). onbod owns onboarding. Cross-daemon reads are read-only.

Outbound audit trail

messages has source, group_folder, and is_from_me columns for recording outbound messages. StoreOutbound() is not yet implemented โ€” the columns exist and are included in the schema but are not populated. Full spec: specs/7/22-audit-log.md.

Key store functions

FunctionDescription
PutMessageInsert inbound message, upsert chat record
NewMessages(since)Return unprocessed messages after timestamp
MessagesSince(jid, cursor)Per-chat message slice for agent prompt
FlushSysMsgs(groupID)Return and delete pending system messages for group
GetSession / SetSession / DeleteSessionSession ID by (folder, topic)
ActiveWebJIDs(since)Web JIDs with messages newer than timestamp
GetRegisteredGroupsAll active groups for routing