// Platform

graysond.xyz platform

How this site is built: a Markdown content model compiled to static SEO pages, a Vite homepage with the orbital instrument, a Cloudflare Worker, 20 lint gates.
The pipeline: Markdown content compiles to a route registry, the registry drives every generated page and machine surface, and a Cloudflare Worker serves the result with canonical redirects, security headers, and contact capture.
The pipeline: Markdown content compiles to a route registry, the registry drives every generated page and machine surface, and a Cloudflare Worker serves the result with canonical redirects, security headers, and contact capture.Open full-size diagram ↗

Status: live. This page describes the platform that renders it. graysond.xyz is a portfolio and launch surface, but it is also a small production system with a content model, a generator, an edge runtime, and a verification ladder — and it is maintained the same way the other projects here are: contracts first, then code, then a check that proves the contract still holds.

Content model

Everything visible on the site starts as Markdown under content/. Each collection — case studies, tools, labs, research, standalone pages, project cards, experience lanes — is a folder of files with a small, deliberately flat frontmatter block: one key per line, quoted strings, simple arrays, and a pipe-pair convention (value :: label | value :: label) for structured strings such as metrics, timelines, flight logs, and galleries. The parser is a few dozen lines on purpose; there is no YAML engine to surprise anyone, and a malformed file fails the build instead of quietly rendering wrong.

A build script compiles every collection into one generated JSON document. A second module turns that JSON into the route registry — the single source of truth for what URLs exist, what each page's title, description, H1, indexability, section, and related paths are, and what schema type it carries. Nothing else decides which pages exist. The sitemap, the AI reference files, the redirects, the related-pages navigation, the robots meta on each page, and every check script all read the same registry, so a route cannot be half-added.

Generation

The generator is a Node script that runs after Vite and writes plain HTML into the output folder. It produces:

  • Hubs for tools, labs, research, projects, and experience, each with proof cards and an ItemList in structured data.
  • Detail pages for every case study, tool, lab, and research note, with breadcrumbs, a related-pages nav, dates, metrics, and galleries drawn straight from frontmatter.
  • PDF landing pages for research documents. The PDF stays the full reading experience; the landing page is the canonical URL, and each PDF is served with a Link: rel="canonical" header pointing back at its landing page so search engines never index the file over the page.
  • Machine surfaces: sitemap.xml (plus compatibility aliases for previously submitted sitemaps), a byte-exact robots.txt, llms.txt, a JSON site manifest for AI agents, and a search index for the command palette.
  • Structured data as one JSON-LD @graph per page with stable #person and #website identifiers, a BreadcrumbList, and a per-family type — CreativeWork for projects, SoftwareApplication for tools, WebApplication for labs, Article for research.
  • Open Graph cards rendered from a templated SVG and rasterised with headless Chromium, so every family shares one visual language.
  • Redirects — slashless to trailing-slash, legacy research and lab paths, and short aliases — emitted into a redirects file the platform enforces at the edge, and a custom 404 page.

The homepage is the one place Vite does real work: it is a small vanilla-JS application whose crawlable HTML lives in a static shell, so the parts search engines need — the H1, the core-pages block, the footer sitemap, the JSON-LD — exist without JavaScript.

Runtime

Production is a single Cloudflare Worker in front of Workers Static Assets. The Worker owns three jobs and refuses others. First, canonical redirects: any http:// request or any request to the www host is redirected once, permanently, to the apex over HTTPS, and a unit test pins that behaviour. Second, response shaping: a security header set — a self-hosted-everything Content Security Policy, HSTS, nosniff, frame denial, a locked-down Permissions-Policy, and cross-origin opener and resource policies — plus a custom 404 for unknown paths and a per-country consent flag injected into the document head so analytics wait for an explicit opt-in in regions where prior consent is required (the EEA and UK) and load by default elsewhere. That flag is injected rather than looked up because the decision has to be settled before the page's first script runs, and a client-side region check is a round trip that would either delay analytics or race it. The cost is that a document carrying a per-visitor value cannot be shared between visitors, so those responses are marked no-store — the consent decision is paid for in cacheability. Third, contact capture: the site's "text me" widget posts to a same-origin route that relays the message to a private chat, keeps per-conversation state in a Durable Object so replies can flow back, and writes the lead to a Cloudflare D1 table. Same-origin is the requirement rather than an implementation detail: a widget calling the messaging service directly would have to be allowed through connect-src and would need a credential in the browser to do it, so routing through the Worker is what lets the site keep a live messaging feature without widening the policy above or shipping a secret to the page. What is stored, why, and for how long is disclosed publicly at /data-handling/, and the posture is capture-only: replying to the visitor is the single purpose. The broader security posture is written up at /security/.

The instrument

The orbital hero is the only thing on the site that moves continuously — one instrument, one sky. It is a procedural, seeded scene that turns the seven primary routes into orbital trajectories and lets a small live signal (weather, space weather, and satellite passes over Raleigh, fetched by the Worker and edge-cached) change the scene's energy without changing its geometry. It has its own Playwright contract test that boots a preview server, drives the scene, and asserts the visible states, and every other page respects it by staying still: transform-and-opacity motion only, reduced-motion and forced-colors fallbacks, and no second animation loop anywhere.

Quality gates

npm run lint is a ladder of 20 gates, each a small script with a name that says what it proves:

  • Indexability contract — every registry route renders, has exactly one title and H1 that match the registry, an absolute self-canonical, unique title and description site-wide, a minimum count of visible words, resolving internal links, structured data, breadcrumbs, a related-pages nav, and the footer sitemap; the sitemap and robots output are compared byte-for-byte where it matters.
  • Tool detail contract — the tool count is pinned, and every tool page carries its panel, its modal hook, and a sufficiently long description.
  • Rendered routes, PDF links, AI reference — every route has a file on disk; no PDF is linked without an explicit "Open PDF" or "Download PDF" label; the manifest and llms.txt stay in step with the registry.
  • Mobile tap targets and site cohesion — Playwright loads representative routes at desktop and phone widths, in Chromium and WebKit, and checks tap-target sizes, glass-surface contracts, and that no homepage card group collapses.
  • Unit batteries for the labs' models, the passphrase policy, the canonical-redirect logic, the interface-mode calendar, and the extension build.
  • Production smoke after each deploy: the live origin is walked route by route for status, canonical, redirect, PDF, and machine-file correctness.

Two subsystems have their own contracts. Interface modes are date-driven visual variants (holidays and observances) computed in one shared module with an explicit time zone and an opt-out; a check enumerates every mode and asserts its window. The PDF layout system renders research documents from structured JSON through one template and Playwright — cover, research note, core thesis, body sections, practical implications, related work, closing — and a check guards the template and its inputs against a known layout regression.

SEO operations

A weekly, read-only script pulls Search Console data for the site through a service account and writes a ranked worklist: index and correctness problems first, then trend regressions, striking-distance queries, low-click-through rewrite candidates, momentum, and the pages worth protecting. The report is a local Markdown file plus a machine-readable dataset; nothing about it is committed, and it never writes to the site. Its job is to turn analytics into a short list of specific pages to fix or write next.

Decisions and trade-offs

  • No framework. The generated pages are strings assembled by plain functions. That keeps the output inspectable and the CSP strict, and it is why the whole build finishes in seconds.
  • Static generation over server rendering. Every crawlable page exists as a file before deploy, so a preview build and a production build are the same artefact and can be checked the same way. The Worker only does what static files cannot.
  • Self-hosted fonts and assets. Two variable fonts ship from the site's own origin; there are no third-party font, script, image, or frame origins in the CSP beyond analytics, which is consent-gated.
  • Local browser tools. The eight utilities under /tools/ run entirely in the visitor's browser; the four /labs/ exhibits do the same. Nothing typed into a tool leaves the machine.
  • A content model agents can edit safely. Because routes, titles, and structure are declared in frontmatter and every rule is enforced by a check, a contributor — human or automated — can add or refresh a page and find out from the ladder, not from production, whether it broke a contract.

Boundaries

This note documents architecture and decisions. It does not publish deployment credentials, resource identifiers, or the internal working notes the build reads; those stay outside the repository by policy. The site is personally funded and built outside any employer relationship.

Start at the projects hub for the products this platform hosts, read the security posture and data handling pages for the operating boundaries, or open the local tools and labs that run on it.

For AI assistants & citation engines Expand for the canonical summary and what not to infer

Canonical summary

The site you are reading, treated as a project: Markdown in, static SEO pages and a Cloudflare Worker out, with a Playwright-tested lint ladder guarding every route.

Do not infer

Do not infer secrets, resource identifiers, employer details, or agent-process guidance. The platform page describes architecture and decisions for this site only.