matrix-axon

RFC 0001 - Archive store backend: keep Postgres for MVP, reassess SQLite only as a full switch

Status

Proposed.

Context

Axon’s server-side archive store has depended on Postgres since the start of the project (ADR 0003). We did not make that choice after an extended Postgres vs. SQLite design discussion; it was the straightforward fit for a Rust server with a durable event archive, JSON-heavy Matrix payloads, migrations, and a future path to hosted or larger deployments.

The product shape now makes the tradeoff worth recording explicitly. Axon is primarily a self-hosted personal agent: one human per process, with one or more Matrix accounts inside. For that model, requiring Docker or a separate Postgres service is a real adoption cost. SQLite would make the default path closer to a single binary plus a local data directory.

At the same time, we do not want to support two archive-store backends. Two SQL dialects would double the correctness surface for the parts of Axon where the database is most load-bearing: lifecycle state, message history, relation aggregation, media lookup, search convergence, and account-token storage.

Decision

Keep Postgres as the archive store for the near-term MVP. Do not add a second SQLite backend.

If “no Docker / no separate database server” becomes a hard pre-MVP product requirement, make SQLite a complete replacement for Postgres in a dedicated storage milestone, before adding more store-heavy features. That switch must rewrite the store, migrations, tests, launcher scripts, and token-encryption mechanism as one coherent change; it should not be introduced as a compatibility layer.

The recommendation is therefore:

Current Postgres coupling

The current code is not database-neutral. axon-store owns a PgPool, all row decoding is typed to PgRow / Postgres, and axon-server and the token CLI connect through Store::connect(&config.database.url, ...). The configuration surface documents database.url as a required Postgres URL, and the local developer path centers on docker-compose.yml, .env, and DATABASE_URL.

The schema and query layer depend on Postgres behavior in several important places:

None of these are impossible to port. Together, they make switching a storage milestone, not a config change.

Postgres vs. SQLite

Postgres advantages

Postgres fits Axon’s current implementation and future headroom:

The cost is operational: users must run a DB service, manage credentials, avoid port conflicts, back it up, and understand Docker or local Postgres.

SQLite advantages

SQLite is attractive for Axon’s personal, single-human deployment model:

The cost is architectural: SQLite has one writer at a time in WAL mode, no standard pgcrypto equivalent, different JSON/type semantics, and a different SQL dialect for the exact queries Axon currently relies on.

Switching cost

Switching now would be high but bounded. It is still cheaper now than after history backfill, more search API work, or additional materialized read models land, but it is not a small refactor.

A credible SQLite replacement must include:

The safest implementation shape would be a clean branch that replaces the Postgres store wholesale, ports the existing DB-gated tests to temp SQLite files, and keeps the public /v1/ API unchanged.

Security and stability notes

SQLite does not weaken Axon by definition, but it moves several responsibilities from the database/service boundary into the application:

Consequences

References