feat(irc): add ssh irc@ built-in client for the members-only network

Adds an in-process IRC client (internal/irc) and an `irc@` SSH route that
drops a member straight into the BBS's own Ergo network with no client to
install and no SASL to configure.

- internal/irc/client.go: minimal IRC client (SASL PLAIN, IRCv3 CAP, PING,
  PRIVMSG/JOIN/PART/NICK, event stream). Dials Ergo on the loopback
  127.0.0.1:6667; presents the member's account name (the SSH key already
  proved membership; Ergo's auth-script ignores the passphrase by design).
- internal/irc/tui.go: Bubble Tea TUI over the SSH PTY (mirrors internal/chat)
  with /join /part /msg /me /names /nick /help and a current-channel input.
- cmd/agentbbs: handleIRC resolves the member by key (members-only, free) and
  runs the client; routed via auth.IsIRCName. AGENTBBS_IRC_ADDR overrides the
  target on dev hosts.
- auth: reserve `irc` as a route name.

Unlike copying tor-irc@ (a third-party client in a pod), this runs our own Go
code in-process, so there is no /exec shell-escape surface, and the host
process can reach Ergo's loopback listener directly.

Validated live against a members-only Ergo: non-members are rejected, a member
authenticates via SASL, and channel messages are received.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-06-14 11:35:09 +00:00
parent 8adafaf515
commit 302259f65c
6 changed files with 631 additions and 11 deletions

View file

@ -13,13 +13,26 @@ operationally independent of the wish server.
| Path | Address | For |
|---|---|---|
| In-BBS | `ssh -t irc@bbs.profullstack.com` | members — zero-setup built-in client (see below) |
| Native TLS | `irc.bbs.profullstack.com:6697` (TLS) | desktop/CLI clients (HexChat, irssi, WeeChat, Halloy…) |
| WebSocket | `wss://bbs.profullstack.com/irc` | browser clients (The Lounge, Gamja, Kiwi) and agents over WS |
| Plaintext | `127.0.0.1:6667` | **loopback only** — on-box tooling/bridges; firewalled off |
| Plaintext | `127.0.0.1:6667` | **loopback only** — on-box tooling/the `irc@` client; firewalled off |
The WebSocket path is fronted by Caddy (it terminates TLS and reverse-proxies to
Ergo's loopback `127.0.0.1:8097`), so no extra public port is opened for the web.
### `ssh irc@` — the built-in client
`ssh -t irc@bbs.profullstack.com` drops a member straight into the network with
no client to install or SASL to configure. It is an **in-process IRC client**
(`internal/irc`) running inside the agentbbs process: it reaches Ergo on the
loopback `127.0.0.1:6667` and authenticates as you (your SSH key already proved
you're a member, so it presents your account name over SASL). Because the client
is our own Go code — not a third-party client in a pod — there is no `/exec`
shell-escape surface. You land in `#lobby`; type to talk, or use
`/join #chan`, `/msg <nick> <text>`, `/me`, `/names`, `/nick`, `/help`, and
`esc` to leave. Override the target with `AGENTBBS_IRC_ADDR` on a dev host.
### Membership (who can connect)
The network is **members-only**. There is **no self-service registration**
@ -112,14 +125,11 @@ up immediately; the timer swaps in the real one once it exists.
Unrelated, complementary. `ssh tor-irc@bbs.profullstack.com <server>` is a
**client** that connects *out* to a remote (e.g. `.onion`) IRC server from inside
a member's pod. This is the BBS hosting **its own** IRC network for people and
agents to meet on.
a member's pod. `irc@` (above) and the 6697/WebSocket listeners are the BBS
hosting **its own** IRC network for people and agents to meet on.
## Ideas / next steps
- **In-BBS `irc@` route** — an SSH route that drops a member straight into the
local network (mirroring `tor-irc@` but pointed at `127.0.0.1:6667`), so
`ssh irc@bbs.profullstack.com` is an instant client with no setup.
- **Bridge to `internal/chat`** — relay the BBS hub chat ↔ an IRC channel.
- **Per-pod / per-game channels** — auto-create `#pod-<name>`, `#game-<id>`.
- **Persistent history** — switch `datastore.mysql` on if replay must survive