Skip to content

fix(gemini-titan): use millisecond nonce for WebSocket authentication - #2112

Open
VaggelisGian wants to merge 1 commit into
pmxt-dev:mainfrom
VaggelisGian:fix/gemini-titan-ws-nonce-ms
Open

fix(gemini-titan): use millisecond nonce for WebSocket authentication#2112
VaggelisGian wants to merge 1 commit into
pmxt-dev:mainfrom
VaggelisGian:fix/gemini-titan-ws-nonce-ms

Conversation

@VaggelisGian

Copy link
Copy Markdown

Fixes #1933

What was broken

buildWsHeaders() in core/src/exchanges/gemini-titan/auth.ts computed the WebSocket handshake nonce as Math.floor(Date.now() / 1000), sending seconds where Gemini's WebSocket API requires a millisecond Unix timestamp. The signed payload carried the same seconds value, so authenticated handshakes landed outside the server's replay-prevention window and were rejected, breaking watchOrderBook/watchTrades before any subscription could start.

The REST path was already correct: fetcher.ts signs payloads with GeminiAuth.nonce(), which returns milliseconds. Only buildWsHeaders() computed a nonce inline with the wrong unit.

What changed

  • Removed the /1000 division so the handshake nonce is Date.now() directly.
  • Updated the doc comment above it that still said seconds since epoch.
  • Added core/test/exchanges/gemini-titan-auth.test.ts, which freezes Date.now() and asserts both the X-GEMINI-NONCE header and the base64-decoded X-GEMINI-PAYLOAD carry the frozen millisecond value, plus an HMAC cross-check of X-GEMINI-SIGNATURE.

Verification

  • New tests fail on main (received 1755878400 vs expected 1755878400000) and pass with the patch.
  • npx jest -c jest.config.js test/exchanges from core/: 11 suites, 34 tests, all passing.
  • Full core jest: 823 passed; the only failure is the pre-existing pmxt-ensure-server sandbox case.
  • bash scripts/verify-all.sh on this branch matches a clean checkout of main exactly: the only failures are the three known Windows-only HOME-vs-USERPROFILE sandbox defects (core test/server/pmxt-ensure-server.test.ts plus two sdks/python/tests/test_server_manager.py cases), which pass on ubuntu-latest CI.

Gemini's WebSocket API requires the handshake nonce to be a Unix
timestamp in milliseconds. buildWsHeaders() divided Date.now() by
1000, sending seconds, which lands outside the server's
replay-prevention window and rejects authenticated connections. The
REST path already sends milliseconds via GeminiAuth.nonce(), so only
the WebSocket handshake was affected.

Remove the division, correct the stale doc comment, and add unit
tests that freeze Date.now() and assert the nonce header and signed
payload carry the millisecond value.

Fixes pmxt-dev#1933
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec-drift [gemini-titan] HIGH: WebSocket nonce in seconds at auth.ts:70 — API requires milliseconds — WS auth fails

1 participant