www #
atgc.codes: an Astro site, with the parts only the binary knows generated into it.
cd www
npm install
npm run dev # emit the assets, then serve on 127.0.0.1:4321
npm run build # emit the assets, build to www/dist, then verify it
npm test # the scripts' and lib's own tests
npm run dev and npm run build both run npm run assets first, which is
cargo run --features www -- www-assets --out src/generated.
The dev server binds loopback and takes its port strictly: several agents share this machine, and a server that quietly moved to the next free port would have one of them reading somebody else's site.
Who owns what #
Every word on the site is in src/pages/ and the components beside it.
Change the copy, change a heading, add a card, write a note: no Rust, no
rebuild of anything.
src/generated/ is written by the binary on every build and is ignored by
git. It holds only what the page cannot be trusted to state for itself:
| File | Why the binary has to say it |
|---|---|
facts.json |
The version cargo release computed, and the install line cargo test asserts against the README's |
theme.css, field.css, mark.svg |
The palette, the same bytes the login page atgc serves is built from |
field.html |
Nineteen thousand cells of generated art, from crate::art |
terminal.html |
atgc about's own drawing, not a screenshot of it |
fonts/ |
Comic Neue, vendored in src/html/site/fonts/ |
Updates #
The blog. Two names, both in src/lib/feed.ts: the menu says updates,
because a nav item has one job and a reader should know where a link goes
before clicking it. The publication is genetic drafts @ atgc.codes.
Neither is explained on the page.
A note is a markdown file in src/content/posts/, with frontmatter the
schema in src/content.config.ts checks:
---
title: What changed
description: One line, and it is what the feed and the index show.
publishDate: 2026-09-02
tags: [atgc, release]
draft: false
---
Drafts render under npm run dev and are left out of a build. The rule is
import.meta.env.DEV rather than a flag, so there is no way to ship one by
forgetting to turn something back on.
/posts lists them, /posts/<slug> is one, and /atom.xml is the feed —
hand-written, because the whole of Atom this needs is one entry per note and
a feed is a thing you have to be able to read to trust.
Publishing to atproto #
Each note can also be a site.standard.document record in the publishing
account's own repository, the way substandard.blog does it:
ATP_IDENTIFIER=… ATP_APP_PASSWORD=… npm run publish:notes -- --dry-run
sequoia.json configures it. It still has a PLACEHOLDER
publicationUri — create the publication once with sequoia login && sequoia init, then put its at:// URI there. That is the only place it
goes: the two things standard.site reads are both generated from it.
<link rel="site.standard.publication">in every page's head, and<link rel="site.standard.document">on a note that has been published. sequoia injects whichever a built page is missing; emitting them from the source instead means it never rewritesdist/, so what shipped is what was built./.well-known/site.standard.publication, served by a route rather than checked in underpublic/.
scripts/publish.sh refuses to run while the placeholder is there, and
verify:dist checks both once it is gone.
sequoia init asks for the publication name at a prompt rather than reading
it from the config, so give it exactly:
genetic drafts @ atgc.codes
That is PUBLICATION in src/lib/feed.ts, which is also what the Atom feed
calls itself — so a reader who subscribes and a reader who follows the
record see the same name. verify:dist fails if the feed's title stops
naming the host it is served from.
Publishing is the last step of a deploy, never part of one. A document
record says "this note is at this address", so the address has to answer
first; the script fetches every URL it is about to claim and checks the
canonical link before writing anything. ATP_APP_PASSWORD is an app
password, never an account password, and is never printed.
Once a note is published, sequoia writes its at:// URI back into the
frontmatter — commit that, and the page starts carrying a
rel="site.standard.document" link beside its canonical one.
Deploying #
AWS_PROFILE=jmm-atgc-codes-admin scripts/deploy.sh
That builds, uploads the tree to s3://atgc.codes/releases/<sha>/ and runs
tofu apply to point CloudFront at it. A release tree is never rewritten, so
a deploy is one pointer move rather than a bucket edited under a reader, and
a rollback is the same apply with a sha already uploaded:
tofu -chdir=../infra apply -var release_sha=<sha>
../infra holds the hosting itself. Three things about it are worth knowing
before you apply:
- The zone is read, not created. It was made with the account and its
records belong to this repository, so a
tofu destroyhere cannot take the delegation down with the site. - The domain is delegated and the certificate is issued. If that ever
stops being true — a zone rebuilt, a registrar moved — ACM cannot see the
validation record an apply writes and the apply sits in certificate
validation until it times out.
tofu -chdir=../infra output zone_name_serverssays what the registrar's four NS records should be, and answers without applying anything, because the zone is read. deploy.shrefuses to run against the wrong account. Credentials that work but belong elsewhere are the failure worth catching: the SSO sessions share one start URL, so a login authenticates whoever the browser is already signed in as and returns a valid token under whatever profile was asked for.
A miss is answered with the site's own /404.html. The bucket is private, so
S3 reports a key it does not hold as 403 rather than 404, and CloudFront will
not remap a status without a page to serve alongside it — verify:dist fails
a build that does not emit one, because a release tree without it would send
every typo to CloudFront's own error page.
The distribution sends a default-src 'self' CSP, which the site already
keeps: it fetches nothing from anywhere else, and verify:dist fails a build
that reaches a third party. The one inline script is allowed by a hash
computed from src/scripts/site.js itself, and verify:dist fails a build
whose inlined script is not that file — a stale hash would kill the script at
the edge with nothing visibly wrong in the page.
One look, two surfaces #
atgc auth login serves a page over loopback while it finishes, and that
page and this site have to look like the same project. They do not merely
resemble each other: html::brand holds one palette, one mark and one field
stylesheet, the loopback pages compile it in, and www-assets writes the
identical bytes out for Astro. There is no second copy of --a anywhere, and
the_site_and_the_login_page_share_one_look fails if one appears.
Rules #
The page states no fact it cannot be held to. The install line and the
version come from facts.json. A Rust test walks these sources and fails if
an .astro file spells either of them out itself; npm run build then runs
scripts/verify-dist.mjs, which fails if the built site is missing the
generated half, ships a font nothing references, reaches a third party, or
serves a note at an address its feed and its record would disagree with.
Nobody's dependency. No atgc command fetches this page, no build needs it, and no error message sends anybody here for a fix.
Same-origin only. No font CDN, no analytics, no third-party anything: the fonts and the favicon are served from here. Not a rule handed down from anywhere — just worth keeping, since there is then no CDN to be down and nothing that learns who read the page except the host serving it.
Not documentation. --help has the flags, docs/ has the model, and
git log has what changed. A feature here is one line and a command name. A
landing page that starts explaining pull request semantics has turned into
the worst copy of docs/architecture.md in the tree. A note may explain
things at length — that is what it is for.