Files
CurrenciCombo/orchestrator
Devin 7bcc4e38c6 PR F: idempotency keys + replay protection (arch step 9 / \u00a713 / \u00a715)
Adds an Idempotency-Key middleware backed by a new idempotency_keys
table. Covers arch \u00a713 security (replay protection) and \u00a715
non-functional requirement (idempotent event handling, resilience to
duplicate messages).

Middleware (src/middleware/idempotency.ts):
  - Mounted on POST /api/plans and POST /api/plans/:planId/execute.
  - Key format /^[A-Za-z0-9_\-:.]{8,255}$/; malformed -> 400.
  - On hit, replays cached status+body with Idempotent-Replayed: true.
  - Reuse with a different body hash -> 422 idempotency_key_reused.
  - Scopes by (method, path, key) so the same key is safe across
    unrelated endpoints.
  - Only 2xx is cached. Non-2xx stays retryable.
  - res.json() is shimmed so handlers need no changes.
  - Fail-open on dedup-store unavailability (warn log).

Migration 004 (db/migrations/004_idempotency_keys.ts):
  - idempotency_keys(method, path, key, request_hash, status_code,
    response_body, created_at, expires_at) with UNIQUE(method,path,key)
    and an expires_at index. 24h TTL.

Tests: tests/unit/idempotency.test.ts \u2014 6 cases covering no-header
pass-through, malformed-key 400, replay on second call, 422 on body
divergence, retryable non-2xx, (method,path,key) scoping.

tsc clean. 80 tests pass across 7 suites.
2026-04-22 16:45:44 +00:00
..