Skip to main content
ZombieMutex

30 results · ↑ ↓ navigate · Enter open · Esc close

All writing
November 6, 20263 min read

Reproducing a one-time challenge replay race with 200 contenders

I tried consuming one action-bound challenge 200 times. My single-process guard accepted one call and rejected 199; shared-store races remain untested.

By

Share

Send this article to someone or copy its link.

Suggest a correction

Reader tools

3 min remaining · 0% of article

Select a passage and choose Highlight selection to save it. Local browser storage only. Shift+F toggles focus.

I implemented a one-time challenge consumer and exercised it with 200 competing attempts in one Node.js process. The case I cared about was atomic claim behavior: when every caller presents the same valid challenge, exactly one should succeed.

The guard checks the principal, action, and expiry before consuming the token. The run accepted one call and rejected the other 199. I also checked mismatched context and expiry. What I haven't proved is the hard part of a deployed API: one authoritative claim when two separate workers reach the same database.

Hypothesis and test boundary

Hypothesis: A synchronous check-and-consume operation in a single Node.js event loop should allow precisely one successful claim, even when multiple Promise callbacks try to consume the same token.

This is intentionally a limited test of a local in-memory model. A separate process, geographically distributed service, crashed worker, and externally shared database would have different concurrency and persistence properties.

Reproduce the experiment

The test project has no external npm dependencies.

I ran the test with npm test on Node.js 24.21 on macOS Apple Silicon. The repository is private, but the central assertion is below.

One test creates a one-time token for principal “a” and action “sign”, schedules 200 Promise-based consume calls for that same token, and counts successful results.

typescript
const outcomes = await Promise.all(
  Array.from({length: 200}, () =>
    Promise.resolve().then(() =>
      guard.consume({token, principal: "a", action: "sign"})
    )
  )
);

assert.equal(outcomes.filter(result => result.ok).length, 1);

Observed results

Test caseObserved local outcome
Same token consumed by 200 scheduled callbacksExactly 1 accepted; 199 rejected
Reuse after successful consumeRejected as already used
A different principal or action uses that tokenContext mismatch; rejected
Challenge used after configured expiryRejected
Expired record swept1 expired record removed in test

All five repository tests passed in the local execution used to publish this report. These are test assertions, not throughput benchmarks or live network measurements.

Why this result is insufficient for production

The map-based consume is synchronous and local to one JavaScript process. It does not address the classic check-then-set race when two independent workers consult a shared datastore, where both can observe an unused token.

A deployable system needs a durable authoritative challenge store and an atomic claim operation. That typically means a transactional unique constraint or compare-and-swap primitive across every worker and region allowed to accept the proof. The token is a replay guard input; it is not App Attest verification, account authentication, or per-object authorization.

The test harness also returns detailed rejection reasons for investigation. An externally exposed endpoint may need uniform errors, request rate limits and privacy-safe event telemetry to avoid creating a new oracle.

What to test next

The next experiment should use an actual shared database with concurrent independent processes and crash injection between validation and commit. Verify a single winner across process boundaries, recovery after abrupt termination, retention, one-time-use expiration, and the behavior when the datastore is unavailable.

Read device integrity versus authorization for the platform-specific trust distinction.


I used AI assistance to build this reference example. The local tests aren't findings against a real client or a third-party system.

Related technical notes with overlapping topics.

Discuss a passage

Select a paragraph above, copy it with its article link, then paste into the discussion. Don't include private data.

Jump to discussion ↓

Public conversation / GitHub Discussions

Discuss this article

Read and reply using GitHub Discussions. A GitHub account is needed to post. Comments are public and subject to GitHub moderation.

For privacy, the external giscus widget does not load until you choose to open it. Opening it connects your browser to giscus and GitHub. Do not post credentials or private project data.