Journal
Status: Supported
Milestone: 1 — Journal + module core
Spec: Journal §4
Package: internal/journal
Goal
Implement the append-only SQLite event store — the single source of truth every other component depends on.
Interfaces
type Journal interface {
Append(ctx context.Context, e Event) error
Query(ctx context.Context, f Filter) ([]Event, error)
Get(ctx context.Context, id string) (Event, error)
Watch(ctx context.Context) (<-chan struct{}, error)
}
Implementation notes
- Schema DDL from spec §4 (
eventstable + indexes on time, type, source) - FTS5 virtual table (
events_fts) for keyword search viaFilter.Text - SQLite driver:
modernc.org/sqlite(pure Go, no CGO) vsmattn/go-sqlite3— prefer pure Go for Pi cross-compile unless FTS5/vec needs CGO - ULID generation at append time
Watchsignals coalesced wakeups after each append; consumers pull events viaQueryorQueryAfter(the router uses both with a durable cursor)router_stateandevent_dispatchtables support cursor-based routing replay
Acceptance criteria
- [x] Append persists event with ULID
- [x] Query by type prefix, source, time range
- [x] Query with
Filter.Textperforms FTS5 keyword search - [x] Get by id
- [x]
Watchwakes pull-based consumers on new appends - [x] Optional
retention_daysprunes events older than the configured window on startup - [x] Router cursor (
router_state) enables pull-based dispatch viaWatch
Dependencies
- Blocks: HTTP ingest, MCP query
- Blocked by: config loader (for db path)
Open questions
Retention / pruning— resolved:[journal].retention_daysdeletes events older than N days on startup (FTS rows included). Blob orphan cleanup is a follow-up.