Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ mac/dist/
# Marketing site (site/) build artifacts
site/node_modules/
site/dist/
# `wrangler pages dev site/dist` — used to reproduce Cloudflare Pages' header
# and not-found behaviour locally — drops its state here.
.wrangler/
# Dashboard build output — produced by `make dashboard-build`.
# A committed `.gitkeep` keeps dist/ non-empty so `//go:embed all:dist` works
# on a fresh clone (the embed.FS needs at least one entry). The real
Expand Down
74 changes: 74 additions & 0 deletions site/public/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<!doctype html>
<!--
Cloudflare Pages serves this file, with a real 404 status, for any path that
does not match a built asset. Without it Pages answers those requests with
the root index.html and a 200 — which, combined with the year-long immutable
Cache-Control that public/_headers puts on /assets/*, let a deploy-window
miss on a hashed bundle poison visitors' browser caches with HTML pretending
to be JavaScript. The module then failed its MIME check and the whole page
rendered blank until the cache entry expired (a year) or the asset URL
changed. Keep this file: it is what makes a missing asset fail loudly.

Deliberately standalone — no bundle, no webfont, no image — so it renders
even when the assets it is reporting on are the thing that is broken.
-->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Not found · cix · CodeIndeX</title>
<meta name="robots" content="noindex">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<meta name="theme-color" content="#F2E6CF">
<style>
:root {
--bg: #F2E6CF;
--paper: #F8EFDC;
--ink: #1F1410;
--ink-mute: #7C5F50;
--red: #D63D2C;
}
html, body { margin: 0; height: 100%; }
body {
background: var(--bg);
color: var(--ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
main {
background: var(--paper);
border: 3px solid var(--ink);
border-radius: 22px;
box-shadow: 8px 8px 0 var(--ink);
max-width: 34rem;
padding: 2.5rem;
text-align: center;
}
.code { font: 700 4rem/1 ui-monospace, "SF Mono", Menlo, monospace; color: var(--red); margin: 0; }
h1 { font-size: 1.5rem; margin: 0.75rem 0 0.5rem; }
p { color: var(--ink-mute); line-height: 1.6; margin: 0 0 1.5rem; }
a {
display: inline-block;
background: var(--red);
color: var(--paper);
border: 3px solid var(--ink);
border-radius: 999px;
padding: 0.6rem 1.4rem;
font-weight: 600;
text-decoration: none;
}
a:hover { background: var(--ink); }
</style>
</head>
<body>
<main>
<p class="code">404</p>
<h1>Nothing indexed at this path</h1>
<p>The page you asked for does not exist. If you landed here from a link on the site, it is a bug worth reporting.</p>
<a href="/">Back to codeindex.app</a>
</main>
</body>
</html>
12 changes: 12 additions & 0 deletions site/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ const here = dirname(fileURLToPath(import.meta.url));
export default defineConfig({
plugins: [react()],
build: {
// Cache generation, bumped to evacuate a poisoned URL. On 2026-08-10 a
// deploy window answered requests for /assets/*.js with index.html and a
// 200, and public/_headers stamped that HTML with the year-long immutable
// Cache-Control meant for hashed bundles — so every browser that hit the
// site during the window pinned "HTML that claims to be JavaScript" until
// 2027 and rendered a blank page. Content hashes cannot rescue those
// clients (the poisoned entry is keyed by the URL, and the URL was right),
// so the whole directory moves instead; index.html is served
// must-revalidate and picks the new paths up immediately.
// public/404.html now makes a missing asset a real 404, which is what
// stops this from recurring — bump this only if it somehow happens again.
assetsDir: 'assets/g2',
rollupOptions: {
input: {
main: resolve(here, 'index.html'),
Expand Down