---
title: 'HackerCave BBS'
url: 'https://wiki.teletypegames.org/services/hackercave-bbs'
markdown: 'https://wiki.teletypegames.org/services/hackercave-bbs.md'
lang: en
date: '2026-11-03'
description: "The hacker's reading room of the BBS network: every RFC, the documents the Internet is made of, from RFC 1 of 1969 to the newest, the Internet Experiment Notes before them, and the Jargon File. Search them by number, series, words of the title or anywhere in the text, follow what obsoletes what, s…"
taxonomy:
  tag:
    - howto
---

# HackerCave BBS

> The hacker's reading room of the BBS network: every RFC, the documents the Internet is made of, from RFC 1 of 1969 to the newest, the Internet Experiment Notes before them, and the Jargon File. Search them by number, series, words of the title or anywhere in the text, follow what obsoletes what, see the errata, and read them as they were published, or reflowed for a Commodore 64.
> 
> **Live at `teletypegames.org:2334`**, in the browser on [bbsweb](https://bbsweb.teletypegames.org), and through [TTG Gate BBS](https://wiki.teletypegames.org/services/ttg-gate-bbs)

# Overview

A Ruby app on the [**rubbs**](https://git.teletypegames.org/engines/rubbs) gem, like [Scriptorium BBS](https://wiki.teletypegames.org/services/scriptorium-bbs) and [Starbase BBS](https://wiki.teletypegames.org/services/starbase-bbs).

It has two faces on one port:

- **Modern**: ANSI colours, windows, menus and the mouse (SyncTERM, NetRunner, PuTTY, bbsweb). The shelves and their RFCs in two-pane windows with an RFC's record on the right, a search box, an RFC's links as a list of their own, and a reader that shows the RFC **as it was published**: its 72-column lines unchanged, the running head and foot of each page dim, the headings bright.
- **Commodore 64**: PETSCII on a 40×25 screen, for a C64 terminal program (CCGMS, Novaterm, UltimateTerm) behind a WiFi modem. A cursor menu, the shelves and their RFCs as numbered lists, an RFC's record a page, and the RFC **reflowed to 38 columns** in pages that wait for RETURN.

A call starts with rubbs' `BBS::Terminal` question (RETURN takes the guess, `1` picks the modern face, `2` the C64), then the TTG logo. Through TTG Gate BBS neither comes twice.

# Where the data comes from

| What | Source |  |
|---|---|---|
| The index: every RFC's title, authors, date, pages, status, stream, keywords, abstract, what it obsoletes and updates, the STD, BCP and FYI series, the numbers never issued | the RFC Editor's [rfc-index.xml](https://www.rfc-editor.org/rfc-index.xml), about 14 MB, read again every day |  |
| The texts | the RFC Editor's rsync module `rfcs-text-only`, mirrored every day (about 540 MB); an RFC not there yet is fetched when it is first read |  |
| The errata | [errata.json](https://www.rfc-editor.org/errata.json), with the index every day |  |
| The IENs (1977-1982) | the RFC Editor's `ien/` folder: its `ien-index.txt` and the texts (134 of 209; the rest are scans) |  |
| The Jargon File 4.4.7 | shipped with the board (`config/jargon/jarg447.txt`) | public domain |

About 9,850 RFCs. The index is streamed into SQLite with a full-text index of the titles, authors, keywords and abstracts; an import takes about fifteen seconds and swaps the new database in, so callers keep reading meanwhile. The mirror's texts (`data/rfc/rfcN.txt`, the IENs as `ienN.txt`) go into a second, contentless full-text index (about 135 MB) of every word of every RFC; a day's sync indexes only what changed. The IENs are under negative numbers in the same index (IEN 137 is -137).

The RFCs are the work of the IETF and their authors; copyright the IETF Trust and the persons identified as authors. Every record credits "RFC N, IETF" and links to the RFC Editor, also as a QR code (rubbs' `BBS::QR`).

# Using it

**The library:**

- **Classics**, **Telnet** and **April 1st**, the board's own shelves, each RFC with a note. Telnet is the protocol of the board itself: the notes say which options rubbs speaks (ECHO, SGA, NAWS, BINARY for the downloads, NEW-ENVIRON for TTG Gate BBS).
- **STD**, **BCP** and **FYI**: the series, each RFC with the number it stands for ("STD 7").
- **By status** (Internet Standard, Proposed Standard, Best Current Practice, Informational, Experimental, Historic…), the newest first, and **by decade**; at most 500 each, the rest is a search away.
- **This month**: the RFCs published in the month we are in, from 1969 on. **Newest**: the 30 RFCs of the highest numbers. **Most read**: what the callers here opened most.
- **IENs**: the Internet Experiment Notes, the working papers of TCP/IP before the RFCs took over (IEN 137 named the little- and big-endians).

**Search:** a number (`854`, `rfc 2119`), a series (`std 7`, `bcp 14`), or words of the title, the authors, the keywords and the abstract as word prefixes (`telnet window`). `status:standard` (`proposed`, `draft`, `bcp`, `info`, `experimental`, `historic`) and `year:1983` narrow it. A number that was never issued says so; `ien 137` finds an IEN.

**Search the text** (F9, `6` on the C64): every word anywhere in every RFC and IEN, best matches first, each with the first line that has the word.

**An RFC's record** shows its authors, date, pages, status and series, a red line when a newer RFC replaced it ("Obsoleted by: RFC 9293"), the RFCs it obsoletes and updates and the ones that update it, and the abstract. **Links** lists those RFCs, to open any of them. **Errata** shows what the RFC Editor's errata say: the text as published and as it should read, the verified ones first.

**In the reader** the RFCs a text names are coloured; **Contents** (Alt-O, `t` on the C64) lists its headings to jump to, **References** (Alt-R, `f`) the RFCs it names.

**On a modern terminal:**

| Key | Action |
|---|---|
| F2 | Classics |
| F3 | Search |
| F4 | Shelves |
| F7 | Most read |
| F8 | Newest |
| Enter | Read the RFC |
| F9 | Search the text of every RFC |
| Alt-L / Alt-E / Alt-Q | Its links / its errata / its QR code |
| Alt-O / Alt-R | In the reader: the contents / the RFCs it names |
| PgUp/PgDn, Home/End | Move in the reader; the line under it says "RFC 793 · page 3/91 · line 140/5247" |
| F10 | The menu |
| Alt-X | Log off |

**On a Commodore 64** the main menu is `1` Classics, `2` Telnet, `3` April 1st, `4` all shelves, `5` search, `6` search the text, `J` the Jargon File, `7` most read, `A` about, `Q` log off. Under a record: `R` read, `L` links, `E` errata, `C` QR code. In the reader RETURN turns the page, `B` goes back one, `T` the contents, `F` the RFCs it names, `Q` back to the record.

# The Jargon File

The Jargon File 4.4.7 (29 Dec 2003), the lexicon of hacker slang, is in the public domain and comes with the board: 2,308 entries and the 17 other parts (the introduction, Hacker Folklore with The Story of Mel and the AI koans, the portrait of J. Random Hacker, the bibliography). The Jargon menu (`J` on the C64) has the **word of the day** (the same for everyone on a day), the lexicon **A-Z**, a **search** (the very headword first), a **random entry**, and the **chapters and folklore** to read. An entry's links are coloured; Links (Alt-L, `L`) lists the entries it links to. Every entry says "Jargon File 4.4.7 (public domain)".

**How the C64 reflow works:** prose is joined and wrapped again (a list item keeps a hanging indent); a figure, a table or an address keeps its lines, a line too long breaking at the gaps between its columns; a box drawing wider than the screen comes in panels ("(columns 39-72)"); dot leaders get short; the running head and foot of each page are left out, and a paragraph broken by a page is joined again. Accented letters and the characters the C64 lacks become ASCII.

# Configuration and data

| Variable | Default |  |
|---|---|---|
| `HACKERCAVE_BBS_SITE` | `https://www.rfc-editor.org` | where the index and the texts come from |
| `HACKERCAVE_BBS_INDEX_TTL` | `1` | days before the index is imported again |
| `HACKERCAVE_BBS_SHELVES` | `config/shelves.yml` | the board's own shelves |
| `HACKERCAVE_BBS_DB` | `data/hackercave.sqlite3` | the reads (the Most read list) |
| `HACKERCAVE_BBS_INDEX_DB` | `data/index.sqlite3` | the index (a cache, rebuilt from the download) |
| `HACKERCAVE_BBS_TEXTS` | `data/rfc` | the texts, as published (the mirror) |
| `HACKERCAVE_BBS_MIRROR` | `on` | `off`: no mirror, no text search |
| `HACKERCAVE_BBS_MIRROR_TTL` | `1` | days between two syncs |
| `HACKERCAVE_BBS_FULLTEXT_DB` | `data/fulltext.sqlite3` | the full-text index of the texts |
| `HACKERCAVE_BBS_JARGON_DB` | `data/jargon.sqlite3` | the Jargon File, built from `config/jargon` |

The board has no secrets.

# Under the hood

A push to master deploys it from Woodpecker (RuboCop, the tests, compose check, `ci-deploy hackercave-bbs`); the container is `hackercave-bbs`, in `/srv/stacks/hackercave-bbs`, and `data/` stays between deploys (about 700 MB with the mirror). Ruby takes about 65 MB; the container has 256 MB, as the cgroup counts the page cache of the daily sync too.

The code follows the house layering: `lib/domain` (`RfcModel`, `EntryModel`, `ShelfModel`, `DocumentModel`; `IndexParser` and `IndexBuilder` for rfc-index.xml, the index, RFC Editor, texts, shelves and reads repositories; `LibraryService`, `ReadingService`, `ReflowService`; the composition root `HackerCaveBBS`) knows nothing of telnet, and `lib/ui` holds `UI::Modern` and `UI::Petscii`. Also: `ImportService` (index, errata, IENs), `MirrorService` (rsync + `FulltextRepository`), `JargonService` (`JargonParser`, `JargonRepository`). The plan, with the decisions taken, is `docs/HACKERCAVE_BBS.md` in the TTG workspace.

'; var prevLevel = 2; for (var i = 0; i < headings.length; i++) { var h = headings\[i\]; var text = h.textContent.trim(); var slug = text.toLowerCase() .replace(/\[áàâä\]/g,'a').replace(/\[éèêë\]/g,'e').replace(/\[íìîï\]/g,'i') .replace(/\[óòôöő\]/g,'o').replace(/\[úùûüű\]/g,'u') .replace(/\[^a-z0-9\]+/g, '-').replace(/^-|-$/g, ''); if (!slug) slug = 'heading-' + i; if (used\[slug\]) { slug += '-' + i; } used\[slug\] = true; h.id = slug; var level = parseInt(h.tagName.charAt(1)); var cls = level > 2 ? ' class="toc-child"' : ''; html += '- [' + text + '](#'%20+%20slug%20+%20')
'; } html += '
'; toc.innerHTML = html; // Scroll-spy: highlight current heading var links = toc.querySelectorAll('a'); var ids = \[\]; for (var j = 0; j < links.length; j++) ids.push(links\[j\].getAttribute('href').slice(1)); function updateActive() { var current = ''; for (var k = 0; k < ids.length; k++) { var el = document.getElementById(ids\[k\]); if (el && el.getBoundingClientRect().top <= 80) current = ids\[k\]; } for (var m = 0; m < links.length; m++) { links\[m\].parentNode.classList.toggle('active', links\[m\].getAttribute('href') === '#' + current); } } window.addEventListener('scroll', updateActive, { passive: true }); updateActive(); })();

---

## Navigation

- Parent: [Services](https://wiki.teletypegames.org/services.md)
- Previous: [Feedia BBS](https://wiki.teletypegames.org/services/feedia-bbs.md)
- Next: [Impostor BBS](https://wiki.teletypegames.org/services/impostor-bbs.md)
