urit/docs/ROADMAP-v0.3.0.md
2026-05-02 21:11:50 -04:00

12 KiB
Raw Permalink Blame History

URIT BBS — v0.3.0 Roadmap

Status: Planning Previous: v0.2.0 (steps 10a17) — telnet, auth, message boards, mail, file libraries, bulletins, HTTP file server, inter-node chat, sysop console, sysop guide.


Step 18 — SSH Support

Priority: 1 — High value, low effort

The config section ([ssh]) is already defined and parsed. The session layer (internal/session) is transport-agnostic — it works on any net.Conn. Wiring up SSH is primarily a listener and key-management task.

Sub-steps

  • 18a — SSH listener and host key management. Add an SSH listener to the server using golang.org/x/crypto/ssh. Generate a host key on first run (or via urit init) and store it at the configured path. Accept connections, negotiate a PTY channel, and hand the resulting net.Conn equivalent to the existing handleConnection flow.

  • 18b — Password authentication callback. Wire the SSH password callback to the same auth.Check path used by telnet login. On successful auth, skip the telnet login prompts and drop the user directly into the post-login flow (logon.ans → main menu). Guest access via SSH should be configurable (allow/deny in config).

  • 18c — Terminal handling. Read PTY dimensions from the SSH channel request (replaces telnet NAWS). Map SSH window-change requests to the session's terminal size updates. Verify ANSI escape passthrough works correctly over the SSH channel.

Notes

  • The SSH user's identity is known at connection time (unlike telnet, where login happens after connecting), so the session lifecycle needs a minor branch: skip the welcome.ans → login prompt sequence and jump to the authenticated path.
  • Public key auth is a natural follow-on but not required for the initial implementation. Password auth matches the existing BBS credential model.
  • Vendor golang.org/x/crypto/ssh into the vendor directory.

Step 19 — Door Games

Priority: 2 — Highest user-facing impact

Door games are external programs launched from within the BBS, with the user's terminal piped through. This was the killer feature of multi-node BBSes in the late '80s and '90s. Supporting even a basic door interface opens up a large ecosystem of existing door game binaries.

Sub-steps

  • 19a — Door configuration and menu integration. Add a [doors] config section defining available doors: name, command path, arguments, working directory, and required SecStatus. Add a [D]oors command to the main menu (relocate the existing [D] Download to another key or make it a sub-option). List available doors and let the user select one.

  • 19b — Dropout file generation. Generate a DOOR32.SYS dropout file before launching the door. DOOR32.SYS is the most widely supported modern format and includes: comm type (2=telnet), socket handle, baud rate (0 for telnet), BBS name, user info, time remaining, and node number. Write the file to a per-node temp directory.

  • 19c — Process execution and I/O bridging. Launch the door process with os/exec, connecting its stdin/stdout to the user's telnet (or SSH) connection. Set environment variables that doors commonly expect: DOORNODE, DROPFILE, etc. The door process inherits the terminal. On exit, return the user to the main menu. Handle process timeouts tied to the user's remaining session time.

  • 19d — Cleanup and logging. Remove temp dropout files after the door exits. Log door usage (door name, user, duration). Handle abnormal door exits gracefully (process crash, timeout, signal). Ensure the terminal state is sane after the door returns (re-send ANSI reset, re-negotiate telnet options if needed).

Notes

  • Many classic doors expect a DOS-like environment. Running them may require DOSBox or similar on Linux. URIT's role is just the I/O bridge and dropout file — the door binary's compatibility is the sysop's responsibility.
  • Native Linux doors (written for systems like Mystic, Synchronet) will work more naturally. Some modern door games are written specifically for telnet BBSes.
  • Consider a door_log table for tracking door usage statistics.

Step 20 — Message Threading

Priority: 3 — Model is ready, UI work needed

The ReplyTo field exists on the Message model but is not surfaced in the UI. Wiring it up gives boards a threaded conversation feel rather than a flat chronological list.

Sub-steps

  • 20a — Reply command in message reader. When reading a message, add an [R]eply option that pre-fills the subject with "Re: {original subject}" and sets ReplyTo to the parent message ID. The compose flow is otherwise identical to posting a new message.

  • 20b — Thread-aware message listing. Modify the board message list to indicate threading. Two approaches to consider: indented tree view (classic Usenet style) or flat list with "Re: ..." subjects and a "view thread" command that filters to a single conversation. The flat approach is simpler and more BBS-authentic.

  • 20c — Store support for thread queries. Add ListMessageThread(boardID, rootMessageID) to the store interface. This returns all messages in a thread (the root plus all descendants) in chronological order. Use a recursive CTE in SQLite for efficient traversal.

Notes

  • Keep the default board view as a flat chronological list — threading is additive, not a replacement. Users who don't care about threads should see no change in behavior.
  • The HTTP side doesn't currently display messages, but if it ever does, threading support in the store will carry over naturally.

