djrmun.com

Colophon

Everything below is observable from outside anyway. The parts that aren’t, aren’t here.

Build

Astro, building to static HTML. Posts are Markdown with a typed schema, so a malformed front-matter block fails the build instead of rendering a page with a blank date on it.

New posts go out on an Atom feed, built from the same collection, so it can’t list a post the index doesn’t. A post revised after publication says so under its title and carries the new date in the feed. Project releases aren’t repeated here; they have their own feeds on GitHub.

No JavaScript ships. Not a single byte — no framework runtime, no hydration, no client router. The only script the page permits at all is a page-count beacon, and it isn’t mine.

Type

Headings are Fraunces, self-hosted: 35KB of woff2 served from this origin, so there is no third-party request and nothing to preload from someone else’s CDN. Body text stays on the system stack, which costs nothing and looks native everywhere.

Self-hosting it needed a policy change, and the reason is worth writing down. font-src falls back to default-src, which is 'none' here — so without an explicit font-src 'self' the browser blocks a font served from this very origin. The page then renders in the fallback stack, returns 200, and logs the violation only to the console. The symptom reads as “the font didn’t load” rather than “the policy blocked it”, which is the more useful sentence and the one you don’t get.

Colour

Custom properties, with a dark variant behind prefers-color-scheme. The accent is a burnt orange —#b0552a in light, lifted to #d97757 in dark so it holds contrast against a near-black background.

Three of the properties are section tones, one per kind of thing, because colour here is meant to tell you where you are rather than to decorate. Each clears 4.5:1 against the page background in both themes — measured in the browser, not eyeballed. One candidate came in at 4.14 and was thrown out.

The project marks are inline SVG rather than image files: no request, nothing to keep in sync, and they scale to any size. The plimsoll mark is a ship’s load line drawn in the overloaded condition — water above the mark, which is the state that tool exists to catch.

Headers

The content security policy starts at default-src 'none' and adds back only what the page provably needs. script-src does not include 'self', because nothing same-origin needs to execute; if that changes, the symptom will be a script that silently doesn’t run while the request still returns 200. That failure mode is most of what I write about.

HSTS, frame denial, nosniff, a referrer policy and a permissions policy that switches off every sensor API are all set. You can check all of it yourself:

curl -sI https://djrmun.com

Caching

HTML is sent no-cache, so edits appear immediately. Stylesheets carry a content hash in the filename and a one-week immutable lifetime — an edit produces a new filename, so there’s never anything to purge.

The build checks itself

Two scripts run before anything is copied to the origin, and the deploy refuses if either fails. One asserts the things the policy above assumes: no JavaScript, no <script> tags, no inline styles, a content-hashed stylesheet, security.txt present in the output. The other hunts for a missing space where text meets an inline tag, which the template compiler will silently eat.

Neither of those would break a build on its own. Both produce a page that returns 200 and validates and is quietly wrong — which is the entire subject of this site, so it seemed poor form to leave them to chance.

Reporting something

There’s a security.txt. It points at private vulnerability reporting on GitHub rather than an email address, so nothing gets handed to crawlers.