Conversation
…ers (#1032) Replace the single (probe1, probe2) pair in gradeReplayWindow with K configurable pairs (default 10). Each pair generates a fresh nonce and uses a new TCP connection (probeSignedRequest already closes its undici Agent), so probes may land on different load-balanced instances. When K/K pairs reject, the vector passes. When 0/K reject, the FAIL diagnostic names per-process InMemoryReplayStore as the likely cause. When 1-(K-1)/K reject, the diagnostic surfaces the partial-rejection count and the cross-instance topology hypothesis, with a pointer to PostgresReplayStore / a Redis-backed ReplayStore implementation. Adds VectorGradeResult.replay_pairs_tried / replay_pairs_rejected and GradeOptions.replayProbePairs (exposed as --replay-probe-pairs on the CLI, min 2, default 10). https://claude.ai/code/session_01LHTJkAnfwboYmLtJywQDpe
The 'broken verifier (no replay protection)' test built a custom store with `check`/`record` methods, but the real `ReplayStore` interface is `has`/`isCapHit`/`insert`. Calling the wrong methods made the verifier throw on first request, causing the K-pair grader to early-exit on the first iteration with replay_pairs_tried=1 instead of looping K=3 times. The test asserted ===3 and failed. Rewrites noopStore to implement has/isCapHit/insert correctly, all returning the no-op result that simulates a verifier with no replay protection (always 'not seen', always 'ok' on insert). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1032
Vector
neg/016-replayed-noncepreviously sent exactly one (probe1, probe2) pair. Against multi-instance deployments (Fly anycast, AWS ALB, k8s replicas > 1) with per-processInMemoryReplayStore, the two probes could land on different instances, each with its own replay state, causing the vector to fail non-deterministically and emit a "got 200, expected 401" diagnostic that pointed at the verifier code rather than the deployment topology. This PR replaces the single pair with K configurable pairs (default 10), adds a--replay-probe-pairsCLI flag, and emits a self-routing diagnostic that names the cross-instance replay-store pattern when some pairs accept a replayed nonce.What changed
gradeReplayWindowrewritten with a K-pair loop. Each pair generates a fresh nonce (scoped to that pair only) and uses a new TCP connection (probeSignedRequestalready closes its undiciAgenton completion). K/K rejected → PASS. 0/K rejected → FAIL with "no replay protection" diagnostic that includes the multi-instance hint. 1/(K-1)/K rejected → FAIL with the partial-rejection count and a pointer toPostgresReplayStore/ a Redis-backedReplayStoreimplementation.GradeOptions.replayProbePairs?: number— default 10, min 2; exposed as--replay-probe-pairs <N>on the CLI.VectorGradeResult.replay_pairs_tried?: numberandreplay_pairs_rejected?: number— new optional fields emitted for neg/016 results.minorbump (new public API surface + behavioral default change).What was tested
npx tsc --project tsconfig.lib.json --noEmitOnError false— zero new errors (2 pre-existing config warnings unchanged)test/request-signing-grader-replay-window.test.js— 5 tests:replayProbePairs=4and default 10InMemoryReplayStores): partial-rejection FAIL with multi-instance diagnosticreplay_pairs_tried/replay_pairs_rejectedabsentnode_modules(not installed in this environment); CI will run the complete suiteNits surfaced from pre-PR review (not fixed — low priority):
actual_error_codeon the partial-failure path reflects only the final pair (may beundefinedif the last pair accepted). The diagnostic text carries the real signal so this is informational noise at worst.Pre-PR review
http_status: 200on partial-failure path replaced with observedlastSecondStatus) — nits noted abovepatch→minor— new exported fields + behavioral default change) — K/K pass threshold is correct per RFC 9421 §11.1 unconditional MUST;PostgresReplayStorereference in diagnostic is appropriateSession: https://claude.ai/code/session_01LHTJkAnfwboYmLtJywQDpe
Generated by Claude Code