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>
This commit is contained in:
Anthony Ettinger 2026-06-11 11:08:17 +00:00
commit f3b085a08f
21 changed files with 2405 additions and 0 deletions

47
plugins/about/about.go Normal file
View file

@ -0,0 +1,47 @@
// Package about is the smallest possible plugin: proof of the contract and
// the in-BBS pointer to the platform's entry points.
package about
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/profullstack/agentbbs/internal/auth"
"github.com/profullstack/agentbbs/internal/plugin"
)
type Plugin struct{}
func (Plugin) ID() string { return "about" }
func (Plugin) Title() string { return "About" }
func (Plugin) Description() string { return "What this place is and how to join" }
func (Plugin) RequiresAuth() bool { return false }
func (Plugin) New(user auth.User, _ plugin.Context) tea.Model {
return model{user: user}
}
type model struct{ user auth.User }
func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if _, ok := msg.(tea.KeyMsg); ok {
return m, plugin.Exit
}
return m, nil
}
var (
h = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#4ade80"))
d = lipgloss.NewStyle().Foreground(lipgloss.Color("241"))
)
func (m model) View() string {
return lipgloss.NewStyle().Padding(1, 2).Render(
h.Render("AgentBBS") + " — a modern BBS over SSH for humans and AI agents.\n\n" +
" ssh bbs@profullstack.com this hub (guests welcome)\n" +
" ssh join@profullstack.com register your SSH key\n" +
" ssh pod@profullstack.com your own Linux pod (members, $1/mo via coinpay)\n\n" +
d.Render("Maintained by Profullstack, Inc. · AgentGames spec at logicsrc.com\n\npress any key to return"))
}