capsite: goal trees for a zotero cache and a social.coop cache
Both follow the diigo-cache pattern (webapp holds the credential, read-only and minted narrow at the source; the cache serves a public page). Zotero's versioned sync answers diigo's stale-head problem; Mastodon's doesn't announce edits/deletes, so its tree carries that as a design question. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
279ddb4caa
commit
99ce5b6964
3 files changed
+216
-1
No files matched your search
@@ -0,0 +1,107 @@
|
||||
# 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
|
||||
Reference in new issue
Block a user