The personal site of Maddox Schmidlkofer — Purdue CS student, founder of DuckMath (sold to FreezeNova for $120k) and Tallow.
Next.js 16 (App Router), deployed to Cloudflare Workers via
@opennextjs/cloudflare.
npm install
npm run dev # http://localhost:3000 — normal Next dev server
To exercise the real Worker runtime:
npm run preview # opennext build + populateCache + wrangler dev
npm run preview is the only correct way to test locally. Plain
wrangler dev against an existing .open-next/ will serve 404s for every
blog post: next build writes pages produced by generateStaticParams into
.open-next/cache/<BUILD_ID>/, not into the assets bundle, and it is
opennextjs-cloudflare populateCache — which preview and deploy run and
build does not — that copies them to where the Worker can read them. See
open-next.config.ts.
npm run deploy # opennext build + populateCache + wrangler deploy
Requires wrangler login (or CLOUDFLARE_API_TOKEN) once. There are no
secrets, no bindings and no KV namespaces to provision — every route is
prerendered at build time.
This repo is named maddox05.github.io and used to be served by GitHub
Pages straight out of the repository root. The root no longer contains an
index.html, so after the first Cloudflare deploy:
maddoxschmidlkofer.com at the Worker (Workers & Pages → the worker →
Settings → Domains & Routes → Add custom domain).maddox05.github.io with a broken build.Until step 1 is done the live site is still the old one.
app/
layout.tsx Site-wide metadata, Person + WebSite JSON-LD, analytics
page.tsx Home
globals.css The whole design. Plain CSS, no Tailwind.
opengraph-image.tsx The default social card, drawn at build time
blog/
posts.ts The post index — ADD NEW POSTS HERE
posts/*.md Post bodies (GitHub-flavored markdown)
[slug]/page.tsx Post template
projects/<id>/ One write-up per project
*-sitemap.xml/ Split sitemap set (see lib/sitemap-data.ts)
feed.xml/ RSS 2.0
components/ Shared UI. `"use client"` only where it is unavoidable.
lib/
seo.ts Titles, descriptions, canonicals, structured data
projects.ts The project timeline data
goose/ The easter egg. Untouched vanilla ESM.
public/assets/ Images, the goose sprites, the Marble Skies WebGL build
backup-site/ The pre-Next.js static site, kept for reference
app/blog/posts/<slug>.md. No front matter, and no top-level
# heading — the template renders the title.posts array in app/blog/posts.ts.public/images/blog/.The index, the sitemap, the RSS feed and the static route all follow from that
one array. app/blog/posts.ts documents every field.
Set draft: true on an entry to keep a finished post in the repo without
publishing it. The markdown, the cover and the entry all stay; the production
build drops the post from the index, the sitemap, the feed, the previous/next
links and the index’s JSON-LD, and does not prerender its route — so there is no
unlisted URL to stumble onto, it 404s. npm run dev still serves it at
/blog/<slug> so it can be read and edited.
Deleting the flag publishes it.
The seven competitor case studies (Scout, MenuFit, Exposr, Oasis, Seed Oil Scout, Olive, Claim Money) are currently drafts.
The site was hand-written HTML on GitHub Pages. The port kept the design byte-for-byte and fixed what was underneath it:
/blog loaded
blogbott.com/aiblog.js, which rendered every post client-side into an
empty <div> and addressed them as ?blog=<slug> query parameters on one
URL — one <title> between all of them, no server HTML to index, and a
canonical pointing at the parameterised URL. The seven posts Maddox actually
wrote are now markdown in this repo at their own prerendered /blog/<slug>.
The old URLs 308 to the new ones (next.config.ts).Person node in the
root layout is referenced by @id from every post and project, so
the site reads as one entity graph.| Maddox's Site, a phrase with no search
volume, inside a 60-character budget. See lib/seo.ts./projects
index, previous/next links between posts, and cross-links between the
write-ups.public/assets/img is now 7.7MB./tallow was a <meta http-equiv="refresh">, which Google treats as a
weak hint rather than a redirect. It is a 308./projects/duckmath, which is this site’s own account of that story.maddox.page domain. Machine-generated
filler, one a day for six months, all of them rehashing “null handling”.app/blog/posts.ts has the details on both.