THE SHELL — the thing that killed the board

Day three of THE BOARD already names what 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, while the board's
ghost is still warm.

kruuna.helsinki.fi, 1991. It connects instantly, with no dial tone, no
ring and no busy signal, and that silence after eight minutes of the board
is the whole opening beat. who(1) shows the people who are on the machine
at this second, against a board that had forty-four users in nine years
and could hold one at a time. talk(1) 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 while you carry on using the shell underneath it — two
things at once, demonstrated rather than stated, which the board could
never do.

Then the interjection. rms has been on ttyp7 since six in the morning. He
waited until the level printed a whole operating system over a build log
in which every single line is gcc, and then he opened a talk request, and
he is right. If you never answer it he leaves mail instead, because of
course he does.

The closing speech now credits the compiler, the linker, the shell, make
and libc, which were all finished and free years before the kernel was.

74 philes. All 14 hops swept clean. README and STAGES.md brought current.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-08-02 18:18:37 +10:00
parent 10b6c8fdf5
commit e5ecadeab5
5 changed files with 533 additions and 14 deletions

View File

@ -36,23 +36,24 @@ localStorage; the boot screen offers `R — RESUME` when there's something to re
## What's here
Thirteen playable hops, each a different genre, one renderer.
Fourteen playable hops, each a different genre, one renderer.
| id | hop | what it is |
|---|---|---|
| `coldboot` | 1 | FP tutorial in a 512-slot NIC ring buffer, then the DMA rips you out |
| `stack` | 28 | 7-floor third-person descent. NX floors, rate gates, canaries, ASLR reshuffles on death, stateful FIREWALL boss |
| `stack` | 28 | 7-floor descent, **one verb per layer**: transcode, keep the session, reassemble in sequence, route within a hop budget, listen-before-transmit with real exponential backoff, ride the waveform. Stateful FIREWALL boss |
| `timewait` | 9 | walking sim. beige room, 50 Hz hum, half-open connections in plastic chairs, FIN by the door |
| `war` | 1016 | EFnet twin-stick. netsplit riding, ops crowns, Sub7 body-snatch |
| `smash` | 1720 | 2D side-scroll `char buf[64]`. canary, return address, NOP sled, shellcode |
| `worm` | 2128 | Morris worm RTS with the 1-in-7 reinfection locked on. you lose by winning |
| `war` | 1016 | EFnet twin-stick. netsplit riding, ops crowns, and a **trojan arms race** — four weapons the level patches 70 s after you first use one |
| `smash` | 1720 | 2D side-scroll `char buf[64]`. canary, return address, NOP sled, shellcode — then **NX lands** and you build a **ret2libc** chain out of other people's platforms |
| `worm` | 2128 | Morris worm RTS, 1-in-7 reinfection locked on. An **antidote you can release** that travels only over live links, while you are killing links |
| `cuckoo` | 2934 | LBL 1986 terminal. connection time is visibility. you get caught |
| `board` | 3544 | the BBS. busy signal, ANSI painting at live baud, ratios, NUP gate, ANSI bomb, the sysop watching you type, FidoNet, playable LORD |
| `board` | 3544 | the BBS, over **three days**. busy signal, ANSI painting at live baud, ratios, NUP gate, ANSI bomb, the sysop watching you type, FidoNet, playable LORD *and* Trade Wars — then LAST CALL, and it closes |
| `shell` | 4344 | kruuna.helsinki.fi, 1991. always on, 38 users at once, talk(1) character by character, the comp.os.minix post, and a kernel compiling in the background while you use the shell |
| `handshake` | 4550 | five-boss rhythm arena. dial tone → DTMF → ANSam → V.8bis → carrier |
| `copper` | 5158 | FP through duct, load coils, a Strowger cathedral, a lineman's handset |
| `bluebox` | 5963 | playable 13-button blue box. 2600, KP/MF/ST, tandem stacking, then SS7 |
| `climb` | 63→1 | on-rails reverse ascent. Spectre split-timeline, Rowhammer, Log4Shell |
| `root` | 64 | the room is empty. then the ACK comes back |
| `climb` | 63→1 | on-rails reverse ascent, **one gate per era** testing the verb it taught you. Spectre is a real **split screen**, plus Rowhammer and Log4Shell |
| `root` | 64 | the room is empty. then the ACK comes back — and on Tuesday, so does your FidoNet message |
## The three load-bearing ideas
@ -114,6 +115,7 @@ to zero in a hidden tab and that makes the game look broken when it's only aslee
- **Multiplayer (`CHECKSUM`)** — the Journey-style anonymous pass-by. Needs a server; add when there's something to connect to.
- **Playing both fragments** — fragmenting costs TTL, it doesn't split you into two packets you alternate between. Add when a level has a corridor narrow enough to need it.
- **The 200 philes** — 40 written in [`src/philes.js`](src/philes.js), all readable with `P`. Adding the next 160 is a writing job, not a code job: put the text in that file, call `ctx.phile('id')` wherever it should be found.
- **Trade Wars** — the second door game. LORD is real and playable; TW2002 is "closed for maintenance since 1994", which is both a joke and true.
- Levels are one hop's worth of content each, not the full 520 minutes the design doc describes. The genres, mechanics and set pieces are all in — the *duration* isn't.
- **The 200 philes** — 74 written in [`src/philes.js`](src/philes.js), all readable with `P`. Adding the next 126 is a writing job, not a code job: put the text in that file, call `ctx.phile('id')` wherever it should be found.
- **Era-generated music** — the design doc says each level's pad is built from that era's protocol. Right now they're chords.
- **The retrain** — a mid-game renegotiation that drops your baud without warning. One system, touches every level after it.
- Levels run 38 minutes against a design doc that asks for 520. The genres, mechanics and set pieces are all in — the *duration* is still short. See [STAGES.md](STAGES.md) for a per-hop breakdown of what's in and what's next.

