feat(native): cipher accepted by PX collector (live-validated) - #19
Merged
Conversation
Live run results (curl probes against pedidosya.com.ar via AR proxy): * PX collector (collector-PXeT15wiaE.px-cloud.net/api/v2/collector and /b/s) returns **HTTP 200 + sensor ACK** for our `encrypt_sensor` output. The cipher round-trips cleanly to the wire format the runtime uses. * Customer-domain `/eT15wiaE/xhr/b/s` returns 403 from Cloudflare (cf-mitigated: challenge). The path that issues `_px3` is behind CF, so a pure-native end-to-end still needs a Camoufox warmup for cf_clearance + __cf_bm. Hybrid is the realistic shape. Iteration that got us here: * `sensor_capture_hook.js`: hooks `sendBeacon` (the actual PX channel — XHR was a red herring) including async Blob.text() reads. Also hooks `Array.prototype.join` since `hY()` builds JSON manually instead of calling `JSON.stringify`. Diagnostic counters. * `sensor_capture.rs`: spreads mouse/scroll/keydown nudges across the wait; triggers pagehide so PX flushes pending sensors. * `decrypt_payload_lossy`: strips non-base64 chars (heuristic vQ inverse), then b64-decode → XOR(IS=50). Recovers JSON prefix. * `px-cli calibrate`: falls back to lossy-decrypting `xhr_sends` bodies + regex extracting tag/key vocab when plaintext_events is empty. * `events::batch::default_batch`: rewritten using ground truth. Two stable tags (`AzNweUVTcEw=`, `egoJQD9rDHs=`) and ~90 field keys. Calibrate: 0 missing tags, 0 missing keys. Captures gitignored (proxy egress + session fingerprints). 132 workspace tests pass; clippy clean. 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.
What's proven (live, via AR proxy from this machine)
POSTtocollector-PXeT15wiaE.px-cloud.net/api/v2/collectorwith body produced byencrypt_sensor(default_batch(), pf, cu)returns HTTP 200 + sensor ACK ({"do":["sid|…","cls|…","sts|…","drc|…"]}).px-cli calibrateagainst a live Camoufox capture shows[AzNweUVTcEw=] missing=0and[egoJQD9rDHs=] missing=0. The native batch shape matches the runtime byte-for-byte (modulo per-call values).What's still blocked
Customer-domain
/eT15wiaE/xhr/b/sreturns 403 from Cloudflare (cf-mitigated: challenge). That's the path that issues_px3, and CF blocks any client lackingcf_clearance+__cf_bm. A pure-native end-to-end still needs a Camoufox warmup for those cookies. Hybrid (Camoufox seeds CF cookies, native rotates_px3on top) is the realistic v2 path.Changes
sensor_capture_hook.js—sendBeaconhook with Blob.text() reads,Array.prototype.joinhook (sincehY()builds JSON manually), diagnostic counters.sensor_capture.rs— spreads synthetic mouse/scroll/keydown nudges; pagehide flush before reading captures.px-native::cipher::decrypt_payload_lossy— strips non-base64, b64-decode, XOR(IS). Recovers the readable JSON prefix.px-cli calibrate— falls back to lossy-decryptingxhr_sendsbodies + regex vocab extract whenplaintext_eventsis empty.events::batch::default_batch— rewritten from ground truth: 2 tags + ~90 field keys, all observed in a real capture.132 tests pass; clippy clean; captures
.gitignored.🤖 Generated with Claude Code