Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
d12e5af
Add: plan retention recovery from restart evidence
flyingrobots Sep 9, 2026
48c9998
Add: execute a retention recovery plan through a blocking storage port
flyingrobots Sep 9, 2026
16f22a9
Add: recover retained retention stages under filesystem authority
flyingrobots Sep 9, 2026
7e6cf87
Test: recover every retention publication crash prefix to its documen…
flyingrobots Sep 9, 2026
3d031b4
Fix: run retention recovery as the first step of publication
flyingrobots Sep 9, 2026
7a512c1
Add: kill real writers at every retention publication coordinate
flyingrobots Sep 9, 2026
4aad28d
Add: fenced, double-collected reader views of a version-two store
flyingrobots Sep 9, 2026
c9277ea
Test: prove retention transitions against a namespace-to-anchor-set m…
flyingrobots Sep 9, 2026
24c2d93
Docs: add the feature and task roadmap
flyingrobots Sep 30, 2026
29690fc
Test: complete the retention record corruption matrices
flyingrobots Sep 30, 2026
a5c6701
Docs: describe the shipped v1 initialization and recovery on the livi…
flyingrobots Sep 30, 2026
cec0559
Docs: fold the second architecture assessment into the roadmap
flyingrobots Sep 30, 2026
68b81ee
Fix: compare only restart-stable root coordinates on version-two reopen
flyingrobots Sep 30, 2026
e4fb098
Feat: canonical GC retirement intent and receipt codecs
flyingrobots Sep 30, 2026
5a23238
Feat: explicit-depth verification reports for the reference view
flyingrobots Sep 30, 2026
fed314e
Docs: route every status page to live work
flyingrobots Sep 30, 2026
2bce29d
Feat: storage-independent migration recovery planning
flyingrobots Sep 30, 2026
ce3bc67
Feat: recover an interrupted migration from any prefix
flyingrobots Sep 30, 2026
4b32c02
Fix: hash each chunk once per reference-store read
flyingrobots Sep 30, 2026
132b1c7
Feat: measured memory ceiling and floor for reference-store staging
flyingrobots Sep 30, 2026
65ed69e
Feat: process-death crash matrix for the 21 migration phases
flyingrobots Sep 30, 2026
925d592
Fix: gate the migration crash-task helpers behind repository-tasks
flyingrobots Sep 30, 2026
34732b2
Merge PR #99 (retention recovery, reader fence, model evidence) into …
flyingrobots Sep 30, 2026
5bba27e
Feat: re-verify closure members under filesystem retention authority
flyingrobots Sep 30, 2026
668fd5f
Feat: deterministic GC planning from a fenced liveness snapshot
flyingrobots Sep 30, 2026
2859f68
Feat: register the disposition enumerations and ship the receipt codec
flyingrobots Sep 30, 2026
aa371ae
Feat: explicit finalize-or-retire disposition of protected retention …
flyingrobots Sep 30, 2026
9a10b22
Feat: GC execution, retirement, and recovery with its process-death m…
flyingrobots Sep 30, 2026
5f49800
Test: permanent corruption ledgers over every durable structural field
flyingrobots Sep 30, 2026
3aebec4
Feat: durable, replayable verification receipts
flyingrobots Sep 30, 2026
393accb
Feat: identity-preserving compaction with recovery on version-two roots
flyingrobots Sep 30, 2026
0bafaa7
Feat: durable authenticated reads over a fenced version-two snapshot
flyingrobots Sep 30, 2026
0d300db
Feat: backend-neutral content-store port with count-and-byte staging …
flyingrobots Sep 30, 2026
92f3eb4
Fix: repin rematerialized version-two digests and satisfy the pinned …
flyingrobots Sep 30, 2026
1de4f66
Fix: make the spliced crash-point table a const fn for the pinned clippy
flyingrobots Sep 30, 2026
ee4ef54
Feat: durable staged ingestion with deduplication through the content…
flyingrobots Sep 30, 2026
1dbbf7f
Feat: bounded streaming write-through pipeline with exactly-once sink…
flyingrobots Sep 30, 2026
80c8042
Fix: keep the transfer benchmark and port suite within the pinned clippy
flyingrobots Sep 30, 2026
c7f9f57
Fix: keep the mutation-ledger and receipt test crates within the pinn…
flyingrobots Sep 30, 2026
666922a
Fix: qualify the compaction and GC error doc links behind their aliases
flyingrobots Sep 30, 2026
d0998b3
Fix: preserve typed durable refusals and bound sealed-stage reads
flyingrobots Oct 1, 2026
652059e
Refactor: move transfer-source ports into the store boundary
flyingrobots Oct 1, 2026
03dc989
Fix: count authenticated chunks once in read benchmarks
flyingrobots Oct 1, 2026
420964e
Test: cover retention release and restore in model sequences
flyingrobots Oct 1, 2026
1a586d8
Docs: reconcile durable implementation and version-two guarantees
flyingrobots Oct 1, 2026
4b9c389
Docs: record completed-roadmap audit and GitHub follow-up ownership
flyingrobots Oct 1, 2026
fb9f129
Fix: assert GC parser canonicality through runtime encoders (#107)
flyingrobots Oct 4, 2026
05b804b
Docs: preserve GC receipts under the whitespace gate (#107)
flyingrobots Oct 4, 2026
b1eaa59
fix(gc): distinguish reader-lock coordinate roles (#107)
flyingrobots Oct 4, 2026
7af834a
docs: make coordinate reduction evidence replayable (#107)
flyingrobots Oct 4, 2026
9a5fb8f
docs: correct coordinate receipt paragraph spacing (#107)
flyingrobots Oct 4, 2026
65c0707
docs: verify GC fixture provenance with pinned tools (#107)
flyingrobots Oct 4, 2026
f00e780
refactor(gc): prove receipt widths before encoding (#107)
flyingrobots Oct 4, 2026
6a84324
test(#107): name retention head seal choices
flyingrobots Oct 4, 2026
a62bfb8
test(#107): assert opaque GC coordinates at the public decoder
flyingrobots Oct 4, 2026
5b533b0
test(#107): assert exact GC diagnostic coordinates
flyingrobots Oct 4, 2026
b5ecd97
test(#107): distinguish retention admission refusal causes
flyingrobots Oct 4, 2026
e8ae339
docs(#107): consolidate decoder oracle calibration evidence
flyingrobots Oct 4, 2026
4158001
docs(#107): link opaque-coordinate runtime evidence
flyingrobots Oct 4, 2026
7cdc2cf
docs(#107): bound retention refusal evidence claims
flyingrobots Oct 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Keep is foundational storage infrastructure. Optimize for correctness, recoverability, auditability, and maintainability before performance or convenience.

## End-of-turn commits

Stage and commit the work completed during each turn before the final response.
Use focused commits with issue references and preserve unrelated user changes.
If a required check fails or a commit cannot be made, preserve the work and
report the blocker explicitly. Do not push unless the user requests it.

## Core Law

For a given content identity, Keep must return exactly the bytes named by that identity—or refuse.
Expand Down
392 changes: 392 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ divan = { version = "=0.1.21", default-features = false }
name = "streaming_cdc"
harness = false

[[bench]]
name = "transfer_pipeline"
harness = false

[workspace]
members = [".", "benchmark", "repository-process-spawn", "xtask"]
resolver = "3"
Expand Down
84 changes: 65 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,38 +51,46 @@ Keep is required to refuse all three, before mutating anything.
generation-versioned catalogs, and a fixed-width `HEAD` are published
through an ordered protocol whose every step is a named crash point.
Platform admission is Linux ext4, non-casefolded, one writer.
- **Proven restart recovery for version 1.** The crash matrix kills real
writer processes at 105 before/during/after coordinates
(`KEEP-CRASH-001`–`035`) and verifies the store lands in exactly one
documented lawful state each time.
- **Proven restart recovery.** The crash matrix kills real writer processes
at 266 before/during/after coordinates (`KEEP-CRASH-001`–`087`) and
verifies the store lands in exactly one documented lawful state each time,
for version-1 publication, version-2 retention publication, the one-way
migration, and GC retirement; every interrupted migration recovers to one
complete migration with every version-1 byte intact, and no crash prefix
of a retirement loses a live segment.
- **Version-2 retention and migration, forward path.** Explicit retention
roots, deterministic closure verification, a one-way 21-phase migration,
and a 17-phase retention publication — all with production filesystem
writers, all preserving every version-1 byte. Reopening a migrated store
jointly admits its marker, intent, and receipt, binds the root's device,
mount, and inode identity to the intent, and pins the directories it
admitted. Publication binds this store's own catalog head and the catalog
jointly admits its marker, intent, and receipt, binds the root's
restart-stable device and inode identity to the intent (a remounted store
admits; a moved one refuses), and pins the directories it admitted. Publication binds this store's own catalog head and the catalog
it selects, and refuses retained stages, superseded candidates, substituted
files, replaced protocol directories, and every namespace or capacity
violation before it writes anything. Each refusal is a typed value, not a
string.

## What it does not do yet

Version 2 writes correctly from a clean start and, if it finds the residue of
an interrupted publication, refuses rather than guesses. Nothing yet recovers
that residue, and readers have no fence, so **an interrupted version-2
publication waits for a human until #19 lands.** A version-1 store stays
admitted until its owner migrates it; migrate only if you accept that wait.
Version 2 writes correctly from a clean start, and the next publication
recovers the residue of an interrupted one: a stage cut mid-write is
discarded, a head already synchronized is finalized, and a byte-identical
retry reports already committed. A complete orphan, a crash between the root
link and the head finalization, stays recovery-protected until a person or an
explicit policy calls `dispose` with a finalize-or-retire decision, which
records a durable receipt before it changes anything; nothing decides on
their behalf. The crash matrix proves publication recovery by killing real
writer processes at all 51 retention coordinates, and an interrupted
migration the same way at all 68 migration coordinates:
`FilesystemStoreMigrationAuthority::reopen_for_recovery` and
`recover_store_migration` resume any prefix of the twenty-one phases. Readers
hold a shared fence and double-collect both heads, so a view never straddles
a publication. A version-1 store stays admitted until its owner migrates it.

| Gap | Tracked |
| --- | --- |
| Restart recovery for retention publication and migration | [#19](https://github.com/flyingrobots/keep/issues/19) |
| Restart-stable root identity coordinate in the migration intent | [#97](https://github.com/flyingrobots/keep/issues/97) |
| Reader fence binding one consistent catalog + retention snapshot | [#19](https://github.com/flyingrobots/keep/issues/19) |
| Precise verification reports at explicit depths | [#20](https://github.com/flyingrobots/keep/issues/20) |
| Garbage collection and identity-preserving compaction | [#21](https://github.com/flyingrobots/keep/issues/21) |
| Bounded production ingestion through the durable store | [#82](https://github.com/flyingrobots/keep/issues/82) |
| Durable authenticated reads bound to a fenced snapshot | [#109](https://github.com/flyingrobots/keep/issues/109) |
| Verification reports at durable depths and a replayable receipt | [#20](https://github.com/flyingrobots/keep/issues/20) |
| Encrypted representations | [#86](https://github.com/flyingrobots/keep/issues/86) |

Keep also does not claim secure deletion. Releasing a retention root
Expand Down Expand Up @@ -154,7 +162,9 @@ the content means, who owns it, or whether deleting it is legally safe.
Keep is `0.0.0` and unpublished; build from source. The in-memory
[non-durable reference CAS](docs/architecture/reference-store/README.md) is
executable evidence for the storage laws, not a durable backend — process
death loses everything in it.
death loses everything in it. Code written against the
[content-store port](docs/architecture/content-store/README.md) runs on it
in tests and on a durable snapshot in production.

```rust
use std::io::Cursor;
Expand All @@ -176,6 +186,42 @@ assert_eq!(output, b"exact bytes, or nothing");
# Ok::<(), Box<dyn std::error::Error>>(())
```

A migrated version-two store writes and reads the same way on disk:
`DurableWriter` stages a source in one bounded pass, reusing every chunk the
catalog already holds, and commits it through the catalog protocol;
`DurableStore` reads with every read pinned to one fenced snapshot and every
receipt naming the view. Production admission is Linux ext4; this example is
not run on other hosts.

```rust,no_run
use std::path::Path;
use keep::{CatalogRestartByteLimit, CatalogRestartPolicy, DurableStore, DurableWriter,
FilesystemVersionTwoAdmission, LayoutEntryLimit, ReaderAttemptLimit, SegmentReadPolicy,
SegmentRecordLimit, StagingLimits};

let root = Path::new("/var/lib/keep/store");
let policy = CatalogRestartPolicy::new(
SegmentReadPolicy::new(SegmentRecordLimit::MAXIMUM, LayoutEntryLimit::MAXIMUM),
CatalogRestartByteLimit::new(1 << 30)?,
);

// Write: one pass, chunks the catalog already holds are reused by exact bytes.
let mut writer = DurableWriter::open(FilesystemVersionTwoAdmission::reopen(root)?, root, policy)?;
let mut source = std::fs::File::open("build/artifact.tar")?;
let receipt = writer.stage(&mut source, StagingLimits::entries(LayoutEntryLimit::MAXIMUM))?.commit()?;
println!("{} new, {} reused", receipt.accounting().physical_new_bytes(),
receipt.accounting().physical_reused_bytes());
drop(writer);

// Read: the committed layout is readable at once; by identity once anchored.
let store = DurableStore::open(root, policy, ReaderAttemptLimit::DEFAULT);
let snapshot = store.snapshot()?; // shared reader fence held until dropped
let mut output = Vec::new();
let read = snapshot.reconstruct_layout(receipt.layout_id(), &mut output)?;
println!("generation {}", read.view().catalog_generation().get());
# Ok::<(), Box<dyn std::error::Error>>(())
```

Run the full gate suite the way CI does:

```bash
Expand Down
Loading