Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
7.2 KiB
social.coop cache goal tree
The same conventions as the diigo goal tree: each
- [ ] is a goal, its indented children must pass first, working notes
sit under each goal.
-
madmode's weekly roundup includes my fediverse activity (#23, PESOS):
fedi-cacheholds the posts, boosts, favourites, bookmarks, and mentions of @dckc@social.coop. The access token stays inside webapp, so madmode holds no credential. Mastodon's client API is one source behind the cache, not its model: items are keyed by their ActivityPuburi, and what the protocol can serve, it serves-
0. Decide the shape (user's call before code): - [ ] 0-i. A new worker
fedi-cache/besidediigo-cache/; extract shared pieces (date window/qquery module, fill job, webkey page) once two caches work. - [ ] 0-ii. Which collections reach the public page? Posts and boosts withvisibility: public, surely. Favourites (1,403 in the May 2025 archive) are the richest PESOS vein but were never published by me; bookmarks are private by nature. Opt-in per collection, or per item? -
1. Credential discipline: a new token from social.coop → Preferences → Development → New application, scopes read-only and narrow (
read:statuses read:accounts read:favourites read:bookmarks read:notifications, for mentions) — not mastodon-archive's token, whose scopes are whatever that tool asked for. It rides asAuthorization: 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 (
fedi-cache/, the 1c pattern from diigo): - [ ] 2-i. Mint and wire againsthttps://social.coop/api/;accounts/lookup?acct=dckcgives the account id. - [ ] 2-ii. Storage: one table per collection (statuses, favourites, bookmarks, mentions), keyed byuri, holding the status JSON andedited_at. Durable state from day one: aSCHEMA_V1step. - [ ] 2-iii. Fill: page backward following theLinkheader (favourites and bookmarks paginate only byLink; their cursors aren't status ids). HonorX-RateLimit-Remaining/-Reset. - [ ] 2-iv. Tests:pnpm lint && pnpm test, the real worker against a fake Mastodon (Linkpaging, a rate-limit response). - [ ] 2-v. Complete: a filled cache holds everything in the mastodon-archive output (make syncin~/projects/office-admin/toots;social.coop.user.dckc.jsonhas the same API shapes), the analogue of diigo'sscripts/compare-export.ts. -
3. Freshness: new items are cheap (
min_id, or theLinkprev, from the newest stored). Edits and deletions of older posts aren't announced to an API poller: refetch the last N days on each refresh (the window a roundup cares about). A test: a post added, one edited, one deleted in the fake; the weekly window reflects all three. A cron alarm in the cache DO drives it. -
4. Read surfaces:
/public/dckcwith diigo 1d's date window andqfilter, HTML and JSON; JSON shape pinned by a test. Onlyvisibility: publicposts and opted-in collections (0-ii) appear; followers-only and direct posts never do, and 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: a 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.
-
8. Protocol-level sources (later; portability): - [ ] 8-i. Public posts from the actor's ActivityPub
outbox(WebFinger → actor →outbox), no token; works against any server, so an account move needs no new source. - [ ] 8-ii. Push: fedi-cache as a follower actor (its own keypair, HTTP Signatures), soUpdate/Deletearrive instead of being polled for. Needs a public HTTPS origin whose hostname is its identity; choose it once. Candidates (user's call), each a possible single public origin for all the caches (diigo, zotero, fedi), with routes or a front worker dispatching by path over service bindings: -pesos.madmode.com: names the purpose, but one direction only (elsewhere → my site). -syndicate.madmode.com: covers both directions, PESOS and POSSE. -peers.madmode.com: names the caches as peers of each other, for if-this-then-that interactions between them (a favourite becomes a diigo bookmark, a zotero item becomes a post). Those need write authority, which no cache holds today: each would be a new, narrowly minted capability, not a widening of the read tokens. The follower actor's handle follows the hostname (e.g.@dckc@peers.madmode.com). - [ ] 8-iii. Neither reaches favourites, bookmarks, or mentions: those stay on the client API (or the server's account archive).
-
How to run
TBD with goal 2; it should mirror the diigo how-to-run.
Working rules
capsite AGENTS.md: dev servers are the user's job; durable state, loud about breaking changes; secrets discipline; generated config types.
Pointers
~/projects/office-admin/toots/— the current export: mastodon-archive viamake sync(its OAuth secrets sit there as*.secret, gitignored)- Mastodon API: accounts, favourites, bookmarks, OAuth scopes, rate limits, pagination
- ActivityPub, WebFinger, RFC 7033, Mastodon's HTTP Signatures profile — for goal 8
- #23 — the PESOS goal