Repository navigation
clients report what paints red in the browser console to one server route - #3337
Merged
Merged
Conversation
… regains two routes The identity, Treevite and POST-comments recorders matched each case's route hits by the route's global registration ordinal, so mounting one more Express app before the routes shifted every id and made live Node disagree with every committed fixture. The recorders now discover the mounted apps and resolve a hit by its method and path, so a new app leaves the fixtures untouched; the re-recorded fixtures differ from the old ones only in route identity (every old id was one too high), with the comparators, expected differences, cases and policies byte-identical. The route census regains GET /api/v3/version, which was registered but missing, and names the participation route's middleware as registered; the census check now passes on the source it describes. The legacy census test is updated for the repaired census; two tests that walked all registrations select them explicitly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…oute Until now no Polis client reported errors anywhere and no server route accepted such reports, so a broken response or a broken page was visible only in a participant's browser console. Every page of the admin console, the report, the legacy participation client (including vis2 and the embed) and the alpha participation client now carries a dependency-free reporter that catches uncaught errors and unhandled rejections, script, style, image and font load failures, console.error and console.warn, failed fetch and XMLHttpRequest calls (status 0 or 400 and above; 304 is success) and CSP violations. Each distinct position is its own report, carrying a stable signature (client, kind, status or message category, and the location with the bundle's content hash removed) and a count; repeats batch after one second or on page hide; a page sends at most 50 signatures, 20 sends a minute and 200 per tab session, by sendBeacon or a credential-less keepalive fetch to the client's own origin only. The server gains POST /api/v3/clientErrors, handled before the body parser and authentication. It reads no cookie, identity or database, touches no pool or lock, caps the body, admits a report only when both a per-address limit (burst 10, then 1 a minute; IPv4 address or IPv6 /64, taken from the X-Forwarded-For entry before a recognized Cloudflare edge) and a per-process limit (burst 100, then 1 a second) admit, always answers 204, and writes one structured warn line, client_error, with no address in it. The participation path shares nothing with it. Triage is deliberate: a Rust binary, render-client-errors, turns an exported Logs Insights result into a table of signatures with their message, count and pages; precise rows by default, an opt-in cross-build grouping. Nothing feeds an automated fixer. The route census gains the new route; the recorders already name routes by method and path, so no fixture changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The only conflict was server/characterization/inventory.json: edge carries the regenerated census from the recording prerequisite, and this branch carries the same census plus the row for POST /api/v3/clientErrors; the branch's version is kept.
The only conflict was the Makefile: edge added five local check targets at the place this branch adds check-client-errors; both sets are kept.
Contributor
Delphi Coverage Report
|
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.
Stacked on #3336 (recordings match routes by method and path); this PR's base is that branch and retargets to edge when it merges. Supersedes #3334, which carried the recordings in the same PR.
Observable changes
POST /api/v3/clientErrorson Node. Always answers 204, empty,Cache-Control: no-store, including for invalid, oversized, compressed, wrong-type and rate-limited bodies. Handled before the shared body parser and authentication; reads no cookie, identity or database; shares no pool, lock or wait with the participation path. Other methods keep Node's existing answer.client_error(warn), one per accepted report. Fields: client, build, page, kind, error_message, stack, resource, method, status, duration_ms, signature, count, user_agent. No address is logged. The signature (16 hex characters) comes from the client, the kind, the status or message category, and the location (first stack frame with the bundle's content hash removed, line and column kept; else the resource), so two errors at different positions are two signatures and the same position keeps its signature across builds. Admission: both limits must admit before either spends. Per participant address, burst 10 then 1 per minute (IPv4 address or IPv6 /64, taken from theX-Forwarded-Forentry before a recognized Cloudflare IPv4 edge, otherwise the trusted proxy hop); per process, burst 100 then 1 per second; at most 4096 addresses held, an entry replaceable after 10 minutes idle./api/v3/clientErrorsbysendBeaconor a credential-less keepalive fetch, on: an uncaught error or unhandled rejection; a script, style, image or font load failure;console.errororconsole.warn; a fetch or XHR with status 0 or 400 and above (304 and other statuses below 400 are not reported), including network failure, timeout or navigation abort; a CSP violation. Each distinct position is its own report; repeats of one position batch after 1 s or on page hide. Limits: 50 signatures per page, 20 sends per minute, 200 per tab session.fetch,XMLHttpRequest.prototype.open/sendandconsole.error/warnare replaced by observing wrappers. Newwindow.PolisClientErrors,window.__polisClientErrorReporters, and the sessionStorage keypolis-client-errors-rate(counters only).<script>at the top of<head>on admin, report, legacy participation and every alpha page. A future Content-Security-Policy must allow it.maxLength(no existing schema uses it).POST /api/v3/clientErrors(the recorders name routes by method and path since recordings: cases name their route by method and path, and the census regains two routes #3336, so no fixture changes).docs/client-error-contract.md,docs/client-error-triage.md; Rust binaryrender-client-errorsin queue-rs (precise rows by default;--group-buildsopt-in cross-build view);make check-client-errorswithci/local/client-errors.shand the generatorci/local/client-error-paths.cjs; workflow.github/workflows/client-errors.yml; aROUTING.mdrow and an assertion infile-server/nginx/test-edge-routing.py.No schema change. No third-party host. Nothing posts to any automated system; triage of what this reveals is a person's decision.
Why
No Polis client reported errors anywhere, and no server route accepted reports, so a broken page or a broken API response was visible only in a participant's browser console. This is a prerequisite for routing the first endpoint to the Rust API.
Evidence
Three independent review rounds, the last in a real browser (three planted TypeErrors in three modules of a production admin build gave three reports with three stacks; 31 participants behind one Cloudflare edge gave 31 log lines; one participant sending 31 gave 10; nine API routes gave nine signatures). On this split: all five recording targets pass against the #3336 fixtures unchanged; lint on four packages;
make check-client-errors123, admin 343, report 106, alpha 181 plus format/lint/build; focused server Jest 81; the production import script 170; routing recordings 104 replay cases with all 12 mutation refusals.The full server integration suite showed intermittent failures on a work box that an independent investigation traced to the box's port allocation (macOS hands IPv6 sockets ephemeral ports already held on IPv4, and the test database is reached as
localhost), present on unchanged edge in the same way; a harness fix follows separately. Matched runs: edge 3 of 3, this change 3 of 3.🤖 Generated with Claude Code