Skip to main content
Start with the collector result and browser request sequence. Successful ordinary collection returns a request ID only after the tenant projection is committed.

The collector returns ok: false

heretic(config) resolves normal failure paths instead of rejecting into the host application. The collector runs once per page. Later calls reuse the first run. Check the options passed to the first call.

The collector does not appear to start

With auto: true, the collector waits for page load when necessary and then for an idle callback. Browsers without requestIdleCallback wait no more than 500 ms. Use the browser network panel to confirm:
With zdr: true, the last path is /z. budgetMs governs scheduling inside the core measured phase. Rendering and declared-surface collection run later. postTimeoutMs applies only to the final POST.

Endpoint resolution picks the wrong edge

Resolution checks explicit endpoint, injected edge, session cache, optional DoH, optional control plane, and fallback in that order. A successful discovery can remain in sessionStorage until its TTL expires. Clear heretic.edge.v1 when testing a changed discovery setup. The browser must connect directly to the measurement edge. A CDN or reverse proxy in front of it causes the edge to observe that intermediary’s TCP and TLS construction.

A request is blocked

Check these boundaries:
  • connect-src permits the paired tcp.<pop>.probe.heretic.quest and quic.<pop>.probe.heretic.quest origins plus configured discovery endpoints.
  • The endpoint presents valid HTTPS and CORS responses.
  • Content filters or network controls do not block the edge hostname.
  • /n and the final POST use the paired TCP origin. Both /healthz requests use the QUIC origin for the same POP.
  • The page remains active long enough to finish the POST.
The parallel probe uses blob: workers. If CSP blocks them, the payload records:
Other sections continue. Permit the worker with:

The tenant API returns 404

Check that the identifier is the raw 32-character lowercase hexadecimal value returned by ordinary collection. Do not add or remove a prefix. The same 404 covers:
  • a missing or mistyped ID
  • ZDR
  • a row attributed to another site
  • an archived site or revoked ownership
  • tenant deletion
  • retention pruning
These states are deliberately indistinguishable.

The tenant API returns 401

401 means the bearer key is missing, malformed, unknown, or revoked. Confirm that:
  • the header starts with Bearer
  • the value is an hrtc_sk_<32 lowercase hex> key
  • the value is complete and remains server-side
  • the key has not been revoked
  • the site has not been retired
A valid secret for the wrong site returns 404, not 401. The route defines no application-level 429 contract. Treat other statuses as unexpected HTTP failures, not verdict values.

Collection succeeds but no row appears

Confirm that the first collector call used an active public key for the same site as the server secret. The Worker rechecks active key and site ownership in the immutable write. A projection failure makes ordinary collection fail and returns no request ID.

A ZDR request cannot be retrieved

This is expected. /z creates no assessment, identity, projection, NDJSON record, request ID, or retrievable session. The collector returns { ok: true }.

The tenant response lacks deep evidence

The tenant endpoint returns a compact projection. It omits the full session, raw IP, raw claims, detailed transports, geometry, the full concealment report, the sensor report, contradicting_families, and per-finding sensor admissions. After ownership is verified, the dashboard maps the row’s recorded POP through a fixed Warsaw, Singapore, and San Jose allowlist. It uses a separate server-only operator credential for that POP. If the full live record is gone or the POP cannot return it, the dashboard falls back to the stored projection. Browser and database values cannot select an arbitrary edge host. Live sessions normally expire 900 seconds after creation. Memory pressure can remove them sooner. Failed local persistence can retain an unpersisted session through the retry window, up to about 20 additional minutes at the current 30-second sweep interval.

A family has incomplete coverage

Read coverage.state before interpreting status: Common collector-side causes include:
  • budgetMs elapsed before a measured section started
  • one probe failed while later sections continued
  • CSP blocked the parallel worker
  • surface: false removed rule-bearing inputs
  • the browser did not expose a required API
render: false does not disable identity. rawSurface: false does not disable the rest of the declared surface.

The verdict is insufficient

Inspect the stable insufficient_reason: An unreachable edge produces a collector failure, not an insufficient assessment.

Identity IDs are absent or changed

Identity version 2 omits an ID when its anchors are absent:
  • machine needs cores, display, and timezone.
  • instance needs canvas, fixed WebGL raster, or audio.
Compare IDs only within one identity version and one public-site-key scope. Public-key rotation changes them. Changes to component inputs can also change them.