Repository navigation
v0.2.0 — writes that do not lose each other
Atomic update operators, preconditions, self-bounding collections, grouped counts, and a scheduler fix — all of it from fit-checking bkn against a 131k-line control plane, asking not "could it run on bkn" but "would it be less code on bkn".
patch was a lost update
It read a document, merged in Go, and wrote the whole thing back, so concurrent patches erased each other. Measured against the previous code:
| before | now | |
|---|---|---|
16 concurrent $inc |
tries = 1 — 15 updates lost |
tries = 16 |
| two patches, two fields | one erased the other | both survive |
| 12 contenders claiming a job | all 12 "won" it | exactly 1 |
Fields may now carry operators computed from the current value, written under compare-and-set:
bkn store patch app/runs r1 --data '{"tries":{"$inc":1},"log":{"$append":"started\n"}}'
bkn store patch app/runs r1 --data '{"status":"done"}' --if status=running
bkn store patch app/runs r1 --data '{"worker":"w1"}' --if-absent worker # claims it exactly onceA plain object is still a plain value — only a single $-prefixed key is an operator. A failed precondition writes nothing and exits 95.
Collections that bound themselves
The trim query and the job that ran it both go away:
bkn store create app/memories --retain-last 20 --retain-per tag,repo_id,user_idEnforced on every write and the moment the policy is declared, so a bound set on a collection that already holds a million documents applies immediately.
Rollups
How many, not which — one field, one aggregate, never documents:
bkn store count app/runs --where repo_id=7 --by statustotal counts matching documents and groups counts distinct values before any limit, so truncation is visible rather than silent.
@every fired early, and sometimes twice
Schedule.Next rounded intervals down to the second to match second-resolution storage, so an @every 1s job ticked at :01.999 was scheduled 1 ms later and re-fired for the rest of that second. It rounds up now: late is harmless, early is a bug. Observed in a live deployment — one pair of runs in the same second across 11,246.
What it deliberately will not do
AGENTS.md gained a Deliberate omissions section, each entry argued from the codebase that asked for it: no transactions (a transaction is caller-held state, and a one-shot CLI over a stateless API has nowhere to hold it — locks is the multi-statement answer), no joins (denormalize), no regex/LIKE, no multi-field sort (ordering is already total), no age-based retention.
Together these took the fit-checked codebase from 33 statements beyond the store's surface to 11 — and all 11 were refused rather than absorbed. Three primitives, no query language.
Upgrading
Schema change: two columns on collections and one index, applied automatically on open. Verified against a copy of a live 16 MB database — 140 documents and 23,156 events read back byte-identical, and the previous binary can still read and write the migrated file, so a rollback is safe.
curl -L https://github.com/javimosch/bkn/releases/download/v0.2.0/bkn -o bkn && chmod +x bkn
./bkn installLinux x86_64, statically linked, no runtime dependencies.
bkn update does not use these tags — versions are content hashes served at GET /version and /dl/bkn. A tag is for people; the hash is what a machine updates against.