trails/apps/journal/app/jobs
Ullrich Schäfer 105659df7c
feat(journal): inbound federation replay defense
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>
2026-07-13 22:07:30 +02:00
..
backfill-user-keypairs.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
consumed-jti-sweep.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
deliver-activity.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
demo-bot-generate.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
demo-bot-prune.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
federation-dedup-sweep.ts feat(journal): inbound federation replay defense 2026-07-13 22:07:30 +02:00
federation-kv-sweep.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
garmin-import-activity.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
import-batches-sweep.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
komoot-bulk-import.test.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
komoot-bulk-import.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
notifications-fanout.integration.test.ts Cursor-based pagination for /notifications 2026-04-26 01:42:32 +02:00
notifications-fanout.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
notifications-purge.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
payloads.test.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
payloads.ts feat(journal): inbound federation replay defense 2026-07-13 22:07:30 +02:00
poll-remote-actor.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
poll-remote-outboxes.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
send-welcome-email.ts journal: typed job seam + Komoot credentials through the manager 2026-06-10 02:05:24 +02:00
surface-backfill.test.ts route-surface-breakdown (Phase 2): async Overpass backfill + SSE 2026-06-14 19:18:03 +02:00
surface-backfill.ts route-surface-breakdown (Phase 2): async Overpass backfill + SSE 2026-06-14 19:18:03 +02:00