# HANDSHAKE — stage briefs

> One page per hop. What's built, what's thin, what I'd add, ranked.
> Write your direction under **DIRECTION** and I'll build to it.

Current state: **14 playable hops**, ~8,300 lines, all clean.

**Since the first pass** (you gave me the wheel; these were my calls): the philes
are readable, the game saves, MTU costs you distance, ROOT reads your whole save and
your FidoNet message comes back on Tuesday, TIME_WAIT is a room with sixteen people
in it and one of them ages out mid-sentence, SMASHING THE STACK has ret2libc,
THE CLIMB is a graded final exam with one gate per era, THE BLUE BOX has the routing
card and the 2111 bridge, and THE COPPER lets you follow one call through seven
banks of relays to a wrong number. Items marked **DONE** below.

Remaining gap is still duration: most hops are 3–6 minutes against a doc that calls
for 5–20.

Repo: `ssh://git@100.71.119.27:222/monster/haxtory.git` · run: `python3 serve.py 8123`

**Global rule I'm working to:** nothing gets added that doesn't earn its minutes.
A longer level is not a better level; a level with a second idea in it is.

---

## 1 · COLD BOOT — `src/levels/coldboot.js`
*hop 1 · first person · ~90 s · v.90*

**In:** 512-slot NIC ring buffer, instanced packets breathing in their slots, HEAD
and TAIL pointers circling, six lines of script establishing what you are, then the
DMA fires and rips the camera out at 1400 units/sec into the title card.

**Thin:** it's a corridor with narration. You can walk but there's nothing to walk
*to*. The "you have left your slot" beat is the only interaction.

**What I'd add, ranked**
1. **The tail pointer as a threat you can see coming.** It's already circling. Make it actually reach you — a slow, unhurried countdown of the ring, so the DMA feels like something that arrived rather than something scheduled.
2. **One other packet who talks.** A neighbour in the next slot who has been transmitted before and come back. Three lines. Sets up FIN at hop 9.
3. **The descriptor you can read.** Walk to the slot wall, press E, see your own header laid out — seq number, window size, flags. Establishes the SYN/ACK vocabulary the ending depends on.

**DIRECTION:**
> _

---

## 2 · THE STACK — `src/levels/stack.js`
*hops 2–8 · third-person platformer · ~5 min · v.90*

**In:** seven descending layers, one per OSI layer, each shedding a header shell.
NX floors you fall through, rate-limit gates on a real 1.2 s cycle, canaries that
scream, ASLR reshuffling the NETWORK layer on every death, CSMA/CD platforms
flickering on DATA LINK, and the stateful FIREWALL boss where the solution is to
make it send something outbound and arrive as the reply.

**Thin:** seven platforms per layer generated from one rule, so all seven layers
play the same. The hazards are one-note — you meet each idea once.

**What I'd add, ranked**
1. ~~Give each layer its own verb.~~ **DONE** — seven floors, seven jobs:
   - **7 APPLICATION** — plain. The only polite floor.
   - **6 PRESENTATION** — *transcode*. The same bytes in two encodings, RAW and BASE64, and only one set of platforms is real at a time. `Q` switches representation, in mid-air.
   - **5 SESSION** — *keep it alive*. A platform you have stood on starts closing the moment you leave it. Standing still is the mistake.
   - **4 TRANSPORT** — *reassemble*. They arrived out of order. Only the next platform **in sequence** is solid, and the sequence has nothing to do with where they are.
   - **3 NETWORK** — *route*. Every platform is a router and every router costs a hop out of a budget of five. Overrun it and the floor ASLR-reshuffles under you.
   - **2 DATA LINK** — *listen before transmit*. Shared media; each platform is busy on its own cycle. Land on a busy one and you both lose, with real exponential backoff — 0.35s, then 0.7, then 1.4, then 2.4.
   - **1 PHYSICAL** — *be a signal*. No platforms at all: 46 segments of a travelling wave you ride, attenuating down the wire.
2. **Make the firewall a three-stage fight.** It still has one idea.
3. **Vertical shortcuts.** Skip a layer, keep the header, and have the copper levels behave differently because you're still wearing TLS.
4. **The canaries should be worth waking.** (Note: canaries and NX floors were retired in the verb rebuild — the hazards are now the verbs. Re-add only if a floor needs a second idea.)

