agentbbs/docs/custom-domains.md
Anthony Ettinger 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

2.7 KiB

Custom domains

Members can point their own domain (e.g. chovy.com) at their AgentBBS homepage — the same public_html that is served at https://bbs.profullstack.com/~name. HTTPS is provisioned automatically on the first request.

For a member

# list the domains pointed at your homepage
ssh domain@bbs.profullstack.com

# point a domain at your homepage
ssh domain@bbs.profullstack.com add chovy.com

# remove one
ssh domain@bbs.profullstack.com rm chovy.com

domain@ requires your registered SSH key (run ssh join@bbs.profullstack.com first if you haven't). After add, set DNS at your registrar:

  • Subdomain (blog.example.com): CNAMEbbs.profullstack.com
  • Apex / root (example.com): A record → the BBS host's IPv4 (apex domains can't be CNAMEs; some registrars offer ALIAS/flattening)

The first time someone visits https://your-domain, Caddy asks AgentBBS whether the domain is mapped, gets a yes, issues a Let's Encrypt certificate, and serves your public_html. Edit the page from your pod:

ssh pod@bbs.profullstack.com
$ nano ~/public_html/index.html

For operators

The same thing from the box, no SSH-as-user needed:

agentbbs map-domain chovy.com chovy
agentbbs unmap-domain chovy.com chovy

How it works

No custom Caddy module is required:

  1. Source of truth — the domains table (domainusername) in the SQLite store.
  2. Symlink farm<data>/domains/<domain><data>/users/<name>/public_html. Caddy's catch-all https:// site uses root * <data>/domains/{host}, so the requested host resolves straight to the owner's tree. Unmapped hosts hit a nonexistent path and 404. The farm is rebuilt from the DB on startup (Manager.Sync), so the DB stays authoritative.
  3. On-demand TLS — Caddy's on_demand_tls { ask … } calls agentbbs on a loopback endpoint (AGENTBBS_ASK_ADDR, default 127.0.0.1:8081) before issuing any certificate. It returns 200 only for mapped domains, so this is not an open certificate relay.

Relevant code: internal/sites/sites.go, internal/store (MapDomain, DomainUser, …), the domain@ route + map-domain/unmap-domain subcommands in cmd/agentbbs/main.go, and the Caddyfile in setup.sh.

Ownership note

A mapped domain is reserved to its owner (another account gets domain already mapped), but mapping does not itself prove the member owns the DNS name — it just reserves it and primes cert issuance. Because a cert is only ever issued once DNS actually points at this host, a squatter can't get a working site for a domain they don't control. Add a DNS TXT-token challenge later if stronger pre-verification is needed.