Skip to content

Count each staff Function response as one site.request data point - #189

Open
sdcarlson wants to merge 1 commit into
mainfrom
telemetry/site-request
Open

sdcarlson wants to merge 1 commit into
mainfrom
telemetry/site-request

Conversation

@sdcarlson

Copy link
Copy Markdown
Contributor

Needs Seth's handle: wrangler.toml floors at keys. Merge after #188 (privacy copy).

What changed

  • server/app.js: one Analytics Engine write per response in handle(). count() runs once the response is final (after secure(), CORS and the HEAD copy): env.EVENTS.writeDataPoint(point) inside a try, never awaited, skipped when there is no binding. Error codes travel beside responses through tag() in fail(), the 403 page and unavailable(); the two inline account-API 503s became one down() helper with the same bytes.
  • server/telemetry.js (new): sitePoint(fields) builds { indexes: ['site.request'], blobs: [event, release, route, method, status, code, app], doubles: [1, ms] } from the allowlist or returns null; tag/codeOf keep a code in a WeakMap. It imports nothing but the allowlist, so app.js and account-api.js share it without an import cycle.
  • telemetry/events.json (new): the allowlist in the telemetry.v1 contract form (owner Seth, decision "Are account saves working for each consumer app?"). The code reads it: a field it does not list is never written, and a value outside a list drops the point.
  • server/routes.js: template(table, pathname), the pattern a path matches whatever the method (so a 405 still has its template).
  • server/account-api.js: accountRoute(pathname) (its three templates) and failure() tags its error code.
  • server/release.js (new): RELEASE = 'unknown'. CI's "Build and check the staff Function" step rewrites it with ${GITHUB_SHA::7} just before the bundle and greps the bundle for it, so production carries the commit it was built from.
  • wrangler.toml: [[analytics_engine_datasets]] binding = "EVENTS", dataset = "syberlabs_events", top level only (bindings do not inherit; env.preview has none).
  • server/CONTRACT.md: a site.request section and a test-table row. server/test/telemetry.test.js (new): 7 tests.
blob value
release short sha of the built commit (CI), unknown everywhere else
route the route table's template (/admin/saves/:id/download), the account API's (/admin/api/v1/saves/:id), else unmatched; never the raw path
method GET, HEAD, POST, OPTIONS, other
status 2xx, 3xx, 4xx, 5xx
code the HttpError code (not_found, csrf, method_not_allowed, too_large), forbidden, the account API's error (signin_required, account_changed, ...), unavailable for the fail-closed 503s, else none
app omni, rise, sketch (Origin in APP_ORIGINS), site (Origin is ORIGIN), else none
doubles 1, ms

No user, session or save id, address, User-Agent or query string is read into a point.

Why

RFC 0004 packet P5 (https://github.com/SyberLabs/MasterMind/pull/68) and docs/contracts/telemetry-v1.md: account saves have three consumers and no per-consumer error count.

Tests

  • node --test 'server/test/**/*.test.js': 199 tests, 199 pass, 0 fail (192 existing, 7 new).
  • telemetry.test.js: responses byte-identical (status, statusText, every header, body) across 27 requests with the binding absent, recording, throwing and malformed ({}), plus D1-down and missing-config 503s on a page and on the API; one point per response; every point's fields, order and closed sets; templates for routes with ids; method, status, code and app for 20 cases; text pushed into the path, query, Origin, User-Agent, CF-Connecting-IP, cookie, expected-user header, method and JSON body never reaching a point (nor the user id, session token or save id); sitePoint refusing out-of-set values and dropping unlisted fields; the allowlist's shape; the binding only at the top level of wrangler.toml.
  • Mutation pass (each restored after): the raw path as route fails 3 tests, no catch around the write 1, an extra blob 2, the account API's code lost 1, any Origin counted as site 1, the binding added to preview 1, a response header changed when bound 1; a no-op control fails none.
  • node --test scripts/account-nav.test.mjs: 9 pass.
  • The CI Function step, run verbatim here with a stand-in GITHUB_SHA after npm ci --prefix deploy and pnpm build:site (pnpm 11.25.0 through corepack, Node 22.22.0; CI uses 24): every guard passed, and wrangler 4.141.0 compiled a 246,902-byte bundle with the allowlist inlined and RELEASE = "3d16ebe". node scripts/qa.mjs: 24 pages, 0 errors, 0 warnings. server/release.js is committed as unknown.

Live and local verification

  • Not deployed; merging deploys. In the lockfile-pinned wrangler 4.141.0, analytics_engine_datasets is among the supported Pages config fields, its schema is {binding, dataset} and "not automatically inherited" by environments, and miniflare's local binding is a synchronous no-op writeDataPoint(event). wrangler pages dev on this branch listed env.EVENTS (syberlabs_events) Analytics Engine Dataset and served a request through count().
  • GitHits grounding was not available in this session (its MCP server failed to connect), so the binding and API were checked against that installed source and the telemetry.v1 contract instead.
  • The live account API answered 401 {"version":1,"error":"signin_required"} at 2026-10-31T02:02:11Z.

Keys and sign-offs

  • Seth's handle in Admitted-by: node tools/ledger/cli.mjs floor origin/main...HEAD says keys: wrangler.toml. Nothing to create by hand: Analytics Engine creates syberlabs_events on the first production write. Reading it needs the P4 read token.
  • What the counts will hold, for the reader of the board:
    • The probe (Probe production every ten minutes; two failures in a row open an incident #187) GETs /admin/api/v1/account with no Origin every ten minutes: about 144 points a day as /admin/api/v1/account GET 4xx signin_required none (288 at most with retries).
    • Browsers send no Origin on same-origin GETs, so staff page views count as none; site appears on the site's own form posts.
    • No public page calls the account API today (no page renders the .sy-account control), so no visitor page view becomes a count. If that header control returns, its per-page check would, and the privacy lede needs a look first.
    • Analytics Engine samples at high volume: queries sum _sample_interval.
  • CF_PAGES_COMMIT_SHA is not used for release: wrangler only emulates it locally from git, and the deploy job has no checkout that could give a deployment its commit.

Merge order

After #188, so the privacy page describes the counts before they start. It touches cloudflare-pages.yml in a different step from #187, so the two merge in either order.


Generated by Claude Code

handle() writes one Analytics Engine point per response (MasterMind RFC 0004
packet P5, telemetry.v1): blobs [event, release, route, method, status, code,
app], doubles [1, ms]. The route is the route table's template, never the raw
path; app is the request Origin against the account API's allowlist; release
is the short sha CI writes into server/release.js before the Function build.
telemetry/events.json is the allowlist, and server/telemetry.js writes nothing
outside it. The write is never awaited and runs in a try once the response is
final, so responses stay byte-identical with the binding absent, present or
throwing. wrangler.toml binds EVENTS to syberlabs_events in production only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHrJ4hmTtR1RFNpc215EK7
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-31T02:22:36.278501Z e53f69b PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

This branch has not been deployed

No deployments
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.

2 participants