Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e1d6765
Docs: plan catalog publication evidence
flyingrobots Jul 29, 2026
4100557
Add checked catalog generations
flyingrobots Jul 29, 2026
3fd39f8
Add checksummed publication heads
flyingrobots Jul 29, 2026
ac06c40
Add checksummed catalogs
flyingrobots Jul 29, 2026
61e13de
Bind catalogs to admitted records
flyingrobots Jul 29, 2026
90356fc
Enforce exact catalog successors
flyingrobots Jul 29, 2026
db38f81
Pin immutable catalog snapshots
flyingrobots Jul 29, 2026
9f13b6b
Enforce persistent writer exclusion
flyingrobots Jul 29, 2026
14c8afd
Emit canonical catalogs and heads
flyingrobots Jul 29, 2026
e8f31c2
Order durable catalog publication
flyingrobots Jul 29, 2026
95c3d3a
Load published catalog snapshots
flyingrobots Jul 29, 2026
67f6b73
Publish catalogs through the filesystem
flyingrobots Jul 29, 2026
5eec233
Model catalog transitions and lookups
flyingrobots Jul 29, 2026
5ad5ca4
Docs: record catalog publication evidence
flyingrobots Jul 29, 2026
2d2a777
Close segment stages before publication
flyingrobots Jul 29, 2026
039ce7e
Bind segment staging to writer authority
flyingrobots Jul 29, 2026
8ed7533
Retain writer authority through publisher teardown
flyingrobots Jul 29, 2026
aad0309
Bound catalog admission record scans
flyingrobots Jul 29, 2026
8fe1c5a
Refuse unreferenced catalog segments
flyingrobots Jul 29, 2026
ddb5243
Refuse unreferenced catalog inputs
flyingrobots Jul 29, 2026
5ab28cd
Document exact catalog input contracts
flyingrobots Jul 29, 2026
f0b1527
Make durable publication retries idempotent
flyingrobots Jul 29, 2026
98d8e80
Refuse retained stages before publication
flyingrobots Jul 29, 2026
45b4e9f
Fix fuzz dependency policy configuration
flyingrobots Jul 29, 2026
c3cc0bd
Update fuzz lock for capability dependencies
flyingrobots Jul 29, 2026
a8e3af1
Fuzz catalog format boundaries
flyingrobots Jul 29, 2026
7e33c16
Fix Linux catalog directory synchronization
flyingrobots Jul 29, 2026
a1c5acb
Bind segment stages to publisher authority
flyingrobots Jul 29, 2026
e3e7c00
Refuse orphaned pools without a catalog head
flyingrobots Jul 29, 2026
e9cb1eb
Require platform admission for publishers
flyingrobots Jul 29, 2026
4bd62c8
Verify catalog integrity before entry semantics
flyingrobots Jul 29, 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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ jobs:
run: cargo deny check

- name: Check fuzz dependency policy
run: cargo deny --manifest-path fuzz/Cargo.toml check --config deny.toml
run: cargo deny --manifest-path fuzz/Cargo.toml check --config ../deny.toml

- name: Check security advisories
run: cargo audit
Expand Down
65 changes: 58 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ after its public API and format compatibility policies are established.

### Changed

- Catalog decoding now verifies the catalog checksum and physical digest before
interpreting entry semantics. Corrupt identity-bearing bytes therefore fail
at the integrity boundary instead of producing a semantic entry error.
- Filesystem catalog publisher construction now consumes an unforgeable
`FilesystemPlatformAdmission`. No public producer exists until crash-tested
initialization can establish the platform contract in issue #17; acquiring
`FilesystemWriterLock` alone no longer authorizes production construction.
- Filesystem segment selection now consumes sealed stages through the publisher
that created them. Process-local publisher authority prevents an unrelated
metadata-equivalent `ClosedSegment` from authorizing retained
`staging/current.seg` bytes.
- First catalog publication now admits an absent `HEAD` only after proving that
both immutable pools are empty. Retained segment or catalog bytes require
explicit recovery and remain untouched.
- Filesystem catalog publishers now retain no-follow, read-capable directory
handles so required durability synchronization works on Linux instead of
failing on `O_PATH` descriptors.
- The fuzz-workspace dependency gate now loads the reviewed repository
`deny.toml` explicitly and admits non-Apache licenses only through exact
package/version exceptions.
- Documentation corpus selection, pinned tool admission, Markdown and fragment
checks, workflow linting, Dependabot coverage, and Node lock-graph policy now
run through bounded Rust `xtask` code; CI and `cargo xtask verify` use that
Expand Down Expand Up @@ -184,6 +204,35 @@ after its public API and format compatibility policies are established.

