EN /HU | Login

Impostor BBS

The post-game terminal of Definitely not an Impostor, a puzzle/ARG game: players who finish the game get a personal code, bring it here with their name, and find out what comes next.

Live at teletypegames.org:2324, in the browser on bbsweb, and through TTG Gate BBS

Overview

A Ruby app on the rubbs gem, like the Teletype BBS. rubbs itself was extracted from this board (rubbs 0.1.0). It has two faces on one port:

  • Modern — ANSI/xterm, 80 columns (SyncTERM, NetRunner, PuTTY, bbsweb).
  • Commodore 64 — PETSCII on a 40×25 screen, the same terminal with its own screens, for a C64 terminal program behind a WiFi modem.

A call starts with rubbs' BBS::Terminal question: RETURN takes the guess from the telnet answers, 1 picks the modern face, 2 the C64.

How a call goes

  1. The board asks for the player's name and personal code.
  2. The code is decrypted with a salt-based algorithm and checked against the name. Only the first three characters of the name count, after trimming and upper-casing (NORMAN and NORBERT share a code).
  3. A correct code unlocks a short identity confirmation, then a congratulations screen and a post-game menu: the player can leave feedback and an email for updates.
  4. When the debrief ends (or the code is refused), the board thanks the caller and hangs up with NO CARRIER.

Every session, successful or not, is a row in data/sessions.csv:

TXT
session_id,timestamp,player_name,code,feedback,email

The row is written when the name and code arrive, then updated in place as the feedback and the email come in. IMPOSTOR_BBS_SESSIONS moves the file; the compose file mounts data/, so the records survive restarts.

Generating a code

The personal code of a player name comes from the CLI tool, which reads the name from IMPOSTOR_BBS_PLAYER_NAME:

BASH
IMPOSTOR_BBS_PLAYER_NAME=norman bin/generate_code
# => 2J5GFN

As a door

The repository is two things: the impostor gem (lib/impostor*, screens/), the debrief terminal with its rules, and the standalone board around it. Another rubbs board can pull the gem and run the same terminal as a door, with a sessions store of its own (the Teletype BBS did until 2026-11-01; since then this board on 2324 is the one place of the terminal):

RUBY
gem 'impostor', git: 'https://git.teletypegames.org/bbs/impostor-bbs.git'

require 'impostor'
sessions = Impostor.sessions_store('data/impostor_sessions.csv')
door :impostor, Impostor::Modern.flow(sessions: sessions), label: '&Impostor'

A C64 caller gets Impostor::Petscii.flow(sessions: sessions). When the flow ends, the door asks for ENTER and hands the caller back to the host BBS. The host's Gemfile.lock pins the revision, so a change here reaches the door only with a lock bump there.

Under the hood

Path What it is
bbs.rb Entry point: one ImpostorBBS, BBS::Terminal with the two faces
lib/domain/impostor_bbs.rb The composition root: reads the environment, builds the sessions store
lib/ui/modern.rb, lib/ui/petscii.rb The board's faces: the gem's flows, then goodbye and NO CARRIER
lib/impostor.rb, lib/impostor/ The gem: the debrief flow, Impostor::Modern, Impostor::Petscii, the code generator (a Ruby port of the Lua original)
screens/modern/, screens/petscii/ The welcome and congratulations screens of the two faces
bin/generate_code The code generator CLI

Environment: BBS_PORT, BBS_MAX_SESSIONS (rubbs) and IMPOSTOR_BBS_SESSIONS; env-example lists them with their defaults.

A push to master deploys it from Woodpecker (RuboCop, compose check, ci-deploy impostor-bbs); the container is impostor-bbs, in /srv/stacks/impostor-bbs, public port 2324, and data/ stays between deploys. The entrypoint updates rubbs on start. See the BBS Network for the other boards.