**DIRECTION:**
> _

---

## 3 · TIME_WAIT — `src/levels/timewait.js`
*hop 9 · walking sim · ~2 min · v.32bis*

**In:** the beige room. Plastic chairs, 50 Hz strip-light hum synthesised from
actual 50/100/150 Hz, a wall clock doing a real two-minute lap, six seated
half-open connections with three lines each, and FIN by the door with nine.

**Thin:** it's the shortest emotionally-load-bearing level in the game. Six NPCs is
not a room. And FIN's nine lines fire on a single E-mash.

**What I'd add, ranked**
1. ~~More people, fewer words each.~~ **DONE** — sixteen seated, one or two lines apiece, most of it not about you.
2. ~~Someone ages out mid-sentence.~~ **DONE** — at 38 s, quietly, and nobody in the room looks up.
3. ~~The clock should matter.~~ **DONE** — a full two-minute lap is noticed, and is a phile.
4. ~~FIN refuses if you rush him.~~ **DONE** — three real conversations before he'll tell you about the door.
5. **Still open:** somebody should arrive while you're in there, not just leave.

**DIRECTION:**
> _

---

## 4 · THE WAR — `src/levels/war.js`
*hops 10–16 · top-down twin-stick · ~3 min · 9600*

**In:** #2600 on EFnet. Sixteen nicks milling and saying things, the op wearing a
crown and chasing you to kick you, ten seconds of telegraphed lag, the netsplit,
the ghost crown on the empty side, and the rejoin that makes it real. Sub7
body-snatch on the bots.

**Thin:** two successful split-rides and it's over. The Sub7 possession has no
purpose — you take somebody over and then there's nothing to do with them.

**What I'd add, ranked**
1. ~~The trojan arms race.~~ **DONE** — WinNuke, Sub7, Smurf, DCC send, each patched by the level 70 s after first use, with the countdown on screen. You cannot finish on one tactic.
2. ~~DCC send.~~ **DONE** — PAMELA.JPG.EXE. They take it. They always take it, and then they're yours and they body-block the rivals.
3. ~~Rivals.~~ **DONE** — a crew turns up after the first rejoin who've read the same text files you have, and they'll take the ghost crown out from under you.
4. **A second channel.** #phreak next door, different rules, and the war is over which one the population ends up in.
5. **Give possession more point.** A possessed client should go where you can't — through a ban, into +i.

**DIRECTION:**
> _

---

## 5 · SMASHING THE STACK — `src/levels/smash.js`
*hops 17–20 · 2D side-scroll · ~3 min · 9600*

**In:** one `char buf[64]` you walk right along, the canary value on a ledge, the
canary bird at the boundary, four return-address bytes you write little-endian by
standing on them in order, a 40-tile NOP sled you slide down, shellcode at the end,
and Aleph One chain-smoking at the start.

**Thin:** ret2libc is described in the design doc and isn't in. The level is one
straight line — no reason to ever go back.

**What I'd add, ranked**
1. ~~ret2libc as a real second route.~~ **DONE** — NX lands the instant you reach the shellcode; libc appears; four gadgets in order, four real decoys, and the chain lays the bridge behind you.
2. **Make the address matter.** Any four bytes work because they're prewritten. Let the player choose, including wrongly.
3. **A second buffer.** Off-by-one into the low byte of the saved pointer. Harder, better.
4. **Aleph One should say more than one line.** He's the mentor and he's got four words.

**DIRECTION:**
> _

---

## 6 · THE WORM — `src/levels/worm.js`
*hops 21–28 · RTS · ~3 min · 9600*

**In:** 60 ARPAnet hosts on a graph with real names, three vectors (fingerd
overflow, sendmail DEBUG, rsh trust) with different reach and rate, load
accumulating per host, hosts dying at load 7, links dying with them, and the
1-in-7 reinfection locked on and displayed and unchangeable.

**Thin:** you pick a vector and watch. The only input is 1/2/3.

