Task group 2 of federation-hardening. The narrow inbox
(Follow/Undo/Accept/Reject) had no replay protection — only Create(Note)
did, via the activities.remote_origin_iri unique constraint. A remote
redelivering a signed follow-graph activity would re-run its side
effects.
- New `federation_processed_activities` table (activity IRI PK,
received_at + index). Additive, so drizzle-kit push creates it; no
hand-written migration needed.
- `federation-replay.server.ts`: `markInboundActivityProcessed` does an
insert-or-drop (ON CONFLICT DO NOTHING RETURNING) and reports whether
the IRI is fresh; `sweepProcessedActivities` deletes rows > 30 days old
(signature date-freshness already rejects older replays).
- Each inbox listener drops a duplicate before side effects. The
follow-graph handlers are idempotent, so a handler failure whose retry
is later dropped as a duplicate can't corrupt state.
- `federation-dedup-sweep` job (daily 04:30 UTC) runs the TTL sweep.
Verified: db + journal typecheck + lint clean; drizzle-kit push creates
the table; replay integration test (fresh-vs-duplicate + 30-day sweep)
green against real Postgres; journal unit suite 355 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task group 1 of federation-hardening. Fedify was configured with
`InProcessMessageQueue`, so every queued outbound delivery, its pending
retry state, and inbox processing task was lost on a container restart
(routine here: deploys, OOM history) — directly contradicting the
social-federation spec's promise that fan-out survives a deploy.
- `federation-queue.server.ts`: `PgBossMessageQueue` implementing Fedify's
`MessageQueue` over the pg-boss instance the journal already runs,
mirroring the `PostgresKvStore` adapter. `nativeRetrial = false` keeps
Fedify the retry-policy owner; pg-boss supplies durability + delayed
jobs (delay → whole-second `startAfter`, `retryLimit: 0`).
- Swap it in for `InProcessMessageQueue` in `federation.server.ts`;
document the two intentional queueing layers (our fan-out jobs feed
Fedify; Fedify's sends now durable underneath).
- `server.ts`: create the durable queue at startup when federation is on.
- Broaden the structural `BossLike` in `boss.server.ts` with the
work/offWork/createQueue/getQueue methods the adapter needs.
- Tests: enqueue maps retry/delay correctly, consume roundtrip, restart
durability (fresh listener drains a prior instance's backlog), abort
stops the worker, depth reports ready vs delayed.
Verified: journal typecheck + lint clean, 7/7 new unit tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add docs/inspirations.md as the durable record of the 2026-07-05/06
prior-art research — per-project learnings with source paths, canonical
credit lines, and the changes each spawned — and extend the
acknowledgment lists in philosophy.md/architecture.md (Organic Maps,
Endurain, wanderer).
New OpenSpec changes (proposal/design/specs/tasks each):
- Organic Maps: elevation-profile-hardening, gpx-parser-robustness,
hiking-time-estimate, poi-index, hiking-foot-profile
- Endurain: account-export, activity-duplicate-review,
fit-parsing-hardening, activity-locations, self-hosting-guide,
activity-privacy-controls
- wanderer: federation-hardening, link-share-tokens
- credits-page (user-visible acknowledgments)
Updated in-flight changes with wanderer prior-art sections:
route-federation, route-discovery.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>