Changes touching this path
- Scaffold site/ + wire Vercel #2, loot. subdomain, install proxy (#257)
Thin end-to-end tracer for loot.millerbyte.com (spec docs/specs/loot-site.md
SS1-3): site/ = TanStack Start SSG consuming published @millerbyte/ui@^0.1.0
(theme.css at the root), five placeholder surface routes prerendered, deployed
on Vercel project #2 (root site/) at https://loot.millerbyte.com.
Install proxy: the spec's vercel.json external-rewrite mechanism was falsified
live (Vercel passes GitHub's releases/latest 302 through to the client), so
/install.sh and /install.ps1 are Start server routes that fetch upstream and
stream the bytes back 200 - spec SS2 amended in place.
site/** explicitly public in .lootattributes; .vercelignore allowlists site/
so .loot/ keys and docs/pitch/ can never reach the Vercel builder; site build
artifacts lootignored.
1931146c · dbf3dbe6… - Scaffold site/ + wire Vercel #2, loot. subdomain, install proxy (#257) (#291)
* Scaffold site/ + wire Vercel #2, loot. subdomain, install proxy (#257)
Thin end-to-end tracer for loot.millerbyte.com (spec docs/specs/loot-site.md
SS1-3): site/ = TanStack Start SSG consuming published @millerbyte/ui@^0.1.0
(theme.css at the root), five placeholder surface routes prerendered, deployed
on Vercel project #2 (root site/) at https://loot.millerbyte.com.
Install proxy: the spec's vercel.json external-rewrite mechanism was falsified
live (Vercel passes GitHub's releases/latest 302 through to the client), so
/install.sh and /install.ps1 are Start server routes that fetch upstream and
stream the bytes back 200 - spec SS2 amended in place.
site/** explicitly public in .lootattributes; .vercelignore allowlists site/
so .loot/ keys and docs/pitch/ can never reach the Vercel builder; site build
artifacts lootignored.
Landed via break-glass git branch: the loot-native lane land was blocked by
two live shared-store bugs (review-ferry folds a described WIP into a
reconcile finalize; describe/status disagree on the working change under
concurrent sessions) - tickets to follow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Apply #291 review findings
Cache only successful upstream responses in the installer proxy (a transient
GitHub failure must not be CDN-cached for 5 minutes); extract the install
one-liners to one shared module (three copies drifted apart otherwise); fix
the vite.config "fully static" comment and spec SS0 framing to name the two
server-route exceptions; tighten the .lootattributes rationale (the interim
manual deploy is gated by .vercelignore, not the GitHub projection); drop the
cargo-culted overrides block (its pins targeted server deps the site lacks).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Git-Author: Connor Miller <53197564+Connor-Miller@users.noreply.github.com>
231eb5f8diff - Author the five surfaces' real content (#259) (#301)
Fill the deployed site/ shells with real content per the loot-site spec section 4:
- Landing: thesis hook + install one-liner + "what works today" loop + three
demo vignettes (private .env / embargo / grant) deep-linking into Docs + CTA row.
- Install: OS-detected default command, 5-native-triple platforms table (v0.1.0),
build-from-source footnote, verify-your-download (checksum + gh attestation,
TLS-only Windows note), troubleshooting. Win ARM64 noted as x64-emulated, not a
native download (#270).
- Docs: getting started (.env quickstart, current verbs), seven distilled core
concepts, four task guides, and a hand-written CLI reference grouped
setup/local/docks/sync/grants/identity - sourced from the binary's usage text
so flags can't drift.
- Why loot: fresh sell-only copy (hook / the claim / why now / grants over
permission bits / proof). No bytes from the sealed docs/pitch/.
- Evidence: proof-log index, one card per committed run over verbatim output
(sealed path, hard embargo, concurrent agents, grant+maroon, amend divergence,
loot-first).
Styling extends styles.css with @millerbyte/ui theme tokens only; nav, footer,
routes, and the install proxy are untouched. tsc clean; all five routes (plus
deep-link hashes) prerender.
Git-Author: Connor Miller <53197564+Connor-Miller@users.noreply.github.com>
e088f034diff - the forge's published surfaces render themselves anonymously, and each response's cache class rides out of the selector that produced it (#751)
#749 built the read tier and nothing called it. This is the six surfaces
#466's table named -- profile, repo home, tree, blob, history, diff -- as
SSR routes over that tier, plus robots and a per-owner sitemap.
Three properties are structural rather than configural, which was the point:
STRICTLY ANONYMOUS (#486 Q4). No handler in site/src/server/forge.ts reads
a request, a header, a cookie or a token; each takes only what the URL
already says. So 'never serve one identity's view to another' is a fact
about the module's signature rather than a cache rule someone must
remember. Adding an identity parameter is the one edit that would break it.
THE CACHE CLASS IS CARRIED, NEVER RE-DERIVED. resolveSelector already
answers how a resolution may be cached; routes read loaderData.cache and
map it in one place. That is what keeps the ADR 0032 trap shut: a
change_id LOOKS like an address but is the durable handle carried across
amends, so it resolves 'revalidate', and a pinned version_id is the only
cache-forever class. Asserted twice -- a unit test on the header text, and
a live render proving one URL family splits three ways.
HONEST RENDERING (#497). A locked path shows its visibility tag and no
bytes; grant_log is never queried, so a grantee set cannot leak from a
surface with no way to learn one. Burned reads as a tombstone, sealed as
'Not published', absent as absent -- three outcomes a blank page would
have made indistinguishable. The 404 is deliberately one 404: 'no such
repo' and 'metadata private' must not be told apart.
Also here: a published-only repo (metadata private, paths published) gets
its index and nothing else -- no tree, no history, no diff -- which is
0005's barrier showing through the UI unchanged. Per #748 the profile
lists metadata-public repos only, there is no global directory, and the
sitemap is therefore per-owner: readable at a known URL is not consent to
be advertised.
vite.config.ts grows the prerender deny-list its own comment called
unnecessary -- true until a route read a database.
Acceptance: 49 tests green (31 new pure ones; the 8 read-tier tests against
a throwaway 0001-0005 cluster), npm run build clean with exactly the five
authored surfaces prerendered, and a 34-assertion end-to-end smoke -- real
sealed bytes through R2 -> decrypt -> unzstd -> highlighted HTML, all three
cache classes, every honest-rendering outcome. The client bundle carries no
pg, no SQL and no credential.
Publication: README.md, LICENSE-MIT and LICENSE-APACHE reach @world here,
the set .lootattributes has declared since #750. Operator-authorised at the
landing sign; nothing else in the tree changes tier.
2aa21bbe · dbf3dbe6…diff - the change ledger becomes the history surface, banded by what the server can read (#760)
8ad80be0 · dbf3dbe6…diff - the browser mints the seed and the account binds the key: Clerk in the site's own routes, variant-B onboarding, and the account tier's one producer (#753)
Migration 0007 adds account_seed (the passphrase-wrapped envelope, opaque
ciphertext the server cannot open) and forge_identity, a NOLOGIN role scoped
to exactly the identity tier -- SELECT/INSERT on tenant, SELECT/INSERT plus a
column-scoped UPDATE of retired_at on account_key, the full row on
account_seed, and nothing else (pinned live by
forge_identity_is_scoped_to_the_identity_tier).
The producer lives in site/src/server/identity/ behind a verified Clerk
session (verifyToken in the site's own route handlers, reusing the millerbyte
Clerk instance and pattern, never the gateway service). Registration is one
transaction: upsert tenant on the verified clerk_user_id, add the key, store
the envelope, retire what rotation names -- and never zero live keys. The
Rust service still writes none of the tier; tests/account_tier.rs now guards
that boundary instead of a vacancy.
Onboarding is #487's variant B, the two-column contract: the app owns the 32
bytes (crypto.getRandomValues -> 24-word BIP39 over the ENTROPY, never the
64-byte derivation -> ed25519 pubkey via @noble/curves, held to the engine's
from_seed by the pinned RFC 8032 vector), a WebCrypto PBKDF2+AES-GCM envelope,
a .txt download that excludes the passphrase, and two consent checkboxes that
no download waives. Clerk mounts only under /account, which joins /k/ on the
prerender deny-list; every static surface stays static.
Follow-ons filed rather than folded: #926 (CLI raw-seed import, so BIP39
recovery reaches the CLI) and #927 (account-aware may_push, burn entitlement
via account, and the #506 historical-key 301s the anonymous read tier
deliberately cannot serve today).
278e1886 · dbf3dbe6…diff - the receipts are served, not linked: the Evidence page stops pointing at a repo its readers cannot open (#840)
Twelve github.com links, every one a 404 for the only audience this site has —
Connor-Miller/loot is private permanently (ADR 0045). Nine of them were the
Evidence page run-output and evidence-doc links, i.e. the receipt for every
thesis claim that page makes, so it asserted a set of proofs and handed the
reader nothing to open. The other three were the nav/footer repo links and a
LICENSE link, dead twice over since no file of that name exists either.
The nine receipts are now inlined at build time with `?raw` and disclosed in
place under each excerpt. A link — to GitHub, to a forge surface, to a route of
our own — is a promise checked at READ time by someone not here to fix it; an
import is checked by `vite build`, so moving one of these files breaks the build
in the commit that broke it. This page cannot go stale the way it just spent
months being stale.
Not served from the forge, which ADR 0045 says is where the world should read
loot: measured today, /k/connor and /k/connor/loot both answer 404 — it carries
no repo yet, so pointing there would have swapped nine dead links for two. When
it does, these become links again and the imports stay as the build-time check.
The license moved to a page of its own rather than being dropped: a reader
running curl | sh has a real reason to want the terms, and both texts are
imported rather than transcribed so they cannot drift from the repo.
A sweep test asks the reader question of the whole src/ tree, not the two files
that happened to be wrong. It bans href/to/url pointing at github.com and
deliberately allows the string in prose — installerProxy documents what dist
bakes into the installers, and install.tsx prints cargo install --git as text
(that command is #841 own decision). ⭐ The defect was never a wrong URL: it was
that nobody ever opened one anonymously, and the person editing these files is
always signed in.
Verified: no github.com href on any of the 6 prerendered pages, /license
prerenders, suite 77 passed | 23 skipped, build green.
⚠ Residue: the inlined evidence DOCUMENTS quote issue URLs in their own prose.
They render as inert text in a <pre>, not as links, and rewriting a historical
evidence document to hide its citations would falsify the thing it is evidence of.
ad95716f · dbf3dbe6…diff - 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 landing hero becomes the receipt: one published object rendered as the ciphertext this host stores beside the plaintext a key opens, live from the forge, and no panes at all rather than invented ones (#1046)
c729cf41 · dbf3dbe6…diff - the trust matrix says what each reader can actually read, and /why stops claiming the relay cannot read your code (#1130)
Publishes /trust as the one canonical answer to who reads what: six readers
against four tiers, with a regardless-of-tier block for path names and DAG
metadata, and footnotes on the two cells whose plain answer misleads —
Restricted vs the git mirror rests on ADR 0028 projection omission rather than
keys, and an embargo reveal is enforced by the relay clock rather than by maths
(ADR 0027, written down nowhere until now).
#1089 falls out of the matrix as its ticket predicted: the /why headline was
false over ordinary code, since Internal is the default and a relay reads it by
design. Every other surface now cites /trust instead of answering independently,
which is what let /why and /docs drift into contradicting each other.
ADR 0069 records that Internal stays the default deliberately — Restricted by
default would make a clone readable by nobody and empty the git projection.
ADR 0068 records the at-rest design for #1138: .loot/keyring is raw oid-key
pairs beside the ciphertext it opens, so the unit of exposure is the whole
.loot/ directory.
Carries ADR 0067 from #1132, which shared this tree uncaptured.
361b2026 · dbf3dbe6…diff - the site gets a typeface: Geist and Geist Mono self-hosted from a subset derived from the site own characters rather than a stock list that silently drops the arrow the landing page renders, and twenty-one ad-hoc sizes collapse into a six-step scale
43c52bad · dbf3dbe6…diff - the landing page leads with the claim and the receipt arrives on scroll: the hero opens on Commit your .env with the mechanism as its subhead, the panes move below the fold as the first thing there, and the three edges and the invite-only paragraph move to the pages where those questions are actually asked
fbdc00bf · dbf3dbe6…diff - the hero gets its field: two plumes converge and merge over 210 frames, then the loop stops, the canvas hides and the GPU is released onto a 1.7 KB twin that was already underneath it, and a test refuses the day the twin and the shader that made it stop matching
b3f716f7 · dbf3dbe6…diff - the evidence page gets the one proof a reader can run: two identities are minted in the browser, a content key is sealed to one of them, and the other is refused by the engine in its own words rather than in ours, with the 161 KB core fetched only when someone presses the button
846010f8 · dbf3dbe6…diff - the footer stops dragging a horizontal scrollbar onto every page: sizing inline code at 1em left the repeated install one-liner with no break opportunity a browser would take, so a curl line ran past a 390px viewport and widened the document itself
141ca6ca · dbf3dbe6…diff - the site chrome catches up to its typography: one radius scale from the theme tokens, shadows for elevation with borders kept for structure, a glass header and footer aligned to the content column, hairline guide lines framing the measure, dark scrollbars via color-scheme, named-property transitions behind a reduced-motion guard, and the CodeBlock shell and primary CTA label that unresolved Tailwind classes and the doc-prose anchor rule had left bare and blue-on-blue
55d1c8aa · dbf3dbe6…diff - the measure cap names the panes lede that only renders with live forge data, and a static pin over the stylesheet and hero source checks every paragraph the panes render is capped, since no local build ever shows them (#1298)
4cbb2f0c · dbf3dbe6…diff - the Network sync entry says what a multi-batch pull actually declares, the measure rule records why its selectors stay direct-child, four restated stories become pointers to one telling each, and the last two hand-rolled repo walk-ups join tests/common (#1314)
a906a965 · dbf3dbe6…diff - the landing page becomes the Deck: the receipt is promoted into the hero as one frame where the ciphertext and the plaintext of the live object share the same lines and a dragged seam cuts between them, a live stats strip follows, the three ideas and the loop and the recipe become a bento, and a closing plate ends the page - superseding the receipt-below-the-fold placement while keeping the pane semantics, re-pointing the measure-cap pin at the seam, and raising every surface ceiling on the record for the shared stylesheet and routes chunk it costs
c25f21d1 · dbf3dbe6…diff - the site chrome becomes an instrument: a slim mono Console bar carries the six links, the release tag it now imports rather than retypes and the account key chip on every page, the footer band collapses into a one-line end-mark that still carries the disclosure route, docs trades its in-page contents list for a prop-driven side navigation any later surface can mount, and the account page is re-presented as a dashboard of key rows and a four-step ceremony with every Clerk call, every byte of crypto and every load-bearing sentence untouched - the #1323 prototype verdict, raising every surface ceiling on the record for the shared stylesheet it costs
786e5a21 · dbf3dbe6…diff - the two nav entries that leave your machine carry an EXP marker, keyed on their own href so the bar still says nothing about who is reading, and a strict 20rem suppression keeps the chip from adding fourteen pixels of horizontal scroll to a Fold
112c1212 · dbf3dbe6…diff - the receipt path in an evidence summary gets a soft-wrap opportunity that counts toward min-content, so the one unbreakable token stops pushing the whole page sideways on a narrow screen
d291543e · dbf3dbe6…diff - the docs become fifty-six routed entries derived from one registry that the nav, the reading order, the search and the prerenderer all read, so those four can no longer drift apart the way ten hand-listed surfaces already had to be watched; the thirty-eight pages nobody has written yet are real bookmarkable URLs that say what they will cover and carry noindex until they earn a place in an index, rather than dead rows in a sidebar; and the byte gate learns to walk nested surfaces, which is the only reason the fifty-two new pages weigh anything at all instead of shipping past a gate that read one directory level (#1378)
424be083 · dbf3dbe6…diff - the land reads its position once and hands that one corpus to three gates that still run in ADR 0065's order, undoing the two-and-a-half-fold read the previous change caused by leaving a walk inside each gate while claiming there was only one
ec75201e · dbf3dbe6…diff - adopt: catch up to landed main af5a1d19
87a00d35 · dbf3dbe6…diff - the Console bar stops asking for more width than its box has at 280px, and the half of this ticket that had not landed turns out to be worse than it was filed: the recorded 22px shortfall reproduces to the pixel only once a scrollbar is suppressed, because the ticket took innerWidth as the box - with a classic scrollbar the same row spills 5px past the viewport on seven of the eight budgeted surfaces, and only the landing escapes because its gutter is wider than its own over-run, so the sentence saying the page does not scroll was true of the overlay shape alone. the fix is spacing and nothing else, three rules inside the media block that already cuts the EXP hole, tightening a row gap and two padding pairs while every item, its content and the gutter that aligns the bar to the text column stay exactly as they were - after it, all eight surfaces read zero horizontal scroll with nine pixels of slack in the box and twenty-four on the overlay shape, and 240, 300, 320 and 390 were swept for regressions. every reading carried a positive control injected in the same page evaluation and removed after, so no zero here comes from a probe that was not first shown able to see an overflow. the durable half is a budget rather than a render, since ADR 0071's gate weighs bytes without ever rendering at a width and nothing in this repo can: spacing is read live out of the stylesheet, brace-matched and shorthand-aware, against a box computed from the live gutter declaration, with one recorded font-metric constant labelled as recorded - the model reproduces four independently measured layouts to a hundredth of a pixel, and eleven mutations were each run red, including one that only moves a rule below the media block and changes no string at all. what it cannot catch is a font change or a new glyph, which is written at the top of the file rather than implied away; and the stale claim about fourteen pixels of scroll is narrowed in all three places that carried it, since correcting one of three is the trap #1629 records (#1360)
dd791126 · 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.