Why a bad ingest can't break Dokki
Immutable versions, snapshots as a manifest, and rollback as a mere pointer flip — how Dokki protects the production corpus.
The obvious way to maintain a RAG corpus: re-import the document, overwrite the old chunks, done. The problem — if the new import is broken (mangled PDF layout, half the text missing), you've just damaged production live, and the old good state is gone.
Dokki does it the other way around. The production state is untouchable until you actively activate something else.
Versions are immutable
Every re-ingest of a document produces a new document_version with its own
version_no and its own chunks — never an update of the old one.
arabic-grammar.pdf
├── version_no = 1 (ready) → 500 chunks [in the active snapshot]
└── version_no = 2 (ready) → 512 chunks [in NO active snapshot]
The chunks of version_no = 2 already sit in the database — but are
invisible to queries until they're deliberately added to a snapshot and that
snapshot is activated. A version moves through a simple state machine:
pending → ingesting → ready → failed
A snapshot is just a manifest
A snapshot stores no data. It is a list: "which version of each document belongs to this state?"
snapshot "lughaty-2026-06-20" (active)
└── snapshot_members
├── grammar.pdf → version_no 1
├── vocabulary.epub → version_no 3
└── dialects.pdf → version_no 2
Per collection, exactly one active_pointer row points at the production
snapshot. Queries run only against its members — everything else is in the DB but
out of view.
Promotion: build, check, switch
A draft snapshot is built from the desired versions. The
quality gate checks the candidate. Only on pass
does the pointer flip and snapshot.activated inform the RAG service. The old
state stays live untouched throughout the entire check.
Rollback is a pointer flip
Because all old vectors still live in the DB, a rollback is instant — no recomputation, no re-embedding:
UPDATE knowledge.active_pointer
SET active_snapshot_id = $previousSnapshot
WHERE collection_id = $c;
-- then: NATS event snapshot.activated → RAG service invalidates cache
A garbage-collector job later clears away versions no longer referenced by any of the last N snapshots — the only place anything is deleted at all.
What this means in practice
Rollback is the safety net, not the normal case.
A bad ingest never goes active in the first place thanks to the quality gate — rollback only kicks in if a regression surfaces during operation. The combination of immutable versions and "live only after promotion" makes production structurally robust: you can import, experiment, and build snapshots at any time without ever risking the running state.
Exactly how the gate decides whether a candidate is better or worse than the active snapshot is in the next post.