**What I'd add, ranked**
1. ~~Let the player try to stop it.~~ **DONE** — an antidote, on K, three seeds. It travels only over live links, one hop per carrier per tick, while you are killing links. It cures fast and then **stops dead** with its component severed while the death count climbs past it. In testing: 4 → 12 → 18 → 19, stall, dead 20 → 30. The cure works perfectly on everything it can reach. It cannot reach the rest, because you already took the road.
2. ~~Named hosts that fall over.~~ **DONE** — the first six are named as they go.
3. ~~The phone calls.~~ **DONE** — six, and none of them can send mail to warn anybody, because the broken thing is the thing you'd have used.
4. **Pacing note:** attempt rate now scales with the infection (every infected host runs a copy), which is both correct and the only way it reached its own milestones inside a human lifetime. First deaths ~22 s, endgame ~60 s.
5. **A win condition that is worse.** Spread to everything before anything dies. Should be possible and should require restraint the worm didn't have.

**DIRECTION:**
> _

---

## 7 · THE CUCKOO'S EGG — `src/levels/cuckoo.js`
*hops 29–34 · terminal stealth · ~3 min · 1200*

**In:** LBL VAX 11/780, logged in as sventek. A filesystem with six real documents,
`ls`/`cd`/`cat`/`who`/`ps`, and a "lines still being watched" bar that decays on
real elapsed connect time. The SDINET bait file costs you a third of your margin
for reading it. You get caught; the only variable is what you got first.

**Thin:** six files and five commands. The tension is a timer, not a decision.

**What I'd add, ranked**
1. **Make him visible in `ps`.** He's already in there as a joke. Let the player actually watch his processes change — `cat /dev/tty01 > /dev/lp0` appearing on *your* line is the moment the level turns.
2. **Choices that trade time for cover.** Clearing logs costs 40 seconds and buys you 10 lines. Reading a file costs time and gains a phile. Right now everything is free except reading.
3. **More filesystem.** The pleasure is poking around. Twenty files, most of them boring on purpose, three that matter.
4. **The Hannover number.** It's mentioned in `roster.txt` and goes nowhere. It should go somewhere — even if where it goes is the blue box level, forty hops later.

**DIRECTION:**
> _

---

## 8 · THE BOARD — `src/levels/board.js`
*hops 35–44 · terminal · ~8 min · whatever you earned*

**In:** the heart, and the most finished level. Busy signal on the first dial,
ANSI logo painting at `baud/10` characters a second, six message-base messages,
five files with a real 1:3 ratio, the ANSI bomb that actually remaps your movement
keys, the sysop breaking into chat and telling you he can see you typing, the NUP
gate, the elite sub-board that tells you how to leave, FidoNet's overnight run, and
a playable LORD in the corner.

**Thin:** LORD is one fight. Trade Wars is a joke that's closed. The message base
is read-only.

**What I'd add, ranked**
1. ~~Come back tomorrow, properly.~~ **DONE** — **three days.** Day 1 as before. You post to FidoNet on logoff, watch the 04:00 run dial five nodes across the world while nobody is awake, and come back to *"you were last on yesterday, calls: 2, you are user #44 of 44."*
2. ~~Let the player post and be replied to.~~ **DONE** — day 2's first message is FIDONET quoting **what you actually typed**, with an answer. Four days, three boards, a long distance call somebody else paid for.
3. ~~Leeching consequences.~~ **DONE** — over-take on day 1 and day 2 opens with your ratio adjusted to 1:1. He said he would.
4. ~~The board should close.~~ **DONE, and it's the level's payload now** — day 3 is **LAST CALL**. Not raided, not busted: the second line was never coming, the long distance is forty dollars a month, and a third of them have shell accounts and aren't calling any more and he doesn't blame them because it's *better*. Nine years, forty-four users, six real names, never met one of them. Ratio off, elite board open, NUP given away because it does not matter now. "do not say anything sad in the message base. i will only read it." They all ignore him.
5. ~~Finish Trade Wars.~~ **DONE** — ten sectors, seven ports with the real class strings, 24 turns, and the Ferrengi in sector nine.
6. **More messages per day.** Five or six each is still thin for a board.

**DIRECTION:**
> _

---

## 8b · THE SHELL — `src/levels/shell.js`
*hops 43–44 · terminal · ~6 min · whatever you earned*

**Why it exists:** day three of THE BOARD already names the thing that kills it —
*"about a third of you have got shell accounts now and are not calling any more, and
i do not blame you, because it is better, it is genuinely better."* The player never
got to see it. Now they do, immediately afterwards, and the board's ghost is still
warm.

