[BTAPI-68] Present a real browser TLS and HTTP/2 fingerprint to Wealthsimple #79

Merged
chris merged 6 commits from feature/BTAPI-68 into main 2026-08-29 15:39:21 -06:00
Owner

Ticket

BTAPI-68 Present a real browser TLS and HTTP/2 fingerprint to Wealthsimple

Summary

  • Adds a WsTransport seam behind wsFetch (server/lib/wealthsimple/request/transport), so the browser-identity header policy (BTAPI-66) is untouched and the transport is swappable — the default remains undici's fetch.
  • WS_IMPERSONATE=true (shipped enabled in .env.example) routes requests through a curl-impersonate Chrome binary instead, presenting a real browser TLS ClientHello (GREASE in cipher/extension lists) and negotiating HTTP/2 — verified by a test that parses the actual handshake bytes, and live against tls.peet.ws.
  • Request headers are passed to curl via -H @file rather than argv, keeping the live bearer token and session cookies out of /proc/<pid>/cmdline.
  • The binary is pinned from lexiforest/curl-impersonate (the actively-maintained fork) at a checksummed release, currently tracking Chrome 150 — a 1-version gap to the request headers' Chromium 151, down from lwthiker's abandoned Chrome 116 build.
  • Falls back to fetch with a one-time warning if the binary is missing, so a broken/absent binary degrades gracefully rather than breaking sync.
  • server/lib/wealthsimple/CLAUDE.md documents the transport, the flag, and how to detect/fix a stale impersonation profile going forward.

Follow-up (not in this PR)

  • The application-layer header identity (USER_AGENT/sec-ch-ua pinned to Chromium 151 in request/index.ts) has no refresh mechanism and will itself go stale independently of the transport — noted on the ticket, tracked separately.
## Ticket [BTAPI-68](http://192.168.2.100:7123/home/browse/BTAPI-68/) Present a real browser TLS and HTTP/2 fingerprint to Wealthsimple ## Summary - Adds a `WsTransport` seam behind `wsFetch` (`server/lib/wealthsimple/request/transport`), so the browser-identity header policy (BTAPI-66) is untouched and the transport is swappable — the default remains undici's `fetch`. - `WS_IMPERSONATE=true` (shipped enabled in `.env.example`) routes requests through a `curl-impersonate` Chrome binary instead, presenting a real browser TLS ClientHello (GREASE in cipher/extension lists) and negotiating HTTP/2 — verified by a test that parses the actual handshake bytes, and live against `tls.peet.ws`. - Request headers are passed to curl via `-H @file` rather than argv, keeping the live bearer token and session cookies out of `/proc/<pid>/cmdline`. - The binary is pinned from `lexiforest/curl-impersonate` (the actively-maintained fork) at a checksummed release, currently tracking Chrome 150 — a 1-version gap to the request headers' Chromium 151, down from lwthiker's abandoned Chrome 116 build. - Falls back to `fetch` with a one-time warning if the binary is missing, so a broken/absent binary degrades gracefully rather than breaking sync. - `server/lib/wealthsimple/CLAUDE.md` documents the transport, the flag, and how to detect/fix a stale impersonation profile going forward. ## Follow-up (not in this PR) - The application-layer header identity (`USER_AGENT`/`sec-ch-ua` pinned to Chromium 151 in `request/index.ts`) has no refresh mechanism and will itself go stale independently of the transport — noted on the ticket, tracked separately.
chris merged commit 5bc0b373a4 into main 2026-08-29 15:39:21 -06:00
chris deleted branch feature/BTAPI-68 2026-08-29 15:39:21 -06:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chris/budget-tracker!79
No description provided.