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.

docs: documentation update

Malpercio 7053d91e e21f20b1

+123 -281
+61 -2
CLAUDE.md
··· 45 45 pnpm test # run all tests across all packages 46 46 pnpm clean # remove all dist/ directories 47 47 devenv up # start appview + web servers via process manager 48 + pnpm --filter @atbb/appview db:migrate # run database migrations 48 49 pnpm --filter @atbb/appview dev # run a single package 49 50 pnpm --filter @atbb/appview test # run tests for a single package 50 51 pnpm --filter @atbb/spike spike # run the PDS spike script ··· 60 61 - `APPVIEW_URL` — URL the web package uses to reach the appview API 61 62 - `FORUM_HANDLE`, `FORUM_PASSWORD` — forum service account credentials (for spike/writes) 62 63 64 + **OAuth & session management (required for production):** 65 + - `OAUTH_PUBLIC_URL` — public URL where AppView is accessible (used for client_id and redirect_uri) 66 + - `SESSION_SECRET` — signing key for session tokens (generate with `openssl rand -hex 32`) 67 + - `SESSION_TTL_DAYS` — session lifetime in days (default: 7) 68 + - `REDIS_URL` — optional Redis URL for session storage (recommended for multi-instance deployments) 69 + 70 + ## Deployment 71 + 72 + ### Docker 73 + 74 + The project includes production-ready Docker infrastructure for single-container deployment: 75 + 76 + ```sh 77 + # Build the Docker image 78 + docker build -t atbb:latest . 79 + 80 + # Run with docker-compose (recommended) 81 + cp docker-compose.example.yml docker-compose.yml 82 + # Edit docker-compose.yml with your DATABASE_URL, FORUM_DID, etc. 83 + docker compose up -d 84 + ``` 85 + 86 + **What's included:** 87 + - Multi-stage Dockerfile (Node 22 Alpine, ~200MB final image) 88 + - Nginx reverse proxy serving both appview (port 3000) and web (port 3001) on port 80 89 + - Non-root user (`atbb:atbb`) for security 90 + - Health checks on `/api/healthz` 91 + - Production-ready entrypoint script 92 + 93 + **Key files:** 94 + - `Dockerfile` — multi-stage build definition 95 + - `entrypoint.sh` — startup script (nginx + node servers) 96 + - `nginx.conf` — reverse proxy configuration 97 + - `docker-compose.example.yml` — orchestration template 98 + - `docs/deployment-guide.md` — comprehensive deployment instructions 99 + 100 + **Database migrations:** The container does NOT auto-run migrations. Run manually before starting: 101 + ```sh 102 + docker compose run --rm atbb pnpm --filter @atbb/appview db:migrate 103 + ``` 104 + 63 105 ## Pre-Commit Checks 64 106 65 107 Every commit automatically runs three checks in parallel via lefthook: ··· 90 132 91 133 Use sparingly — hooks catch issues that would fail in CI. 92 134 135 + ## CI/CD 136 + 137 + ### GitHub Actions Workflows 138 + 139 + **`.github/workflows/ci.yml`** — Runs on all pull requests (parallel jobs): 140 + - **Lint:** `pnpm exec oxlint .` — catches code quality issues 141 + - **Type Check:** `pnpm turbo lint` — verifies TypeScript types (allows failure due to 32 baseline errors in generated lexicon code) 142 + - **Test:** `pnpm test` — runs all tests with PostgreSQL 17 service container 143 + - **Build:** `pnpm build` — verifies compilation succeeds 144 + 145 + **`.github/workflows/publish.yml`** — Runs on pushes to `main` branch: 146 + - Builds Docker image and publishes to GitHub Container Registry (GHCR) 147 + - Tags: `latest` (main branch) and `sha-<commit>` (specific commit) 148 + - Image: `ghcr.io/atbb-community/atbb:latest` 149 + 150 + **All checks must pass before merging a PR.** The typecheck job is allowed to fail due to known baseline errors. 151 + 93 152 ### How Hooks Work 94 153 95 - - **Lefthook** manages git hooks (`.lefthook.yml`) 154 + - **Lefthook** manages git hooks (`lefthook.yml`) 96 155 - **Oxlint** provides fast linting (`.oxlintrc.json`) 97 156 - **Turbo** filters checks to affected packages only 98 157 - Hooks auto-install after `pnpm install` via `prepare` script ··· 103 162 - 23 errors in generated lexicon code (`packages/lexicon/dist/types/**/*.ts`) 104 163 - 9 errors in source/test code (test context types, OAuth types) 105 164 106 - These are pre-existing issues that need to be resolved. Until fixed, use `--no-verify` when committing or temporarily disable the typecheck command in `.lefthook.yml`. 165 + These are pre-existing issues that need to be resolved. Until fixed, use `--no-verify` when committing or temporarily disable the typecheck command in `lefthook.yml`. 107 166 108 167 ## Testing Standards 109 168
+46 -4
README.md
··· 82 82 ### Other Commands 83 83 84 84 ```sh 85 - pnpm build # Build all packages 86 - pnpm clean # Remove all build artifacts 87 - pnpm lint # Type-check all packages 85 + pnpm build # Build all packages 86 + pnpm test # Run all tests 87 + pnpm clean # Remove all build artifacts 88 + pnpm lint # Type-check all packages 89 + pnpm --filter @atbb/appview db:migrate # Run database migrations 88 90 ``` 89 91 92 + ## Deployment 93 + 94 + The project includes production-ready Docker infrastructure for containerized deployment. 95 + 96 + ### Quick Start with Docker 97 + 98 + ```sh 99 + # Copy and configure environment variables 100 + cp .env.example .env 101 + # Edit .env with production values (DATABASE_URL, FORUM_DID, OAUTH_PUBLIC_URL, etc.) 102 + 103 + # Copy and configure docker-compose 104 + cp docker-compose.example.yml docker-compose.yml 105 + # Edit docker-compose.yml if needed 106 + 107 + # Start services 108 + docker compose up -d 109 + 110 + # Run database migrations 111 + docker compose exec atbb pnpm --filter @atbb/appview db:migrate 112 + ``` 113 + 114 + ### What's Included 115 + 116 + - Multi-stage Dockerfile (Node 22 Alpine, ~200MB final image) 117 + - Nginx reverse proxy (serves both AppView and Web UI on port 80) 118 + - Health checks on `/api/healthz` 119 + - GitHub Actions CI/CD (automated testing and Docker image publishing) 120 + 121 + See [`docs/deployment-guide.md`](docs/deployment-guide.md) for comprehensive deployment instructions. 122 + 90 123 ## Lexicons 91 124 92 125 atBB defines custom AT Proto record types under the `space.atbb.*` namespace: ··· 107 140 108 141 See [`docs/atproto-forum-plan.md`](docs/atproto-forum-plan.md) for the full project plan and current progress. 109 142 110 - **Current phase:** Phase 0 (Foundation) — complete. Monorepo scaffolding, lexicon definitions, and package stubs are in place. Phase 1 (AppView Core) is next. 143 + **Current phase:** Phase 2 (Auth & Membership) — nearing completion. 144 + 145 + **Completed:** 146 + - ✅ Phase 0: Foundation (monorepo, lexicons, database schema) 147 + - ✅ Phase 1: AppView Core (firehose indexer, read/write API endpoints) 148 + - ✅ Phase 2: OAuth authentication (ATB-14), membership auto-creation (ATB-15) 149 + 150 + **In progress:** 151 + - Role-based permissions (ATB-17) 152 + - Forum DID agent for moderation actions (ATB-18) 111 153 112 154 ## Prior Art 113 155
+6 -6
docs/atproto-forum-plan.md
··· 189 189 - [ ] Basic responsive design 190 190 191 191 #### Phase 5: Packaging & Deployment (Week 9–10) 192 - - [ ] Dockerfiles for AppView and Web UI 193 - - [ ] Docker Compose with Postgres, AppView, Web UI 194 - - [ ] Config file: forum name, domain, admin DID, categories 195 - - [ ] README: setup guide, architecture overview 196 - - [ ] Seed script for initial forum + categories 197 - - [ ] Basic health check / status endpoint 192 + - [x] Dockerfiles for AppView and Web UI — **Complete:** Multi-stage Dockerfile with Node 22 Alpine, nginx reverse proxy, health checks (ATB-28) 193 + - [x] Docker Compose with Postgres, AppView, Web UI — **Complete:** `docker-compose.example.yml` with service orchestration (ATB-28) 194 + - [x] Config file: forum name, domain, admin DID, categories — **Complete:** `.env.example` with all required variables documented 195 + - [x] README: setup guide, architecture overview — **Complete:** README.md includes setup, architecture diagram, deployment instructions 196 + - [ ] Seed script for initial forum + categories — **Deferred:** Manual setup via spike script currently; automated wizard tracked in Future Roadmap 197 + - [x] Basic health check / status endpoint — **Complete:** `GET /api/healthz` and `GET /api/healthz/ready` implemented (ATB-9) 198 198 199 199 ### Key Risks & Open Questions 200 200
+10 -10
docs/oauth-implementation-summary.md
··· 506 506 507 507 ## Next Steps 508 508 509 - ### Immediate (Phase 2 Continuation) 509 + ### Completed Since This Document 510 510 511 - 1. **ATB-15: Auto-Create Membership** — On first login, create `space.atbb.membership` record 512 - 2. **ATB-16: Session Management Enhancements** — Redis-backed session store 513 - 3. **ATB-17: Permission Middleware** — Role-based access control for protected routes 511 + 1. ✅ **ATB-15: Auto-Create Membership** — Membership records now auto-created on first login (PR #27) 512 + 2. ✅ **ATB-12: Write Endpoints** — Topic and reply creation endpoints implemented with OAuth sessions 514 513 515 - ### Phase 3: Write Operations 514 + ### Immediate (Phase 2 Continuation) 516 515 517 - 1. **ATB-12: Write Endpoints** — Implement topic/reply creation using OAuth sessions 518 - 2. **Test Write Flow** — Verify authenticated users can create posts on their PDS 519 - 3. **Error Handling** — Handle PDS write failures, quota limits, network errors 516 + 1. **ATB-16: Session Management Enhancements** — Redis-backed session store 517 + 2. **ATB-17: Permission Middleware** — Role-based access control for protected routes 518 + 3. **ATB-18: Forum DID Agent** — Dedicated agent for forum-level operations 520 519 521 520 ### Phase 4: Web UI Integration 522 521 ··· 562 561 ### Related Issues 563 562 564 563 - [ATB-14: Implement AT Proto OAuth flow](https://linear.app/atbb/issue/ATB-14) (Complete ✅) 565 - - ATB-15: Auto-create membership on first login (Pending) 564 + - [ATB-15: Auto-create membership on first login](https://linear.app/atbb/issue/ATB-15) (Complete ✅) 565 + - [ATB-12: Write endpoints implementation](https://linear.app/atbb/issue/ATB-12) (Complete ✅) 566 566 - ATB-16: Redis-backed session storage (Pending) 567 567 - ATB-17: Permission middleware (Pending) 568 - - ATB-12: Write endpoints implementation (Blocked by ATB-14, now unblocked) 568 + - ATB-18: Forum DID agent (Pending) 569 569 570 570 ## Contributors 571 571
-259
docs/test-coverage-analysis.md
··· 1 - # Test Coverage Analysis 2 - 3 - ## Current State: No Tests Exist 4 - 5 - The monorepo has **zero test infrastructure**. No testing framework is installed, no test scripts exist in any `package.json`, and `turbo.json` has no `test` task. There are approximately **530 lines of source code** across 4 packages with 0% test coverage. 6 - 7 - --- 8 - 9 - ## Recommended Test Framework Setup 10 - 11 - **Vitest** is the best fit for this project: 12 - - Native ESM support (the repo uses `"type": "module"` everywhere) 13 - - Built-in TypeScript support via `tsx`/`esbuild` (no separate ts-jest config) 14 - - Workspace-aware — can share a root config while per-package configs override as needed 15 - - Fast, with watch mode out of the box 16 - 17 - ### Infrastructure needed 18 - 19 - 1. Install `vitest` as a root devDependency 20 - 2. Add a root `vitest.workspace.ts` pointing at each package 21 - 3. Add `"test": "vitest run"` scripts to each package's `package.json` 22 - 4. Add a `"test"` task to `turbo.json` (with `dependsOn: ["^build"]` since appview/web depend on lexicon types) 23 - 5. Add `pnpm test` as a root script 24 - 25 - --- 26 - 27 - ## Package-by-Package Gaps and Recommendations 28 - 29 - ### 1. `@atbb/appview` — API Server (highest priority) 30 - 31 - This is the most complex package (260 LOC) and where most business logic will land as stubs are implemented. It has the database schema, config loading, AT Protocol agent creation, and all API routes. 32 - 33 - #### a) Route handler tests (high value) 34 - 35 - **Files:** `src/routes/health.ts`, `src/routes/forum.ts`, `src/routes/categories.ts`, `src/routes/topics.ts`, `src/routes/posts.ts` 36 - 37 - **What to test:** 38 - - `GET /api/healthz` returns `200` with `{ status: "ok", version: "0.1.0" }` 39 - - `GET /api/healthz/ready` returns `200` with `{ status: "ready" }` 40 - - `GET /api/forum` returns `200` with the expected forum shape 41 - - `GET /api/categories` returns `200` with `{ categories: [] }` 42 - - `GET /api/categories/:id/topics` returns `200` and echoes the `id` param 43 - - `POST /api/topics` returns `501` (not implemented) 44 - - `POST /api/posts` returns `501` (not implemented) 45 - 46 - **How:** Use Hono's built-in `app.request()` test helper — no HTTP server needed: 47 - ```ts 48 - import { describe, it, expect } from "vitest"; 49 - import { apiRoutes } from "../src/routes/index.js"; 50 - import { Hono } from "hono"; 51 - 52 - const app = new Hono().route("/api", apiRoutes); 53 - 54 - describe("GET /api/healthz", () => { 55 - it("returns ok status", async () => { 56 - const res = await app.request("/api/healthz"); 57 - expect(res.status).toBe(200); 58 - expect(await res.json()).toEqual({ status: "ok", version: "0.1.0" }); 59 - }); 60 - }); 61 - ``` 62 - 63 - **Why it matters:** As stubs are replaced with real implementations, having route-level tests in place catches regressions in response shape, status codes, and content-type headers. These tests are cheap to write now and will grow in value. 64 - 65 - #### b) Config loading tests (medium value) 66 - 67 - **File:** `src/lib/config.ts` 68 - 69 - **What to test:** 70 - - Returns correct defaults when env vars are absent (`PORT` defaults to `3000`, `PDS_URL` defaults to `https://bsky.social`) 71 - - Parses `PORT` as an integer (not a string) 72 - - Returns provided env var values when set 73 - - Handles `PORT` set to a non-numeric string (currently `parseInt` would return `NaN` — should this throw?) 74 - 75 - **Why it matters:** Config loading is the root of most "it works on my machine" bugs. The current implementation silently accepts empty strings for `forumDid` and `databaseUrl`, which will cause hard-to-debug runtime failures. Tests would document this behavior and motivate adding validation. 76 - 77 - #### c) Database schema tests (medium value) 78 - 79 - **File:** `src/db/schema.ts` 80 - 81 - **What to test:** 82 - - Schema definitions export the expected table names 83 - - Column types match expectations (e.g., `posts.deleted` defaults to `false`) 84 - - Foreign key references are correct (`posts.did` → `users.did`, `posts.rootPostId` → `posts.id`, etc.) 85 - - Index names are correct and unique constraints are in place 86 - 87 - **How:** These can be pure unit tests against the Drizzle schema objects — no database connection needed. Drizzle table objects expose `._.columns` and other metadata you can assert against. 88 - 89 - **Why it matters:** Schema is the foundation. If someone accidentally removes an index or changes a foreign key, these tests catch it before it hits a migration. 90 - 91 - #### d) Database integration tests (high value, but requires infrastructure) 92 - 93 - **What to test:** 94 - - Insert/select/update/delete for each table 95 - - Foreign key constraints are enforced (e.g., inserting a post with a non-existent `did` fails) 96 - - Unique index violations behave as expected 97 - - The `createDb()` factory produces a working Drizzle client 98 - 99 - **How:** Use a test PostgreSQL instance. Options: 100 - - **Testcontainers** (Docker-based, spins up a real Postgres per test suite) 101 - - **pg-mem** (in-memory Postgres emulator, faster but not 100% compatible) 102 - - A shared test database with transaction rollback between tests 103 - 104 - **Why it matters:** This is where the most subtle bugs live — constraint violations, bad joins, missing indexes. As the appview stubs are fleshed out with real queries, these tests become critical. 105 - 106 - #### e) AT Protocol agent factory test (low value now, higher later) 107 - 108 - **File:** `src/lib/atproto.ts` 109 - 110 - Currently just `new AtpAgent({ service: config.pdsUrl })` — not much to test. But as authentication and record-writing logic is added, this module should have tests verifying: 111 - - Agent is created with the correct service URL 112 - - Authentication errors are handled gracefully 113 - - Record write/read operations produce expected AT URI formats 114 - 115 - --- 116 - 117 - ### 2. `@atbb/web` — Server-Rendered Web UI 118 - 119 - #### a) API client tests (high value) 120 - 121 - **File:** `src/lib/api.ts` 122 - 123 - **What to test:** 124 - - `fetchApi("/categories")` calls the correct URL (`${appviewUrl}/api/categories`) 125 - - Throws an `Error` with status code and status text on non-2xx responses 126 - - Returns parsed JSON on success 127 - - Handles network failures (fetch throws) 128 - 129 - **How:** Mock `global.fetch` with `vi.fn()` or use `msw` (Mock Service Worker): 130 - ```ts 131 - import { describe, it, expect, vi, beforeEach } from "vitest"; 132 - 133 - // Mock fetch globally 134 - const mockFetch = vi.fn(); 135 - vi.stubGlobal("fetch", mockFetch); 136 - 137 - describe("fetchApi", () => { 138 - it("throws on non-ok response", async () => { 139 - mockFetch.mockResolvedValueOnce({ 140 - ok: false, status: 500, statusText: "Internal Server Error", 141 - }); 142 - const { fetchApi } = await import("../src/lib/api.js"); 143 - await expect(fetchApi("/test")).rejects.toThrow("AppView API error: 500"); 144 - }); 145 - }); 146 - ``` 147 - 148 - **Why it matters:** `fetchApi` is the single point of contact between the web UI and the appview. Error handling here determines whether users see useful error messages or blank pages. 149 - 150 - #### b) JSX component / layout tests (medium value) 151 - 152 - **File:** `src/layouts/base.tsx`, `src/routes/home.tsx` 153 - 154 - **What to test:** 155 - - `BaseLayout` renders valid HTML with the provided title 156 - - `BaseLayout` uses the default title "atBB Forum" when none is provided 157 - - `BaseLayout` includes the HTMX script tag 158 - - Home route returns `200` with `text/html` content type 159 - - Home page includes "Welcome to atBB" heading 160 - 161 - **How:** Use Hono's `app.request()` and assert against the HTML string, or use a lightweight HTML parser. Hono JSX components can be tested by rendering them and checking the output string. 162 - 163 - #### c) Config loading tests (low-medium value) 164 - 165 - **File:** `src/lib/config.ts` 166 - 167 - Same pattern as the appview config tests — verify defaults, parsing, and presence of required values. 168 - 169 - --- 170 - 171 - ### 3. `@atbb/lexicon` — Lexicon Definitions 172 - 173 - #### a) YAML-to-JSON build script tests (medium value) 174 - 175 - **File:** `scripts/build.ts` 176 - 177 - **What to test:** 178 - - Each YAML file in `lexicons/` produces valid JSON 179 - - Output JSON matches the expected Lexicon schema structure (has `lexicon`, `id`, `defs` fields) 180 - - No duplicate lexicon IDs across files 181 - - The `id` field in each lexicon matches its file path (e.g., `space/atbb/post.yaml` has `id: "space.atbb.post"`) 182 - 183 - **How:** Rather than testing the build script directly (it's I/O-heavy), write validation tests that run against the YAML source files: 184 - ```ts 185 - import { parse } from "yaml"; 186 - import { readFileSync } from "fs"; 187 - import { glob } from "glob"; 188 - 189 - describe("lexicon definitions", () => { 190 - const files = glob.sync("**/*.yaml", { cwd: "lexicons" }); 191 - 192 - it.each(files)("%s has a valid lexicon structure", (file) => { 193 - const content = readFileSync(`lexicons/${file}`, "utf-8"); 194 - const parsed = parse(content); 195 - expect(parsed).toHaveProperty("lexicon", 1); 196 - expect(parsed).toHaveProperty("id"); 197 - expect(parsed).toHaveProperty("defs"); 198 - }); 199 - }); 200 - ``` 201 - 202 - **Why it matters:** Lexicon definitions are the API contract for the entire AT Protocol integration. A malformed lexicon causes downstream build failures in type generation and runtime validation errors. Catching issues at the YAML level is far cheaper than debugging them at the API level. 203 - 204 - #### b) Schema contract tests (high value) 205 - 206 - **What to test:** 207 - - `space.atbb.post` has `text` as a required string field 208 - - `space.atbb.post` has optional `reply` with `root` and `parent` refs 209 - - `space.atbb.forum.forum` uses `key: literal:self` 210 - - `space.atbb.forum.category` uses `key: tid` 211 - - All `strongRef` usages have both `uri` and `cid` fields 212 - - `knownValues` are used (not `enum`) for extensible fields like `modAction.action` 213 - 214 - **Why it matters:** These are the **contract tests** of the system. If a lexicon field is accidentally renamed or a required field becomes optional, it breaks interoperability with any PDS that stores atBB records. These tests protect the public API surface. 215 - 216 - --- 217 - 218 - ### 4. `@atbb/spike` — PDS Integration Script 219 - 220 - The spike is a manual integration test. It doesn't need unit tests itself, but: 221 - 222 - #### Extractable test utilities (medium value) 223 - 224 - The spike contains reusable patterns for: 225 - - Authenticating with a PDS 226 - - Creating/reading/deleting AT Protocol records 227 - - Generating TIDs 228 - 229 - These should be extracted into a shared test utility module (e.g., `packages/test-utils/`) that integration tests across the monorepo can use. 230 - 231 - --- 232 - 233 - ## Priority Matrix 234 - 235 - | Priority | Area | Package | Effort | Impact | 236 - |----------|------|---------|--------|--------| 237 - | **P0** | Test infrastructure setup (vitest, turbo task, CI) | root | Low | Unblocks everything | 238 - | **P0** | Appview route handler tests | appview | Low | Catches regressions as stubs are implemented | 239 - | **P1** | Web API client tests (`fetchApi`) | web | Low | Validates the only web→appview boundary | 240 - | **P1** | Lexicon schema contract tests | lexicon | Low | Protects the AT Protocol API surface | 241 - | **P1** | Config loading tests (both packages) | appview, web | Low | Documents defaults, catches parse bugs | 242 - | **P2** | Database schema unit tests | appview | Medium | Catches accidental schema changes | 243 - | **P2** | JSX layout/component tests | web | Medium | Ensures correct HTML output | 244 - | **P2** | Lexicon build script validation | lexicon | Low | Catches YAML/JSON conversion issues | 245 - | **P3** | Database integration tests | appview | High | Requires Postgres test infra (Docker/testcontainers) | 246 - | **P3** | AT Protocol integration tests | appview | High | Requires PDS test instance or mock | 247 - | **P3** | Extract spike utilities into shared test-utils | spike | Medium | Enables reuse across integration tests | 248 - 249 - --- 250 - 251 - ## Suggested Implementation Order 252 - 253 - 1. **Set up vitest** at the root + per-package, add `test` task to turbo.json 254 - 2. **Appview route tests** — quick wins since Hono has a built-in test helper and the routes are simple right now 255 - 3. **Lexicon contract tests** — validate YAML schema structure to protect the AT Protocol API 256 - 4. **Web `fetchApi` tests** — mock fetch, verify URL construction and error handling 257 - 5. **Config tests** for both packages — small but catches real bugs 258 - 6. **Database schema tests** — assert on Drizzle metadata objects 259 - 7. **Database integration tests** — add testcontainers or similar once there are real queries to test