Federation: replicate content, re-derive judgement (UR-008)
Implements §9a. The replication surface is four reads and no writes: a change feed, fetch by content_id, a batch have, and a human-facing peer directory — plus a capabilities endpoint carrying the accepted envelope versions, which lets a client discover a schema mismatch in one request instead of a 400 per manifest across a library sweep. Pull, never push: a pulling server chooses what it ingests and when. Push would let any peer inject work into the validation queue — the same abuse surface as anonymous upload, at higher volume. Nothing inherits a peer's judgement. A pulled manifest runs the full §6 stage 1 and 2 validation and this server's own cast check, and the fetched body must hash to the content_id that was asked for — the check that stops an intermediary or a misbehaving peer substituting content under a trusted id. A peer's retraction flags for review rather than delisting, because auto-delisting would hand every peer a remote delete primitive; only the opt-in per-peer abuse channel delists, because a takedown propagating at the speed of manual review is the wrong failure mode for that one case. A test caught a real bug in the first cut: the feed cursor was a ULID, and ULIDs are only monotonic *between* milliseconds — two generated in the same millisecond carry independent random components and can sort opposite to write order. A peer resuming from `seq > cursor` would then silently skip an entry: replication losing manifests with no error anywhere. The cursor is now an AUTOINCREMENT integer, and the test asserts strict monotonicity rather than merely sortedness. Peer administration is deliberately not an API. §9a requires that a peering exist only because an operator typed a URL, so nothing a remote server returns can establish or widen one; there_is_no_endpoint_that_creates_a_peering asserts that absence rather than trusting it. 212 tests. Coverage 25/32 (78%). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: UR-008 | PR-006
This commit is contained in:
@@ -38,6 +38,11 @@ Implemented:
|
||||
- §7 relational storage, no JSON blobs on the write path
|
||||
- §8 Rust + Axum + SQLite, single serialized writer, in-process job queue
|
||||
- §9a content addressing (`content_id`), computed on upload
|
||||
- §9a federation — change feed, fetch by `content_id`, batch `have`, peer
|
||||
directory, capabilities, and a pull worker that **re-derives judgement rather
|
||||
than inheriting it**: every pulled manifest runs the full §6 validation and
|
||||
this server's own cast check, and the body is verified to hash to the
|
||||
`content_id` requested before it is stored
|
||||
|
||||
**Schema version 2** (SR-003). `jmanifest_version` moved to 2 in lockstep with
|
||||
the truth file's `schema_version` — breaking changes are batched and ship
|
||||
@@ -75,9 +80,7 @@ Deferred:
|
||||
`POST /manifests/search` are not wired up. This follows §3's own recommended
|
||||
sequencing: ship the plugin-side computation first, let signatures accumulate,
|
||||
then enable matching once coverage is useful.
|
||||
- §9a federation endpoints (`/federation/*`) and the pull worker. The schema
|
||||
columns (`content_id`, `origin`, `ingested_from`, `peers`) are in place, and
|
||||
`ingest::persist` is already the shared path a pull would reuse.
|
||||
(Federation landed — see below.)
|
||||
|
||||
## Running
|
||||
|
||||
@@ -124,7 +127,7 @@ curl -sX POST -H 'content-type: application/json' -d '{}' \
|
||||
## Tests
|
||||
|
||||
```sh
|
||||
cargo test # 191 tests
|
||||
cargo test # 212 tests
|
||||
cargo deny check # advisories, licences, bans, sources
|
||||
scripts/traceability-gate.sh # requirement coverage
|
||||
```
|
||||
@@ -137,9 +140,8 @@ git submodule update --init --recursive
|
||||
|
||||
It reports coverage against [`docs/requirements.md`](docs/requirements.md),
|
||||
flags orphan tags (an ID no register defines), and fails on a >100% ratio — the
|
||||
signal that the computation itself is broken. Currently **24/32 (75%)**; the
|
||||
untraced remainder is UR-007 (plugin-side) and UR-008 (federation), neither of
|
||||
which is implemented here yet.
|
||||
signal that the computation itself is broken. Currently **25/32 (78%)**; the
|
||||
untraced remainder is UR-007, which is plugin-side.
|
||||
|
||||
Unit tests per module, plus two integration suites:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user