Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d6c38a1
Feat: establish fenced authenticated durable read boundary (#109)
flyingrobots Oct 2, 2026
45c0bfb
Docs: record durable read calibration and explicit admission costs (#…
flyingrobots Oct 2, 2026
ab518b2
Test: exercise durable Worldline reads and exact refusal boundaries (…
flyingrobots Oct 2, 2026
e0a540c
Feat: preserve durable caller-layout read contracts (#109)
flyingrobots Oct 2, 2026
cc189ff
Test: verify durable corruption and range output boundaries (#109)
flyingrobots Oct 2, 2026
8d08da9
Test: close durable output and profile refusal parity gaps (#109)
flyingrobots Oct 2, 2026
18003b6
Test: verify durable range overlap and corruption contracts (#109)
flyingrobots Oct 2, 2026
dd42dcb
Docs: reconcile durable read contracts and Linux example (#109)
flyingrobots Oct 2, 2026
4d801e4
Test: prove durable snapshot refuses incomplete retained closure (#109)
flyingrobots Oct 2, 2026
186ab8a
Docs: record durable refusal calibration requested by review (#109)
flyingrobots Oct 2, 2026
0a43198
test: expose foreign-namespace root admission (#109)
flyingrobots Oct 2, 2026
28f1720
fix: bind selected retention roots to their namespace (#109)
flyingrobots Oct 2, 2026
4f37597
test: expose relative durable locator retargeting (#109)
flyingrobots Oct 2, 2026
082c515
fix: bind durable store locators at construction (#109)
flyingrobots Oct 2, 2026
397164e
test: expose unsupported durable reader platforms (#109)
flyingrobots Oct 2, 2026
f1312d6
fix: enforce production platform admission for readers (#109)
flyingrobots Oct 2, 2026
c0bd6fb
Refactor: own authenticated read policy in the core (#109)
flyingrobots Oct 2, 2026
0a19dde
Docs: disclose reader admission directory synchronization (#109)
flyingrobots Oct 3, 2026
72ff8cc
test: isolate durable reader fixtures across stale process state (#109)
flyingrobots Oct 3, 2026
f352886
Merge main into #109 durable authenticated reads
flyingrobots Oct 3, 2026
8794c9e
docs: distinguish segment byte budget from snapshot memory (#109)
flyingrobots Oct 3, 2026
b512ce7
fix: distinguish supplied durable layout decode failures (#109)
flyingrobots Oct 3, 2026
9d19e2e
fix: preserve durable diagnostic boundaries without repeated causes (…
flyingrobots Oct 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ after its public API and format compatibility policies are established.

## [Unreleased]

- Durable read diagnostics render their boundary once while preserving each original typed source for error-chain reporters (#109).

- Caller-supplied malformed durable layout records now preserve the reconstruction or range input-decode boundary instead of reporting committed-layout corruption (#109).

- Corrected durable-read allocation documentation: the caller's aggregate byte limit caps catalog-selected segment bytes; catalog bytes and decoded metadata have separate bounds and allocate additionally (#109).

- Clarified the root-directory synchronization and precise admission-time I/O failure performed when opening a durable reader snapshot; the existing platform policy and runtime behavior are unchanged (#109).

- Reference and durable reads now share an inward authentication core and immutable chunk port, preserving public receipts, precise errors and codec admission at the adapter boundary (#109).

- Retention and durable snapshot readers now enforce the existing version-two filesystem profile through the same opened directory capability, rejecting unsupported filesystems without acquiring writer authority (#109).

- `DurableStore::open` now fixes an absolute locator and returns a typed result, preventing later working-directory changes from retargeting a handle; locator failures preserve their original I/O source (#109).

- Selected retention-root reads now refuse a canonical root belonging to another namespace, preserving typed expected/observed namespace digests before durable snapshot admission or output (#109).

- Added an in-progress fenced durable read API with view-bound reconstruction and range receipts; full read-law and Worldline acceptance remains tracked in #109.

- Strengthened writer-lock replacement evidence with a private after-lock scheduling checkpoint, exact refusal and preserved file bytes; production lock ordering and public behavior are unchanged (#169).

- Migration compatibility laws preserve version-one bytes and reject version-one authority at every forward prefix; public version/flag refusal tests and bounded seeded recovery-planner fuzzing extend transition evidence (#112).
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,32 @@ assert_eq!(output, b"exact bytes, or nothing");
# Ok::<(), Box<dyn std::error::Error>>(())
```

On Linux with the admitted ext4 profile, read an existing migrated version-two store whose retention roots anchor the requested blob. The following example is also compiled in the `DurableStore` API documentation. Its 16 MiB limit applies to aggregate catalog-selected segment bytes; catalog bytes and decoded metadata allocate separately.

```rust
#[cfg(target_os = "linux")]
fn copy_retained_blob(
root: &std::path::Path,
target: keep::BlobId,
output: &mut impl std::io::Write,
) -> Result<keep::DurableReconstructionReceipt, Box<dyn std::error::Error>> {
use keep::{
CatalogRestartByteLimit, CatalogRestartPolicy, DurableStore, LayoutEntryLimit,
ReaderAttemptLimit, SegmentReadPolicy, SegmentRecordLimit,
};
// Limit catalog-selected segment bytes; catalog and metadata allocate separately.
let policy = CatalogRestartPolicy::new(
SegmentReadPolicy::new(SegmentRecordLimit::MAXIMUM, LayoutEntryLimit::MAXIMUM),
CatalogRestartByteLimit::new(16_777_216)?,
);
let store = DurableStore::open(root, policy, ReaderAttemptLimit::DEFAULT)?;
let snapshot = store.snapshot()?;
Ok(snapshot.reconstruct(target, output)?)
}
```

Snapshot admission materializes catalog bytes under the catalog format bounds and selected segment bytes under the supplied aggregate segment budget. Decoded indexes and retention records allocate additionally under separate format and record-count limits; the segment budget is not a total memory cap. Reads stream to the caller without an additional whole-blob buffer; a failed write may leave an untrusted prefix. Retain an explicit snapshot to make several reads against one fenced view. The receipt records that view but grants no retention after the snapshot is dropped. This read API performs no publication, repair or collection; see the [durable-read evidence and remaining acceptance work](docs/testing-evidence/durable-authenticated-reads.md).

Run the full gate suite the way CI does:

```bash
Expand Down
32 changes: 17 additions & 15 deletions docs/invariants/authenticated-reconstruction/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# Authenticated Reconstruction Contract

**Status:** Normative for every Keep operation that claims authenticated
reconstruction. The public non-durable `ReferenceStore` implements the
complete-object and exact-range forms. A consolidated durable logical-read
surface is not yet implemented.
**Status:** Normative for every Keep operation that claims authenticated reconstruction. The public non-durable `ReferenceStore` implements the complete-object and exact-range forms. Linux `DurableStore` and `DurableSnapshot` compose those read cores with fenced version-two catalog and retained-root admission; final #109 acceptance remains recorded in the [evidence ledger](../../testing-evidence/durable-authenticated-reads.md).

The [rationale](rationale.md) records the governed decisions and rejected
alternatives. The [requirement ledger](requirements.md) maps each law to its
Expand Down Expand Up @@ -208,7 +205,7 @@ range and explicitly carry the narrower range proof posture.

## Durable reconstruction requirement

A future operation claiming durable logical reconstruction must additionally:
An operation claiming durable logical reconstruction must additionally:

- bind reads to one admitted immutable snapshot or catalog generation;
- prevent required supporting evidence from being garbage-collected, deleted,
Expand All @@ -220,12 +217,17 @@ A future operation claiming durable logical reconstruction must additionally:
- separate evidenced refusal from operational failure;
- preserve the output-visibility rule above.

The current durable segment, catalog, publication, and recovery surfaces do
not yet form this consolidated high-level `BlobId`-to-writer contract.
Retention publication now records verified closures as generation-checked
roots, but nothing collects or fences yet, so no current surface protects or
releases the evidence closure this operation requires. These lower-level surfaces must not be
described as an implemented durable logical reconstruction API.
`DurableStore` pins a fresh `DurableSnapshot` per convenience read. An explicit snapshot holds the shared reader fence, verifies manifest-selected retained closures, and preserves its catalog and liveness coordinates while retention successors publish. Blob reads select a retained anchor; exact-layout reads may use an unretained catalogued layout. Both authenticate exact immutable bytes before output and return the reference receipt together with catalog generation/digest and the selected retention head when present.

`DurableStore::open` returns a result and fixes an absolute locator at construction; changing the process working directory later cannot select a different store through that handle. Failure to resolve the locator preserves the original I/O cause through `DurableStoreError::Locator`, before store admission or output.

Snapshot admission requires the existing writable, case-sensitive local Linux ext4 profile across the root and present version-two protocol directories, retaining the admitted root capability through collection without acquiring writer authority. Valid migration records on an unsupported filesystem do not admit a readable snapshot.

Every selected retention root must match its manifest entry's namespace as well as its root generation and digest; a canonical foreign-namespace root refuses with preserved expected and observed namespace coordinates before a durable snapshot or caller output is exposed.

Admission materializes bounded catalog and segment bytes and verifies broader stored evidence than the logical range core. Each read re-admits those owned bytes and decoded indexes; the core's single authentication pass is not an end-to-end single-hash or minimal physical-I/O guarantee. Reads add no whole-blob output buffer. The caller's aggregate byte budget limits catalog-selected segment bytes only. Catalog bytes have separate format bounds, and decoded indexes and retention records allocate additionally under their own format and record-count limits. Retained-root traversal uses the persisted per-root limits.

The fence coordinates cooperating Keep operations in a managed namespace. Arbitrary concurrent out-of-band mutation is outside that isolation guarantee; exact-byte, no-follow, identity and corruption protections still apply. Actual GC remains absent, so collector evidence demonstrates kernel fence exclusion rather than end-to-end collection. A receipt grants no retention beyond the snapshot lifetime and does not make caller output transactional.

## Current public evidence

Expand All @@ -241,12 +243,12 @@ Evidence anchors:
- [exact logical byte identity](../../adr/0001-exact-logical-byte-identity.md)
- [identity and physical-storage separation](../../adr/0002-separate-identity-from-physical-storage.md)
- [`ReferenceStore` contract tests](../../../tests/reference_store_contract.rs)
- [reconstruction implementation](../../../src/reference/reconstruction.rs)
- [range-read implementation](../../../src/reference/range_read.rs)
- [shared reconstruction core](../../../src/authenticated_read/reconstruction.rs)
- [shared range-read core](../../../src/authenticated_read/range_read_execution.rs)
- [whole-object refusal laws](../../../tests/streaming_cas/refusal_laws.rs)
- [range-read refusal laws](../../../tests/range_read_failures.rs)
- [reconstruction receipt](../../../src/reference/reconstruction_receipt.rs)
- [range-read receipt](../../../src/reference/range_read_receipt.rs)
- [reconstruction receipt](../../../src/authenticated_read/reconstruction_receipt.rs)
- [range-read receipt](../../../src/authenticated_read/range_read_receipt.rs)

## Consumer rule

Expand Down
10 changes: 3 additions & 7 deletions docs/invariants/authenticated-reconstruction/rationale.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ successful receipt authenticates the complete emitted sequence. Failure may
leave an untrusted prefix, so consumers that require atomic visibility must
quarantine output and publish it transactionally after receipt validation.

Current `ReferenceStore` behavior is the executable oracle for non-durable
complete-object and range forms. It is not evidence that Keep has a durable
logical reconstruction API. A future durable form must pin one immutable view,
retain its complete supporting evidence, and bind the view into its result.
Current `ReferenceStore` behavior is the executable oracle for non-durable complete-object and range forms. The Linux durable adapter has its own public filesystem witnesses and composes the same immutable read cores with catalog, retained-closure and reader-fence admission. It pins the supporting view and binds its coordinates into successful receipts; reference tests alone are not evidence for those filesystem obligations.

## Governed surfaces

Expand All @@ -34,7 +31,7 @@ This decision governs:
- receipt and refusal meaning;
- output visibility after failure;
- layout selection and committed layout-to-target binding;
- the future durable read aperture and evidence-retention obligation; and
- the durable read aperture and evidence-retention obligation; and
- the public integration boundary available to consumers.

It does not govern Echo semantics, causal authority, application retry law, or
Expand Down Expand Up @@ -86,6 +83,5 @@ is retained under the same contract.
- Caller-supplied range layouts must resolve through an admitted store view.
- A failure after output began returns no success receipt; accepted bytes
remain untrusted.
- Durable integration remains blocked on a pinned-view consumer capability and
evidence retention.
- Durable reads hold a shared reader fence across output; production GC is still absent, and the fence does not isolate arbitrary raw namespace mutation.
- Application-specific meaning remains outside Keep core.
10 changes: 6 additions & 4 deletions docs/invariants/authenticated-reconstruction/requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ gap, not implementation evidence.
| `KEEP-RECONSTRUCT-003` | A range receipt proves only requested bytes from authenticated overlapping chunks; it proves neither the complete blob nor any storage-profile boundary. | Minimal-overlap range model | Unit, property, and public API integration tests | Implemented | `src/reference/range_read_tests.rs`, `tests/range_read.rs`, `tests/range_read_properties.rs` |
| `KEEP-RECONSTRUCT-004` | A range receipt names only a layout-to-target binding admitted by the selected store view. | Forged same-length target-layout fixture | Public API corruption test | Implemented | `tests/range_read_entrypoints.rs` |
| `KEEP-RECONSTRUCT-005` | Success authenticates the complete emitted sequence; failure returns no success receipt and reports the exact accepted prefix, which remains untrusted. | Deterministic prefix-then-fail writer | Public API failure tests | Implemented | `tests/streaming_cas/refusal_laws.rs`, `tests/range_read_failures.rs` |
| `KEEP-RECONSTRUCT-006` | Authenticated success, evidenced content refusal, and operational failure remain distinct outcomes; operational failure supports no content conclusion. | Typed outcome classification | Public API integration tests and contract inspection | Implemented for `ReferenceStore`; durable refusal receipts planned | `tests/streaming_cas/refusal_laws.rs`, `tests/range_read_failures.rs`; [Keep #22](https://github.com/flyingrobots/keep/issues/22) |
| `KEEP-RECONSTRUCT-007` | Whole-object and range receipts bind target, exact layout, proof scope, and exact emitted coordinates without granting retention or application authority. | Receipt type inspection | Public API contract tests | Implemented | `src/reference/reconstruction_receipt.rs`, `src/reference/range_read_receipt.rs`, `tests/range_read_contract.rs` |
| `KEEP-RECONSTRUCT-006` | Authenticated success, evidenced content refusal, and operational failure remain distinct outcomes; operational failure supports no content conclusion. | Typed outcome classification | Public API integration tests and contract inspection | Implemented typed outcomes; persisted refusal receipts remain separate | `tests/streaming_cas/refusal_laws.rs`, `tests/range_read_failures.rs`, `tests/golden_file_worldline/durable_refusal_laws.rs`, `tests/golden_file_worldline/durable_writer_failures.rs` |
| `KEEP-RECONSTRUCT-007` | Whole-object and range receipts bind target, exact layout, proof scope, and exact emitted coordinates without granting retention or application authority. | Receipt type inspection | Public API contract tests | Implemented | `src/authenticated_read/reconstruction_receipt.rs`, `src/authenticated_read/range_read_receipt.rs`, `tests/range_read_contract.rs` |
| `KEEP-RECONSTRUCT-008` | Automatic layout choice is deterministic; an exact requested layout never falls back. | Canonically ordered layout set | Unit and public API integration tests | Implemented | `src/reference/store_tests.rs`, `tests/streaming_cas/reconstruction_laws.rs` |
| `KEEP-RECONSTRUCT-009` | A durable read pins one immutable view and prevents required evidence from being garbage-collected, deleted, or invalidated through completion. | Pinned-generation and retained-closure model | Recovery, concurrency, corruption, and crash-injection tests | Planned gap | [Keep #22](https://github.com/flyingrobots/keep/issues/22), [Keep #23](https://github.com/flyingrobots/keep/issues/23) |
| `KEEP-RECONSTRUCT-010` | Durable reconstruction names its view and returns either authenticated success, evidenced refusal, or operational failure without hidden whole-blob allocation. | Durable consumer conformance model | Public API integration, memory, recovery, and crash-injection tests | Planned gap | [Keep #22](https://github.com/flyingrobots/keep/issues/22), [Keep #23](https://github.com/flyingrobots/keep/issues/23) |
| `KEEP-RECONSTRUCT-009` | A durable read pins one immutable view and prevents required evidence from being garbage-collected, deleted, or invalidated through completion. | Pinned-generation and retained-closure model | Recovery, concurrency, corruption, and crash-injection tests | Implemented for cooperating managed-store operations; final #109 acceptance pending | `src/adapters/retention/durable_view_law_tests.rs`, `tests/golden_file_worldline/durable_assertions.rs`; [scope and evidence](../../testing-evidence/durable-authenticated-reads.md) |
| `KEEP-RECONSTRUCT-010` | Durable reconstruction names its view and returns either authenticated success, evidenced refusal, or operational failure without hidden whole-blob allocation. | Durable consumer conformance model | Public API integration, memory, recovery, and crash-injection tests | Implemented; final #109 acceptance pending | `tests/golden_file_worldline/durable_read_memory.rs`, `tests/golden_file_worldline/durable_corruption_laws.rs`, `tests/golden_file_worldline/durable_output_laws.rs`; [scope and evidence](../../testing-evidence/durable-authenticated-reads.md) |

The durable implementation status describes the current candidate, not a merged release or completed independent review. Snapshot admission materializes catalog-selected segment bytes under the caller's aggregate segment-byte limit; catalog bytes have a separate format maximum, while decoded indexes and retention records allocate additionally under format and record-count limits. The segment limit is not a total snapshot-memory cap; the memory witness measures additional reconstruction allocation. Managed-namespace cooperation is required for the fence guarantee. The #109 ledger distinguishes fresh reopen from process death and kernel exclusion from absent production GC.
Loading
Loading