Skip to content

feat(irc): members-only Ergo IRC network + built-in ssh irc@ client#9

Closed
ralyodio wants to merge 2 commits into
feat/tor-commandsfrom
feat/irc-server
Closed

feat(irc): members-only Ergo IRC network + built-in ssh irc@ client#9
ralyodio wants to merge 2 commits into
feat/tor-commandsfrom
feat/irc-server

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Stands up a co-located, lightweight IRC network on the AgentBBS box for humans and agents, plus a zero-setup built-in client.

Stacked on #8 (base feat/tor-commands). Retarget to main once #8 merges.

What's here

1. Members-only Ergo IRC server (deploy/ergo/, setup.sh §9b)

  • Ergo v2.18.0 — single Go binary; bundles services + bouncer + TLS + IRCv3/history, no Atheme/ZNC.
  • Listeners: public 6697/TLS, loopback 6667, loopback 8097 WebSocket fronted by Caddy at wss://<domain>/irc (no extra public port).
  • Members-only: SASL required, self-service registration off. An auth-script (ergo-auth-member) approves a login only if the account name maps to an existing AgentBBS member home dir — your BBS account is your IRC identity.
  • TLS reuses Caddy's Let's Encrypt cert (refresh timer; self-signed fallback on first boot). Toggle with IRC=0.

2. Built-in ssh irc@ client (internal/irc/)

  • ssh -t irc@<host> drops a member straight into the network — no client install, no SASL config.
  • In-process Go IRC client + Bubble Tea TUI (mirrors internal/chat); /join /part /msg /me /names /nick /help.
  • Reaches Ergo on 127.0.0.1:6667 and authenticates as the member (the SSH key already proved membership). Because it's our own code (not irssi-in-a-pod), there's no /exec shell-escape surface.

Validation

  • Ergo config rendered via setup.sh's exact sed pipeline → ergo run --smoke brings up all four listeners.
  • Live: member SASL login succeeds, non-member rejected, account autocreated, JOIN/PRIVMSG work; CHATHISTORY advertised.
  • Auth-script unit-tested: member approved, non-member / path-traversal / empty denied.
  • internal/irc/client.go compiled and driven live against a members-only Ergo (member in, non-member out, cross-member message received). gofmt clean. (Full go build ./... needs go 1.26, not available in this sandbox; tui.go/main.go mirror existing chat.go APIs.)

Note for the operator

Native clients connect to irc.<domain> — add the DNS record. The cert is for <domain>; add irc.<domain> as a SAN (or wildcard) for a clean hostname match on 6697.

🤖 Generated with Claude Code

ralyodio and others added 2 commits June 14, 2026 11:26
Provision a self-hosted Ergo IRC network (irc.${DOMAIN}) in setup.sh §9b:
single Go binary on its own ports/user, reusing Caddy's Let's Encrypt cert
for 6697 TLS (refreshed by a timer; self-signed fallback on first boot),
loopback 6667 + a loopback WebSocket fronted by Caddy at wss://${DOMAIN}/irc.

Access is MEMBERS-ONLY: every client must authenticate with SASL, self-service
registration is off, and an auth-script (deploy/ergo/auth-script.sh, installed
as /usr/local/bin/ergo-auth-member) approves a login only if the account name
maps to an existing AgentBBS member home dir under <data>/users/. Passphrase is
ignored — membership (the filesystem dir) is the credential. require-sasl has
no IP exemption so WebSocket clients (which reach Ergo via Caddy from 127.0.0.1)
can't bypass the gate; accounts are auto-created on first successful auth.

Public attack surface is TLS-only (ufw opens 6697; 6667 is loopback). Toggle
with IRC=0. See docs/irc.md.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
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 <[email protected]>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio

Copy link
Copy Markdown
Contributor Author

Merged into main in 52b06d8 (ssh irc@ built-in client now on main alongside the Ergo server). Closing — the server + Tor commits from this branch already landed via feat/qrypt-invite-issuer.

@ralyodio ralyodio closed this Jun 14, 2026
@ralyodio ralyodio deleted the feat/irc-server branch June 14, 2026 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant