Changes touching this path

  • the browser opens what the forge cannot: own-private read ships an owner read tier, a once-per-device unlock, and a grant mailbox that never acks (#1043) c8dd4da1 · dbf3dbe6…
  • the private shell learns to diff: two addresses, two keys and two openEntry legs meet diffLines in the page, and an unreadable side is named rather than folded into the pair's verdict (#1057) 94014155 · 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 private shell stops dying a minute after /account: it authenticated with Clerk 60-second __session cookie while no Clerk JS ran on /private to refresh it, so every private call 401d and a reload wiped the vault. per the operator grill (ADR 0096, reversing #930 dec. 4), Clerk JS now runs on the private routes, loaded on demand behind PrivateGate so public pages carry none, under the strict nonce CSP widened only for Clerk (its frontend API host read out of the publishable key, img.clerk.com, the Turnstile and protect hosts, and worker-src blob: for its session timers) with the request nonce passed to ClerkProvider; every private call goes through one privateFetch that sends a Bearer from getToken and retries once with a fresh token, and /api/private/* no longer accepts the cookie; the vault is wiped only when Clerk reports signed out or a different user, or on Lock, and an HTTP status never wipes it; signed-out /private renders SignIn in place and returns to the requested URL, and the private bar gains Sign out beside Lock. the vault, private shell and CSP comments and CONTEXT.md now say what is true: the wrap key can decrypt, so any script on this origin can recover the seed, and the wrap protects it at rest; the boundary is this origin script set, loot code plus Clerk. tests for the CSP, bearer lane, retry and wipe triggers and the built server, each red under a named mutation and restored; site gate 804 passed, every surface lighter; a CSP-enforcing signed-out load renders SignIn with no violation and /why loads no Clerk; workspace suite 4549 passed. the operator live check closes the ticket (#2396) 25ab3fa8 · dbf3dbe6…diff
  • the private shell unlocks without waiting for its mailbox, and the mailbox pull becomes incremental: measured live, forge /pull-grants took 2.78 s of a 4 s /private load, because the whole mailbox of 1,640 standing self-grants was downloaded before the shell counted as unlocked. /pull-grants now takes an optional 17-byte cursor in its signed payload (a tag byte, then the inbox seq high-water mark and the forge clock at the answer, issued by the server and opaque to the browser, which may only send the all-zero one) and answers rows with seq past it or a reveal_at past its time, due on the forge clock, followed by the next cursor after the counted frames; the Postgres read takes a share lock on grant_inbox so a deposit that took a lower seq but commits later cannot fall behind every future cursor, and an empty payload answers byte-identically to before, so the CLI, the SDK and the relay client are unchanged and either deploy order works. the site keeps the sealed grant blobs and the cursor in IndexedDB beside the vault (version 2, the seed record kept), builds the keyring from them, shows the shell unlocked and pulls only the delta in the background; a file whose key is missing waits for an in-flight pull, the bars say keys are updating, and Lock clears every store with the vault. the gate shows Unlocking while a silent unlock runs rather than flashing the unlock card. forge tests on memory and Postgres and site tests, each red under a named mutation and restored; bash ci/local.sh green against Postgres 18 (4559 passed), site gate 819 passed. owes a forge deploy and a site deploy (#2403) 11cc1e3a · 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.