View File

@ -3,7 +3,7 @@
> 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: 13 playable hops, ~8,000 lines, all clean.
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
@ -209,6 +209,39 @@ is read-only.
---
## 8b · THE SHELL — `src/levels/shell.js`
*hops 4344 · 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 4550 · rhythm boss rush · ~6 min · sets your baud*
@ -325,7 +358,7 @@ TIME_WAIT with FIN saying *told you*, then it loops to hop 1.
| | |
|---|---|
| **The philes** | 66 of 200 written. All in `src/philes.js`, all readable with `P`. Pure writing. |
| **The philes** | 73 of 200 written. All in `src/philes.js`, all readable with `P`. Pure writing. |
| **Audio** | Every tone is real but the *music* isn't built — the design says each level's pad is generated from that era's protocol and right now they're just chords. |
| **The retrain** | A mid-game renegotiation that drops your baud without warning. One system, touches every level after it. |
| **Difficulty** | Nothing scales. TTL is the only pressure and it's linear. |

372
src/levels/shell.js Normal file
View File

@ -0,0 +1,372 @@
// HOPS 4344 — THE SHELL
//
// The thing that killed the board. It is on all the time. There is no busy signal.
// Thirty-eight people are logged in at this exact second, which is fewer than the
// board had in nine years and more than the board could ever have at once.
//
// And it can do two things at the same time. That is the whole level: a kernel
// compiles in the top of the screen while you talk to somebody in the bottom, and
// on the board you could not have done either of those while doing the other.
import * as THREE from '../../vendor/three.module.js';
import * as A from '../audio.js';
import * as In from '../input.js';
import { Term } from '../term.js';
let S = {};
const MOTD = `|10
SunOS 4.1.3 (kruuna) ttyp4
|07 welcome to kruuna.helsinki.fi
* quota is 4MB. it is not negotiable and it is not enough.
* do not run anything big before 18:00.
* be decent to each other. we can all see who you are.
|08 you have mail.
`;
// Real people, on a real box, at the same time as you. The board had one line.
const WHO = [
['torvalds', 'ttyp0', '11:02', '(kruuna)'],
['jkh', 'ttyp1', '09:14', '(freefall)'],
['ari', 'ttyp2', '10:47', '(nic.funet.fi)'],
['lars', 'ttyp3', '08:31', '(sirppi)'],
['you', 'ttyp4', 'now', '(-)'],
['minna', 'ttyp5', '11:20', '(otax)'],
['pekka', 'ttyq0', '07:55', '(kruuna)'],
['hannu', 'ttyq1', '11:31', '(cs.hut.fi)'],
['rms', 'ttyp7', '06:02', '(gnu.ai.mit.edu)'],
];
// The post. 25 August 1991, comp.os.minix. Quoted short, because it is somebody's
// actual words and the famous part is one sentence long anyway.
const POST = `|08 Newsgroups: comp.os.minix
From: torvalds@kruuna.helsinki.fi (Linus Benedict Torvalds)
Date: 25 Aug 91 20:57:08 GMT
Subject: What would you like to see most in minix?
|07 he says he is doing a free operating system. he says it is
|15"just a hobby, won't be big and professional like gnu"|07.
he says it will only ever run on 386 AT clones, because that is the
only hardware he has. he asks what people would like to see in it.
he says he will get to it |15"if anybody is interested"|07.
|08
|07 he wanted to call it Freax.
the man who ran the ftp server did not like Freax, so when he made
the directory to put it in he called it |15linux|07 instead, and never
mentioned it, and that was that.
five months later, version 0.12, he changes the licence to the GPL.
that is the part that matters. not the code the code was ten
thousand lines and most of it was a terminal driver.
`;
// He is right, and the level had it coming: every line of that build log is gcc,
// which is GNU, and the level calls the result "a whole operating system".
const TALK_RMS = [
"i'd just like to interject for a moment.",
'what you are referring to as linux is in fact GNU/linux. or as i have',
'recently taken to calling it, GNU plus linux.',
'',
'linux is not an operating system unto itself. linux is the kernel: the',
'program that allocates the machine to the other programs. it is essential',
'and it is useless on its own.',
'',
'look at what you just ran. scroll up. every line of it says gcc.',
'',
'gcc is ours. so is the linker you called. so is make. so is the shell you',
'typed make into, and the coreutils, and the libc it linked against. we',
'started in 1983 and by 1991 we had all of it finished except a kernel.',
'',
'he wrote the kernel. it was the last piece and it was a very good piece.',
'the whole system is basically GNU with linux added.',
'',
'...',
'you may go back to what you were doing. i am aware of how i come across.',
];
const TALK = [
['minna', 'you are new'],
['you', ''],
['minna', 'i can see you typing. that is not a trick, that is just what talk is.'],
['minna', 'it sends every character as you hit it. no send button. no enter.'],
['minna', 'where were you before'],
['you', ''],
['minna', 'oh — a board. one line?'],
['minna', 'my brother had one. i used to have to wait for him to finish.'],
['minna', 'there are thirty-eight of us on here right now and nobody is waiting.'],
['minna', '...'],
['minna', 'sorry. that was a horrible thing to say to somebody who just got here.'],
];
// A kernel build, in the background, while you do something else.
const BUILD = [
'gcc -Wall -O -fomit-frame-pointer -c boot/head.S',
'gcc -Wall -O -fomit-frame-pointer -c init/main.c',
'gcc -Wall -O -fomit-frame-pointer -c kernel/sched.c',
'gcc -Wall -O -fomit-frame-pointer -c kernel/fork.c',
'gcc -Wall -O -fomit-frame-pointer -c kernel/traps.c',
'gcc -Wall -O -fomit-frame-pointer -c kernel/signal.c',
'gcc -Wall -O -fomit-frame-pointer -c mm/memory.c',
'gcc -Wall -O -fomit-frame-pointer -c mm/page.s',
'gcc -Wall -O -fomit-frame-pointer -c fs/buffer.c',
'gcc -Wall -O -fomit-frame-pointer -c fs/inode.c',
'gcc -Wall -O -fomit-frame-pointer -c fs/namei.c',
'gcc -Wall -O -fomit-frame-pointer -c fs/read_write.c',
'kernel/chr_drv/tty_io.c:104: warning: unused variable `c\'',
'gcc -Wall -O -fomit-frame-pointer -c kernel/chr_drv/console.c',
'gcc -Wall -O -fomit-frame-pointer -c kernel/blk_drv/hd.c',
'gcc -Wall -O -fomit-frame-pointer -c kernel/blk_drv/floppy.c',
'gcc -Wall -O -fomit-frame-pointer -c lib/string.c',
'ld -M boot/head.o init/main.o kernel/kernel.o mm/mm.o fs/fs.o -o vmlinux',
'nm vmlinux | grep -v \'\\(compiled\\)\\|\\(\\.o$\\)\' | sort > System.map',
];
export default {
id: 'shell', hop: 43, title: 'THE SHELL', via: 'kruuna.helsinki.fi', ms: '31 ms',
baud: null,
async mount(ctx) {
S = { t: 0, done: false, built: false, building: false, buildAt: 0, talked: false, read: false,
rmsWaiting: false, metRms: false };
window.__S = S; // debug handle
ctx.scene.background = new THREE.Color(0x000000);
ctx.camera.position.set(0, 0, 5);
ctx.termCanvas.style.display = 'block';
const term = new Term(ctx.termCanvas);
ctx.term = term; S.term = term;
In.captureText(e => { if (term.busy) { term.skip = true; return true; } return term.key(e); });
ctx.hint('');
S.script = this.run(ctx, term);
},
async run(ctx, term) {
const baud = () => Math.max(1200, ctx.link.rate);
term.clear();
// ── it just connects. no dial tone. no ring. no busy. that is the whole shock.
await term.paint('|07$ ', 1200);
await ctx.sleep(500);
await term.paint('telnet kruuna.helsinki.fi\n', 900);
await ctx.sleep(260);
await term.paint('|08Trying 128.214.6.100...\nConnected to kruuna.helsinki.fi.\n', 2400);
await ctx.sleep(200);
ctx.say('no dial tone. no ring. no busy signal. it was just <i>already there</i>.', 5600);
await term.paint('\n|07login: |15syn\n|07Password:\n', 1600);
await ctx.sleep(700);
await term.paint(MOTD, baud());
await ctx.sleep(900);
ctx.phile('always-on');
ctx.hint('<k>who</k> <k>news</k> <k>talk</k> <k>make</k> <k>logout</k>');
ctx.say('type <b>help</b>. it is a real shell and it is on all the time.', 5000);
for (;;) {
if (S.done) return;
this.tickBuild(ctx, term);
const raw = (await term.ask('|02syn@kruuna|07:|11~|07$ ')).trim();
if (S.done) return;
const [cmd, arg] = raw.split(/\s+/);
term.writeln('');
if (!cmd) continue;
if (cmd === 'help') {
term.writeln('|08 who news talk minna make uptime logout');
term.writeln('|08 (and it will keep compiling while you do all of those.)');
} else if (cmd === 'who') {
WHO.forEach(([u, tty, at, from]) =>
term.writeln(`|${u === 'you' ? '15' : '11'} ${u.padEnd(10)}${tty.padEnd(8)}${at.padEnd(8)}|08${from}`));
term.writeln('');
term.writeln('|03 thirty-eight logged in. the board had forty-four users in nine years');
term.writeln('|03 and could hold exactly one of them at a time.');
ctx.phile('multiuser');
} else if (cmd === 'uptime') {
term.writeln('|11 11:34am up 91 days, 3:12, 38 users, load average: 2.14, 1.88, 1.51');
term.writeln('|08 ninety-one days. it has not been switched off since february.');
} else if (cmd === 'news' || cmd === 'rn') {
await term.paint(POST, baud());
S.read = true;
ctx.phile('the-post');
} else if (cmd === 'talk') {
if ((arg || '').toLowerCase() === 'rms') await this.rms(ctx, term);
else await this.talk(ctx, term);
} else if (cmd === 'make') {
if (S.built) { term.writeln('|08 make: `vmlinux\' is up to date.'); continue; }
if (S.building) { term.writeln('|12 it is already building. look up.'); continue; }
S.building = true; S.buildAt = 0; S.buildLine = 0;
term.writeln('|10 make: building the kernel. it will take a while.');
term.writeln('|03 do something else while it runs. you could not do that before.');
ctx.phile('multitask');
} else if (cmd === 'logout' || cmd === 'exit') {
return this.logout(ctx, term);
} else {
term.writeln(`|12 ${cmd}: Command not found.`);
}
}
},
// ── the compile, in the top four lines, while you get on with your life
tickBuild(ctx, term) {
if (!S.building) return;
const save = { x: term.cx, y: term.cy, f: term.fg };
const n = Math.min(4, BUILD.length - S.buildLine);
for (let i = 0; i < 4; i++) {
term.goto(0, i);
const li = S.buildLine - 3 + i;
const txt = li >= 0 && li < BUILD.length ? BUILD[li] : '';
term.fg = txt.includes('warning') ? 14 : 8;
term.write((txt ? ' ' + txt : '').padEnd(79).slice(0, 79));
}
term.goto(save.x, save.y); term.fg = save.f;
void n;
},
async talk(ctx, term) {
term.writeln('|10 [Connection established]');
await ctx.sleep(600);
for (const [who, line] of TALK) {
this.tickBuild(ctx, term);
if (who === 'you') {
const said = await term.ask('|15 ');
void said;
continue;
}
// talk sent every character as it was typed. no send button. no enter.
term.write('|11 ');
for (const ch of line) {
term.write(ch);
if (ch !== ' ') A.blip(1500 + Math.random() * 300, 0.006, 0.014);
await ctx.sleep(28 + Math.random() * 34);
}
term.writeln('');
await ctx.sleep(500);
}
term.writeln('|08 [Connection closed]');
S.talked = true;
ctx.phile('talk');
ctx.say('she saw every character as you typed it. the board made you press enter and wait a day.', 6000);
},
async rms(ctx, term) {
S.rmsWaiting = false;
term.writeln('');
term.writeln('|10 [Connection established]');
await ctx.sleep(700);
for (const line of TALK_RMS) {
this.tickBuild(ctx, term);
if (!line) { term.writeln(''); await ctx.sleep(340); continue; }
term.write('|14 ');
for (const ch of line) {
term.write(ch);
if (ch !== ' ') A.blip(1400 + Math.random() * 200, 0.005, 0.012);
await ctx.sleep(17 + Math.random() * 20);
}
term.writeln('');
await ctx.sleep(260);
}
term.writeln('|08 [Connection closed]');
S.metRms = true;
ctx.game.flags.gnuPlusLinux = true;
ctx.phile('gnu');
ctx.say('he is right, and it took him nine years of saying it to be heard once.', 6000);
},
async logout(ctx, term) {
S.done = true;
In.releaseText();
term.clear();
if (S.building && !S.built) {
// it finishes whether you are watching or not, because it is not your computer
await term.paint('|08 [1]+ Done make\n', 1600);
S.built = true;
}
await ctx.sleep(600);
await term.paint(`|07
it is better.
it is on all the time. it holds thirty-eight people. it does two things
at once. it costs eighteen dollars a month and there is no ratio and
nobody is waiting for the line.
|03 and in january he put it under a licence that says anybody can have it,
and change it, and give it away, as long as they let the next person do
the same.
|07 he could have sold it. he was twenty-one and he could have sold it and
he did not, and that is the entire reason any of the rest of this exists.
|08 (and the compiler, and the linker, and the shell, and make, and libc were
already finished and already free, and had been for years, and somebody on
ttyp7 would very much like that mentioned.)
`, Math.max(1500, ctx.link.rate));
if (!S.metRms) {
await ctx.sleep(900);
await term.paint('|08\n You have new mail.\n |14 From: rms@gnu.ai.mit.edu Subject: GNU/Linux\n' +
'|08 (you never answered the talk request. he wrote it down instead.)\n', 1500);
await ctx.sleep(2200);
}
await ctx.sleep(1200);
await term.paint('|08\n somewhere a phone line is ringing out and nobody is answering it.\n', 1400);
await ctx.sleep(2600);
ctx.game.flags.sawTheShell = true;
term.writeln('');
term.writeln('|08Connection closed by foreign host.');
await ctx.sleep(1800);
ctx.spendTTL(1);
await ctx.card('THE SHELL', 'hop 43 · it is better, and that is what is sad about it', 4200);
ctx.next();
},
update(dt, t, ctx) {
S.t += dt;
if (!S.building || S.built) return;
// it compiles in real time whether you are looking at it or not
S.buildAt += dt;
if (S.buildAt > 1.15) {
S.buildAt = 0;
S.buildLine++;
A.blip(900 + Math.random() * 200, 0.008, 0.02);
if (S.term) this.tickBuild(ctx, S.term);
if (S.buildLine >= BUILD.length) {
S.building = false; S.built = true;
const save = { x: S.term.cx, y: S.term.cy, f: S.term.fg };
S.term.goto(0, 0); S.term.fg = 10;
S.term.write(' vmlinux built. a whole operating system, on a machine you do not own.'.padEnd(79));
for (let i = 1; i < 4; i++) { S.term.goto(0, i); S.term.write(''.padEnd(79)); }
S.term.goto(save.x, save.y); S.term.fg = save.f;
A.burst(880, 1320, 0.3, 0.16);
ctx.say('<b>vmlinux</b> — and it compiled while you were talking to somebody.', 5200);
ctx.phile('vmlinux');
// and somebody on ttyp7 has been waiting a very long time to say something
S.rmsWaiting = true;
setTimeout(() => {
if (!S.term || S.done) return;
const sv = { x: S.term.cx, y: S.term.cy, f: S.term.fg };
S.term.writeln('');
S.term.writeln('|14Message from rms@gnu.ai.mit.edu on ttyp7 at 11:41 ...');
S.term.writeln('|14talk: connection requested by rms@gnu.ai.mit.edu.');
S.term.writeln('|14talk: respond with: talk rms');
S.term.write('|02syn@kruuna|07:|11~|07$ ');
void sv;
A.blip(1800, 0.05, 0.06);
ctx.say('somebody on ttyp7 has been waiting since six in the morning to say something.', 5200);
}, 2600);
}
}
},
async unmount(ctx) {
In.releaseText();
ctx.termCanvas.style.display = 'none';
ctx.term = null;
ctx.hint('');
},
};