### Added

- Checked catalog generations; canonical catalog and publication-head codecs;
exact logical-record-to-segment admission with one bounded physical lookup
plan, one scan per referenced segment, and refusal of every unreferenced
caller-supplied segment during construction or admission; deterministic
successor proofs; immutable reader snapshots; seeded parser fuzzing; and
`BTreeMap` transition-model evidence for `keep.segment-store/v1`.
- Blocking `FilesystemCatalogPublisher` publication under a persistent
kernel-managed writer lock and required `FilesystemPlatformAdmission`, with
pinned directory capabilities,
no-replacement immutable-pool links, complete post-link verification,
explicit file and directory synchronization, transitive `head.next`
verification, atomic `HEAD` replacement, and stale or recovery-required
refusal before mutation. New filesystem segment publication consumes the
sealed stage through its creating publisher, checks process-local publisher
authority, and closes the writable handle before any immutable-pool link;
publisher teardown closes every retained writable handle before releasing
writer authority.
Retry of an already-current complete candidate re-synchronizes the root and
returns an explicit `CatalogPublicationOutcome::AlreadyPublished` receipt
without repeating publication mutations. Retained `head.next` or
`current.cat`, an unselected `current.seg`, and every fixed-name stage on an
already-current retry now refuse at current-state verification before any
publication mutation. An absent `HEAD` with any retained segment-pool or
catalog-pool entry also refuses before mutation.
- Bounded `FilesystemCatalogSnapshot` restart loading that follows only exact
checksummed head, catalog, and segment coordinates; refuses symbolic links,
nonregular files, malformed or conflicting bytes, dangling entries, and
resource-limit violations; and retains immutable bytes for pinned logical
reads.
- Public, allocation-free `SegmentHeader` admission and emission for the exact
`keep.segment-store/v1` 64-byte header, with field-complete typed refusals
and golden-corpus evidence.
Expand All @@ -204,9 +253,11 @@ after its public API and format compatibility policies are established.
for exact append-only record writing, streaming seal construction, explicit
prefix/sealed flush-and-sync order, phase-typed I/O refusals, and a fallibly
reserved membership index for sublinear duplicate admission.
- Exclusive `FilesystemSegmentStage` creation for the fixed `current.seg`
staging name, with atomic no-replacement admission, preserved existing
evidence, zero-origin writing, and no implicit cleanup from `Drop`.
- Writer-authorized `FilesystemSegmentStage` creation for the fixed
`current.seg` staging name, with a lifetime that retains the
`FilesystemCatalogPublisher` lock, atomic no-replacement admission,
preserved existing evidence, zero-origin writing, and no implicit cleanup
from `Drop`.
- Rust cargo-fuzz coverage for the public segment header, record header,
complete record, seal, and complete-segment parser boundaries, seeded from
the canonical version-1 segment fixtures through `cargo xtask`.
Expand All @@ -225,10 +276,10 @@ after its public API and format compatibility policies are established.
fixed-name stage into its immutable pool and durably clear the stage without
promoting a publication head. Explicit discard receipts now follow
synchronization of the stage's actual parent: `staging` for segment and
catalog stages, or the store root for `head.next`. Production storage
remains assigned to issues #15–#17. The golden corpus now includes a
generation-2 catalog/head pair whose predecessor field is the exact
generation-1 catalog digest.
catalog stages, or the store root for `head.next`. Segment and catalog
production are implemented; crash recovery remains assigned to issue #17.
The golden corpus now includes a generation-2 catalog/head pair whose
predecessor field is the exact generation-1 catalog digest.
- A deterministic, bounded, license-safe streaming CAS benchmark corpus and
release-only `cargo xtask benchmark-baseline` workflow covering all required
ingestion, edit, deduplication, range-read, verification, and input
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ publish = false

[dependencies]
blake3 = { version = "=1.8.5", default-features = false, features = ["pure", "std"] }
cap-fs-ext = { version = "=4.0.2", default-features = false, features = ["std"] }
cap-std = { version = "=4.0.2", default-features = false }

