Use profullstack.com/motd as the shared Message of the Day across both
the SSH BBS hub and the Ergo IRC server.
- internal/motd: fetch + in-memory cache with background refresh (stdlib
only); Current() never blocks session start, keeps last value on error.
Source overridable via AGENTBBS_MOTD_URL.
- hub: append the daily MOTD below the existing welcome/onboarding text.
- IRC: deploy/ergo/refresh-motd.sh pulls /motd into Ergo's ergo.motd and
rehashes; setup.sh installs it + an ergo-motd.timer (hourly) mirroring
the ergo-certs timer, with a seeded fallback if the source is offline.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auth model change: ergo-auth-member now verifies the SASL passphrase against a
per-member pbkdf2 hash (/var/lib/ergo/irc-passwd) in addition to BBS membership,
replacing the old "membership is the credential, passphrase ignored" gate that
let anyone who knew a member name connect as them. Rewrote deploy/ergo/auth-script.sh
in python3 (drops jq/curl dep); setup.sh already installs it as ergo-auth-member.
- scripts/set-irc-password.sh: set/rotate a member's IRC password (or --all to
backfill); also syncs The Lounge saslPassword so the web client keeps working.
- setup.sh landing page: new "IRC from a desktop client" section with irssi/HexChat/
WeeChat SASL setup (connect by network name ProfullstackBBS, not hostname).
- docs/irc.md: document password auth, the helper, the 6697 cloud-firewall note,
and an irssi quick-start.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WorkingDirectory is /opt/ergo with ProtectSystem=strict (only /var/lib/ergo
and /etc/ergo are writable), but lock-file was relative ("ircd.lock"), so Ergo
tried to create /opt/ergo/ircd.lock on a read-only path and crash-looped with
"failed to acquire flock on ircd.lock: read-only file system" — nothing ever
listened on 6697/6667/8097 (clients got ECONNREFUSED). datastore.path is already
absolute (__DATA__/ircd.db); per Ergo's own config comment the lock-file should
match. Render it to __DATA__/ircd.lock (/var/lib/ergo, ergo-owned + in
ReadWritePaths).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework the IRC access model to the user-level source of truth — the
bbs.profullstack.com user store — per the agreed design:
- Auth via the store, not OS users. New loopback endpoint /irc-auth (on the
/verify server) answers {member,premium} from the store via UserByName. Ergo's
auth-script now curls it instead of `getent passwd`; the OS-user (tilde.town)
reconcile in setup.sh is reverted. require-sasl on, registration off.
- Remove the in-BBS `ssh irc@` route: delete internal/irc (in-process client +
TUI), handleIRC/runIRC, the hub IRC menu entry, and the dispatch case. Members
connect with an external client (or web) at irc.profullstack.com:6697 / wss.
IRCNames kept only to reserve the "irc" name (IsIRCName removed).
- Keep external clients; keep the irc.profullstack.com hostname + cert work.
Premium channel creation: /irc-auth already returns each account's premium
status, but enforcement is NOT yet wired (the old irc@ /create was removed with
the route; Ergo can't gate creation per-account natively). operator-only-creation
stays off for now; server-side premium gating (a ChanServ-style bot) is a
documented follow-up. See docs/irc.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hostname: serve the network as irc.profullstack.com (new IRC_DOMAIN var,
default irc.<root-of-DOMAIN>). Caddy serves an irc.profullstack.com site so it
gets a Let's Encrypt cert; ergo-refresh-certs copies that into Ergo for 6697.
Needs an A record irc.profullstack.com -> the box (self-signed until it resolves).
Members are OS users (tilde.town model): setup.sh reconciles a real OS account
per member dir (root-side, on each deploy + the 15-min timer; nologin shell, so
identity-only — no shell access). The IRC auth-script now gates on
`getent passwd` with uid>=1000 instead of the member dir, so "OS user" == member.
Premium channels: free members may /join; creating channels is a premium perk.
The ssh irc@ client gains /create #name (premium-gated via ensurePremium): it
joins the fresh channel and registers it with ChanServ as the member's founder.
v1 gate is route-level (operator-only-creation left off); external-client
creation hardening is a follow-up. See docs/irc.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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 <noreply@anthropic.com>