Ship the site, not the CMS
NullBound has a public home now. It’s one HTML file, about eight hundred lines, hand-written, with a data-URI favicon. It deploys to Cloudflare Pages in under a minute on every push to main. That’s the whole thing, and that’s the point.
The site
The page lives at nullbound.signalandecho.com. It covers what the game is, six protocols, four elements, the one-countdown pitch, a trailer slot, a screenshots strip, an alpha signup, and links out to Discord and Bluesky. Nothing clever. Static assets, inline CSS, two small scripts for a carousel and form post. No framework, no bundler, no build step.
The version string in the footer comes from project.godot. The deploy script reads it and substitutes a placeholder token into the HTML at push time. That’s as dynamic as the site gets.
I kept looking at the alternatives and none of them earned their weight. A static-site generator means a build step plus a theme I’d end up fighting. A CMS means hosting, a database, auth, a content editor I’d log into twice a year. A Next app means I’ve written a Next app. For a solo dev with a one-page marketing site and a game to finish, the correct answer is “write the HTML.”
The site will grow. There’ll be a Steam page link when Steam exists, a press kit page, a devlog index that mirrors this directory. When that happens and the single file starts to hurt, I’ll split it — Hugo or just two HTML files, whichever feels lighter. I’ll know when the pain shows up. Right now there is no pain.
The infra
The deploy path is boring on purpose.
DNS lives in a YAML file in the repo. A Python script reads it and applies it against the Cloudflare API. Records are declarative, re-runnable, never auto-deleted. If I want to add a subdomain, I add a record to the file, run the sync script, commit. It’s in git forever. I never click around in the Cloudflare dashboard trying to remember what I changed.
The site deploy is a second Python script. It reads the product/marketing/site/ directory, bundles it, pushes it to a Cloudflare Pages project, and tags the deploy with the current version. It runs locally for the first push and from GitHub Actions every time after.
The workflow fires on push to main with paths under product/marketing/site/**, or on release publish, or manually from the Actions tab. Repo secrets hold the API token and account ID. I tested it by doing exactly one manual deploy. Every update after that will be a commit.
This is all about fifty lines of Python per script plus eleven lines of YAML. It’s not a platform. It’s glue. But the glue is in the repo, the glue has a README, and future me can read the README and redeploy the site from scratch in about ten minutes if everything burns down.
Why bother right now
The game isn’t done. The alpha window is close but not here. Writing marketing infra in April for a ship in later is the kind of thing that looks like procrastination if you squint.
Here’s the framing I landed on. The site isn’t marketing yet. It’s plumbing. When the first playable alpha goes out to a Discord group, I want the first message to contain one link — not a screenshot bundle, not a Google Drive folder, not “DM me for the build link.” A proper landing page with a signup form and a pitch. Press people typing “NullBound game” into a search bar need to find something. The Bluesky bio needs to point somewhere.
None of that requires the site to be fancy. It requires the site to exist.
The pieces now fit. Discord is the hangout for people who are already in. The site is the place you land when you don’t know what any of that is and you just want to know what the game is. Bluesky sits between the two — short-form dev posts that pull people toward one or the other. The site was the last missing piece, and now it isn’t.
What’s next
First Discord message when the alpha drops will link to the site. Before that, a balance pass in v0.72 — numbers tuning across the four protocols now that they’re comparable, plus a combat-flow audit for room pacing. Press outreach starts after the Steam page draft. The site will grow to hold it.