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>
This commit is contained in:
Anthony Ettinger 2026-06-14 00:59:52 -07:00 committed by GitHub
parent 1086d57a4d
commit 7a85f2dbbb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 900 additions and 101 deletions

66
docs/deploy.md Normal file
View file

@ -0,0 +1,66 @@
# Autonomous deploy
AgentBBS deploys to a single Ubuntu droplet (`bbs.profullstack.com`). The whole
provisioner — `setup.sh` — is **idempotent**: it pulls the tracked branch,
rebuilds the Go binaries, rewrites the systemd unit / Caddyfile / env, and
restarts the service that answers `ssh join@bbs.profullstack.com`. Re-running it
is always safe, so "deploy" just means "run `setup.sh` again."
Two mechanisms keep the box current, and they cooperate (both go through the
same `flock` in `setup.sh`, so they never race):
## 1. Push-triggered — GitHub Actions (`.github/workflows/deploy.yml`)
On every push to `main` (and via **Run workflow**), CI SSHes to the droplet's
admin port and runs `setup.sh`. This is the "runs on every deploy" path.
Configure these repo secrets — **Settings → Secrets and variables → Actions**:
| Secret | Required | Default | Notes |
| --- | --- | --- | --- |
| `DEPLOY_SSH_KEY` | yes | — | private key; its public half is in the droplet admin user's `~/.ssh/authorized_keys` |
| `DEPLOY_HOST` | yes | — | `bbs.profullstack.com` or the droplet IP |
| `DEPLOY_USER` | no | `root` | admin SSH user (needs passwordless sudo if not root) |
| `DEPLOY_PORT` | no | `2202` | admin OpenSSH port (`setup.sh` moves it off `:22`) |
The job bootstraps a bare box (clones `/opt/agentbbs` if missing), hard-resets to
`origin/main` so it always runs the latest `setup.sh`, then execs it, and finally
smoke-tests that something serves SSH on `:22`.
## 2. Pull-triggered — self-update timer (autonomous backstop)
`setup.sh` also installs `agentbbs-update.timer`, which runs
`scripts/self-update.sh` every `SELF_UPDATE_INTERVAL` (default 15 min). That
script `git fetch`es origin and, **only if the branch advanced or the service is
down**, re-runs `setup.sh`. When nothing changed it costs one fetch and exits, so
the box self-heals and stays current even if CI is unavailable.
Disable it with `SELF_UPDATE=0 ./setup.sh`; change the cadence with
`SELF_UPDATE_INTERVAL=5min ./setup.sh`.
## First-time bootstrap
The droplet is already provisioned. To bring up a fresh box manually:
```sh
git clone https://github.com/profullstack/agentbbs /opt/agentbbs
sudo /opt/agentbbs/setup.sh # DOMAIN/ADMIN_SSH_PORT/etc. overridable via env
```
After that, pushes to `main` deploy automatically.
## Operations
```sh
journalctl -u agentbbs -f # live BBS logs
systemctl status agentbbs # service health
systemctl list-timers agentbbs-update.timer # next self-update
sudo /opt/agentbbs/scripts/self-update.sh --force # force a redeploy now
```
## Note on the logicsrc connector
`@logicsrc/plugin-agentbbs` (in the `logicsrc` monorepo) is a **registry
connector** that talks to this running server over SSH — it is not installed on
the droplet and is not needed for `ssh join@bbs.profullstack.com` to work. The Go
server provisioned here is what serves all SSH routes.