**In:** kruuna.helsinki.fi, 1991. It connects instantly — no dial tone, no ring, no
busy — and that silence after eight minutes of the board is the whole opening beat.
`who` shows eight of the thirty-eight people logged in *at this second*, against a
board that had forty-four users in nine years and could hold one at a time. `news`
is the 25 August 1991 comp.os.minix post, the Freax/linux directory story, and the
January 1992 GPL relicence. `talk` puts every character on screen as it is typed,
because that is what talk did. And `make` compiles a kernel **in the top four lines
of the screen while you carry on using the shell underneath it** — two things at
once, which the board could never do, demonstrated rather than stated.

**Thin:** it is mostly reading. One real interaction (the compile) and one scripted
one (the talk).

**What I'd add, ranked**
1. **Let the player be talked *to* unprompted.** Somebody opens a talk request while you are mid-command. That is the actual texture of a shared machine.
2. **`finger` and `.plan` files.** The whole social layer of a Unix box was people leaving notes in a file anyone could read.
3. **Quota.** 4MB, enforced. Try to keep something and have to delete something else.
4. **The Tanenbaum debate.** "LINUX is obsolete", comp.os.minix, January 1992 — a second news thread, and it is funnier and sadder than the first.

**DIRECTION:**
> _

---

## 9 · THE HANDSHAKE — `src/levels/handshake.js`
*hops 45–50 · rhythm boss rush · ~6 min · sets your baud*

**In:** the showpiece. Five bosses, every one a real frequency doing the real thing.
Dial tone as two-circle interference you chase the intersection of; DTMF as a 4×4
grid where you attack at the crossing of a row and column tone; ANSam at 2100 Hz
flipping its attack direction on its own 450 ms phase reversals; V.8bis as bullet
hell where clean dodges declare capabilities and your score becomes your baud rate
for the next two hours; then the carrier ride. Ends on a real CONNECT string.

**Thin:** the bosses don't escalate into each other. Each is a self-contained idea
that stops when it's beaten.

**What I'd add, ranked**
1. **A second phase for each.** Dial tone should eventually stop being patient. ANSam should shorten its deaf window. Right now every boss is at full difficulty from the first second and stays there.
2. **Let the player fail upward.** Failing the scream currently just means worse graphics. It should also mean the *carrier ride is harder* — a bad negotiation is a bad line.
3. **Retrain.** Real modems renegotiated mid-call when the line degraded. A retrain event later in the game that drops you back into a 30-second scream fight, at whatever hop you happen to be on, is the best possible callback.
4. **The DTMF number should be dialable wrong on purpose.** Dial something that isn't GET-ROOT and see where it goes.

**DIRECTION:**
> _

---

## 10 · THE COPPER — `src/levels/copper.js`
*hops 51–58 · first person · ~4 min · whatever you earned*

**In:** 300 m of sodium-lit duct with cable bundles, three load coils, a lineman's
handset you clip onto a pair to listen to a stranger's phone call, and a central
office with 4,200 instanced Strowger switches stepping in a wave, plus the Almon
Strowger plaque.

**Thin:** it's a corridor with three exhibits. The cathedral is the best-looking
thing in the game and you walk through it in forty seconds.

**What I'd add, ranked**
1. ~~Follow one call.~~ **DONE** — start it at the MDF and follow it down the aisles through seven banks of relays. Each digit is counted out at ten pulses a second, because that is how a step-by-step switch dials. It connects. It is a wrong number.
2. **Make the cathedral navigable.** You now walk the aisles, but it should be somewhere you can get lost.
3. **More to listen to.** One phone call on the lineman's handset. There should be six, and one should be a modem, and one should be somebody who hears you.
4. **Weather.** The doc says it's the first level with weather. There's no water in it.

**DIRECTION:**
> _

---

## 11 · THE BLUE BOX — `src/levels/bluebox.js`
*hops 59–63 · music/puzzle · ~5 min · whatever you earned*

