Changes touching this path

  • Forge service skeleton: /ingest, the sync surface, and CLI forge remotes (#502) A new `loot-forge` crate and binary, sibling to the relay and never a mutation of it: `loot-net` is a dependency of `loot-cli`, so a storage driver in the relay would ship inside the released CLI binary (ADR 0041:53-57). The ban runs one way -- the forge may use the other crates; none of them may gain a driver. Every endpoint is envelope-authenticated, where a relay authenticates only /stow. The one declared exception is GET /info: a client reads the capability probe before it knows how to authenticate. POST /ingest = envelope_sign(bundle || head_declaration), one signature over both, so a genuine bundle can never be replayed under someone else's head claim. CAS first; nothing is written before the generation compare. A lost CAS answers with a 409 BODY carrying the current generation and head set -- behind, not bounced -- so the client re-carries with no extra round trip. /stow keeps relay parity and moves no ref, and consumes no generation either, since a client reads the generation once and then stows N batches. It DEFERS a change whose objects have not all arrived; /ingest REFUSES one, because a declared head whose objects are missing is a repo no pull can complete. That asymmetry is what keeping /stow is for. Bundles are filtered PER OBJECT by the caller's access class, closing the leak the ticket names: bundle_impl ships a key for every ANYONE-granted object, i.e. every Internal object. Change metadata rides the second #494 axis and gates the whole walk -- a ChangeNode carries the full tree, so shipping one while withholding its bytes still discloses every path, address, message and author. Validation before any write: author signatures, and a tree whose visibility contradicts its objects is rejected. vis_tag is derived from the SealedObject and never trusted from the tree, which is what makes content-addressed upserts safe. Forge purge policy: burn iff burner == object.introduced_by, recorded at first arrival as introducing.author else the envelope pubkey; maroon read off grant_log, global and exact. Both are judged before any object is stored and outside the CAS transaction. Repos are pubkey-addressed at /k/<pubkey>/<repo>, auto-created on first push, and may_push holds iff the signer owns the namespace. resolve_remote is untouched -- still no default host. CORS from day one. Storage sits behind three narrow traits with in-process reference implementations the whole suite runs against; the Postgres DDL they mirror is checked in at docs/sql/forge-schema.sql and its driver is #516. CLI: /info capability detection, the live head declaration off the Liveness view, and a refusal to push a lane -- v1 ingests the primary position only. verify_authored_change moves to loot-codec so the forge runs the identical gate without linking the fs-hardwired engine. Recorded in ADR 0041, the spec and CONTEXT.md: the forge stores no content key but a published one, because filing the ANYONE key lane would let the server read every Internal object, and the keystone is that only Published is server-readable. The cost is stated rather than hidden -- a fresh clone of your own Internal content from a forge is ciphertext you cannot open until #488 lands. Closes #502. 300e06ff · dbf3dbe6…
  • Sweep the forge blob tier for bytes that no longer address to their oid (#523) 3a1109bd · dbf3dbe6…diff
  • Forge storage drivers: Postgres metadata tier, S3 blob tier, and one conformance suite both must pass (#516) f65970e6 · dbf3dbe6…diff
  • the forge's five hand-kept wrapper lists stop being lists that could drift and become one macro stamp per adapter, so a case reaches every adapter by construction rather than because somebody remembered: each suite is a callback macro handing its names to the adapter's own arm, and one repetition emits both the tests and the const the roll call compares against, which is the stronger form of the ticket's ask - making the list unforgeable beats parsing for evidence it drifted, and the parse the ticket specified would have had to survive three Case::new calls that are not cases, two spellings of the path, and calls sitting in helpers. no source walker was needed or added, because everything here already lives under cfg(test) so test code is the subject rather than the thing to exclude. one text parse survives and only one side of it is text, since a case can still be written and named by no suite and rust has no reflection over a module's functions while this repo cannot run deny warnings: it reads column-zero pub(crate) fn in its own file only - and its premise is true by construction rather than by care, because the checker had to move inside an indented module or it named itself, which is not a thing anyone designed but what the guard did on its first run, red, on its own name. both halves were proved able to fail: deleting a case from a suite leaves every roll-call test green and the total quietly falling from 278 to 275 with only the parse catching it, and an adapter stamping a subset names the adapter, the count, the suite and the case. the limit is stated narrowly on purpose - every case is asked of every adapter that takes a suite, never that any adapter answered, since 56 of 66 pg cases and 7 of 12 s3 cases skip green with no credentials in CI - and a second limit nobody asked for is written down too, that nothing counts implementors, so a new store whose module never invokes the suite is asked nothing and this mechanism stays silent about it (#1655) 66876d46 · dbf3dbe6…diff

Renames are not followed. loot's tree maps a path to an address, so a rename is a delete and an add. This list is the history of the name, not of the bytes.