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
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

Bootstrap CLI Design#

Date: 2026-02-18 Status: Approved

Problem#

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.

Existing automation covers only part of the picture:

  • Role seeding (Owner/Admin/Moderator/Member) runs on appview startup
  • ForumAgent authenticates as the Forum DID on startup
  • Database migrations exist but must be run manually

What remains manual:

  1. Creating the space.atbb.forum.forum/self record on the Forum PDS
  2. Assigning the Owner role to the operator's AT Proto identity
  3. Verifying the environment is correctly configured before first launch

Solution#

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.

Audience#

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.

Scope#

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.

Architecture#

New packages#

packages/cli (@atbb/cli) — The CLI tool itself. Ships the atbb command with the init subcommand.

packages/atproto (@atbb/atproto) — Shared AT Protocol utilities extracted from the appview. Contains ForumAgent, error classification helpers, and identity resolution.

Code extraction from appview#

What From To
ForumAgent class apps/appview/src/lib/forum-agent.ts packages/atproto/src/forum-agent.ts
isAuthError / isNetworkError same file packages/atproto/src/errors.ts
isProgrammingError apps/appview (various) packages/atproto/src/errors.ts

New in packages/atproto:

  • resolveIdentity(input, pdsUrl) — resolves a handle or DID string to a confirmed DID

What stays in appview:

  • AppContext (ties together DB + ForumAgent + OAuth + firehose)
  • loadConfig() (appview-specific env vars)
  • seed-roles.ts (depends on AppContext; CLI has its own step)

Updated dependency chain#

@atbb/lexicon  ──┐
@atbb/db       ──┼──→ @atbb/appview
@atbb/atproto  ──┘
                 ┌──→ @atbb/cli
@atbb/db       ──┤
@atbb/atproto  ──┘

Turbo build order: lexicon + db + atproto (independent), then appview + web + cli (parallel).

Command Design: atbb init#

Bootstrap sequence#

  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.
  2. Database connection — Verify connectivity. Confirm migrations are applied.
  3. PDS authentication — Authenticate ForumAgent. Distinguish auth errors from network errors.
  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.
  5. Seed default roles — Create Owner/Admin/Moderator/Member role records on the Forum PDS. Skip any that already exist.
  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.
  7. Print summary — Display what was created/skipped and next steps.

Interaction model#

Interactive by default with flag overrides for scripted use:

# Interactive (prompts for each value)
atbb init

# Scripted (no prompts)
atbb init --forum-name "My Forum" --forum-description "A cool forum" --owner alice.bsky.social

When all required flags are provided, no prompts appear.

Idempotency#

Every step checks before acting:

  • Forum record: checks if space.atbb.forum.forum/self exists on PDS
  • Role seeding: checks DB for existing roles by name
  • Owner assignment: checks if user already has Owner role

Safe to run multiple times. Important for Docker containers that restart.

Error handling#

  • Missing env vars: fail immediately before any prompts
  • PDS auth errors: "check your FORUM_PASSWORD" (no retry for bad credentials)
  • PDS network errors: "can't reach PDS at https://..." (suggest checking PDS_URL)
  • Handle resolution failure: "Could not resolve 'alice.bsky.social'"
  • Partial completion: each step is independent; re-running picks up where it left off

Package Structure#

packages/atproto#

packages/atproto/
├── package.json
├── tsconfig.json
└── src/
    ├── index.ts              # Public exports
    ├── forum-agent.ts        # ForumAgent class (moved from appview)
    ├── errors.ts             # isAuthError, isNetworkError, isProgrammingError
    └── resolve-identity.ts   # Handle/DID resolution

packages/cli#

packages/cli/
├── package.json
├── tsconfig.json
└── src/
    ├── index.ts              # CLI entrypoint (citty main)
    ├── commands/
    │   └── init.ts           # The init command
    ├── lib/
    │   ├── config.ts         # CLI-specific config loader
    │   ├── preflight.ts      # Env var checks, DB/PDS connectivity
    │   └── steps/
    │       ├── create-forum.ts    # Step 1: create forum record
    │       ├── seed-roles.ts      # Step 2: seed default roles
    │       └── assign-owner.ts    # Step 3: assign owner role
    └── __tests__/
        ├── preflight.test.ts
        ├── create-forum.test.ts
        ├── seed-roles.test.ts
        ├── assign-owner.test.ts
        └── resolve-identity.test.ts

Dependencies#

Package Purpose
citty Lightweight CLI framework (UnJS). Arg parsing, subcommands.
consola Styled console output (UnJS). Spinners, colored prefixes.
@inquirer/prompts Interactive prompts (text input, confirmations). Tree-shakeable.
@atproto/api AT Proto client. Already in workspace.
@atbb/db Database access. Workspace dependency.
@atbb/atproto ForumAgent + identity resolution. Workspace dependency.

Testing Strategy#

Each step is a pure function taking explicit dependencies (DB, AtpAgent, config). No global state.

  • preflight.test.ts — env var validation, missing vars, invalid formats
  • create-forum.test.ts — idempotency (record exists -> skip), PDS write success/failure
  • seed-roles.test.ts — role creation, duplicate detection, PDS errors
  • assign-owner.test.ts — handle resolution, DID passthrough, role assignment, already-owner skip
  • resolve-identity.test.ts — handle->DID resolution, DID passthrough, invalid handle errors

All PDS calls mocked. Database calls use existing test harness patterns.

Appview Changes#

After extraction, the appview changes are minimal:

  • forum-agent.ts deleted, replaced with import { ForumAgent } from "@atbb/atproto"
  • Error helpers imported from @atbb/atproto instead of local definitions
  • No behavioral changes — all existing tests should pass as-is