Step 21 — Email Notifications

Priority: 4 — Bridges BBS and modern communication

Lightweight email notifications that pull users back to the BBS when something happens that's relevant to them.

Sub-steps

  • 21a — SMTP configuration. Add an [email] config section: enabled flag, SMTP host/port, from address, auth credentials (optional), TLS mode (none/starttls/tls). Add an Email field to the User model for the notification address (set during registration or via account settings).

  • 21b — Notification triggers. Send emails on: new private mail received, reply to a message the user posted (requires step 20), and sysop broadcast (optional). Each trigger should be individually toggleable per user via account preferences.

  • 21c — Mail queue and sender. Implement a background goroutine that processes a notification queue. Outbound emails are queued (in-memory channel or a database table for persistence) and sent asynchronously so they never block the BBS session. Rate limit to avoid overwhelming the SMTP relay. Include an unsubscribe link or instructions in every email.

  • 21d — User preferences. Add a notification preferences submenu to the [A]ccount command. Let users set their email address, enable/disable each notification type, and set a digest preference (immediate vs. daily summary). Store preferences in a new user_preferences table or as additional fields on the user record.

Notes

  • Keep emails plain text with minimal formatting — matches the BBS aesthetic and avoids HTML email complexity.
  • Consider a daily digest mode that batches notifications into a single email to avoid spamming active boards.
  • The email address doubles as an account recovery mechanism in the future.

Step 22 — Automated Backup

Priority: 5 — Operational reliability

A urit backup subcommand that snapshots all BBS state into a single archive. Designed to be run from cron on unattended systems.

Sub-steps

  • 22a — Database snapshot. Use SQLite's online backup API (sqlite3_backup_init) via the CGo binding to create a consistent snapshot of the database while the server is running. This is safe for concurrent reads/writes — no need to stop the server. Write the snapshot to a temp file, then move it into the backup directory.

  • 22b — File collection. Copy the screens directory and all library file directories (paths read from the library records in the database) into the backup staging area. Skip files that haven't changed since the last backup if a --incremental flag is set (compare mtimes).

  • 22c — Archive and rotation. Tar/gzip the staged files into a timestamped archive: urit-backup-YYYYMMDD-HHMMSS.tar.gz. Support a --keep N flag that automatically deletes backups older than the N most recent. Default output directory configurable via --output flag or a [backup] config section.

  • 22d — Cron integration. Document cron usage in the sysop guide. Example: 0 3 * * * /opt/urit/urit backup -config /opt/urit/config.toml --keep 7 The command should exit cleanly with appropriate exit codes (0=success, 1=partial failure, 2=fatal error) and log to stdout for cron mail capture.

Notes

  • The backup command connects to the database read-only — it does not need the server to be running, but it works safely if it is.
  • Consider adding a --verify flag that restores the backup to a temp directory and runs a schema integrity check.
  • A urit restore subcommand is a natural follow-on but lower priority.

Steps 23+ — Future Considerations

The following features are candidates for future versions. Order and scope are flexible.

Full-text search (Step 23)

Add keyword search across message boards and mail using SQLite's FTS5 extension. A [/] command at the board level searches subject and body text. Results displayed as a filtered message list. FTS5 is already compiled into the CGo binding — the work is creating the virtual table, keeping it in sync with inserts, and building the search UI.

File descriptions on telnet (Step 24)

The telnet library browser shows filenames but lacks the rich file listing that classic BBSes were known for. Add a proper file list display with descriptions, sizes, dates, download counts, and uploader names — the traditional columnar BBS file list format. The data is all in the store already; this is purely a display task.

ANSI auto-detection (Step 25)

Detect terminal capabilities during telnet negotiation or ask during login. Provide plain-text fallback screens for minimal clients. Add a user preference to override detection. Low priority since virtually all modern telnet clients support ANSI, but it's a polish item for accessibility.

Rate limiting and connection throttling (Step 26)

Add per-IP connection limiting (max N concurrent connections per IP) and a brief delay after failed login attempts. The max node count provides a global cap, but per-IP limits would prevent a single source from consuming all nodes. Important for public-facing systems.

Web admin console expansion (Step 27)

Extend the existing /admin dashboard with full CRUD for users, boards, libraries, and bulletins — a complete browser-based replacement for the telnet sysop menu. The middleware, template system, and store methods all exist; this is primarily HTML form work. See the step 16 discussion for scope analysis.


Version Summary

Step Feature Priority Effort
18 SSH support 1 Medium
19 Door games 2 Medium
20 Message threading 3 Low
21 Email notifications 4 Medium
22 Automated backup 5 LowMed
23 Full-text search Low
24 Telnet file listings Low
25 ANSI auto-detection Low
26 Rate limiting Low
27 Web admin expansion High