Skip to content

state.db — Session State Store

state.db is the main SQLite database for Hermes Agent on the Agent Host. It is the single source of truth for session history, message logs, and full-text search. A separate file sessions.db exists but is a 0-byte placeholder — all data lives in state.db.

~/.hermes/state.db # Live database (WAL mode)
~/.hermes/state-snapshots/ # Auto-snapshots (pre-update, etc.)
TablePurpose~Size
sessionsOne row per conversation session. Stores session ID, source platform (slack/telegram/cli), model config, token usage, cost tracking, timestamps, parent session (for handoff chains), and title.38 rows
messagesIndividual messages per session. Stores role (user/assistant/tool/system), content, tool call metadata, timestamps, reasoning outputs, and platform message IDs.2 219 rows
messages_ftsFTS5 virtual table over messages.content with trigram tokenizer. Powers the session_search tool for fast full-text retrieval across all sessions.2 219 docs
schema_versionMigration version counter. Used by Hermes to apply schema upgrades on startup.1 row
state_metaGeneric key-value metadata store.2 rows
compression_locksAdvisory locks for session compression. Prevents concurrent compression of the same session.0 rows
sqlite_sequenceAuto-updated by SQLite for AUTOINCREMENT on messages.id.1 row

The trigram FTS5 index creates supporting tables automatically: messages_fts_config, messages_fts_content, messages_fts_data, messages_fts_docsize, messages_fts_idx, plus messages_fts_trigram_*. These are all internal to the FTS5 virtual table and should not be queried directly.

CREATE INDEX idx_sessions_source ON sessions(source);
CREATE INDEX idx_sessions_parent ON sessions(parent_session_id);
CREATE INDEX idx_sessions_started ON sessions(started_at DESC);
CREATE INDEX idx_messages_session ON messages(session_id, timestamp);
CREATE INDEX idx_compression_locks_expires ON compression_locks(expires_at);
ColumnPurpose
idPrimary key, e.g. slack:C0B7C9ZFY4R:1780260332.893459
sourcePlatform: slack, telegram, discord, cli, …
parent_session_idSelf-referential FK for compression handoff chains
model, billing_provider, billing_base_urlModel/billing config at session creation time
input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, reasoning_tokensToken consumption counters
estimated_cost_usd, actual_cost_usdCost tracking (estimated vs. confirmed)
titleAuto-generated or user-edited session title
handoff_state, handoff_platformCross-platform handoff support
ColumnPurpose
idAUTOINCREMENT primary key
session_idFK → sessions.id
roleuser, assistant, tool, system
contentMessage text (indexed by FTS5)
tool_calls, tool_call_id, tool_nameTool invocation metadata
reasoning, reasoning_content, reasoning_detailsThinking/reasoning output
platform_message_idCross-platform message correlation (e.g. Slack TS)
observedFlag for deduplication

state.db is included in both backup mechanisms:

  1. hermes backup (full zip)state.db is listed in _QUICK_STATE_FILES (line 489 of hermes_cli/backup.py). The backup uses _safe_copy_db() which calls sqlite3.backup() for a consistent snapshot of the WAL-mode database, avoiding corruption from copying live files. Excluded from zip: .db-wal, .db-shm, .db-journal sidecars (would pair a fresh snapshot with stale journal data).

  2. Quick snapshots (_QUICK_SNAPSHOTS_DIR)state.db is also in the quick snapshot manifest (line 275 validation marker), meaning hermes update and pre-flight operations snapshot it to ~/.hermes/state-snapshots/<timestamp>/ before mutations. A maximum of 20 snapshots are retained; state.db copies are pruned more aggressively due to their size.

Terminal window
# List files in the latest full backup
unzip -l ~/.hermes/backups/hermes-backup-*.zip | grep state.db
# Check latest quick snapshot
ls ~/.hermes/state-snapshots/*/state.db

The file ~/.hermes/sessions.db exists as a 0-byte file. It is a legacy placeholder — all session data has been stored in state.db since the hermes_state.py SQLite store replaced the per-session JSONL approach. The backup system targets state.db explicitly and does not need sessions.db.