[dev-dependencies]
allocation-counter = { version = "=0.8.1", default-features = false }
Expand Down
47 changes: 35 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,44 @@ and verifies the complete named `BlobId` before writing any bytes. Range reads
load only the minimal overlapping chunks and state their narrower verification
claim explicitly.

The public `keep.segment-store/v1` boundary provides exact segment, record, and
seal codecs plus explicit immutable-segment transitions. `StagedSegment`
writes only content-admitted chunk or layout records, while `AdmittedSegment`
exposes payloads only after complete framing, checksum, logical-identity,
duplicate, and physical-digest verification. `FilesystemSegmentStage`
exclusively creates the fixed `current.seg` stage without truncating existing
evidence.
The public `keep.segment-store/v1` boundary provides exact segment, record,
seal, catalog, and publication-head codecs plus explicit immutable-segment and
catalog-generation transitions. `StagedSegment` writes only content-admitted
chunk or layout records, while `AdmittedSegment` exposes payloads only after
complete framing, checksum, logical-identity, duplicate, and physical-digest
verification. A platform-admitted `FilesystemCatalogPublisher` exclusively
creates the fixed `current.seg` stage without truncating existing evidence,
and the `FilesystemSegmentStage` lifetime keeps that writer authority borrowed
until the writable stage closes. Publisher construction consumes an
unforgeable `FilesystemPlatformAdmission`; no public producer exists until
issue #17 implements crash-tested initialization and platform admission.

`FilesystemCatalogPublisher` retains one kernel-managed writer lock and pinned
root, staging, segment-pool, and catalog-pool capabilities for the complete
blocking publication. It reopens and verifies synchronized stages, uses
no-replacement immutable-pool links, synchronizes every required file and
directory, verifies the complete `head.next` view, atomically replaces `HEAD`,
and returns a receipt only after root synchronization. New filesystem segment
publication requires `FilesystemCatalogPublisher::select_segment` to consume
the sealed writable stage, prove that this publisher created it, and bind its
synchronized metadata to exact admitted bytes. A storage-agnostic
`ClosedSegment` receipt alone cannot authorize a retained filesystem stage.
`FilesystemCatalogSnapshot` follows only the exact checksummed head, catalog,
and segment coordinates and retains caller-bounded immutable bytes for pinned
logical reads.

The reference CAS is executable evidence for M2 storage laws, not a durable
backend. Its committed state is process memory; process death loses it all.
The segment boundary does not publish catalogs, synchronize its containing
directory, open a durable namespace, or perform restart recovery. Catalog
publication, retention, recovery, verification of complete durable namespaces,
compaction, and garbage collection remain planned work. Presence in the
reference CAS does not claim retention, crash recovery, or durability.
The durable boundary does not yet initialize, platform-admit, or recover a
store root. Acquiring `FilesystemWriterLock` alone cannot construct a
filesystem publisher. Issue #17 must admit the exact existing `writer.lock`,
`staging`, `segments`, and `catalogs` namespace before it can return
`FilesystemPlatformAdmission`. Leftover `head.next`, staged recovery evidence,
unknown namespace entries, and ambiguous crash states remain explicit recovery
work. An absent `HEAD` is admitted for first publication only when both
immutable pools are empty. Retention, complete namespace verification,
compaction, and garbage collection remain planned. Presence in the reference
CAS does not claim retention, crash recovery, or durability.

