mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 22:37:28 +00:00
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>
79 lines
2.4 KiB
Go
79 lines
2.4 KiB
Go
// Package payments gates paid features (the pod subscription, $1/mo) on
|
|
// CoinPay — the default LogicSRC payment/DID/wallet plugin.
|
|
//
|
|
// v1 integration is CLI-shaped: join@ hands the user a `coinpay` command
|
|
// carrying a unique payment reference, and verification shells out to the
|
|
// coinpay CLI. The exact command templates are env-configurable so the
|
|
// deployed CoinPay surface can evolve without a rebuild:
|
|
//
|
|
// AGENTBBS_COINPAY_PAY_TMPL e.g. "coinpay pay --to profullstack --amount 1 --currency USDC --memo %s"
|
|
// AGENTBBS_COINPAY_VERIFY_CMD e.g. "coinpay verify --memo %s" (exit 0 == paid)
|
|
//
|
|
// Operators can also grant manually: `agentbbs grant-pod <user> --months N`.
|
|
package payments
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// PodPriceLabel is the human-readable price for the pod membership.
|
|
const PodPriceLabel = "$1/mo"
|
|
|
|
// PodTerm is how much access one payment buys.
|
|
const PodTerm = 31 * 24 * time.Hour
|
|
|
|
// Reference derives a stable, short payment reference for a user+plan from
|
|
// the user's key fingerprint, so CoinPay memos can be reconciled to accounts.
|
|
func Reference(plan, pubkeyFP string) string {
|
|
mac := hmac.New(sha256.New, []byte("agentbbs."+plan))
|
|
mac.Write([]byte(pubkeyFP))
|
|
return "abbs-" + plan + "-" + hex.EncodeToString(mac.Sum(nil))[:12]
|
|
}
|
|
|
|
// PayCommand renders the coinpay command a user should run, with the payment
|
|
// reference substituted.
|
|
func PayCommand(ref string) string {
|
|
tmpl := os.Getenv("AGENTBBS_COINPAY_PAY_TMPL")
|
|
if tmpl == "" {
|
|
tmpl = "coinpay pay --to profullstack --amount 1 --currency USDC --memo %s"
|
|
}
|
|
if strings.Contains(tmpl, "%s") {
|
|
return fmt.Sprintf(tmpl, ref)
|
|
}
|
|
return tmpl + " " + ref
|
|
}
|
|
|
|
// Verify checks a payment reference against the coinpay CLI. It returns
|
|
// (paid, checked): checked is false when no verifier is configured or the
|
|
// coinpay binary is unavailable, so callers can fall back to store state.
|
|
func Verify(ref string) (paid bool, checked bool) {
|
|
tmpl := os.Getenv("AGENTBBS_COINPAY_VERIFY_CMD")
|
|
if tmpl == "" {
|
|
return false, false
|
|
}
|
|
var line string
|
|
if strings.Contains(tmpl, "%s") {
|
|
line = fmt.Sprintf(tmpl, ref)
|
|
} else {
|
|
line = tmpl + " " + ref
|
|
}
|
|
parts := strings.Fields(line)
|
|
if len(parts) == 0 {
|
|
return false, false
|
|
}
|
|
if _, err := exec.LookPath(parts[0]); err != nil {
|
|
return false, false
|
|
}
|
|
cmd := exec.Command(parts[0], parts[1:]...)
|
|
if err := cmd.Run(); err != nil {
|
|
return false, true
|
|
}
|
|
return true, true
|
|
}
|