The AtmosphereConf talks your skyline missed
0
fork

Configure Feed

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

Merge pull request #25 from musicjunkieg/docs/add-readme

docs: add README

authored by

chaos gremlin and committed by
GitHub
7436aca9 3e0b6e09

+206
+206
README.md
··· 1 + # Understory 2 + 3 + A social anti-algorithm for ATmosphereConf 2026 VODs, built on AT Protocol. 4 + 5 + `understory.watch` shows you the conference talks your network *missed* — not 6 + the ones it amplified. Where a normal feed surfaces what's loud, Understory 7 + inverts that signal: the brighter a talk glows, the fewer of your follows 8 + engaged with it. The visual metaphor is a forest floor at night — 9 + bioluminescent green for the undiscovered, fading darkness for what your 10 + network already covered. 11 + 12 + ## How it scores 13 + 14 + All scoring runs in the browser. There is no backend scoring server. Three 15 + layers combine into the final glow: 16 + 17 + 1. **Network attention (inverted).** When you log in, Understory crawls your 18 + Bluesky follows' posts during the conference window, matches them to 19 + talks via `community.lexicon.calendar.event` linkage and Constellation 20 + RSVP backlinks, and inverts the result: high attention → low score → no 21 + glow. Talks no one in your network discussed glow brightest. 22 + 2. **Interest similarity.** Cosine similarity between dense embeddings of 23 + your recent posts and per-talk transcript embeddings published as 24 + `watch.understory.topicIndex` records. 25 + 3. **Friend overrides.** `watch.understory.recommendation` records published 26 + to friends' own PDS repos can promote specific talks regardless of how 27 + much general attention they got. 28 + 29 + The three layers are weighted, normalized against the engaged subset of your 30 + follows (not your full follow list — that dilutes everything to near-zero), 31 + and projected onto a continuous CSS `--glow` variable that drives both the 32 + card opacity fade and the multi-layer box-shadow. 33 + 34 + ## Custom AT Protocol lexicons 35 + 36 + Authority NSID: `watch.understory` 37 + 38 + | Lexicon | Purpose | 39 + |---|---| 40 + | `watch.understory.talkRef` | Joins `place.stream.video` records to `community.lexicon.calendar.event` | 41 + | `watch.understory.transcript` | Timestamped, speaker-attributed transcript segments | 42 + | `watch.understory.topicIndex` | Dense embeddings + topic labels for interest matching | 43 + | `watch.understory.recommendation` | User-published social recommendations (lives on each user's own PDS) | 44 + 45 + ## Tech stack 46 + 47 + - **Next.js 16** with the App Router, **React 19**, **TypeScript 5** 48 + - **Tailwind CSS 4** with a custom "bioluminescent understory" theme 49 + - **HLS.js** for VOD playback 50 + - **`@atproto/api`** + **`@atproto/oauth-client-node`** for AT Protocol OAuth and PDS calls 51 + - **AssemblyAI** (`universal-3-pro` model) for offline transcript generation 52 + - **Vitest** for testing 53 + 54 + ## Local development 55 + 56 + ### Prerequisites 57 + 58 + - **Node.js 20+** 59 + - **ffmpeg** (only required if you plan to re-run the transcription pipeline) 60 + - An AssemblyAI API key (only required for transcription) 61 + 62 + ### First-time setup 63 + 64 + ```bash 65 + npm install 66 + cp .env.example .env # if .env.example exists; otherwise see below 67 + ``` 68 + 69 + Create `.env` with at least: 70 + 71 + ``` 72 + APP_URL=http://localhost:3000 73 + ASSEMBLYAI_API_KEY=your-key-here # only needed for `npm run transcribe` 74 + ``` 75 + 76 + `APP_URL` must be your app's public URL — it's used to generate the AT 77 + Protocol OAuth client metadata. Use `http://localhost:3000` for local dev 78 + and your production URL when deployed. 79 + 80 + ### Run the dev server 81 + 82 + ```bash 83 + npm run dev 84 + ``` 85 + 86 + The site is then served at `http://localhost:3000`. 87 + 88 + For local UI work without the social graph crawl (no Bluesky login needed), 89 + seed mock crawl data and start the server with the mock toggle: 90 + 91 + ```bash 92 + npx tsx scripts/seed-mock-crawl.ts 93 + MOCK_CRAWL=1 npm run dev 94 + ``` 95 + 96 + ### Build for production 97 + 98 + Understory builds in Next.js standalone mode and bundles the `data/` 99 + directory (~125 MB of transcripts and talk metadata) into the standalone 100 + output via `outputFileTracingIncludes`. 101 + 102 + ```bash 103 + npm run build 104 + npm start 105 + ``` 106 + 107 + `npm run build` runs Next's build, then a `postbuild` script copies 108 + `.next/static`, `public/`, and `data/` into `.next/standalone/`. `npm start` 109 + runs the standalone server, which expects `APP_URL` and `HOSTNAME=0.0.0.0` 110 + in production. 111 + 112 + ### Run tests 113 + 114 + ```bash 115 + npm test # vitest run 116 + npm run test:watch 117 + ``` 118 + 119 + ### Lint 120 + 121 + ```bash 122 + npm run lint 123 + ``` 124 + 125 + ## Data pipeline 126 + 127 + Two scripts in `scripts/` populate `data/`. Both write to `data/` directly 128 + and are intended to be run offline, not at request time. 129 + 130 + ```bash 131 + npm run build-talk-index # fetches VODs + schedule from AT Protocol → data/talks.json 132 + npm run transcribe # extracts audio via ffmpeg → AssemblyAI → data/transcripts/{rkey}.json 133 + ``` 134 + 135 + `build-talk-index` matches `place.stream.video` records on `iameli.com` 136 + against `community.lexicon.calendar.event` records on the conference's 137 + schedule repo via explicit `vodAtUri` links, with a fuzzy fallback for 138 + unmatched VODs (timestamp + title similarity). 139 + 140 + `transcribe` is idempotent — it skips any rkey that already has a 141 + transcript on disk, so re-running it after deleting specific files only 142 + regenerates those. 143 + 144 + ## Deployment 145 + 146 + Deploy target is **Railway**. The full deploy spec lives at 147 + `docs/superpowers/specs/2026-04-10-railway-deploy.md`. 148 + 149 + ### Branch workflow 150 + 151 + Feature PRs target `staging` for Railway validation, then `staging` is 152 + promoted to `main` via a separate PR. **Never PR directly to `main`.** 153 + 154 + ``` 155 + feature/* → staging (Railway staging environment) 156 + staging → main (Railway production environment) 157 + ``` 158 + 159 + Required Railway environment variables: 160 + 161 + - `APP_URL` — the public URL (e.g. `https://understory.watch`) 162 + - `HOSTNAME=0.0.0.0` — Next.js standalone server bind address 163 + 164 + ## Project layout 165 + 166 + ``` 167 + src/ 168 + app/ Next.js App Router pages and API routes 169 + api/crawl/ Server-side social graph crawl endpoint 170 + components/ React components (LumeCard, ScoredTalksGrid, etc.) 171 + hooks/ Client-side hooks (useCrawlData) 172 + lib/ 173 + auth/ AT Protocol OAuth client + session management 174 + crawl/ Network attention crawler (follows, search, RSVPs) 175 + scoring/ Three-layer scoring engine 176 + data/ 177 + talks.json Built talk index 178 + transcripts/ Per-talk transcript JSON (one file per rkey) 179 + scripts/ 180 + build-talk-index.ts AT Protocol → data/talks.json 181 + transcribe.ts HLS → ffmpeg → AssemblyAI → data/transcripts/ 182 + docs/ 183 + understory-design.md Full design spec 184 + superpowers/ Implementation specs and plans 185 + ``` 186 + 187 + ## Credits 188 + 189 + - **VODs** are hosted by [Streamplace](https://stream.place) on the 190 + `iameli.com` PDS. Conference recordings published as `place.stream.video` 191 + records. 192 + - **Schedule data** comes from the 193 + [ATmosphereConf](https://atmosphereconf.org) `bsky.social` repo as 194 + `community.lexicon.calendar.event` records. 195 + - **Backlinks index** for RSVPs is provided by 196 + [Constellation](https://constellation.microcosm.blue) on microcosm.blue. 197 + - Built on the [AT Protocol](https://atproto.com) and the public 198 + [Bluesky AppView](https://docs.bsky.app). 199 + 200 + ## License 201 + 202 + [MIT](LICENSE) © 2026 chaos gremlin 203 + 204 + --- 205 + 206 + Made with [Claude Code](https://claude.com/claude-code).