Changes touching this path

  • Extract loot-codec + wasm core; de-risk the in-memory SDK boundary (#423) Slice 1 (the tracer bullet) is a multi-session build; this lands its riskiest integration — the Rust→WASM crypto/codec boundary — proven end-to-end, per the #381 "verify spikes before building" mandate. Spike result (ADR 0040): zstd-sys will not build for wasm32 (needs clang for its C), while aes-gcm/blake3/ed25519/getrandom-js do. So: - Extract `loot-codec`: a no-fs, wasm-buildable crate holding the byte format, sync-bundle codec, sealed content (AES-GCM + blake3), and the leaf types (Oid/Visibility/RepoError/ChangeNode). loot-core depends on it and re-exports every item at its original path — a pure relocation, proven by the unchanged, still-green workspace (767 tests pass). - zstd is an optional loot-codec feature (default on for the native host; off for wasm). A new zstd-free `sealed::decrypt` single-sources the AES-GCM step so native `open` and the wasm core share decrypt code; public content is inflated host-side in JS. - `loot-wasm`: wasm-bindgen exports (bundle decode, decrypt, blake3 address) over a pure native-testable `core`, plus the minimal diskless identity (generate/fromSeed/publicKey) on ed25519 directly — not loot-identity, whose OpenSSH/passphrase machinery is native-only (#383). - Golden-parity harness (loot-wasm/tests/parity.rs) runs identical assertions natively and under `wasm-pack test --node`, freezing native-computed vectors; both green. Remaining for slice 1 (next sessions): the TS package, fetch transport + client-side path-scoping, host-side zstd, and the live-relay behavior suite. Refs #423, #421. ADR 0040. 491cf780 · dbf3dbe6…
  • TS SDK slice 2: author & push a signed public change (#424) The write half of the in-memory loop: a pre-registered key edits files and pushes a signed full-tree change to the relay, read back to confirm. All composition (change-id fold, dual signatures, bundle encode, /stow envelope) stays in Rust/WASM (#381); TS owns only the capture-first overlay. Prefactor: - Move the change-id fold into loot-codec (change_id module: compute_change_id_raw / change_signing_message / mint_change_id / canonical_predecessors); loot-core's compute_change_id(&Change) delegates. Behavior-preserving (loot-core 296 tests unchanged). - loot-codec: seal_uncompressed (the wasm author path can't run zstd). - loot-identity: public Identity::from_seed(&[u8;32]) (the flagged #383/#424 ctor, native side). loot-wasm: - Identity.sign + wrapEnvelope ([0x01][pubkey][sig][bundle], sig over bundle). - ChangeBuilder: carry(unchanged) / put(edited, seals uncompressed public) / finish() -> {envelope, changeId, versionId} — folds, signs the finalize message, encodes the Sync frame, wraps the envelope, all in Rust. - Golden parity (Seam 2) extended: frozen pubkey, signature, envelope bytes, and change-id fold — green natively and under wasm-pack test --node. sdk/: - Capture-first overlay: edit/remove mutate an in-RAM overlay that IS the pending change; describe names it; status/diff report it; push composes via ChangeBuilder (carry unchanged paths, put edited, skip removed) and POSTs the envelope to /stow, returning the durable change-id. - Seam 1: a write behavior suite drives a real relay whose allow-list holds the SDK key; the SDK authors the first change and reads it back through a fresh connection (4 tests). Full SDK suite: 9 green. Slice 2 authors PUBLIC content stored UNCOMPRESSED (valid + readable; zstd's C won't build for wasm, and fzstd is decompress-only). Deferred: unauthorized error mapping (slice 3), private/grant writes (slice 4). Refs #424, #421. 2defc2f5 · dbf3dbe6…diff
  • Rename the Public visibility tier to Internal (ADR 0041 §2, #480) 6969626d · dbf3dbe6…diff
  • Publish mechanism: the `published` keyword + @world grantee (ADR 0041, #481) a6758352 · dbf3dbe6…diff
  • Forge publish ingest: file the published key the bundle already carries (#499) 417aaf83 · dbf3dbe6…diff
  • Stop shipping grantee nicknames on the wire (ADR 0015, #521) 55ba71eb · dbf3dbe6…diff
  • the published index outflanks the metadata gate for exactly the published facts, and the site grows the anonymous read tier that serves it (#749) 8ed81d63 · dbf3dbe6…diff
  • a seal reuses one zstd context per thread and stops compressing objects it would expand (#798) Perf-Baseline: reset #798 reuses the zstd compression context and skips compression that expands small objects, so bundle_bytes and store_bytes drop 7e424b52 · dbf3dbe6…diff
  • the seal comments name the tier the code actually gates on (Internal, not Public), and the README count guard pins the sentence it claimed to pin (#1116) f7bce81c · dbf3dbe6…diff
  • a delta snapshot stops decrypting all 2000 base objects to fill in a bool, because the two gates that only ever read is_err now ask a cipher-free can_open lifted from sealed::open's own embargo and key-custody steps, and the one case that changes, a held key that will not decrypt, is deliberately readable because surface propagates that error rather than skipping the path the carry-forward exists for (#1422) 06a47a15 · dbf3dbe6…diff
  • every open of a compressed object stops building a fresh zstd context and asking for a 64 MiB buffer, because the decompressor is now thread-local and the buffer is the frame's own declared size clamped by the same zip-bomb ceiling, and the harness gains the first fixture whose objects are actually compressed - without which nothing could have seen the 72.6% it removed (#1424) 7c16d4d4 · dbf3dbe6…diff
  • five claims from this run's review sweep get pins instead of louder prose: sealed.rs's header names can_open beside the chokepoint it did not weaken, is_canonical is held to encode_graph(decode_graph(bytes)) == bytes over every golden with the rewrite predicate no longer said in two files, the #988 separator arm is pinned by bytes encode_graph cannot write instead of an if that never ran, the inflate guard's third arm gets the test its prose stood in for - which measures that arm as a narrowing of where a lie is refused rather than of whether - and a refusal that could only print one number twice prints both (#1431) 102fbec6 · dbf3dbe6…diff
  • the browser stops inflating a sealed blob with no zip-bomb bound, because the one rule its three copies had already drifted over is now stated once with the crypto injected, and the single suite that states it runs the refusals, the bound and the cross-language vector through every decoder rather than the server's alone (#1410) 9419981e · dbf3dbe6…diff
  • the adr 0004 guard stops being a claim about a struct and becomes a question asked of the repo on disk: the pin it left asserts that no field of SealedObject is a function of plaintext, which stays green over a store that has the deleted digest back beside the struct, and that is measured rather than argued, since with DagRepo::put appending blake3 of the plaintext to a file in .loot the pin reports 1 passed and 0 failed while the new census reports 4 passed and 2 failed. plaintext_equality_census records two worlds through the same verbs, one with two paths holding the same bytes and one with them holding different bytes of the same length, builds each world four times, and refuses a persisted difference its readings can see between them: structure cancels because both worlds record the same tree, randomness is filtered by keeping only what every build of a world agrees on, and the wall clock is blanked by value, because adr 0043 stamps a second per version and a gap between two builds read red on that second alone. it walks the repo root rather than a named place, so relocation is the thing it is aimed at, and it is a lower bound and not a proof: a token shorter than its window, an index encrypted per build, and any write path the fixture does not run (a push, a pack, the mirror, the forge and the wire) are outside what it has looked at, which the new adr 0004 amendment states before a reader meets either guard. three oracles are planted as standing controls, one per reading, each the shape of a proposal that has actually been made. red under mutation, counts read each time: the sidecar planted in DagRepo::put (the census 4 passed and 2 failed, the struct pin 1 passed and 0 failed beside it), the value reading removed (5 passed and 1 failed), the shape reading stripped of its lengths (5 passed and 1 failed), the repetition reading removed (5 passed and 1 failed), the walk stopped from recursing (2 passed and 4 failed) and the two worlds made identical (3 passed and 3 failed). adr 0086 gains a pointer where it records the proposal this gap would have waved through, and CONTEXT.md says what the new reading reaches. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4092 passed over 131 binaries, 8 ignored) (#1894) 327f63f8 · 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.