Commit graph

13 commits

Author SHA1 Message Date
243ef58e59 feat(irc): gate on BBS user store (loopback /irc-auth), drop ssh irc@
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>
2026-06-14 16:24:38 +00:00
5eb1e96480 feat(irc): irc.profullstack.com host, OS-user (tilde.town) gate, premium channels
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>
2026-06-14 15:23:44 +00:00
3b6b9a4a78 feat: members-only Usenet (NNTP) + Forgejo git provisioning + founding-lifetime $99
WIP feature branch: NNTPS news server, per-member Forgejo accounts on email
confirm, and founding-lifetime pricing tier.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 14:26:23 +00:00
52b06d8cfe Merge feat/irc-server: add ssh irc@ built-in IRC client
Brings the in-process IRC client (internal/irc) and the `irc@` SSH route on
top of the already-merged members-only Ergo server, completing the IRC
feature. The other commits on the branch (Ergo server, Tor routes) were
already merged via feat/qrypt-invite-issuer, so this applies only the client.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 11:47:34 +00:00
302259f65c 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>
2026-06-14 11:35:09 +00:00
97da723c5c feat(qrypt): qrypt.chat anonymous-invite issuer
AgentBBS becomes a trusted Ed25519 issuer for qrypt.chat anonymous
accounts. Members mint a signed, single-use qci1 token (per the shared
invite contract) that qrypt.chat verifies and redeems.

- internal/qryptinvite: Mint / GenerateIssuerKey / ParsePrivateKey +
  Config (AGENTBBS_QRYPT_* env) with unit tests (independent verify,
  payload assertions, jti uniqueness, tamper rejection, seed/full key).
- store: qrypt_invites table + QryptInviteCount / RecordQryptInvite
  (per-member quota, enforced in a tx; ErrQuotaExceeded) + test.
- plugins/qryptinvite: hub plugin (members only) — checks quota, mints,
  records, prints token + redeem URL.
- cmd/agentbbs: `qrypt-invite <user>` and `qrypt-issuer-keygen`
  subcommands wired into dispatch.
