Skip to content

feat(observability): admin-gated per-request Server-Timing via X-OS-Debug-Timing (#2408) - #3163

Merged
os-zhuang merged 3 commits into
mainfrom
claude/per-request-perf-timing-tp6lq4
Jul 18, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/per-request-perf-timing-tp6lq4

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What & why

Closes the gating part of #2408's perf-tuning design. The Server-Timing mechanism (auth/db/hooks/serialize/total spans, request-scoped collector, SQL query counting) already landed in #3107, but perf-tuning was global-only (serverTiming option / OS_SERVER_TIMING), which discloses internal phase durations — a mild backend-fingerprinting surface — to every caller.

This wires up the second gating path the issue describes: an operator (or the cloud console) can pull one request's timing breakdown on a live environment by sending X-OS-Debug-Timing: 1, and it is returned only to an admin/service identity — without flipping timing on for everyone.

How

  • Disclosure gate (@objectstack/observability) — a request-scoped AsyncLocalStorage gate kept separate from the pure PerfTiming collector, so the collector keeps its "only measures, never decides to emit" invariant. Pinned to a Symbol.for registry key (same reason as the collector store) so the middleware that seeds it and the dispatcher that opens it share one store across ESM/CJS module copies. New API: runWithPerfDisclosure, allowPerfDisclosure, isPerfDisclosureAllowed, PerfDisclosureGate.
  • Hono middleware (plugin-hono-server) — now registered by default (unless serverTiming: false hard-disables it). Opens the collector when timing is global or the request carries X-OS-Debug-Timing: 1; emits the header only when the gate is open. When neither applies it's a single header read then straight through — zero collector overhead.
  • Dispatcher (runtime) — after resolving the execution context (the single timedResolveExecutionContext choke point), opens the gate for admin/service/system principals via isPerfDisclosurePrincipal (isSystem, principalKind service/system, posture PLATFORM_ADMIN/TENANT_ADMIN). Ordinary human/guest/agent callers get no header even if they send the debug header.
  • Env alias — OS_PERF_TIMING=1 now also enables global mode, matching the issue's naming; OS_SERVER_TIMING=true still works.

Header shape (unchanged, now admin-gated per-request)

Server-Timing: auth;dur=42, db;dur=210;desc="6 queries", hooks;dur=18, serialize;dur=7, total;dur=355

Gating summary

Mode How Who sees the header
Global serverTiming: true / OS_SERVER_TIMING=true / OS_PERF_TIMING=1 every caller
Per-request caller sends X-OS-Debug-Timing: 1 admin/service/system only
Hard off serverTiming: false nobody (no middleware)

Open-core boundary

Pure mechanism (the request path, driver, engine). Policy — who counts as "admin", whether it surfaces as a cloud "perf panel" — stays cloud-side, mirroring the existing observability split.

Tests

  • perf-timing.test.ts — disclosure-gate primitive (seed state, open-after-await, independence from the collector scope, shared-symbol store).
  • server-timing.test.ts — per-request path: debug header alone withholds; header + proven admin identity emits; opt-in only (no header without the debug header); serverTiming: false hard-off; global discloses to everyone; OS_PERF_TIMING=1 alias; isDebugTimingRequested spellings.
  • http-dispatcher.perf-gating.test.ts — the admin/service/system predicate.
  • Existing global-mode behavior (unit + real-HTTP integration + SQL db span) unchanged.

Two CORS tests were updated to assert the precise intent (cors() not configured) instead of "no use() at all", since the perf-timing middleware now legitimately registers its own use('*') by default; one also gained a try/finally so a failed assertion can't leak OS_CORS_ENABLED.

All affected package builds (DTS typecheck) pass. One pre-existing, unrelated runtime seed-replay test failure was confirmed to fail identically on the base revision.


Generated by Claude Code

…ebug-Timing (#2408)

Adds the per-request gating from the perf-tuning design so an operator can pull
a single request's `Server-Timing` breakdown on a live environment without
turning the header on for every user.

Until now perf-tuning was global-only (`serverTiming` option / `OS_SERVER_TIMING`),
which discloses internal phase durations — a mild backend-fingerprinting surface
— to every caller. This wires up the second gating path the issue describes:

- **Disclosure gate** (`@objectstack/observability`): a request-scoped
  `AsyncLocalStorage` gate, separate from the pure `PerfTiming` collector so the
  collector keeps its "only measures, never decides to emit" invariant. Pinned
  to a `Symbol.for` registry key like the collector store so the middleware
  (which seeds it) and the dispatcher (which opens it) share one store across
  ESM/CJS module copies.
- **Hono middleware**: always registered unless `serverTiming: false`. Runs the
  collector when timing is global OR the request carries `X-OS-Debug-Timing: 1`;
  emits the header only when the gate is open. Zero collector overhead (one
  header read) when neither applies.
- **Dispatcher**: after resolving the execution context, opens the gate for
  admin/service/system principals (`isSystem`, `principalKind` service/system,
  posture PLATFORM_ADMIN/TENANT_ADMIN). Ordinary callers get no header even if
  they send the debug header.
- **Env alias**: `OS_PERF_TIMING=1` now also enables global mode, matching the
  issue's naming; `OS_SERVER_TIMING=true` still works.

Tests cover the gate primitive, the middleware's per-request/global/hard-off
paths, and the admin predicate. Existing global-mode behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011upHdyr5AnNc6dWAu63qxD
@vercel

vercel Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 18, 2026 5:31am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/observability, @objectstack/plugin-hono-server, @objectstack/runtime.

17 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/plugin-hono-server, @objectstack/runtime)
  • content/docs/permissions/authentication.mdx (via @objectstack/plugin-hono-server, @objectstack/runtime)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-hono-server)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-hono-server, @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/implementation-status.mdx (via @objectstack/observability, @objectstack/plugin-hono-server, @objectstack/runtime)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…ng path (#2408)

Updates the Server-Timing section for the new gating: the global mode
(`serverTiming` / `OS_SERVER_TIMING` / `OS_PERF_TIMING`) plus the per-request
`X-OS-Debug-Timing: 1` path that returns timing only to an admin/service
identity, and the `serverTiming: false` hard-off. Keeps the go-live checklist
in sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011upHdyr5AnNc6dWAu63qxD
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jul 18, 2026

This branch was successfully deployed

1 active deployment
Preview — 00ba926d Deployed Jul 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants