WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto

docs: bootstrap CLI design for first-time forum setup

Adds design document for `atbb init` CLI command that automates
forum bootstrapping — creating the forum record on the PDS, seeding
default roles, and assigning the first Owner. Includes extraction
of ForumAgent into shared `packages/atproto` package.

+180
+180
docs/plans/2026-02-18-bootstrap-cli-design.md
··· 1 + # Bootstrap CLI Design 2 + 3 + **Date:** 2026-02-18 4 + **Status:** Approved 5 + 6 + ## Problem 7 + 8 + atBB has a bootstrapping gap. After deploying the appview and database, operators must manually create the forum record on the Forum DID's PDS and assign themselves the Owner role via raw AT Proto API calls. There is no automated first-time setup flow. 9 + 10 + Existing automation covers only part of the picture: 11 + - Role seeding (Owner/Admin/Moderator/Member) runs on appview startup 12 + - ForumAgent authenticates as the Forum DID on startup 13 + - Database migrations exist but must be run manually 14 + 15 + What remains manual: 16 + 1. Creating the `space.atbb.forum.forum/self` record on the Forum PDS 17 + 2. Assigning the Owner role to the operator's AT Proto identity 18 + 3. Verifying the environment is correctly configured before first launch 19 + 20 + ## Solution 21 + 22 + A CLI tool (`atbb init`) that bootstraps a new forum instance in one command. It handles environment validation, forum record creation, role seeding, and first-owner assignment. 23 + 24 + ### Audience 25 + 26 + Both server operators (Docker deployments) and developer-operators (running from source). The CLI works as a pnpm workspace script during development and as a Docker exec command in production. 27 + 28 + ### Scope 29 + 30 + Bootstrap only. Ongoing admin operations (categories, boards, bans) go through the web admin UI. The CLI handles one-time setup that must happen before the web UI is functional. 31 + 32 + ## Architecture 33 + 34 + ### New packages 35 + 36 + **`packages/cli` (`@atbb/cli`)** — The CLI tool itself. Ships the `atbb` command with the `init` subcommand. 37 + 38 + **`packages/atproto` (`@atbb/atproto`)** — Shared AT Protocol utilities extracted from the appview. Contains `ForumAgent`, error classification helpers, and identity resolution. 39 + 40 + ### Code extraction from appview 41 + 42 + | What | From | To | 43 + |---|---|---| 44 + | `ForumAgent` class | `apps/appview/src/lib/forum-agent.ts` | `packages/atproto/src/forum-agent.ts` | 45 + | `isAuthError` / `isNetworkError` | same file | `packages/atproto/src/errors.ts` | 46 + | `isProgrammingError` | `apps/appview` (various) | `packages/atproto/src/errors.ts` | 47 + 48 + New in `packages/atproto`: 49 + - `resolveIdentity(input, pdsUrl)` — resolves a handle or DID string to a confirmed DID 50 + 51 + What stays in appview: 52 + - `AppContext` (ties together DB + ForumAgent + OAuth + firehose) 53 + - `loadConfig()` (appview-specific env vars) 54 + - `seed-roles.ts` (depends on AppContext; CLI has its own step) 55 + 56 + ### Updated dependency chain 57 + 58 + ``` 59 + @atbb/lexicon ──┐ 60 + @atbb/db ──┼──→ @atbb/appview 61 + @atbb/atproto ──┘ 62 + ┌──→ @atbb/cli 63 + @atbb/db ──┤ 64 + @atbb/atproto ──┘ 65 + ``` 66 + 67 + Turbo build order: `lexicon` + `db` + `atproto` (independent), then `appview` + `web` + `cli` (parallel). 68 + 69 + ## Command Design: `atbb init` 70 + 71 + ### Bootstrap sequence 72 + 73 + 1. **Preflight checks** — Validate required env vars (DATABASE_URL, FORUM_DID, PDS_URL, FORUM_HANDLE, FORUM_PASSWORD). Fail immediately with a clear message listing what is missing. 74 + 2. **Database connection** — Verify connectivity. Confirm migrations are applied. 75 + 3. **PDS authentication** — Authenticate ForumAgent. Distinguish auth errors from network errors. 76 + 4. **Create forum record** — Prompt for forum name and description (or accept via flags). Create `space.atbb.forum.forum/self` on the Forum PDS. Skip if already exists. 77 + 5. **Seed default roles** — Create Owner/Admin/Moderator/Member role records on the Forum PDS. Skip any that already exist. 78 + 6. **Assign owner** — Prompt for owner handle or DID (or accept via flag). Resolve handle to DID if needed. Assign the Owner role. Skip if already assigned. 79 + 7. **Print summary** — Display what was created/skipped and next steps. 80 + 81 + ### Interaction model 82 + 83 + Interactive by default with flag overrides for scripted use: 84 + 85 + ```sh 86 + # Interactive (prompts for each value) 87 + atbb init 88 + 89 + # Scripted (no prompts) 90 + atbb init --forum-name "My Forum" --forum-description "A cool forum" --owner alice.bsky.social 91 + ``` 92 + 93 + When all required flags are provided, no prompts appear. 94 + 95 + ### Idempotency 96 + 97 + Every step checks before acting: 98 + - Forum record: checks if `space.atbb.forum.forum/self` exists on PDS 99 + - Role seeding: checks DB for existing roles by name 100 + - Owner assignment: checks if user already has Owner role 101 + 102 + Safe to run multiple times. Important for Docker containers that restart. 103 + 104 + ### Error handling 105 + 106 + - Missing env vars: fail immediately before any prompts 107 + - PDS auth errors: "check your FORUM_PASSWORD" (no retry for bad credentials) 108 + - PDS network errors: "can't reach PDS at https://..." (suggest checking PDS_URL) 109 + - Handle resolution failure: "Could not resolve 'alice.bsky.social'" 110 + - Partial completion: each step is independent; re-running picks up where it left off 111 + 112 + ## Package Structure 113 + 114 + ### `packages/atproto` 115 + 116 + ``` 117 + packages/atproto/ 118 + ├── package.json 119 + ├── tsconfig.json 120 + └── src/ 121 + ├── index.ts # Public exports 122 + ├── forum-agent.ts # ForumAgent class (moved from appview) 123 + ├── errors.ts # isAuthError, isNetworkError, isProgrammingError 124 + └── resolve-identity.ts # Handle/DID resolution 125 + ``` 126 + 127 + ### `packages/cli` 128 + 129 + ``` 130 + packages/cli/ 131 + ├── package.json 132 + ├── tsconfig.json 133 + └── src/ 134 + ├── index.ts # CLI entrypoint (citty main) 135 + ├── commands/ 136 + │ └── init.ts # The init command 137 + ├── lib/ 138 + │ ├── config.ts # CLI-specific config loader 139 + │ ├── preflight.ts # Env var checks, DB/PDS connectivity 140 + │ └── steps/ 141 + │ ├── create-forum.ts # Step 1: create forum record 142 + │ ├── seed-roles.ts # Step 2: seed default roles 143 + │ └── assign-owner.ts # Step 3: assign owner role 144 + └── __tests__/ 145 + ├── preflight.test.ts 146 + ├── create-forum.test.ts 147 + ├── seed-roles.test.ts 148 + ├── assign-owner.test.ts 149 + └── resolve-identity.test.ts 150 + ``` 151 + 152 + ## Dependencies 153 + 154 + | Package | Purpose | 155 + |---|---| 156 + | `citty` | Lightweight CLI framework (UnJS). Arg parsing, subcommands. | 157 + | `consola` | Styled console output (UnJS). Spinners, colored prefixes. | 158 + | `@inquirer/prompts` | Interactive prompts (text input, confirmations). Tree-shakeable. | 159 + | `@atproto/api` | AT Proto client. Already in workspace. | 160 + | `@atbb/db` | Database access. Workspace dependency. | 161 + | `@atbb/atproto` | ForumAgent + identity resolution. Workspace dependency. | 162 + 163 + ## Testing Strategy 164 + 165 + Each step is a pure function taking explicit dependencies (DB, AtpAgent, config). No global state. 166 + 167 + - **preflight.test.ts** — env var validation, missing vars, invalid formats 168 + - **create-forum.test.ts** — idempotency (record exists -> skip), PDS write success/failure 169 + - **seed-roles.test.ts** — role creation, duplicate detection, PDS errors 170 + - **assign-owner.test.ts** — handle resolution, DID passthrough, role assignment, already-owner skip 171 + - **resolve-identity.test.ts** — handle->DID resolution, DID passthrough, invalid handle errors 172 + 173 + All PDS calls mocked. Database calls use existing test harness patterns. 174 + 175 + ## Appview Changes 176 + 177 + After extraction, the appview changes are minimal: 178 + - `forum-agent.ts` deleted, replaced with `import { ForumAgent } from "@atbb/atproto"` 179 + - Error helpers imported from `@atbb/atproto` instead of local definitions 180 + - No behavioral changes — all existing tests should pass as-is