Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
141 lines
7.4 KiB
Markdown
141 lines
7.4 KiB
Markdown
# 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 fediverse activity**
|
|
([#23](https://github.com/dckc/madmode-blog/issues/23), PESOS):
|
|
`fedi-cache` holds the posts, boosts, favourites, bookmarks, and
|
|
mentions of [@dckc@social.coop](https://social.coop/@dckc). 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 ActivityPub `uri`, and what the
|
|
protocol can serve, it serves
|
|
|
|
- [ ] **0. Decide the shape** (user's call before code):
|
|
- [ ] 0-i. A new worker `fedi-cache/` beside `diigo-cache/`;
|
|
extract shared pieces (date window/`q` query module, fill
|
|
job, webkey page) once two caches work.
|
|
- [ ] 0-ii. Which collections reach the public page? Posts and
|
|
boosts with `visibility: 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 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** (`fedi-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 collection (statuses,
|
|
favourites, bookmarks, mentions), keyed by `uri`, holding
|
|
the status JSON and `edited_at`. **Durable state** from
|
|
day one: a `SCHEMA_V1` step.
|
|
- [ ] 2-iii. Fill: page backward following the `Link` header
|
|
(favourites and bookmarks paginate *only* by `Link`;
|
|
their cursors aren't status ids). Honor
|
|
`X-RateLimit-Remaining`/`-Reset`.
|
|
- [ ] 2-iv. Tests: `pnpm lint && pnpm test`, the real worker
|
|
against a fake Mastodon (`Link` paging, a rate-limit
|
|
response).
|
|
- [ ] 2-v. Complete: a filled cache holds everything in the
|
|
[mastodon-archive](https://github.com/kensanata/mastodon-archive)
|
|
output (`make sync` in `~/projects/office-admin/toots`;
|
|
`social.coop.user.dckc.json` has the same API shapes),
|
|
the analogue of diigo's `scripts/compare-export.ts`.
|
|
|
|
- [ ] **3. Freshness**: new items are cheap (`min_id`, or the `Link`
|
|
`prev`, 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/dckc` with diigo 1d's date
|
|
window and `q` filter, HTML and JSON; JSON shape pinned by a
|
|
test. Only `visibility: public` posts 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), so `Update`/`Delete` arrive
|
|
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.
|
|
- `sync.madmode.com`: the word
|
|
[bib sync #195](https://github.com/dckc/madmode-blog/issues/195)
|
|
already uses; two-way like `syndicate`, plainer.
|
|
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](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
|
|
|
|
- `~/projects/office-admin/toots/` — the current export:
|
|
[mastodon-archive](https://github.com/kensanata/mastodon-archive) via
|
|
`make sync` (its OAuth secrets sit there as `*.secret`, gitignored)
|
|
- Mastodon API: [accounts](https://docs.joinmastodon.org/methods/accounts/),
|
|
[favourites](https://docs.joinmastodon.org/methods/favourites/),
|
|
[bookmarks](https://docs.joinmastodon.org/methods/bookmarks/),
|
|
[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)
|
|
- [ActivityPub](https://www.w3.org/TR/activitypub/),
|
|
[WebFinger, RFC 7033](https://www.rfc-editor.org/rfc/rfc7033),
|
|
[Mastodon's HTTP Signatures profile](https://docs.joinmastodon.org/spec/security/)
|
|
— for goal 8
|
|
- [#23](https://github.com/dckc/madmode-blog/issues/23) — the PESOS goal
|