Retro Bulletin Board Systems on atproto. Web app and TUI. lazy mirror of alyraffauf/atbbs atbbs.xyz
forums python tui atproto bbs
3
fork

Configure Feed

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

web: fixup README.md

+19 -92
+19 -92
web/README.md
··· 1 - # atboards (React SPA) 1 + # atbbs web 2 2 3 - A static SPA reimplementation of the atboards web UI. No server, no database — all reads go directly to Slingshot/Constellation, all writes go directly to the user's PDS via atproto OAuth (DPoP). Designed to be hosted as static files on Cloudflare Pages or any static host. 3 + Static React SPA. No backend — reads go to Slingshot/Constellation, writes go to the user's PDS via atproto OAuth. 4 4 5 - ## Stack 6 - 7 - - **Vite + React 19 + TypeScript** 8 - - **react-router-dom v7** (history routing) 9 - - **`@atproto/oauth-client-browser`** for OAuth (same library red-dwarf uses) 10 - - **`@atproto/api`** `Agent` for authenticated XRPC writes 11 - - **Tailwind CSS v4** (via `@tailwindcss/vite`) 5 + ## Development 12 6 13 - All reads (boards, threads, replies, news, bans, hides, identity resolution) go through public Microcosm services: 7 + ```sh 8 + cd web 9 + npm install 10 + npm run dev 11 + ``` 14 12 15 - - `slingshot.microcosm.blue` — getRecord, listRecords, resolveMiniDoc 16 - - `constellation.microcosm.blue` — getBacklinks (used to find threads in a board, replies to a thread, news for a site, quotes of a reply) 17 - - `ufos-api.microcosm.blue` — random BBS discovery on the home page 13 + OAuth works automatically on `http://127.0.0.1:5173` via atproto's loopback client flow. 18 14 19 - All writes go to `agent.com.atproto.repo.{createRecord, putRecord, deleteRecord, uploadBlob}` against the user's PDS, using the OAuth/DPoP session held by `@atproto/oauth-client-browser`. 15 + ## Production 20 16 21 - ## Layout 17 + ### Static deploy (Cloudflare Pages, etc.) 22 18 23 - ``` 24 - react/ 25 - ├── index.html 26 - ├── package.json 27 - ├── vite.config.ts 28 - ├── tsconfig.json 29 - ├── public/ 30 - │ ├── client-metadata.json # OAuth client metadata for production (edit before deploy) 31 - │ ├── _redirects # Cloudflare Pages SPA fallback 32 - │ ├── favicon.svg 33 - │ └── hero.svg 34 - └── src/ 35 - ├── main.tsx # Root, BrowserRouter + AuthProvider 36 - ├── App.tsx # Routes 37 - ├── index.css # Tailwind entry 38 - ├── components/ 39 - │ ├── Layout.tsx # Header / footer / breadcrumb 40 - │ └── Localtime.tsx 41 - ├── lib/ 42 - │ ├── lexicon.ts # xyz.atboards.* collection IDs 43 - │ ├── util.ts # date / AT-URI helpers 44 - │ ├── atproto.ts # Slingshot + Constellation read wrappers 45 - │ ├── bbs.ts # `resolveBBS()` — port of core/resolver.py 46 - │ ├── oauth.ts # BrowserOAuthClient setup 47 - │ ├── auth.tsx # AuthProvider / useAuth() hook 48 - │ └── writes.ts # PDS write helpers (createThread, createReply, …) 49 - └── pages/ 50 - ├── Home.tsx 51 - ├── Login.tsx 52 - ├── Callback.tsx # /oauth/callback (no logic — provider handles it) 53 - ├── Site.tsx # /bbs/:handle 54 - ├── Board.tsx # /bbs/:handle/board/:slug 55 - ├── Thread.tsx # /bbs/:handle/thread/:did/:tid 56 - ├── Account.tsx # /account (inbox + BBS controls) 57 - ├── SysopCreate.tsx # /account/create 58 - ├── SysopEdit.tsx # /account/edit 59 - ├── SysopModerate.tsx # /account/moderate 60 - └── NotFound.tsx 19 + ```sh 20 + VITE_PUBLIC_URL=https://your-domain.com npm run build 61 21 ``` 62 22 63 - ## Routes 64 - 65 - Mirror the Python app exactly: 23 + Deploy `dist/`. The `_redirects` file handles SPA routing on Cloudflare Pages. 66 24 67 - | Route | Page | 68 - |---------------------------------|---------------| 69 - | `/` | Home | 70 - | `/login` | Login | 71 - | `/oauth/callback` | Callback | 72 - | `/account` | Account | 73 - | `/account/create` | SysopCreate | 74 - | `/account/edit` | SysopEdit | 75 - | `/account/moderate` | SysopModerate | 76 - | `/bbs/:handle` | Site | 77 - | `/bbs/:handle/board/:slug` | Board | 78 - | `/bbs/:handle/thread/:did/:tid` | Thread | 79 - 80 - The old `/api/threads/...` and `/api/replies/...` JSON endpoints are gone — pages do the same aggregation client-side via `lib/atproto.ts`. 81 - 82 - ## Development 25 + ### Docker 83 26 84 27 ```sh 85 - cd react 86 - npm install 87 - npm run dev 28 + docker run -d -p 8080:80 -e PUBLIC_URL=https://your-domain.com ghcr.io/alyraffauf/atbbs:latest 88 29 ``` 89 30 90 - For OAuth in dev, `BrowserOAuthClient` automatically falls back to a **loopback client** when no `clientMetadata` is provided. This works for `http://localhost:5173` without any tunneling — the client_id becomes `http://localhost/?...` and atproto auth servers accept it. 31 + The entrypoint generates `config.json` and `client-metadata.json` at runtime from `PUBLIC_URL`. 91 32 92 - ## Production deployment (Cloudflare Pages) 33 + ### OAuth 93 34 94 - 1. Edit `public/client-metadata.json` and replace every `REPLACE_WITH_YOUR_DOMAIN` with your deployed origin (e.g. `https://atbbs.app`). 95 - 2. Set the build env var `VITE_PUBLIC_URL=https://atbbs.app` so `lib/oauth.ts` uses the production metadata path. 96 - 3. `npm run build` — outputs static files to `dist/`. 97 - 4. Deploy `dist/` to Pages. The included `public/_redirects` makes Pages serve `index.html` for all routes (history routing). 98 - 5. Verify `https://your.domain/client-metadata.json` is publicly fetchable — that URL is your `client_id`, atproto auth servers will fetch it during the OAuth handshake. 99 - 100 - ## Auth flow 101 - 102 - 1. User hits `/login`, types handle, presses log in. 103 - 2. `useAuth().login(handle)` → `BrowserOAuthClient.signIn(handle)` → DPoP keypair generated, PAR pushed, browser redirected to the user's authserver. 104 - 3. Authserver redirects back to `/oauth/callback?code=…&state=…`. 105 - 4. The `AuthProvider` runs `client.init()` on every mount; on the callback page that detects the code, exchanges it, and returns a `OAuthSession`. 106 - 5. We wrap that session in an `Agent` and stash `{did, handle, pdsUrl}` in context. 107 - 6. Session/refresh tokens are persisted by the OAuth client in IndexedDB; reloads silently restore the session. 108 - 35 + `https://your-domain.com/client-metadata.json` must be publicly fetchable — atproto auth servers fetch it during the OAuth handshake.