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

chore: remove spike package and references (#29)

Remove the spike package which was used for early PDS testing but is no longer needed. Updates all documentation references in CLAUDE.md, README.md, environment files, and planning docs.

Changes:
- Delete packages/spike directory
- Update CLAUDE.md: remove spike from packages table and commands
- Update README.md: remove spike from packages table
- Update .env.example and .env.production.example
- Update docs: atproto-forum-plan.md, oauth-implementation-summary.md, write-endpoints-design.md
- Update pnpm-lock.yaml via pnpm install

authored by

Malpercio and committed by
GitHub
eb489aca 7053d91e

+10 -221
+1 -1
.env.example
··· 11 11 # WEB_PORT=3001 # set in web package, or override here 12 12 APPVIEW_URL=http://localhost:3000 13 13 14 - # Forum Service Account credentials (for spike and AppView writes) 14 + # Forum Service Account credentials (for AppView writes) 15 15 FORUM_HANDLE=your-forum-handle 16 16 FORUM_PASSWORD=your-forum-password 17 17
-4
.env.production.example
··· 55 55 # - Bluesky PDS: https://bsky.social 56 56 PDS_URL=https://pds.example.com 57 57 58 - # Note: FORUM_HANDLE and FORUM_PASSWORD are only used by the spike test script, 59 - # not by the production applications. The appview and web services do not require 60 - # forum credentials to operate. 61 - 62 58 # ============================================================================ 63 59 # Application URLs 64 60 # ============================================================================
+2 -4
CLAUDE.md
··· 23 23 |---------|-------------| 24 24 | `@atbb/db` | Drizzle ORM schema and connection factory for PostgreSQL | 25 25 | `@atbb/lexicon` | AT Proto lexicon definitions (YAML) + generated TypeScript types | 26 - | `@atbb/spike` | PDS read/write test script for validating AT Proto operations | 27 26 28 27 **Dependency chain:** `@atbb/lexicon` and `@atbb/db` build first, then `@atbb/appview` and `@atbb/web` build in parallel. Turbo handles this via `^build`. 29 28 ··· 48 47 pnpm --filter @atbb/appview db:migrate # run database migrations 49 48 pnpm --filter @atbb/appview dev # run a single package 50 49 pnpm --filter @atbb/appview test # run tests for a single package 51 - pnpm --filter @atbb/spike spike # run the PDS spike script 52 50 ``` 53 51 54 52 ### Environment Variables ··· 59 57 - `FORUM_DID` — the forum's AT Proto DID 60 58 - `PDS_URL` — URL of the forum's PDS 61 59 - `APPVIEW_URL` — URL the web package uses to reach the appview API 62 - - `FORUM_HANDLE`, `FORUM_PASSWORD` — forum service account credentials (for spike/writes) 60 + - `FORUM_HANDLE`, `FORUM_PASSWORD` — forum service account credentials 63 61 64 62 **OAuth & session management (required for production):** 65 63 - `OAUTH_PUBLIC_URL` — public URL where AppView is accessible (used for client_id and redirect_uri) ··· 338 336 - **Hono JSX `children`:** Use `PropsWithChildren<T>` from `hono/jsx` for components that accept children. Unlike React, Hono's `FC<T>` does not include `children` implicitly. 339 337 - **HTMX attributes in JSX:** The `typed-htmx` package provides types for `hx-*` attributes. See `apps/web/src/global.d.ts` for the augmentation. 340 338 - **Glob expansion in npm scripts:** `@atproto/lex-cli` needs file paths, not globs. Use `bash -c 'shopt -s globstar && ...'` to expand `**/*.json` in npm scripts. 341 - - **`.env` loading:** Dev and spike scripts use Node's `--env-file=../../.env` flag to load the root `.env` file. No `dotenv` dependency needed. 339 + - **`.env` loading:** Dev scripts use Node's `--env-file=../../.env` flag to load the root `.env` file. No `dotenv` dependency needed. 342 340 - **API endpoint parameter type guards:** Never trust TypeScript types for user input. Change handler parameter types from `string` to `unknown` and add explicit `typeof` checks. TypeScript types are erased at runtime — a request missing the `text` field will pass type checking but crash with `TypeError: text.trim is not a function`. 343 341 ```typescript 344 342 // ❌ BAD: Assumes text is always a string at runtime
-1
README.md
··· 43 43 |---------|-------------| 44 44 | [`packages/db`](packages/db) | Drizzle ORM schema and connection factory for PostgreSQL | 45 45 | [`packages/lexicon`](packages/lexicon) | AT Proto lexicon schemas (YAML) and generated TypeScript types | 46 - | [`packages/spike`](packages/spike) | PDS read/write test script | 47 46 48 47 ## Getting Started 49 48
+4 -4
docs/atproto-forum-plan.md
··· 139 139 - [x] Audit existing `space.atbb.*` lexicons — **Result:** 2 existing (`forum.forum`, `post`), 5 new needed for MVP. No separate topic type; unified post model with reply refs. 140 140 - [x] Review prior Rust AppView — **Result:** Axum/SQLx scaffold with jetstream-oxide firehose, minimal DB schema. Reference for route structure and Docker setup; MVP will be Node/TS rewrite. 141 141 - [x] Define new lexicons in YAML: `forum.category`, `forum.role`, `membership`, `reaction`, `modAction` — **Result:** All 5 defined in `packages/lexicon/lexicons/`, with YAML→JSON→TypeScript build pipeline via `@atproto/lex-cli`. 142 - - [x] Set up project scaffolding: monorepo with `packages/lexicon`, `apps/appview`, `apps/web` — **Result:** Turborepo + pnpm workspaces, devenv for Nix toolchain. AppView (Hono JSON API, port 3000), Web (Hono JSX + HTMX, port 3001), plus `packages/spike` for PDS testing. Apps live in `apps/`, shared libraries in `packages/`. 142 + - [x] Set up project scaffolding: monorepo with `packages/lexicon`, `apps/appview`, `apps/web` — **Result:** Turborepo + pnpm workspaces, devenv for Nix toolchain. AppView (Hono JSON API, port 3000), Web (Hono JSX + HTMX, port 3001). Apps live in `apps/`, shared libraries in `packages/`. 143 143 - [x] Create Forum Service Account (generate DID, set up PDS or use existing hosting) — **Complete:** Forum DID configured in `.env`, PDS credentials set (ATB-5) 144 - - [x] Spike: write a test record to a PDS, read it back via AT Proto API — **Complete:** Full implementation in `packages/spike/src/index.ts` validates write/read/list/delete for all MVP record types (ATB-6) 144 + - [x] Spike: write a test record to a PDS, read it back via AT Proto API — **Complete:** Validated write/read/list/delete for all MVP record types (ATB-6) 145 145 146 146 #### Phase 1: AppView Core (Week 3–4) 147 147 - [x] Implement firehose subscription — connect to relay, filter for `space.atbb.*` records — **Complete:** Production-ready implementation in `apps/appview/src/lib/firehose.ts` with Jetstream WebSocket client, cursor persistence, circuit breaker, and exponential backoff reconnection logic (ATB-9) ··· 193 193 - [x] Docker Compose with Postgres, AppView, Web UI — **Complete:** `docker-compose.example.yml` with service orchestration (ATB-28) 194 194 - [x] Config file: forum name, domain, admin DID, categories — **Complete:** `.env.example` with all required variables documented 195 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 196 + - [ ] Seed script for initial forum + categories — **Deferred:** Automated wizard tracked in Future Roadmap 197 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 ··· 221 221 React Native + Expo cross-platform apps consuming the same `/api/*` endpoints as the web UI. Phased rollout: read-only browse → write/interact → push notifications → offline support & app store release. Full plan in [`docs/mobile-apps-plan.md`](mobile-apps-plan.md). 222 222 223 223 ### Other Future Work 224 - - **Setup wizard for first-time initialization** — Interactive web-based wizard for administrators to initialize a new forum instance (create forum record on PDS, configure categories, set admin roles). Currently requires manual spike script execution or direct PDS API calls. 224 + - **Setup wizard for first-time initialization** — Interactive web-based wizard for administrators to initialize a new forum instance (create forum record on PDS, configure categories, set admin roles). Currently requires manual PDS API calls. 225 225 - Nested/threaded replies 226 226 - Full-text search (maybe Meilisearch) 227 227 - User profiles & post history
+3 -13
docs/oauth-implementation-summary.md
··· 475 475 476 476 ## Migration from Password Auth 477 477 478 - ### Current State (packages/spike) 479 - 480 - The spike package uses password-based authentication: 481 - ```typescript 482 - const agent = new AtpAgent({ service: pdsUrl }); 483 - await agent.login({ identifier: handle, password }); 484 - ``` 478 + ### OAuth Implementation 485 479 486 - ### Future State (OAuth) 480 + OAuth flow: 487 481 488 482 Replace with OAuth flow: 489 483 ```typescript ··· 501 495 502 496 1. ✅ **OAuth implemented** — Users can now log in via web UI 503 497 2. ⬜ **Update write endpoints** — Use OAuth sessions instead of password auth 504 - 3. ⬜ **Deprecate password auth** — Remove spike password logic (keep for admin operations?) 505 - 4. ⬜ **Admin login** — Decide: OAuth or keep password for forum service account? 498 + 3. ⬜ **Admin login** — Decide: OAuth or keep password for forum service account? 506 499 507 500 ## Next Steps 508 501 ··· 554 547 **Tests:** 555 548 - `apps/appview/src/lib/__tests__/config.test.ts` — Configuration validation tests 556 549 - `apps/appview/src/lib/__tests__/test-context.ts` — Test helper for OAuth context 557 - 558 - **Reference:** 559 - - `packages/spike/src/index.ts` — Old password auth (for comparison) 560 550 561 551 ### Related Issues 562 552
-1
docs/plans/2026-02-09-write-endpoints-design.md
··· 301 301 302 302 **Files to reference:** 303 303 - `apps/appview/src/middleware/auth.ts` - Use `requireAuth` from ATB-14 304 - - `packages/spike/src/index.ts` - Reference for `putRecord` usage (lines 54-73) 305 304 - `packages/lexicon/lexicons/space/atbb/post.yaml` - Source of truth for record structure 306 305 307 306 **Dependencies:**
-22
packages/spike/package.json
··· 1 - { 2 - "name": "@atbb/spike", 3 - "version": "0.1.0", 4 - "private": true, 5 - "type": "module", 6 - "scripts": { 7 - "spike": "tsx --env-file=../../.env src/index.ts", 8 - "lint": "tsc --noEmit", 9 - "lint:fix": "oxlint --fix src/", 10 - "clean": "rm -rf dist" 11 - }, 12 - "dependencies": { 13 - "@atbb/lexicon": "workspace:*", 14 - "@atproto/api": "^0.15.0", 15 - "@atproto/common-web": "^0.4.0" 16 - }, 17 - "devDependencies": { 18 - "@types/node": "^22.0.0", 19 - "tsx": "^4.0.0", 20 - "typescript": "^5.7.0" 21 - } 22 - }
-141
packages/spike/src/index.ts
··· 1 - import { AtpAgent } from "@atproto/api"; 2 - import { TID } from "@atproto/common-web"; 3 - 4 - const PDS_URL = process.env.PDS_URL ?? ""; 5 - const HANDLE = process.env.FORUM_HANDLE ?? ""; 6 - const PASSWORD = process.env.FORUM_PASSWORD ?? ""; 7 - 8 - if (!PDS_URL || !HANDLE || !PASSWORD) { 9 - console.error( 10 - "Missing required env vars: PDS_URL, FORUM_HANDLE, FORUM_PASSWORD" 11 - ); 12 - console.error("Copy .env.example to .env and fill in your credentials."); 13 - process.exit(1); 14 - } 15 - 16 - async function main() { 17 - // 1. Authenticate 18 - const agent = new AtpAgent({ service: PDS_URL }); 19 - await agent.login({ identifier: HANDLE, password: PASSWORD }); 20 - const did = agent.session!.did; 21 - console.log(`Authenticated as: ${did}\n`); 22 - 23 - // 2. Write a forum record (space.atbb.forum.forum, key: self) 24 - console.log("--- Writing forum record ---"); 25 - const forumResult = await agent.com.atproto.repo.putRecord({ 26 - repo: did, 27 - collection: "space.atbb.forum.forum", 28 - rkey: "self", 29 - record: { 30 - $type: "space.atbb.forum.forum", 31 - name: "Test Forum", 32 - description: "A test forum created by the PDS spike", 33 - }, 34 - }); 35 - console.log(`Forum record written: ${forumResult.data.uri}`); 36 - 37 - // 3. Write a category record (space.atbb.forum.category, key: tid) 38 - console.log("\n--- Writing category record ---"); 39 - const categoryTid = TID.nextStr(); 40 - const categoryResult = await agent.com.atproto.repo.putRecord({ 41 - repo: did, 42 - collection: "space.atbb.forum.category", 43 - rkey: categoryTid, 44 - record: { 45 - $type: "space.atbb.forum.category", 46 - name: "General Discussion", 47 - description: "Talk about anything", 48 - slug: "general", 49 - sortOrder: 0, 50 - createdAt: new Date().toISOString(), 51 - }, 52 - }); 53 - console.log(`Category record written: ${categoryResult.data.uri}`); 54 - 55 - // 4. Write a post record (space.atbb.post, key: tid) 56 - console.log("\n--- Writing post record ---"); 57 - const postTid = TID.nextStr(); 58 - const postResult = await agent.com.atproto.repo.putRecord({ 59 - repo: did, 60 - collection: "space.atbb.post", 61 - rkey: postTid, 62 - record: { 63 - $type: "space.atbb.post", 64 - text: "Hello from the atBB spike!", 65 - forum: { 66 - forum: { 67 - uri: forumResult.data.uri, 68 - cid: forumResult.data.cid, 69 - }, 70 - }, 71 - createdAt: new Date().toISOString(), 72 - }, 73 - }); 74 - console.log(`Post record written: ${postResult.data.uri}`); 75 - 76 - // 5. Read records back 77 - console.log("\n--- Reading records back ---"); 78 - 79 - const readForum = await agent.com.atproto.repo.getRecord({ 80 - repo: did, 81 - collection: "space.atbb.forum.forum", 82 - rkey: "self", 83 - }); 84 - console.log("Forum:", JSON.stringify(readForum.data.value, null, 2)); 85 - 86 - const readCategory = await agent.com.atproto.repo.getRecord({ 87 - repo: did, 88 - collection: "space.atbb.forum.category", 89 - rkey: categoryTid, 90 - }); 91 - console.log("Category:", JSON.stringify(readCategory.data.value, null, 2)); 92 - 93 - const readPost = await agent.com.atproto.repo.getRecord({ 94 - repo: did, 95 - collection: "space.atbb.post", 96 - rkey: postTid, 97 - }); 98 - console.log("Post:", JSON.stringify(readPost.data.value, null, 2)); 99 - 100 - // 6. List records in a collection 101 - console.log("\n--- Listing forum.category records ---"); 102 - const listResult = await agent.com.atproto.repo.listRecords({ 103 - repo: did, 104 - collection: "space.atbb.forum.category", 105 - limit: 10, 106 - }); 107 - console.log(`Found ${listResult.data.records.length} category records`); 108 - for (const rec of listResult.data.records) { 109 - console.log(` - ${rec.uri}`); 110 - } 111 - 112 - // 7. Clean up test records 113 - console.log("\n--- Cleaning up test records ---"); 114 - await agent.com.atproto.repo.deleteRecord({ 115 - repo: did, 116 - collection: "space.atbb.post", 117 - rkey: postTid, 118 - }); 119 - console.log("Deleted post record"); 120 - 121 - await agent.com.atproto.repo.deleteRecord({ 122 - repo: did, 123 - collection: "space.atbb.forum.category", 124 - rkey: categoryTid, 125 - }); 126 - console.log("Deleted category record"); 127 - 128 - await agent.com.atproto.repo.deleteRecord({ 129 - repo: did, 130 - collection: "space.atbb.forum.forum", 131 - rkey: "self", 132 - }); 133 - console.log("Deleted forum record"); 134 - 135 - console.log("\nSpike complete!"); 136 - } 137 - 138 - main().catch((err) => { 139 - console.error("Spike failed:", err); 140 - process.exit(1); 141 - });
-8
packages/spike/tsconfig.json
··· 1 - { 2 - "extends": "../../tsconfig.base.json", 3 - "compilerOptions": { 4 - "outDir": "./dist", 5 - "rootDir": "./src" 6 - }, 7 - "include": ["src/**/*.ts"] 8 - }
-22
pnpm-lock.yaml
··· 148 148 specifier: ^2.7.0 149 149 version: 2.8.2 150 150 151 - packages/spike: 152 - dependencies: 153 - '@atbb/lexicon': 154 - specifier: workspace:* 155 - version: link:../lexicon 156 - '@atproto/api': 157 - specifier: ^0.15.0 158 - version: 0.15.27 159 - '@atproto/common-web': 160 - specifier: ^0.4.0 161 - version: 0.4.16 162 - devDependencies: 163 - '@types/node': 164 - specifier: ^22.0.0 165 - version: 22.19.9 166 - tsx: 167 - specifier: ^4.0.0 168 - version: 4.21.0 169 - typescript: 170 - specifier: ^5.7.0 171 - version: 5.9.3 172 - 173 151 packages: 174 152 175 153 '@atcute/atproto@3.1.10':