Mastodon is one implementation; the cache speaks the protocol: outbox for backfill, and fedi-cache as a follower actor so Update/Delete are delivered rather than polled for. Its own keypair replaces an access token, so webapp drops out of this one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
5.7 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 and boosts of @dckc@social.coop, speaking ActivityPub only — no server-specific API, so the cache works against any server and survives an account move. social.coop grants it nothing; madmode holds no credential-
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. Pull, push, or both? Pull: read the actor'soutbox(anOrderedCollectionofCreateandAnnounce) — public posts only. Push: fedi-cache is itself an actor that follows @dckc; social.coop deliversCreate/Update/Delete/Announceto its inbox, including followers-only posts once the follow is approved. Recommendation: both — outbox for backfill, inbox for freshness. - [ ] 0-iii. What's out of reach at the protocol level: likes (Likegoes to the post's author, not to followers, and Mastodon serves nolikedcollection) and bookmarks (not ActivityPub at all). Accept the gap, or take them from the account archive (likes.json,bookmarks.json) as a manual import. -
1. Authority: fedi-cache's own actor keypair, generated in its DO and never exported; HTTP Signatures on outgoing GETs (servers in authorized-fetch mode require them) and on the
Follow. The authority it holds is exactly what @dckc grants by approving the follow, and removing the follower revokes it. No token, no cookie, so webapp isn't needed. Ids → password manager, as with diigo. -
2. Pull: the outbox cache: - [ ] 2-i. Resolve the actor: WebFinger (
/.well-known/webfinger?resource=acct:dckc@social.coop) → actor document →outbox. - [ ] 2-ii. Storage: activities and objects keyed by theiridURI, objects withupdated. Durable state from day one: aSCHEMA_V1step. - [ ] 2-iii. Fill: follow the collection'sfirst/nextpages withAccept: application/activity+json; politeness delay per request; back off on 429. - [ ] 2-iv. Tests:pnpm lint && pnpm test, the real worker against a fake ActivityPub server (WebFinger, actor, paged outbox, a 429). - [ ] 2-v. Complete: a filled cache matches the account archive'soutbox.json— the archive is itself an ActivityPub collection, so the comparison stays at the protocol level (the analogue of diigo'sscripts/compare-export.ts). -
3. Push: fedi-cache as a follower: - [ ] 3-i. Serve its own actor: WebFinger, actor document with
publicKey,inbox. Needs a public HTTPS origin, so dev wants a tunnel or a deployed test instance (and the user runs it, per AGENTS.md). - [ ] 3-ii. SendFollow; recordAccept. - [ ] 3-iii. Inbox: verify the HTTP Signature against the sender's key, accept only activities whoseactoris @dckc, applyCreate/Update/Delete/Announce/Undo. - [ ] 3-iv. Tests: signed deliveries in; a forged one and one from another actor rejected; an edit and a delete reflected. -
4. Read surfaces:
/public/dckcwith diigo 1d's date window andqfilter, HTML and JSON; JSON shape pinned by a test. Only objects addressed toas:Publicappear there: followers-only posts (which only push brings in) 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: a raw list, or a draft the user edits. -
6. Deployed: rate-limit
/public/*; the actor's origin is its identity (moving it changes the follower), so choose the hostname once. -
7. Revocation: if the follow is removed or deliveries stop, the public page keeps serving what's stored and says when it last heard from @dckc; outbox polling still covers public posts.
-
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
- ActivityPub (outbox, inbox, delivery) and Activity Streams 2.0 (collections, paging)
- WebFinger, RFC 7033
- Mastodon's ActivityPub notes and its HTTP Signatures profile — what social.coop actually sends and requires (the server is Mastodon; the cache doesn't depend on it)
- #23 — the PESOS goal