# social.coop cache goal tree The same conventions as [the diigo goal tree](capsite-checklist.md): each `- [ ]` is a goal, its indented children must pass first, working notes sit under each goal. - [ ] **madmode's weekly roundup includes my social.coop activity** ([#23](https://github.com/dckc/madmode-blog/issues/23), PESOS): a cache of [@dckc@social.coop](https://social.coop/@dckc) — posts, boosts, and (with discretion) bookmarks and favourites; the access token stays inside webapp, so madmode holds no credential - [ ] **0. Decide the shape** (user's call before code): - [ ] 0-i. New worker `masto-cache/` beside `diigo-cache/`, or generalize diigo-cache? Recommendation: a new worker; extract shared pieces once two caches work. - [ ] 0-ii. Which feeds? Public posts and boosts need no credential (`GET /api/v1/accounts/:id/statuses`, or the ActivityPub outbox). Bookmarks, favourites, and followers-only posts need a token. Bookmarks are private by nature: publish them only behind an explicit opt-in (what counts as opting in is open). - [ ] 0-iii. Mastodon API or ActivityPub? The API is simpler and covers bookmarks/favourites; ActivityPub works against any server (portability if the account moves). Recommendation: the API now, keyed by the post's ActivityPub `uri` so a later move keeps ids stable. - [ ] **1. Credential discipline**: a token from social.coop → Preferences → Development → New application, scopes **read-only and narrow** (`read:statuses read:accounts read:bookmarks read:favourites`, per 0-ii) — attenuation at the source. It rides as `Authorization: Bearer`, which webapp's mint form already blanks. Token → webapp DO storage only; ids → password manager. Revoking the app on social.coop is the kill switch. - [ ] **2. The cache** (`masto-cache/`, the 1c pattern from diigo): - [ ] 2-i. Mint and wire against `https://social.coop/api/`; `accounts/lookup?acct=dckc` gives the account id. - [ ] 2-ii. Storage: one table per feed (statuses, bookmarks, favourites), keyed by `uri`, storing the status JSON and its `edited_at`. **Durable state** from day one: a `SCHEMA_V1` step. - [ ] 2-iii. Fill: page backward with `max_id` (40 per page) following the `Link` header — bookmarks and favourites paginate *only* by `Link` (their cursors aren't status ids). Honor `X-RateLimit-Remaining`/`-Reset` (300 per 5 minutes is Mastodon's default). - [ ] 2-iv. Tests: `pnpm lint && pnpm test`, the real worker against a fake Mastodon, including `Link` pagination and a rate-limit response. - [ ] 2-v. Complete: a filled cache matches the account archive (Preferences → Import and export → Request your archive: `outbox.json`, `likes.json`, `bookmarks.json`), the analogue of diigo's `scripts/compare-export.ts`. - [ ] **3. Freshness**: new posts are cheap (`min_id` = newest stored, page forward). Edits and deletions of older posts are not announced to an API poller; options: re-fetch the last N days on each refresh (the window a roundup cares about), or follow the streaming API. A test: a post added, one edited, and one deleted in the fake; the weekly window reflects all three. A cron alarm in the cache DO drives it. - [ ] **4. Read surfaces**: `/public/dckc` with the date window and `q` filter of diigo's 1d (the same query module, if 0-i shares it), HTML and JSON; JSON shape pinned by a test. Serve only `visibility: public` posts (and opted-in bookmarks) there — followers-only and direct posts never reach a public page; a test says so. - [ ] **5. madmode's weekly page includes it**: diigo's goal 4 (`/bookmarks/2026-W41`) grows a section from this cache, or becomes a combined `/weekly/2026-W41`. Same editorial question as there: raw list, or a draft the user edits. - [ ] **6. Deployed**: after diigo's goal 5; rate-limit `/public/*`; mint against prod; fill. - [ ] **7. Token revocation**: when social.coop rejects the token (401), refreshes fail; the public page keeps serving what's stored and says so. Re-mint is manual; same re-wire question as diigo's goal 6. ## How to run TBD with goal 2; it should mirror [the diigo how-to-run](capsite-checklist.md#how-to-run-continuation-prompts). ## Working rules [capsite AGENTS.md](AGENTS.md): dev servers are the user's job; durable state, loud about breaking changes; secrets discipline; generated config types. ## Pointers - [Mastodon API: accounts](https://docs.joinmastodon.org/methods/accounts/), [bookmarks](https://docs.joinmastodon.org/methods/bookmarks/), [favourites](https://docs.joinmastodon.org/methods/favourites/), [OAuth scopes](https://docs.joinmastodon.org/api/oauth-scopes/), [rate limits](https://docs.joinmastodon.org/api/rate-limits/), [pagination](https://docs.joinmastodon.org/api/guidelines/#pagination) - [#23](https://github.com/dckc/madmode-blog/issues/23) — the PESOS goal - [webapp/README.md](webapp/README.md) — the mint form and its secret header list