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

269 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 |