TITLE=BBS Web ROUTE=/services/bbsweb LOCALE=en MODIFIED=1792746206 URL=https://wiki.teletypegames.org/services/bbsweb L=1 https://bbsweb.teletypegames.org L=2 https://xtermjs.org/ L=3 https://github.com/FGasper/zmodem.js L=4 #66 L=5 https://git.teletypegames.org/services/bbs-web L=6 https://git.teletypegames.org/infra/host-config q│ bbsweb puts our telnet BBSes on the web without changing a line of them. The q│ dialer screen lists the systems, and each one opens in a real terminal in q│ the browser. q│ q│ bLive at 1bbsweb.teletypegames.org 1Overview 1════════ Our BBSes speak plain telnet on ports 2323–2325, which needs a telnet client. bbsweb adds a web front end in front of them: a Node server bridges WebSocket to TCP, and the browser runs 2xterm.js. It is a generic product, not a Teletype installation. A single cconfig.json describes which systems appear, under what names, in which languages and with what styling — the application itself contains no site-specific text, colour or address. Adding a foreign BBS is one block of JSON. p browser nginx (TLS) bbsweb container BBSes p ┌────────────┐ wss:// ┌──────────────┐ http ┌────────────────┐ TCP ┌───» p ─────┐ p │ xterm.js ├─────────►│ proxy /ws ├────────►│ WS↔telnet brdg │────►│ :2» p 323 │ p │ 256 colour │ │ proxy / │ │ + static web │ │ :2» p 324 │ p │ UTF-8, mouse └──────────────┘ └────────────────┘ │ :2» p 325 │ p └────────────┘ └───» p ─────┘ 1What it carries 1═══════════════ The BBSes send full-screen ANSI with 256 colours, UTF-8 box drawing and SGR mouse reporting. All of that survives the trip: • bClickable menus0 — the mouse reports go through, so the menu bar works exactly as in a terminal. • bTerminal size0 — the browser sends cNAWS0 on every resize, so the BBS knows how many columns to draw. Two modes: a fixed 80×25 that scales the font to the window, and a fitted mode that gives the BBS the whole window. • bEnglish / Hungarian interface0, English by default, the choice kept in the browser. Server-side status messages travel as keys, not sentences, so a running session switches language too. • bFull screen0 — the icon at the right end of the top bar shows the terminal alone. In Chromium browsers Esc still reaches the BBS (hold it to leave full screen); elsewhere Esc leaves full screen. • bTouch key bar0 — ESC / TAB / CTRL / arrows on phones and tablets. • bFile downloads (ZMODEM)0 — when a BBS sends a file by ZMODEM (the Teletype BBS's FTP Browser does), the page receives it with 3zmodem.js and the browser saves it; the status line shows the progress, Esc cancels. The three systems on the dialer are 4Teletype BBS, Impostor BBS and the Wiki BBS. Every one of them is still reachable over plain telnet at the address shown on its card. 1Configuration 1═════════════ cconfig/config.json0 sits next to the compose file (the cconfig0 folder is mounted into the container) and covers the whole installation: t┌────────────────────────────┬───────────────────────────────────────────────┐ t│h Group t│h What it controls t│ t├────────────────────────────┼───────────────────────────────────────────────┤ t│0 csite0 t│0 name, tagline, ASCII logo, footer, favicon, t│ t│0 t│0 accent colour t│ t├────────────────────────────┼───────────────────────────────────────────────┤ t│0 clanguages0, cdefaultLanguage0 t│0 the switcher's buttons and the starting t│ t│0 t│0 language t│ t├────────────────────────────┼───────────────────────────────────────────────┤ t│0 cterminal0 t│0 size, font, theme, cursor, scrollback, t│ t│0 t│0 starting view mode t│ t├────────────────────────────┼───────────────────────────────────────────────┤ t│0 cserver0 t│0 session limits, timeouts, ping, the cTTYPE0 t│ t│0 t│0 answer t│ t├────────────────────────────┼───────────────────────────────────────────────┤ t│0 cboards0 t│0 the systems, each with its own colour and t│ t│0 t│0 size t│ t└────────────────────────────┴───────────────────────────────────────────────┘ Every key is optional — what is missing falls back to a built-in default. Any text field may be a per-language object: c"name": { "en": "…", "hu": "…" }0. The server watches the file's modification time, so ban edit needs no restart0 — reloading the page is enough. If the JSON is broken, the last good configuration stays in effect and the error goes to the log. The full reference is in the repo's 5README. 1Why it is not an open proxy 1═══════════════════════════ The browser only ever sends an cid0 (c/ws?id=teletype0). The actual host and port never come from the client — they come from cconfig.json0, and they do not appear in the public API at all. Without this the bridge would forward to any TCP address on request. 1Telnet details worth knowing 1════════════════════════════ The bridge handles IAC negotiation (cECHO0, cSGA0, cBINARY0, cTTYPE0, cNAWS0) and refuses everything else politely. Two things matter in practice: • bA lone cCRb is not a line ending.0 xterm.js sends only cCR0 on cEnter0, while the BBS waits for a line ending, so in NVT ASCII mode the bridge appends cLF0 the way a real telnet client does. Without it the login screen never advances — and the symptom is deceptive, because the typed name appears on screen normally. • bnginx drops silent connections.0 A BBS session stays quiet for minutes while someone reads, and the default cproxy_read_timeout0 of 60s would cut it off. The vhost uses 3600s, and the server pings over WebSocket every 30 seconds so intermediate NATs keep the connection too. 1Deployment 1══════════ A push to cmaster0 deploys from Woodpecker when it changes ccompose.yaml0, cbbsweb/ or cconfig/config.json0. The pipeline first checks the scripts (cnode --check0), the JSON files and ccompose.yaml0; the deploy step then only asks the server to deploy over SSH. Its key (secret cdeploy_ssh_key0) is bound on the server to one fixed command, cci-deploy bbs-web0 (6host-config), which clones master from Gitea itself, checks ccompose.yaml0 against a policy (no privileged mode, no extra capabilities, no docker socket, bind mounts only inside the stack, no public port), copies those paths into c/srv/stacks/bbs-web0 and runs cdocker compose up c-d --build0. A config-only change needs no rebuild or restart: the deploy replaces cconfig/config.json0, and because the whole folder is mounted, the server sees the new file and reloads it by its mtime. (A single-file mount would keep showing the old file after the replacement.) 1Operating it 1════════════ p cd /srv/stacks/bbs-web p docker compose up -d --build # start by hand (normally the pipeline does» p it) p docker compose logs -f bbsweb # connection log (IP -> BBS) p curl -s localhost:9140/api/health The container listens on c127.0.0.1:91400; TLS and the domain are nginx's job. The vhost is c/etc/nginx/sites-available/bbsweb.teletypegames.org0, the certificate is handled by certbot. One limitation: the BBS sees every web visitor as coming from the bridge container's IP, because telnet has no cX-Forwarded-For0. The real client IP is in the bbsweb log. 2Repository 2────────── https://git.teletypegames.org/services/bbs-web