Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Database Schema

Ennio uses SQLite with WAL journal mode for persistence. The database is created automatically on startup and migrations run on every boot.

Connection

database_url: sqlite:ennio.db      # file-based (default)
database_url: sqlite::memory:       # in-memory (data lost on restart)

The connection pool is configured with:

  • WAL journal mode (concurrent reads)
  • Foreign keys enabled
  • SqlitePool from sqlx

Tables

sessions

Stores session metadata and state.

ColumnTypeDefaultDescription
idTEXT PRIMARY KEYSession ID (e.g., myapp-abc123)
project_idTEXT NOT NULLOwning project
statusTEXT NOT NULL'spawning'Current SessionStatus
activityTEXTCurrent ActivityState
branchTEXTGit branch name
issue_idTEXTLinked issue ID
workspace_pathTEXTAbsolute path to workspace directory
runtime_handleTEXTJSON-serialized runtime handle
agent_infoTEXTJSON-serialized agent session info
agent_nameTEXTAgent plugin name
pr_urlTEXTPull request URL
pr_numberINTEGERPull request number
tmux_nameTEXTTmux session name
config_hashTEXT NOT NULLHash of config at spawn time
roleTEXTSession role
metadataTEXT NOT NULL'{}'JSON metadata
created_atTEXT NOT NULLdatetime('now')ISO 8601 creation timestamp
last_activity_atTEXT NOT NULLdatetime('now')ISO 8601 last activity timestamp
restored_atTEXTISO 8601 restore timestamp
archived_atTEXTISO 8601 archive timestamp

events

Stores the event log.

ColumnTypeDefaultDescription
idTEXT PRIMARY KEYEvent ID
event_typeTEXT NOT NULLEventType variant name
priorityTEXT NOT NULLEventPriority variant name
session_idTEXT NOT NULLAssociated session (FK → sessions)
project_idTEXT NOT NULLOwning project
timestampTEXT NOT NULLdatetime('now')ISO 8601 event timestamp
messageTEXT NOT NULLHuman-readable description
dataTEXT NOT NULL'{}'JSON payload

projects

Stores project metadata.

ColumnTypeDefaultDescription
project_idTEXT PRIMARY KEYProject ID
nameTEXT NOT NULLProject name
repoTEXT NOT NULLGit repository URL
pathTEXT NOT NULLLocal filesystem path
default_branchTEXT NOT NULL'main'Default git branch
config_hashTEXT NOT NULLHash of project config
created_atTEXT NOT NULLdatetime('now')ISO 8601 creation timestamp
updated_atTEXT NOT NULLdatetime('now')ISO 8601 last update timestamp

session_metrics

Stores per-session performance metrics.

ColumnTypeDefaultDescription
session_idTEXT PRIMARY KEYAssociated session (FK → sessions)
total_tokens_inINTEGER NOT NULL0Total input tokens consumed
total_tokens_outINTEGER NOT NULL0Total output tokens generated
estimated_cost_usdREAL NOT NULL0.0Estimated total cost in USD
ci_runsINTEGER NOT NULL0Number of CI runs
ci_failuresINTEGER NOT NULL0Number of CI failures
review_roundsINTEGER NOT NULL0Number of review rounds
time_to_first_pr_secsINTEGERSeconds from spawn to first PR
time_to_merge_secsINTEGERSeconds from spawn to merge
updated_atTEXT NOT NULLdatetime('now')ISO 8601 last update timestamp

Indices

IndexTableColumnsPurpose
idx_events_session_ideventssession_idFast event lookup by session
idx_events_event_typeeventsevent_typeFast event lookup by type
idx_sessions_project_statussessionsproject_id, statusFast session filtering

Foreign Keys

  • events.session_idsessions.id (ON DELETE CASCADE)
  • session_metrics.session_idsessions.id (ON DELETE CASCADE)

Migrations

Migrations are embedded in the binary and run automatically on startup. They are idempotent — using CREATE TABLE IF NOT EXISTS patterns. The migration order is:

  1. V1: sessions table
  2. V2: events table
  3. V3: projects table
  4. V4: session_metrics table
  5. V5: Performance indices

Querying

All database access goes through repository functions in ennio-db:

sessions::insert(pool, session)
sessions::get(pool, session_id) → Option<Session>
sessions::list(pool, project_filter) → Vec<Session>
sessions::update(pool, session)
sessions::delete(pool, session_id)

events::insert(pool, event)
events::get(pool, event_id) → Option<OrchestratorEvent>
events::list_by_session(pool, session_id) → Vec<OrchestratorEvent>
events::list_by_project(pool, project_id) → Vec<OrchestratorEvent>

projects::insert(pool, project)
projects::get(pool, project_id) → Option<ProjectRow>
projects::list(pool) → Vec<ProjectRow>

metrics::insert(pool, metrics)
metrics::get(pool, session_id) → Option<SessionMetricsRow>
metrics::update(pool, metrics)

All queries use parameterized bindings via sqlx::query().bind() — no SQL injection risk.