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 metrics dashboard ships as SIX STATIC PANELS behind an operator allowlist, and the two reviews moved it from a client-fetched page with a 401 to a server-rendered one that cannot be probed: 1623 settled Variant A, the Briefing - one column, one section per question, each caveat as PROSE beside the number it qualifies, which is the structural reason it won, since a caveat there cannot be skipped without skipping its number.
the access decision is that being signed in is not the same as being the operator. anyone can sign up to this host and the data is site-WIDE - every visitor path, referrer host and install-funnel count - so the door is a custody list in the 1045 shape: a Clerk id is on it or it is not. it fails CLOSED in every branch, and the proof is that the fail-OPEN spelling was written and run: allowed.length greater than zero AND not includes, then return true, reads as careful and admits the entire internet the moment the list is empty, which is the state a box is in before its first deploy writes one. exactly two tests went red on it.
the security review then found the route around that allowlist was the wrong shape twice over. the check sat INSIDE withPrivateSession, which maps an unauthenticated caller to 401 - so a stranger probing the endpoint got a 401 where an unserved path gets 404, and the refusal meant to hide the route confirmed it, while a load-bearing comment three lines up asserted the opposite. and the page itself answered 200 to anyone, rendering a document and only saying not found after a client fetch failed. both collapse into one server function returning null for BOTH refusals, which the loader turns into a real notFound: signed-out, signed-in-not-an-operator and a path we do not serve are now one answer.
that rework is also what the ticket asked for and I had not done - the panels render into the HTML from a route loader rather than from a browser round trip, so the acceptance criterion about server-side rendering stops being approximated. the second finding is sharper than it looks: the route declared no headers, and every private route sets private no-store BECAUSE an nginx proxy_cache sits in front. an operator document without it is cacheable at the edge, which is the edge handing one caller a page meant for another. it now carries PRIVATE_HEADERS, and that constant own comment is widened to say it covers two tiers rather than left reading as private-only.
one real data bug came out of the same review and no test I had written could see it: ordering a to_char MM-DD label sorts the series as TEXT, so any window crossing new year draws january before december - the sparkline scrambled about thirty days a year on the one chart whose job is a shape over time. grouped and ordered on the timestamp now, formatted on the way out, and pinned by asserting the grouped expression is not a to_char, verified by restoring the bug and watching it go red.
built against the PRODUCERS rather than the schema, which is what the prototype was really for: visitor_key is null on every log-sourced row and permanently will be since 1632 took the address out of the log, so there are no uniques and no sessions here at all, and device, browser, os and country have no producer either. the panels that wait on one are rendered, named and marked rather than omitted - uniques on 1757, forge usage on 1758, downloads on 1647 and telemetry on 1658 - because a dashboard that silently drops what it cannot see is how you talk yourself into a wrong conclusion.
not prerendered, and that is a security property rather than a build detail: a prerendered page is served as a static FILE with the static route headers, so an operator surface would exist as bytes anyone can fetch with the door never consulted - the same reason private joined that filter in 1043. asserted, and the mutation confirms the assertion bites. it is correspondingly absent from the byte budget, which discovers prerendered surfaces, and its styles stay in the route rather than styles.css because that sheet is eager on all sixty budgeted surfaces and an operator page has no business charging them (#1794)
2c969a0e · dbf3dbe6…diff - the dashboard stops sending a CSP written for a different shell, because that one directive rendered it BLANK for the operator while every gate stayed green: /metrics reused PRIVATE_HEADERS, whose reason applies here exactly - nginx proxy_cache sits in front with no proxy_cache_valid, so an operator document marked cacheable is one the edge can hand to somebody else - but the set also carries PRIVATE_CSP, and that forbids inline script. the private tier can afford that because its shell loads no inline and no foreign script, which is 930 decision 4 and the 753 route split. /metrics renders through the ORDINARY root, and TanStack Start ships its SSR hydration payload as an inline script tag. so the browser blocked it, React threw Invariant failed, and the page came up empty.
METRICS_HEADERS keeps the half that was doing the work and drops the half that was never written for this shell: private no-store, frame-ancestors none as a CSP carrying only that directive so it constrains framing and says nothing about scripts, X-Robots-Tag noindex as a header rather than only the meta the route also emits, and no-referrer. PRIVATE_HEADERS goes back to naming only the private tier - widening its comment to cover two tiers was the wrong repair in the other direction - and now carries the warning that it is not reusable by a normally-rendered route, with the reason, so the next person tempted by the same reuse is told why it looks right and is not.
⭐ the finding worth keeping is about the INSTRUMENTS rather than the header. nothing in this repo could see it: the build passed, tsc passed, 416 tests passed and the byte budget passed, because no gate here executes a document against a content security policy. it took loading the page in a browser. the new test cannot execute a policy either, so it pins the incompatibility instead - this route CSP must not mention script-src at all, it must not be equal to PRIVATE_HEADERS, and the control asserts it still carries the no-store it exists for, since not constraining scripts is also satisfied by sending nothing.
the guard earned itself immediately: it caught that I had changed the import and left headers colon PRIVATE_HEADERS at the call site, which typechecks and would have shipped the identical blank page a second time.
the diagnosis is verified rather than assumed - a normally-rendered page in the built output carries exactly one inline script tag beside one with src, the src one allowed by self and the inline one being what the policy refused. and the failure itself is evidence the rest of the route is right: the operator got a DOCUMENT rather than the 404 an outsider gets, so the allowlist, the env plumbing and the container recreate were all already working (#1794)
79ee0753 · dbf3dbe6…diff - every /private page rendered blank live because the private CSP, script-src self and wasm-unsafe-eval with no unsafe-inline, refused the two inline scripts TanStack writes into every page, its scroll restoration and the per-page $tsr SSR payload; each private document now gets a per-request nonce, minted by a request middleware in the new src/start.ts, stamped by the router on the scripts it writes through ssr.nonce read from that middleware context, and named in the policy the middleware sets on the response, so script-src still carries no unsafe-inline and injected markup cannot run. the start instance restates TanStack default CSRF middleware for server functions, which defining one would otherwise drop. the routes keep the nonce-less policy as the fail-closed fallback, /api/private/* and public pages carry no nonce, and /private now renders under vite dev too. a new test starts the built server and checks every inline script on three private pages carries the nonce its own response names, that nonces differ per response and that a public page has none; red under mutation, counts read each time: the router never given the nonce (3 failed, 2 passed), the middleware writing a policy without it (4 and 1), a constant nonce (1 and 7), unsafe-inline beside the nonce (1 and 7), the path test matching /privateer (1 and 7), each restored to green. ADR 0094 and CONTEXT.md are amended; 777 site tests pass; no migration and no forge change, site deploy only (#2335)
aa34d4f1 · 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 - every compiled-in and current-tense loot host moves to loot.build (map #2412, ADR 0099): loot --help names security@loot.build and https://loot.build/trust, ALPHA_PROMISE_URL is https://loot.build/install and the telemetry DEFAULT_ENDPOINT https://loot.build/api/telemetry, so the next release carries them; the site install one-liners, R2_BASE and HOP_BASE (https://dl.loot.build, https://loot.build/dl), the metrics SITE_SCOPE and own-host referrer, the disclosure links on trust, terms, privacy and known-issues, and the install, quickstart and guides pages follow, with the privacy and terms markers moved to 25 September 2026 and re-pinned since readers see the new address; README, CONTEXT.md (the forge door now forge.loot.build), the release checklist, site CI and test fixtures follow, and the two specs carry a note instead of a partial rewrite. the destination census gains build in WEB_TLDS, without which security@loot.build would be invisible to its bare-host clause, and exempts the two shipped lines that tld makes read as hosts (state.build, r.build()) by their exact text; ADR 0074 section 9 says twelve-entry to match. 37 lines still name millerbyte.com outside ADR bodies, evidence, research and scratch, each kept on purpose: dated history, millerbyte.com the site and its @millerbyte/ui tokens, the two noted specs, and relay-era tooling with no successor host, filed as #2438. census red with build dropped from WEB_TLDS, restored; site gate 819 passed and within budget; workspace suite 4559 passed over 142 binaries, 13 ignored (#2418)
c5663115 · 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.