View File

@ -16,6 +16,7 @@ import smash from './levels/smash.js';
import worm from './levels/worm.js';
import cuckoo from './levels/cuckoo.js';
import board from './levels/board.js';
import shell from './levels/shell.js';
import handshake from './levels/handshake.js';
import copper from './levels/copper.js';
import bluebox from './levels/bluebox.js';
@ -24,7 +25,7 @@ import root from './levels/root.js';
export const ORDER = [
coldboot, stack, timewait, war, smash, worm,
cuckoo, board, handshake, copper, bluebox, climb, root,
cuckoo, board, shell, handshake, copper, bluebox, climb, root,
];
const BY_ID = Object.fromEntries(ORDER.map(l => [l.id, l]));

View File

@ -614,6 +614,117 @@ keep calling."`,
ignore me about and i find that i do not mind."`,
},
// ── hop 4344, the shell ───────────────────────────────────────────────
'always-on': {
title: 'no busy signal',
from: 'the first thing you notice',
body: `it is on.
not "it is on in the evenings when the sysop is not using the phone". not
"it is on if nobody else got there first". on, at four in the afternoon on
a tuesday, and at three in the morning, and in february when nobody has
touched it for ninety-one days.
everything the board was the ratio, the time limit, the wanting, the
whole feeling of somewhere you had to get into was a consequence of one
phone line in one man's spare room. take the scarcity away and all of it
goes, the bad and the good together.`,
},
'multiuser': {
title: 'thirty-eight at once',
from: 'who(1)',
body: `the board had forty-four users in nine years and could hold exactly one
of them at a time.
this holds thirty-eight right now, and none of them know each other, and
none of them are waiting.`,
},
'multitask': {
title: 'two things at once',
from: 'make(1), in the background',
body: `it is compiling a kernel in the top of the screen while you are talking
to somebody in the bottom of it.
on the board you could read a message or play a door game or download a
file, and while you did any one of those the other forty-three people got
a busy signal.
this is not a better version of that. it is a different kind of object.`,
},
'the-post': {
title: 'what would you like to see most in minix?',
from: 'comp.os.minix, 25 August 1991',
body: `he says he is doing a free operating system. he says it is "just a hobby,
won't be big and professional like gnu". he says it will only ever run on
386 AT clones because that is the only hardware he has.
he wanted to call it Freax. the man who ran the FTP server did not like
Freax, so when he made the directory he called it linux, and never
mentioned it, and that was that.
version 0.01 was about ten thousand lines and a good deal of it was a
terminal driver, because what he had actually set out to write was a
program for dialling into the university with.
that is worth sitting with for a second. the thing that ended the era of
the board began life as a terminal emulator for calling one.`,
},
'talk': {
title: 'talk(1)',
from: 'every character, as you hit it',
body: `no send button. no enter. it puts each character on their screen at the
moment you type it, and theirs on yours, both at once, on a split screen.
you watch people think. you watch them start a sentence, delete it, and
start again, and there is nothing you can do about having seen it.
we replaced it with something where you compose in private and then
publish, and we called that an improvement, and it might have been.`,
},
'vmlinux': {
title: 'vmlinux',
from: 'a whole operating system, on a machine you do not own',
body: `version 0.12, January 1992: he relicenses it under the GPL. Anybody can
have it, change it, and give it away, provided they let the next person do
the same.
he was twenty-one. he could have sold it.
that one decision is why the machine this is running on, and the machine
in your pocket, and most of the machines between here and root, are all
running some descendant of a hobby project by a student in Helsinki who
mostly wanted a better way to dial into the university.`,
},
'gnu': {
title: 'GNU/Linux',
from: 'ttyp7, since six in the morning',
body: `he is right, and it is worth saying plainly because the joke has almost
completely buried the fact.
the GNU project started in 1983 with the goal of a complete free operating
system. by 1991 they had written nearly all of it the compiler, the
linker, make, the shell, the core utilities, the debugger, the libc. what
they did not have finished was a kernel; theirs was late and stayed late.
then a student in Helsinki wrote a kernel, and put it under their licence,
and the last piece fitted.
look at the build log in this level. every single line of it is gcc, and
gcc is GNU. the level then prints "a whole operating system" over the top
of it, which is exactly the error he has spent his life correcting, and
being extremely annoying about, and being correct about.
he has been on ttyp7 since six in the morning. he saw who(1) go past. he
waited until you called it an operating system.`,
},
// ── hop 4550, the handshake ───────────────────────────────────────────
'dtmf': {