```rust
use keep::BlobId;
Expand Down
8 changes: 8 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ allow = [
exceptions = [
# `arrayref` is a required transitive dependency of locked BLAKE3 1.8.5.
{ allow = ["BSD-2-Clause"], crate = "arrayref@0.3.9" },
# `libfuzzer-sys` is admitted only by the separate fuzz workspace.
{ allow = ["Apache-2.0", "MIT", "NCSA"], crate = "libfuzzer-sys@0.4.13" },
# `memchr` is activated by fuzz-only repository JSON validation.
{ allow = ["MIT"], crate = "memchr@2.8.3" },
# `winx` is a required transitive dependency of locked `cap-primitives` 4.0.2.
{ allow = ["Apache-2.0 WITH LLVM-exception"], crate = "winx@0.36.4" },
# `zmij` is activated by fuzz-only repository JSON validation.
{ allow = ["MIT"], crate = "zmij@1.0.23" },
]

[sources]
Expand Down
43 changes: 29 additions & 14 deletions docs/dependencies/cap-std-and-cap-fs-ext-4.0.2.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# Dependency Admission: cap-std, cap-fs-ext 4.0.2, and rustix 1.1.4

- Status: Accepted for repository-task filesystem boundaries only
- Status: Accepted for repository-task and segment-store filesystem boundaries
- Date: 2026-07-26
- Owner: Keep repository verification
- Upstream:
[bytecodealliance/cap-std](https://github.com/bytecodealliance/cap-std)

## Admitted use

Keep admits the exactly pinned `cap-std` 4.0.2, `cap-fs-ext` 4.0.2, and
`rustix` 1.1.4 packages only behind the `xtask` crate's `repository-tasks`
feature.
Keep admits the exactly pinned `cap-std` 4.0.2 and `cap-fs-ext` 4.0.2 packages
for the library's segment-store filesystem adapter and behind the `xtask`
crate's `repository-tasks` feature. Rustix 1.1.4 remains admitted only behind
that `xtask` feature.

`cap-std::fs::Dir` pins the admitted repository or corpus directory and opens
entries relative to that capability. `cap-fs-ext` supplies no-follow and
Expand All @@ -21,9 +22,11 @@ these operations let repository checks refuse persistent root replacement,
path substitution, symlinked protocol tables, FIFOs, sockets, devices, and
other ambiguous filesystem state before reading source or protocol bytes.

These packages are absent from Keep's published library graph, public API,
content identities, durable formats, and production behavior. No
dependency-owned type crosses out of the private repository-task adapter.
The capability packages are present in Keep's published library graph and
production filesystem behavior. No dependency-owned type crosses Keep's public
API or enters content identities or durable formats. The segment-store writer
lock retains capability and file handles behind `FilesystemWriterLock`; its
public acquisition boundary accepts only `std::path::Path`.

The bounded subprocess adapter uses Rustix's safe filesystem API to mark child
stdin nonblocking before deadline-bounded input transfer. It uses Rustix's safe
Expand Down Expand Up @@ -64,8 +67,10 @@ work, and require unsafe code that Keep otherwise forbids.

All three direct dependencies disable default features. Keep enables only
`cap-fs-ext`'s `std` feature and Rustix's `fs`, `process`, and `std` features;
`cap-std` has no enabled feature. All declarations are optional and are
activated solely by `repository-tasks`.
`cap-std` has no enabled feature. The library's capability dependencies are
unconditional because the production segment-store adapter requires them.
The `xtask` declarations remain optional and are activated solely by
`repository-tasks`; Rustix is not a direct library dependency.

The locked non-Windows graph introduced for this boundary is:

Expand All @@ -86,12 +91,18 @@ The locked non-Windows graph introduced for this boundary is:

Windows resolution additionally retains the locked `windows-sys`,
`windows-targets`, and architecture packages recorded in `Cargo.lock`.
The library therefore exempts only Clippy's `multiple_crate_versions` cargo
lint; exact direct versions, the committed lockfile, dependency policy, and
advisory checks remain authoritative.

## Safety, licensing, and compatibility

The capability packages declare
`Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT`; Keep selects an admitted
license through repository policy. Rustix declares `Apache-2.0 OR MIT`.
The locked `winx` 0.36.4 transitive package declares only
`Apache-2.0 WITH LLVM-exception`, so `deny.toml` admits that exact package and
license combination rather than broadening the global license allowlist.
Their manifests declare no Rust-version floor. Compatibility is therefore
established only by Keep's pinned stable, MSRV, debug, release, Clippy,
dependency-policy, and advisory lanes.
Expand All @@ -106,11 +117,15 @@ dependencies.

## Failure and recovery boundaries

An open, metadata, read, descriptor-duplication, descriptor-flag,
child-directory setup, child-spawn, stdin-write, output-collection, deadline,
or cleanup failure is a typed refusal. The task never repairs, rewrites, or
substitutes repository data. Retained handles exist only for one verification
process and carry no durability or recovery semantics.
An open, metadata, read, writer-lock acquisition, descriptor-duplication,
descriptor-flag, child-directory setup, child-spawn, stdin-write,
output-collection, deadline, or cleanup failure is a typed refusal.
Repository tasks never repair, rewrite, or substitute repository data.
Repository-task handles exist only for one verification process and carry no
durability or recovery semantics. `FilesystemWriterLock` retains the pinned
store root and persistent lock-file handles for the complete writer-authority
lifetime; dropping the guard releases only the kernel lock and never mutates
the lock file.

Keep can remove these dependencies without changing public or durable behavior
by replacing them with an equally portable, safe implementation that preserves
Expand Down
7 changes: 4 additions & 3 deletions docs/dependencies/libfuzzer-sys-0.4.13.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ the fuzz executable workspace. Keep-owned production crates remain
`unsafe_code = "forbid"`, and fuzz inputs cross into Keep exclusively through
safe public byte-slice and string APIs.

The package is licensed as `(MIT OR Apache-2.0) AND NCSA`; the fuzz-specific
dependency policy contains an exact package/version exception. No global NCSA
or MIT license allowance follows from this admission.
The package is licensed as `(MIT OR Apache-2.0) AND NCSA`; the repository
dependency policy contains an exact package/version exception exercised
against the separate fuzz graph. No global NCSA or MIT license allowance
follows from this admission.

## Version, features, and transitive graph

Expand Down
10 changes: 5 additions & 5 deletions docs/dependencies/serde-and-serde-json-1.0.229-1.0.151.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ workspace lockfile.
Their manifests declare minimum supported Rust versions below Keep's pinned
toolchain.

The fuzz-workspace license policy retains Apache-2.0 as its default allowlist
and grants exact-version MIT exceptions to `memchr` 2.8.3 and `zmij` 1.0.23.
Those exceptions admit only the reviewed transitive graph named above; a
resolved version change remains a policy failure until this record and the
exception are reviewed together.
The repository license policy retains Apache-2.0 as its default allowlist and
grants exact-version MIT exceptions to `memchr` 2.8.3 and `zmij` 1.0.23 when
it checks the separate fuzz graph. Those exceptions admit only the reviewed
transitive graph named above; a resolved version change remains a policy
failure until this record and the exception are reviewed together.

Keep-owned code invokes only safe APIs. The parser and its transitive
dependencies may contain implementation details outside Keep's `unsafe_code`
Expand Down
5 changes: 3 additions & 2 deletions docs/formats/segment-store-v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ visibility, and recovery as one contract.

ADR-0005 records the cross-cutting decision. These pages are a protocol
commitment. Segment writing and verified reading are implemented in issue #15.
Catalog publication remains owned by issue #16, and complete executable crash
and recovery evidence remains owned by issue #17.
Catalog generation, writer-locked publication, and immutable restart snapshots
are implemented in issue #16. Store initialization and complete executable
crash and recovery evidence remain owned by issue #17.

## Core law

Expand Down
18 changes: 13 additions & 5 deletions docs/formats/segment-store-v1/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ payload_length + 144 = record_length

The named segment must exist at the digest-derived immutable-pool name and
verify completely. The record at the declared span must reproduce the entry's
kind, identity, lengths, and checksum exactly.

Catalog admission scans the complete segment grammar from byte 64 through the
declared record count and records every top-level record span before admitting
locations. Each `(record_offset, record_length)` pair must equal one discovered
kind, identity, lengths, and checksum exactly. Every supplied admitted segment
must be named by at least one entry. Catalog construction refuses zero-record
segments, and admission refuses extra unreferenced segments.

Catalog admission creates one bounded plan over the declared entries, groups
that plan by segment digest and physical offset, and scans each referenced
segment exactly once. Each scan runs from byte 64 through the declared record
count. Each `(record_offset, record_length)` pair must equal one discovered
top-level record span. A location into a record header, payload, checksum, or
segment seal is refused even when those embedded bytes independently resemble
a valid record.
Expand All @@ -100,6 +103,11 @@ catalog_digest = framed_blake3_v1(
)
```

After admitting the fixed header and exact declared length, decoding verifies
both trailer fields before interpreting any entry semantics. Covered-byte
corruption therefore returns a checksum or digest disagreement even when the
same bytes would also violate an entry field invariant.

The catalog digest is a physical generation coordinate and predecessor
witness. It does not establish retention or application history.

Expand Down
Loading