A single-page, single-concept interactive explainer for rollback attacks and how The Update Framework stops them.
The reader plays the attacker: they operate a compromised CDN edge node, discover that replaying a genuinely signed, year-old release defeats a signature-and-hash update client, and then run the same attack against a TUF-protected client and watch each escalation get blocked by a specific, named check.
This is deliberately not a TUF overview. It teaches one attack, end to end.
Status: draft. It has not been tested with readers yet. The Feedback section at the bottom of the page is a real channel, and it exists to collect input for the first round of testing — nothing here has been validated internally or externally.
npm install
npm run dev # http://localhost:3000npm run build && npm start # production buildRequires Node 18.18+ (Node 20+ recommended).
Zero extra configuration. Push the repository to GitHub and import it on Vercel, or:
npx vercel # preview
npx vercel --prodThere is no backend, no database, no environment variables, and no analytics — the whole page is statically prerendered plus one client component.
app/
layout.tsx metadata, fonts (system stacks — no webfont requests)
page.tsx the narrative: hero → Act I → reveal → Act III → takeaway
globals.css design tokens (light + dark), prose and terminal styles
components/
Console.tsx 'use client' — the attack simulator (move picker, terminal, verdict)
MetadataCard.tsx annotated timestamp.json / snapshot.json
ActualFiles.tsx collapsed-by-default "see the actual files" — the raw JSON
RealTuf.tsx collapsible "how this maps to real TUF" (native <details>)
Feedback.tsx feedback channel — edit CONTACT to change the address/link
lib/
sim.ts all scenario content: moves, terminal transcripts, verdicts
lib/sim.ts is the whole state machine: two sets of three attacker moves, each a fixed
list of terminal lines plus a verdict. Console.tsx reveals lines on a timer (instantly if
the visitor prefers reduced motion) and then shows which check decided the outcome.
Everything is hardcoded text — no cryptography, no network, no Date.now() — so runs are
deterministic and the server and client always render the same thing.
Act I (ordinary client): send the current release · send the real, signed, year-old release (succeeds).
Act III (TUF client): send the old file with today's metadata (caught by the digest in
signed targets metadata) · send November's whole signed metadata set (caught by
expiry and by the version check together).
Those map to specification §5.4.4 and §5.4.3. The page calls timestamp.json "the note"
until the real names are introduced; the terminal always shows the real filenames, so the
two line up when the reader reaches them.
The brief this was built against asks for documentation that is accessible to people who find TUF hard to understand. Two rules follow from that, and edits should keep them:
- One idea per sentence. If a paragraph needs re-reading to parse, split it.
- If the console already demonstrates something, do not also explain it in prose. Prose earns its place by saying what the demo cannot show.
Terminology, check ordering and the two rollback defenses follow the TUF specification, detailed client workflow. The page's own "what this simplified" section lists what was left out — root key rotation and thresholds, targets delegations, consistent snapshots and length limits — so a reader going to the real docs is not surprised.
Nimbus Systems, nimbus-agent, the CVE id and all digests are fictional.