**In:** the gut punch. A playable thirteen-button blue box with correct R1 MF pairs
and real 100 ms KP / 60 ms digit timing. Hold space for 2600 Hz to seize the trunk,
then KP + digits + ST to route, four times, stacking tandems. Then the call comes
back and rings the payphone six feet behind you and you hear your own signalling a
quarter-second late. Then SS7 arrives and it stops working and Joybubbles keeps
whistling anyway.

**Thin:** the routing codes are dictated. You type what you're told.

**What I'd add, ranked**
1. ~~Let the player route.~~ **DONE** — the routing card is on the wall, the objective names a destination, and Joybubbles refuses to give you the digits.
2. ~~The 2111 conference.~~ **DONE** — dialable from anywhere, forever, by anyone who knows the number. Six voices, no faces, loops indefinitely, and holding 2600 drops the bridge. After SS7 there is nothing on it.
3. **Joybubbles should teach by ear only.** He half does. Take the frequencies off the readout during his lessons.
4. **Free-play mode after SS7.** You keep the box. Let the player still press the buttons.

**DIRECTION:**
> _

---

## 12 · THE CLIMB — `src/levels/climb.js`
*hops 63→1 · on rails · ~2 min · whatever you earned*

**In:** an accelerating flight up through eight era palettes in reverse, with the
three modern toys: Spectre as a split timeline where you carry a token out of the
branch that gets discarded, Rowhammer as a wall you bang on 24 times until a bit
flips next door, and Log4Shell as a sign you hold up.

**Thin:** two minutes for what the design doc calls the best eleven minutes in the
game. The eras flash past as colour changes with no content in them.

**What I'd add, ranked**
1. ~~One mechanic callback per era.~~ **DONE** — seven gates: seize, wiper, 450 ms reversal, ANSI bomb, worm, netsplit, NX. Graded, not lethal (you arrive at TTL 2 by design, so nothing up here spends TTL).
2. ~~Everything on fire.~~ **DONE** — embers, thickening as you climb.
3. ~~Log4Shell's beat of silence.~~ **DONE** — the world stops, the fortress walks over and reads it.
4. **The Spectre section should be a real split screen.** Two viewports, two timelines. Scissor test and a second camera.

**DIRECTION:**
> _

---

## 13 · ROOT — `src/levels/root.js`
*hop 64 · ~3 min · v.90*

**In:** an empty white room with `uid 0` in the middle distance and nobody in it.
Four beats of narration at your own pace, then the ACK arrives from 220 units away,
ESTABLISHED, a sixteen-second countdown, then FIN / FIN-ACK / ACK / CLOSED, then
TIME_WAIT with FIN saying *told you*, then it loops to hop 1.

**Thin:** the loop is instant. And the ending doesn't know what you did.

**What I'd add, ranked**
1. ~~The ending should read your save.~~ **DONE** — philes, expiries, the baud you negotiated and lived with, the climb grade, whether somebody vouched for you, whether you hung up without saying goodbye.
2. ~~Your FidoNet message comes back.~~ **DONE** — Tuesday. Somebody replied, because somebody always did.
3. ~~The loop keeps the philes.~~ **DONE** (`flags.looped` counts rounds) — but hop 1 is not yet different the second time.
4. **CHECKSUM.** The anonymous pass-by. Needs a server. Last.

**DIRECTION:**
> _

---

## Cross-cutting, if you'd rather direct by system than by stage

| | |
|---|---|
| ~~**The philes**~~ | **DONE** — 200 of 200 written and all 200 placed. The board carries everything up to 1991 in seven read-online areas; the climb carries everything after, as signs on the wall. `node tools/philecheck.mjs` verifies. |
| ~~**Audio**~~ | **DONE** — `src/score.js`. Every bed derived from a real number: 1 kHz reference tone, 50 Hz mains, 0x90 as a pitch, 45.45 baud, the 1200 Hz carrier, 20 Hz ringing current, the six MF tones, 2600 folded six times. The handshake is scored silent on purpose. |
| ~~**The retrain**~~ | **DONE** — `src/retrain.js`. Fires once per run after hop 51, drops you a notch, and the recovery is the 450 ms ANSam beat from hop 47. Terminal levels opt out. |
| **Difficulty** | Nothing scales. TTL is the only pressure and it's linear. |
| **The 64 hops** | 13 levels covering 64 hop numbers. Either fill in more levels or make the existing ones properly own their ranges. |
