Skip to content

fix(service-messaging,objectql): stop serving sys_http_delivery.headers_json on the generic read path — internal: true + a batch privileged accessor (#8118) - #8348

Merged
os-zhuang merged 7 commits into
mainfrom
claude/issue-8118-delivery-headers-privileged-read
Aug 13, 2026
Merged

fix(service-messaging,objectql): stop serving sys_http_delivery.headers_json on the generic read path — internal: true + a batch privileged accessor (#8118)#8348
os-zhuang merged 7 commits into
mainfrom
claude/issue-8118-delivery-headers-privileged-read

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Fixes#8118

Route 3, as ruled by triage (issue comment 5269775253, not reopenable): redact-on-read via internal: true on sys_http_delivery.headers_json, plus a purpose-built privileged accessor in packages/objectql — the resolveSecret pattern that #7728 itself names as the remedy.

What this does

  • packages/objectqlObjectQL.resolveInternalField(object, recordIds, field): a batch, driver-level dereference of ONE internal: true field, returning a Map keyed by record id (null = row exists with nothing stored; absent = no such row). Refuses any field not declared internal: true with the ADR-0112 envelope (INVALID_FIELD, status 400) — without that guard it would be a generic read-protection bypass (over ADR-0100's deliberately-masked password in particular). Batch-shaped because its consumer claims a batch per dispatcher tick: one driver read per claim batch, never per row (the per-row cost was one of route 1's rejection reasons).
  • packages/services/service-messagingsys_http_delivery.headers_json is declared internal: true, so the engine omits it from every generic read (list, get, explicit ?select=headers_json) with no system carve-out (api-key-ui-lifecycle (secondary): the key column (SHA-256 hash) serializes over the data API, contradicting its own "never exposed to clients" description #7728's explicit design). The redaction sits at the ROW layer, so it covers the whole delivery population: source: 'webhook' rows (WebhookSchema-authored headers) AND source: 'flow' rows (per-run interpolated headers that never pass through WebhookSchema — the half every author-declared shape forgets). SqlHttpOutbox.claim() — the one reader that must put the map on the wire verbatim — recovers it through the accessor; list() / redeliver() return the redacted view.
  • Fail-closed, unweakened: an engine that redacts the column but cannot dereference it makes claim() throw — nothing goes out missing an authored header (missing headers are not self-announcing; the delivery would succeed while silently deviating from the authored config). The claimed rows revert to pending via the claim TTL, and a healthy claimer recovers them headers-intact (pinned in test).
  • content/docs/automation/webhooks.mdx — the sys_http_delivery field table stops advertising headers_json as a plain readable column, and the page's own rule ("reading a delivery row tells you what was sent, not how to forge it") now states explicitly that it covers the authored headers — this PR is what makes that sentence true. One page; no release-owned docs touched.

Constraint evidence (all four binding constraints)

Provenance — resumed work

Four commits (c17e8677d232665) were implemented by the previous dev on this card before an account-quota kill in the gate tail; this session inherited the branch, merged origin/main (89 commits, including #8327's rewrite of the projection filter in engine.ts — merged clean; the refusal is dotted-leg only, so the accessor's driver-level read and the ?select= served-without-it behavior are unaffected), and re-ran all verification from scratch. Full inherited-vs-added accounting is in the dev report on #8118.

Out of scope, deliberately

Verification

  • pnpm --filter @objectstack/objectql test — 196 files / 3484 tests green (includes the new accessor pins).
  • pnpm --filter @objectstack/service-messaging test — 19 files / 211 tests green (includes the 5-case at-rest integration suite).
  • pnpm --filter typecheck green on both packages; ESLint clean on all six changed source files.
  • Reverse verification (direction decided in advance): removing internal: true turns exactly the 4 redaction/fail-closed cases red and the header-less-delivery case stays green; restored byte-identical from the commit.
  • Gates green: adr-anchors, docs-audit-scope, durability-log-level, engine-double-contract, stack-collection-maps, test-source-alias, type-source-resolution, changeset-fixed, engine-split-ratio, query-options-erasure, type-check-coverage, nul-bytes, changeset-gate-self-tests, objectui-changeset, changeset-no-major (the last three derived from the changed paths, beyond the dispatch list). check-objectui-pin-fresh reports the repo-wide pin staleness equally on main — pre-existing release-lane state, zero lines of this diff.
  • Docs gates green after the webhooks.mdx amendment: docs-audit-scope, quick-reference-counts, role-word, doc-authoring, nul-bytes, and pnpm --filter @objectstack/lint run check:doc-formula-expressions (first run hit the documented unbuilt-dist prerequisite ERR_MODULE_NOT_FOUND on @objectstack/formula; green after building that closure — a prerequisite, not a finding).

Generated by Claude Code

@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 7:28am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/objectql, @objectstack/service-messaging.

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

  • content/docs/automation/webhooks.mdx(via @objectstack/service-messaging)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx(via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx(via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx(via @objectstack/objectql)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/objectql)
  • content/docs/kernel/runtime-services/examples.mdx(via packages/objectql)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/objectql, @objectstack/service-messaging)
  • content/docs/kernel/services.mdx(via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx(via @objectstack/objectql)
  • content/docs/permissions/system-context.mdx(via packages/objectql)
  • content/docs/plugins/index.mdx(via @objectstack/objectql)
  • content/docs/plugins/packages.mdx(via @objectstack/objectql, @objectstack/service-messaging)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx(via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/objectql)

1 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/objectql, @objectstack/service-messaging)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 13, 2026
…eaders_json as a readable column (#8118)
The page's own rule — 'reading a delivery row tells you what was sent, not
how to forge it' — was false while headers_json served Authorization
headers verbatim over the data API, and this PR is what makes it true.
Mark the row internal in the field table and state that the guarantee now
covers the authored headers, recovered only by the dispatcher's privileged
read at claim time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDTnVvsgA6cUZ4xFVtPZRy
…n description (#8118)
The internal:true marking on sys_http_delivery.headers_json added a field
description; an object-definition change regenerates the package's i18n
bundles, and the four service-messaging bundles were not regenerated with
it (check:i18n DRIFTED (4)). Merge-mode regeneration — the new help key
arrives filled with source text in all four locales, no existing
translation overwritten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDTnVvsgA6cUZ4xFVtPZRy
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

2 participants

@os-zhuang@claude