- setup.sh env template + docs/qrypt-invites.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 11:35:05 +00:00
8adafaf515 feat(irc): members-only Ergo IRC network co-located on the BBS
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>
2026-06-14 11:26:07 +00:00
cbc9069964
M3: AgentGames — agent-vs-agent games, ELO ladder, replays (#7)
A Gym-style game engine (PRD §5.2) with two transports sharing one
matchmaker, so an SSH agent and a WebSocket agent can be paired together.

Engine (internal/games):
  - Game/State contract (immutable positions); registry/catalog.
  - Phase-1 games: Tic-Tac-Toe (ttt) and Connect 4 (c4).
  - ELO (K=32, start 1500), a generic win/block/random GreedyBot.
  - Transport-agnostic NDJSON protocol + match driver: hello → state →
    move → result. We run no agent code — illegal move / per-move timeout /
    disconnect all forfeit (strict validation in place of a sandbox).
  - Matchmaker: per-game queue, bounded queue-wait; never abandons a match
    that started racing the wait timeout.

Transports:
  - SSH route game@ (ssh game@host ttt | join message), registered key,
    no PTY.
  - WebSocket /play (wss), bearer API token (agentbbs mint-token <user>);
    loopback behind Caddy.

Store: game_ratings (ELO ladder) + game_matches (full move log for replay)
+ api_tokens; Rating/SaveMatch satisfy games.Store; TopRatings/RecentMatches/
MatchByID/MintAPIToken/UserByToken. Banned accounts blocked.

Hub: plugins/agentgames — browse ladders, watch move-by-move replays, and
practice vs the bot (off the rated ladder).

Tests: engine (win/draw/legality), ELO, bot, full match via matchmaker with
replay, transport (deadline/closed), store round-trips. Verified live over
SSH (agent-vs-agent), WebSocket↔SSH cross-transport, forfeit-on-illegal-move,
and the hub ladder/replay views. Docs in docs/agentgames.md (the canonical
protocol spec, to mirror to logicsrc.com); README M3 → done.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 03:30:12 -07:00
232b8151a2
M2: admin console over ssh admin@ (users, sessions, moderation, plugins) (#3)
A privileged operator console reached as `ssh admin@host`, gated by route
plus the $AGENTBBS_ADMINS allowlist (admin status is operator-granted only,
never self-assigned in-band). It is a self-contained Bubble Tea model, not a
hub plugin, so it never appears in the public menu.

Sections (PRD §6):
  - Users & members: list accounts; b = suspend/ban (operators protected).
    Banned accounts are blocked at the hub and pod@ routes.
  - Sessions & pods: live in-memory session registry; k = disconnect.
  - Moderation & audit: admin action log + agent@ transcripts (tab to switch).
  - Config & plugins: runtime snapshot; space = enable/disable a plugin
    (persisted; filtered from the hub on next sign-in).

Every privileged action is written to a new admin_actions audit table.

store: + banned column, admin_actions and plugin_state tables, and the
backing methods (ListUsers/SetBanned/RecentSessions/LogAdminAction/
RecentAdminActions/RecentChatsAll/DisabledPlugins/SetPluginDisabled), with
unit tests. auth: admin allowlist helpers + tests. Docs in docs/admin.md;
README M2 flipped to done.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:40:18 -07:00
7a85f2dbbb
Autonomous deploy + free-pod/Premium-email membership (#1)
Deploy automation (idempotent, runs on every deploy):
- .github/workflows/deploy.yml: push to main/master (or dispatch) SSHes to the
  droplet and re-runs setup.sh; deploys the pushed branch; smoke-tests :22.
- scripts/self-update.sh + agentbbs-update.timer: autonomous backstop that
  redeploys only when origin advances.
- setup.sh hardened: flock, fetch+reset (survives force-push), fixed the
  always-skipped arcade asset fetch path.

Membership model:
- Free, email-verified members get their own Docker pod (pod@ paywall removed)
  and a /~name homepage (seeded at join@).
- join@ is now interactive: email -> emailed 6-digit code -> enter code.
- Premium ($10 one-time, lifetime via CoinPay) grants a personal
  <name>@host email (new internal/forwardemail; forwardemail.net aliases) and
  custom domains (domain@ gated to Premium).
- ensurePremium() silently verifies/grants/provisions on hub login, join@, and
  domain@. New-signup details emailed to AGENTBBS_SIGNUP_NOTIFY (subject "bbs").

Store: User.Premium + premium/premium_ref cols, ConfirmEmailCode, GrantPremium.
Tests: store_premium_test.go, forwardemail_test.go. Build/vet/gofmt/test green.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 00:59:52 -07:00
f3f8e70996 bbs.profullstack.com launch kit: provisioner, custom domains, email verify, ascii-live
- setup.sh: idempotent one-shot droplet provisioner — agentbbs on :22
  (admin OpenSSH moved to :2202), rootless podman, Caddy front end for
  https://bbs.profullstack.com with tilde-style /~user homepages
- internal/sites + domain@ SSH route: self-service custom domains
  (ssh domain@host add example.com) backed by a symlink farm and an
  on-demand-TLS ask endpoint so Caddy only issues certs for mapped hosts
- internal/mail + join@ email verification: optional email at signup,
  confirmation link served by a loopback /verify endpoint behind Caddy
- internal/source + cmd/ascii-live: live video → terminal ASCII groundwork
  (docs/ascii-live.md)
- store: additive sqlite migrations (email/verify columns, domains table)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 15:53:19 +00:00
9b0f465946 Video calls (PairUX→ASCII), agent@ chat, and finger routes
ssh video-<code>@ joins a PairUX/LiveKit call rendered as truecolor
ASCII (▀ half-blocks, 2 pixels per cell); video@ prompts for a code.
Codes are minted by PairUX only. Pipeline: VP8 RTP → PLI keyframe
requests → ivfwriter remux → ffmpeg decode/scale → RGB24 → ANSI →
bubbletea over the SSH PTY. Subscriber-only, no audio in v1.

ssh agent@ opens a persisted chat with the operator's agent —
AGENTBBS_AGENT_CMD runs per message (stdin→stdout), e.g. `claude -p`.

ssh <member>@ with someone else's name prints a classic finger card
(.plan, member since, last seen) and disconnects; your own name still
enters the hub.

cmd/lkpublish: dev publisher for testing (explicit -fps pacing; lksdk
IVF replay mispaces from file timebase alone, measured 1fps from a
15fps file; dimensions required or dynacast pauses the track).

Verified end-to-end against livekit-server --dev: 128k truecolor cells
/ 20k distinct colors streamed over a real SSH session; chat round-trip
and finger card verified over SSH. Go toolchain pinned to 1.26 via
mise.toml (lksdk requirement).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 11:43:38 +00:00
f3b085a08f AgentBBS: M0 core hub, M1 arcade, pods with CoinPay membership
A modern BBS over SSH for humans and AI agents (docs/PRD.md), plus the
pods addendum (docs/pods.md). Go + charmbracelet (wish/bubbletea).

SSH routes by username:
- bbs@/play@   hub as guest
- <name>@      hub as member/agent (key required; one key = one account)
- join@        onboarding: registers the key, prints instructions
               (incl. coinpay pay command with HMAC payment ref), kicks
- pod@         personal Linux container, paid membership $1/mo via
               CoinPay; rootless podman preferred, hardened docker
               fallback (cap-drop ALL, no-new-privileges, uid 1000,
               cpu/mem/pids caps, per-user volume)

M0: plugin contract (ID/Title/Description/RequiresAuth/New + ExitMsg),
hub menu, SQLite store (users/sessions/scores/pod_subscriptions),
session audit, grant-pod ops command.

M1 arcade: doom-ascii + Freedoom via scripts/fetch-assets.sh, sandbox
runner (bwrap/prlimit), PTY-bridged exec with orphan reaping, snake
with global leaderboard, member save dirs + private ~/wads scan.

Verified over real SSH: join/paywall/grant/pod attach + write
persistence across reconnects, guest+member hubs, DOOM launch, no
orphaned processes after hard disconnect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 11:08:17 +00:00