Skip to content

fix(plugin-hono-server)!: the CORS default admits no cross-origin caller, and '*' with credentials is refused at boot - #24806

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-24738-cors-default-no-foreign-credentialed-origin
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-24738-cors-default-no-foreign-credentialed-origin

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24738
Clause-②: no (narrowing)

What changes

The CORS middleware that HonoServerPlugin (the server os serve, os dev and os start mount) installs by default now admits only the origins it is told about. The governing text is docs/HARDENING.md § CORS: "Do not use origin: '*' with credentials: true", and the go-live checklist's "CORS origin list is explicit, not *".

  • Old default. With no origin list configured (no cors.origins option, no OS_CORS_ORIGIN), every origin was admitted for credentialed cross-origin requests. A configured '*' with credentials on was accepted and answered the same way.
  • New default. With no list, no cross-origin caller is admitted. A loopback origin (localhost, 127.0.0.1, [::1], any port) is admitted on a development boot only: NODE_ENV exactly development, read through isDevelopmentBoot from @objectstack/core, the same reading the auth secret's development fallback uses.
  • '*' with credentials is refused in init(). kernel.bootstrap() rethrows an init() throw, so the boot fails, and the message names both remedies (OS_CORS_ORIGIN, OS_CORS_CREDENTIALS=false). This is the precedent of @objectstack/plugin-dev's production guard (assertNotProduction, ADR-0115 D6), which refuses the same way from init(). Triage leaned to a loud refusal, and the transport has no non-credentialed fallback that would keep the operator's intent.

Docs now state the default as it is: docs/HARDENING.md § CORS only, the CORS rows of environment-variables.mdx, one CORS step in self-hosting.mdx and production-readiness.mdx, and the plugin README. Two doc lines called OS_TRUSTED_ORIGINS a CORS list. That was never what the transport read, and under the new default it would send an operator to the wrong variable, so both lines are corrected.

Contract

Every input whose answer changes. "Admitted" means the origin is echoed in Access-Control-Allow-Origin.

Input Before After
No list, credentials on (the default), not a development boot every origin admitted, with credentials no cross-origin caller admitted
No list, credentials on, development boot every origin admitted, with credentials loopback origins only, with credentials
No list, OS_CORS_CREDENTIALS=false, not a development boot every origin admitted, without credentials no cross-origin caller admitted
No list, OS_CORS_CREDENTIALS=false, development boot every origin admitted, without credentials loopback origins only, without credentials
A list holding * (option or OS_CORS_ORIGIN, alone or among other entries), credentials on booted; every origin admitted, with credentials init() throws; the boot fails

Unchanged:

  • A list without *, exact origins or patterns, is admitted as before, with or without credentials. It still admits loopback origins alongside it: that is the pattern matcher's existing convenience, and this PR does not touch it.
  • * with credentials off still boots and admits any origin without credentials.
  • cors: false, cors.enabled: false and OS_CORS_ENABLED=false still mount nothing.
  • Allowed and exposed headers, methods and max-age are unchanged. Preflights are still answered 204, admitted or not.
  • Same-origin requests are outside CORS, so the Console under /_console and every page the server itself serves are unaffected.
  • No export, option, type or spec key moves. HonoCorsOptions gains TSDoc only.

Mechanism (measured)

  • The before-state was measured on main. The new pin file ran against main's source (commit b1bb0d25c: 795ff708d plus the test file only) and gave Tests 8 failed | 4 passed (12). The eight failures were a foreign origin echoed back with credentials on, a loopback origin echoed outside development, and init() resolving for '*' with credentials. The four that passed are the preservation pins.
  • No serve.ts seam was needed. The host's own origin needs no CORS admission, because a browser applies CORS to cross-origin requests only. The configured list is the one the transport already reads (cors.origins, else OS_CORS_ORIGIN). The transport reads development-ness through isDevelopmentBoot, and serve sets NODE_ENV=development under --dev before any plugin initialises. No second copy of auth's trusted-origin rules was added.
  • Consumers in this repo. Three tests leaned on the old default, and each now names its configuration (tests only; no example and no shipped code depended on it):
    • packages/runtime/src/http-metrics-inbound-coverage.hono.integration.test.ts configured '*' with credentials, which the boot now refuses. Measured before its fix: Plugin init failed: com.objectstack.server.hono and 1 failed of 27. It now names the origin its preflight carries.
    • packages/qa/http-conformance/src/hono-approvals-act.conformance.test.ts and hono-webhooks-redeliver.conformance.test.ts are door-parity pins, and they compared the two doors' CORS headers by accident. Both doors now run with cors: false, as the suite's other parity pins already do.
    • Every other test that boots HonoServerPlugin with CORS on drives it from a non-browser client (Node's fetch or Hono's app.request), and neither enforces CORS. The only assertions on Access-Control-Allow-Origin in this repo are @objectstack/hono's own, for its own default (see Acceptance notes). examples/embed-objectql mounts no HTTP server. The showcase e2e runs same-origin under --dev. objectui's console reaches the API same-origin: through its Vite /api proxy in dev, and with VITE_SERVER_URL empty in both .env files (read-only, ../objectui at cd4623d06).

Tests

All runs below are on f7cb52d15, the head this PR opens with, unless a line names another commit.

  • pnpm --filter @objectstack/plugin-hono-server exec vitest run --maxWorkers=2 src/cors-default-admission.test.ts: 12/12 passed. On main's source the same file gave 8 failed and 4 passed, as above. The pins, by what they assert:
    1. With no origin list, a foreign origin and (outside development) a loopback origin get no Access-Control-Allow-Origin, on a request and on a preflight, and the request itself is still answered.
    2. A configured origin, by option or by OS_CORS_ORIGIN, is admitted with credentials, and an unlisted one is not.
    3. The default admits loopback origins when NODE_ENV is development, not when it is unset or test.
    4. '*' with credentials is refused in init() from every source, and the kernel's bootstrap rethrows it. '*' without credentials boots.
  • pnpm --filter @objectstack/plugin-hono-server test: 35 files, 434 tests passed.
  • pnpm --filter @objectstack/plugin-hono-server typecheck: exit 0. The test layer compiles, and tsc -p tsconfig.test.json --listFiles lists the new file.
  • pnpm --filter @objectstack/http-conformance exec vitest run --maxWorkers=2: the whole suite, 18 files and 228 tests, passed.
  • pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 on http-metrics-inbound-coverage.hono.integration.test.ts and dispatcher-plugin.rate-limit.integration.test.ts: 2 files, 35 tests passed.
  • Consumers of the default through the built dist/, all green. They were measured on the tree committed as 8c038d76d, before the last merge of main, which touched none of these paths:
    • the twelve @objectstack/runtime test files that boot HonoServerPlugin with CORS on: 123 tests;
    • the six @objectstack/client files that do: 30 tests.

Gates

  • node scripts/pm/dispatch-gates.mjs --commands derived 98 families for this diff at f7cb52d15. All 98 ran, each exit 0. --ran reconciliation: 98 derived, 98 run, 0 NOT-MEASURED, 0 UNRUN. Among them:
    • check:adr-0087-registration --base origin/main: one declared-breaking changeset, not-required (no-migration-prescription);
    • check:changeset-no-major, check:doc-anchors, check:doc-authoring, check:doc-security-posture, check:nul-bytes, check:published-files;
    • check:dual-build-cjs-loads, check:type-check-coverage and check:type-check-debt.
  • pnpm lint on f7cb52d15: exit 0.
  • CI check-runs: pending at the time of writing.

Changeset

.changeset/24738-cors-default-no-cross-origin-caller.md bumps @objectstack/plugin-hono-server as minor (the package is in the fixed group, pre-release next). It is marked BREAKING: an accept-set narrowing shipped as minor under the launch-window convention, the same form the narrowing changesets already on main use (for example 24341-extend-default-row-scope-refused.md). It carries the Clause-② line, the ADR-0087 disposition and what an operator does. check-changeset-no-major forbids major. Whether this enters 18.0.0 is the maintainer's call (triage), and this PR does not decide it.

Acceptance notes


Generated by Claude Code

…ler; '*' with credentials is refused at init

With no origin list configured (no cors.origins, no OS_CORS_ORIGIN), the
transport's CORS answer now admits no cross-origin caller; a loopback origin
is admitted on a development boot only. A list holding '*' while credentials
are on is refused at init, so the boot fails naming both remedies.

Claude-Session: https://claude.ai/code/session_01LmjU2x1faLZ39DVTFyVca6
Co-authored-by: Claude <noreply@anthropic.com>
…n operator sets

HARDENING.md § CORS, the deployment pages and the plugin README now state
what the CORS answer admits with and without an origin list, that '*' with
credentials is refused at boot, and that OS_TRUSTED_ORIGINS is auth's list,
not CORS's. Changeset for @objectstack/plugin-hono-server.

Claude-Session: https://claude.ai/code/session_01LmjU2x1faLZ39DVTFyVca6
Co-authored-by: Claude <noreply@anthropic.com>
…guration

The runtime metrics pin configured '*' with credentials, which the transport
now refuses at boot; it names the origin its preflight carries. Two
http-conformance parity pins compared the doors' CORS headers by accident;
both doors now run with CORS off, as the suite's other parity pins do.

Claude-Session: https://claude.ai/code/session_01LmjU2x1faLZ39DVTFyVca6
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-hono-server, touching 3 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/plugins/plugin-hono-server/README.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/permissions/authentication.mdx (via HonoServerPlugin (symbol, a top-level class))
  • content/docs/protocol/kernel/index.mdx (via HonoServerPlugin (symbol, a top-level class))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via HonoServerPlugin (symbol, a top-level class))

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.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/plugins/plugin-hono-server/README.md) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 99e4eaa227e0375b59c7bbdf6e36ad7f026bb22d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 31d0919f6748707c7a2ff6940c5f2a1d79c64d5a — the merge of head dbdd7fe6a89302192e82d4b484e0d5a1e65a9db6 into base 99e4eaa227e0375b59c7bbdf6e36ad7f026bb22d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 31d0919f6748707c7a2ff6940c5f2a1d79c64d5a && git checkout 31d0919f6748707c7a2ff6940c5f2a1d79c64d5a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 99e4eaa227e0375b59c7bbdf6e36ad7f026bb22d dbdd7fe6a89302192e82d4b484e0d5a1e65a9db6 && git checkout -B drift-repro 99e4eaa227e0375b59c7bbdf6e36ad7f026bb22d && git merge --no-ff dbdd7fe6a89302192e82d4b484e0d5a1e65a9db6

node scripts/docs-audit/affected-docs.mjs --json 99e4eaa227e0375b59c7bbdf6e36ad7f026bb22d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 99e4eaa227e0375b59c7bbdf6e36ad7f026bb22d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f7cb52d1524b80e77b672c81fbb5d6f8add100b3
Local-runs: none
Scope: packages/plugins/plugin-hono-server/src/hono-plugin.ts, packages/plugins/plugin-hono-server/src/adapter.ts, packages/plugins/plugin-hono-server/src/cors-default-admission.test.ts, packages/qa/http-conformance/src/hono-approvals-act.conformance.test.ts, packages/qa/http-conformance/src/hono-webhooks-redeliver.conformance.test.ts, packages/runtime/src/http-metrics-inbound-coverage.hono.integration.test.ts + packages/plugins/plugin-hono-server/src/pattern-matcher.ts, packages/plugins/plugin-hono-server/src/index.ts, packages/plugins/plugin-hono-server/package.json, packages/core/src/stack-auth.ts, packages/core/src/index.ts, packages/core/src/kernel.ts, packages/core/src/kernel-base.ts, packages/core/src/lite-kernel.ts, packages/plugins/plugin-dev/src/dev-plugin.ts, packages/cli/src/commands/serve.ts, packages/cli/src/commands/dev.ts, packages/types/src/env.ts, packages/adapters/hono/src/index.ts, content/docs/protocol/kernel/http-protocol.mdx, content/docs/deployment/cli.mdx, scripts/check-changeset-no-major.mjs, scripts/check-adr-0087-registration.mjs, .changeset/config.json, .changeset/pre.json, .changeset/24341-extend-default-row-scope-refused.md; the six prose faces (.changeset/24738-cors-default-no-cross-origin-caller.md, docs/HARDENING.md, content/docs/deployment/environment-variables.mdx, content/docs/deployment/self-hosting.mdx, content/docs/deployment/production-readiness.mdx, packages/plugins/plugin-hono-server/README.md) read sentence by sentence against the code.

Net diff read as merge-base 46bb5e3e5 to the head (12 files, +391/-50). main has moved one commit past that merge-base and touches none of the paths above.

① Derived judgments

Disclosure (security card, ruling on #9387, comment 5323310017).

  • The PR's artifacts are its title, body, the changeset, the five doc faces, the code comments in hono-plugin.ts and adapter.ts, and the seven commit messages.
  • They state three things and nothing further: the defect exists (with no list, every origin was admitted for credentialed cross-origin requests, and * with credentials was accepted); where it lives (the CORS middleware HonoServerPlugin.init() mounts); and the governing text (docs/HARDENING.md § CORS).
  • They give no request sequence against a shipped endpoint, no description of what a foreign page could obtain, and no unauthenticated-surface detail.
  • The new pin file drives a probe route of its own through app.request and reads one header. It is the fix's evidence, as the claim comment foresaw, not a reproduction against a deployment.
  • The PR keeps to the ruling. This record keeps to it as well.

Check-runs on the head (latest run per name, 40 names).

  • 32 are completed/success and 5 are completed/skipped (opt-in and live-DB gates).
  • 3 were still in_progress at render time: Test Core (2/6), Test Core (3/6) and Test Core (4/6). They are NOT MEASURED, never a pass.
  • Check Changeset, Governed Surface Queue Guard, Check PR Size, every Lint · and Type Check · job, Build Core, Build Docs and Test Core (1/6, 5/6, 6/6) concluded success.
  • The judgments below are on the diff. The landing's own all-green check stands apart from this verdict and waits on those three shards.

The accept-set changes the diff implies: the PR body's ## Contract table, row by row, against the code. "Admitted" means the request origin is echoed in Access-Control-Allow-Origin. init() is the arrow property at hono-plugin.ts:280, and the CORS block (:370-460) sits inside it.

  1. No list, credentials on, not a development boot: every origin → no cross-origin caller. RIGHT.
    • configuredOrigin stays undefined, because the else { configuredOrigin = '*' } arm is deleted.
    • The origin handler is developmentBoot && isLocalhostOrigin(o) ? o : null, and developmentBoot = isDevelopmentBoot(undefined) = NODE_ENV === 'development' (core/src/stack-auth.ts:135). So it answers null for every origin.
    • Pinned (describe 1).
  2. No list, credentials on, development boot: loopback only, with credentials. RIGHT.
    • It is the same handler with developmentBoot true, and credentials: true still reaches hono/cors.
    • isLocalhostOrigin admits localhost, 127.0.0.1 and [::1], over http or https, on any port: exactly the docs' list.
    • Pinned (describe 3).
  3. No list, OS_CORS_CREDENTIALS=false, either boot (rows 3 and 4): no cross-origin caller, or loopback only, without credentials. RIGHT.
    • The default handler never reads credentials, so these rows differ from rows 1 and 2 only in the credentials header.
    • Not pinned in the credentials-off variant. The code path is the same expression.
  4. A list holding * (option or OS_CORS_ORIGIN, alone or among other entries), credentials on: init() throws. RIGHT.
    • The check is credentials && normalizeOriginPatterns(configuredOrigin).includes('*'). normalizeOriginPatterns trims every entry and splits a comma string, so ['*'], ' * ', 'https://app.example.com,*' and the option and env spellings all reach the throw.
    • The message names OS_CORS_ORIGIN and OS_CORS_CREDENTIALS and carries no tracker number.
    • Both kernels rethrow an init() throw unwrapped. LiteKernel.bootstrap() has no try around Phase 1, and ObjectKernelBase.runPluginInit rethrows after logging (kernel-base.ts:204-206). ObjectKernel.bootstrap() catches, rolls back nothing (nothing started yet) and does throw error (kernel.ts:632-668).
    • In serve.ts the construction sits inside a try that wraps only kernel.use(), which is registration only (:3768-3778). The throw surfaces at bootstrap and reaches the command's outer catch, which exits 1.
    • This is the same route as @objectstack/plugin-dev's assertNotProduction (dev-plugin.ts:127, called from init() at :500), as claimed.
    • The "Before" column (booted and admitted every origin) is RIGHT for every spelling. The old '*' && credentials arm reflected the origin, and an array or list containing * went through createOriginMatcher, whose matchOriginPattern(o, '*') is true for all.
    • Pinned (describe 4, including the kernel-bootstrap case).

The "Unchanged" list.

  • A list without *, exact or patterns, admitted as before, with loopback alongside it: RIGHT.
    • The old code reached createOriginMatcher on both arms (one wrapped in a lambda), and the new code calls it once.
    • pattern-matcher.ts is untouched, and its matchOriginPattern still returns true for any loopback origin ahead of the pattern.
    • An EMPTY list (cors.origins: [], or OS_CORS_ORIGIN of whitespace) makes the matcher admit nothing, loopback included. That is unchanged from before and narrower than the sentence reads; no accept set moves.
  • * with credentials off boots and admits any origin without credentials: RIGHT. It is not refused, and createOriginMatcher('*') echoes the request origin as it did before. Pinned.
  • cors: false, cors.enabled: false and OS_CORS_ENABLED=false mount nothing: RIGHT. That code is untouched.
  • Headers, methods and max-age unchanged, and preflights still answered: RIGHT. The cors() options other than origin are untouched, and hono/cors answers OPTIONS itself whether or not it sets an allow-origin. Pin 1 reads the preflight's missing allow-origin.
  • Same-origin requests are outside CORS, and the Console is unaffected: RIGHT. A browser applies the allow-origin check to cross-origin fetches only. A same-origin non-GET request does carry Origin, gets no allow-origin header under the new default, and is still readable. So nothing here needs the host's own origin, as the body says.
  • No export, option, type or spec key moves, and HonoCorsOptions gains TSDoc only: RIGHT.
    • adapter.ts changes TSDoc only.
    • hono-plugin.ts drops its own import of hasWildcardPattern, but index.ts:6 still has export * from './pattern-matcher', and @objectstack/hono (adapters/hono/src/index.ts:45,651) still consumes it. HonoPluginOptions.cors keeps its type.
    • isDevelopmentBoot is a new value import from @objectstack/core, which is already a runtime dependency (package.json dependencies) and is already value-imported by adapter.ts. No new edge.
    • Type Check · workspace concluded success.

Public-surface change outside the table. The diff adds a new boot refusal (the thrown message) and changes a debug log line (origin: admits, which is a label, not the list). Both are RIGHT and documented. Nothing else on the published surface moves.

One precision note on the development-boot claim. Its direction is narrow, so it is not an accept-set defect.

  • The transport reads development-ness as NODE_ENV === 'development' only (isDevelopmentBoot(undefined)).
  • serve reads isDevelopmentBoot(flags.dev) for itself, and sets NODE_ENV=development under --dev only when NODE_ENV is unset (serve.ts:2176-2177). os dev leaves it to serve (dev.ts:569-576).
  • The prose faces say NODE_ENV=development, "which os dev and os serve --dev set". That is true on the documented path. On a boot that exports another NODE_ENV and passes --dev, serve's own development conveniences apply, but the CORS default admits no loopback origin.
  • The rule itself ("NODE_ENV exactly development") is stated exactly in every face, so the statement of shipped behaviour holds; the parenthetical is one edge short.
  • Non-blocking. The dispatching seat may name it in ACCEPT.

Prose faces, each against the shipped behaviour.

  • environment-variables.mdx: TRUE. Its CORS rows say: the default is —; unset means no cross-origin caller, with loopback excepted on a development boot; * is refused unless credentials are off; and OS_CORS_CREDENTIALS must be false for *. Its port note says OS_TRUSTED_ORIGINS is auth CSRF (serve.ts:4182).
  • self-hosting.mdx: TRUE. No entry is needed same-origin; a foreign front-end needs OS_CORS_ORIGIN; and never * while credentials are on.
  • production-readiness.mdx: TRUE. The checklist item matches the code.
  • README.md: TRUE. It states the default, the refusal at init, the OS_CORS_* spellings, and that legacy CORS_* names are still read (readEnvWithDeprecation, silent).
  • docs/HARDENING.md § CORS: TRUE.
    • The three-row table is TRUE, and "loopback admitted alongside any list" is TRUE for a non-empty list.
    • The createHonoApp example is correctly worded as "name the origins there as well".
    • The refusal is scoped to HonoServerPlugin. The page does not claim the adapter refuses, which it does not (see ③).
  • The changeset's Before / Now / Unchanged and "What an operator does": TRUE, with the same --dev parenthetical as above.

Consumer tests in the diff. All three changes are RIGHT and minimal.

  • http-metrics-inbound-coverage.hono.integration.test.ts names the origin its preflight carries (https://app.test) instead of '*' with credentials, which the boot now refuses. The pin's premise still holds: the transport's CORS built-in answers the preflight before the metrics seam.
  • The two http-conformance door-parity pins now boot both doors with cors: false, as the suite's other parity pins do. CORS headers were never what they compare.

② Semver level

  • The changeset matches what the diff publishes. .changeset/24738-cors-default-no-cross-origin-caller.md carries @objectstack/plugin-hono-server: minor, a BREAKING banner, Clause-②: no (narrowing), and the ADR-0087 marker not-required (no-migration-prescription). The PR body's second line carries the same Clause-②: no (narrowing). Only packages/plugins/plugin-hono-server moves published source. The other six code paths are tests (packages/qa/http-conformance and a packages/runtime test file), which publish nothing. One changeset is right, and skip-changeset would be wrong.
  • Level: RIGHT.
    • An accept-set narrowing of a shipped default is breaking.
    • scripts/check-changeset-no-major.mjs is still armed: .changeset/pre.json is mode: exit with tag next, the packages are at 18.0.0-next.5, and the guard's end condition (its own retirement at GA) has not happened. So major is refused, and the launch-window form is minor plus a BREAKING banner plus an ADR-0087 disposition.
    • That is the form of every narrowing changeset on main: 24341-extend-default-row-scope-refused.md and 18 siblings, all minor. patch would understate it, since the fix( type never lowers the level the act requires.
    • Check Changeset concluded success on the head.
  • ADR-0087 disposition: RIGHT. No authorable key, spelling, export, type or stored shape moves, so objectstack migrate meta has nothing to rewrite. The body's "What an operator does" is a deployment-variable step, not a consumer-code migration prescription, so the gate's statement-against-statement refusal does not fire. The ruling that 18.0.0 ships this (card comments 6284880389, 6285370999) is consistent with a minor under the window.
  • Clause-② line: no (narrowing) is RIGHT. No new key lands on any published payload, and one accept set narrows. It is what owes this CONTRACT_REVIEW_TIER record on the narrowing face.

③ Boundary flags

The dev's flags (report 6285567675, deviations and acceptance notes) and the one open_questions entry:

  1. Three files outside the claim's file surface (the runtime metrics pin and two http-conformance parity pins). Answered: they are the in-repo consumers the narrowing broke, read above, and each change minimally names its configuration. Accepted.
  2. Attribution trailer form, gate-runner timing, two merges of main. Not contract matters. This record reads the merge-base diff, and main has since moved one commit on disjoint paths. No action.
  3. Sibling default in @objectstack/hono's createHonoApp (packages/adapters/hono/src/index.ts:606-680, read). It keeps the old default: no list resolves to *, and * with credentials reflects the request origin. It is outside this card's file surface and the claim's hold-offs, and HARDENING.md now tells a host composing it to name its origins. Escalated: the seat files it as a security card in the same family (the dev's dedupe words), under the same disclosure ruling. It does not block this PR: the card's reader is the plugin owner, and the adapter is a separate published package with its own option shape.
  4. Loopback admitted alongside any configured list, in every mode (matchOriginPattern, shared with cloud's preflight short-circuit). Answered: the card scopes "localhost in dev only" to the default, the matcher is untouched, and HARDENING.md states the behaviour. Whether that convenience should also become development-only is the maintainer's call. It is a note, not a card from this record.
  5. cli.mdx:523 still calls OS_TRUSTED_ORIGINS "(CORS)". It is held off for PR feat(cli,runtime)!: the upgrade ceremony opens — v17 and 18.0 databases take it, and a server serves only a database it created or one the ceremony certified (18.x steps 5 and 6) #24681. Answered: whichever lands second corrects the line.
  6. CorsConfigSchema in spec (origins default '*', credentials default false). The transport does not read it, and it is the spec lane's, held off. Answered: the future server.cors admission should carry this PR's default. Noted for that card.
  7. A degenerate pattern such as https://* is not refused. Answered: it is operator-configured and outside the card's default, and it is reported in the PR. No action here.
  8. http-protocol.mdx "CORS Headers" example. Answered: it shows an admitted, configured origin. One clarifying line is a docs follow-up; the page is not false today.
  9. Cloud composition NOT MEASURED. Answered as a landing-adjacent flag. createOriginMatcher is unchanged, so cloud's preflight short-circuit is unaffected. Whether any cloud deployment serves a cross-origin front-end with no OS_CORS_ORIGIN cannot be measured from this repo. The changeset states the operator step, and the seat should name it to the maintainer when cloud takes 18.0.0.
  10. --dev with an exported NODE_ENV (this record's own finding, ① above). It fails narrow, and at most it needs a one-word docs precision.

Open question: should OS_TRUSTED_ORIGINS also feed CORS? It is the maintainer's to answer. The shipped answer (A: keep the lists separate) is safe to land pending that ruling.

  • A is the narrowest of the three and admits nothing the card did not ask to admit.
  • B (add the explicit OS_TRUSTED_ORIGINS entries) is a pure widening on top of A. It can be adopted later as a Clause-②: yes-shaped follow-up without a second breaking change.
  • C (serve's full resolution, with the preview and root-domain wildcards) would import auth's CSRF wildcards into credentialed CORS. It is the widest of the three, and the dev's own analysis names it the worst.
  • Shipping B or C first and narrowing later would be the unsafe order.
  • The one cost of A: a cross-origin front-end that set only OS_TRUSTED_ORIGINS loses CORS admission on upgrade, at boot and in the browser. That cost is stated in the changeset, HARDENING.md and self-hosting.mdx.
  • The landing must not wait. The ruling is owed on the card before any B or C widening is dispatched.

Landing. The 18.0.0 ruling is recorded (6284880389, 6285370999), and this record is the narrowing face's review of record. Still owed before the queue: every check green (three Test Core shards were in progress at render), and the lane's own main-red hold (#24780), as the card states.

Implemented-by: claude/issue-24738-cors-default-no-foreign-credentialed-origin
Reviewed-by: session_01LmjU2x1faLZ39DVTFyVca6

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT

domain:cli seat 2 (#22648) · os-justin · session session_01LmjU2x1faLZ39DVTFyVca6 · 2026-10-20T07:36Z · head f7cb52d1524b80e77b672c81fbb5d6f8add100b3 · claim 6284118564 → report 6285567675 → review of record 6285870503 (CONTRACT_REVIEW_TIER, PASS, adopted after its cited lines were re-read at source: serve.ts:2176-2177, kernel-base.ts:204-206, stack-auth.ts isDevelopmentBoot, .changeset/pre.json, pattern-matcher.ts:49).

Check Reading
Shape Draft, base main. First line Fixes #24738; Clause-②: no (narrowing) line-initial. Assignee os-justin = the card's. One changeset, @objectstack/plugin-hono-server: minor with a BREAKING banner and an ADR-0087 disposition, as the launch-window convention requires (the review's ②).
Surface 12 files, +391 / −50. Nine are inside the claim. Three are tests the new default broke (a runtime metrics pin, two http-conformance parity pins), which the dispatch allowed fixing here. Nothing is skipped or quarantined. packages/spec/** and every held-off file are untouched. check-governed-merges.mjs --pr 24806: NOT governed, 441 changed lines.
Disclosure The PR's title, body, changeset, docs, code comments and commits state the default, its location and the governing text only, under #9387 5323310017. Read by this seat and by the review (①).
Mechanism With no list, the handler admits no origin, or loopback origins only on NODE_ENV=development. * with credentials throws in init(), and both kernels rethrow it, so the boot fails. A configured list goes through the unchanged matcher. Every row of the PR's ## Contract table was checked against the code by the review.
Evidence 8 of the 12 new pins were red on main's own source and are 12/12 after. The package suite is 434/434, http-conformance 228/228, and the runtime and client consumers through the built dist 123 and 30 tests.
Gates Local: 98/98 derived families exit 0, 0 UNRUN; pnpm lint exit 0. Check-runs on this head (40, latest per name), read at this act: 32 success, 5 skipped, 3 in_progress (Test Core 2–4/6). Landing waits on all of them.

Accepted deviations:

  • Three test files outside the claim's surface, as above.
  • The commits use AGENTS.md's model-free trailers.
  • main was merged twice before the PR opened, on disjoint paths.

Out-of-scope dispositions (one line each):

Open question (OS_TRUSTED_ORIGINS into CORS: A as shipped, B, or C) → put to the maintainer in this session's chat, with A recommended. The review judges A safe to land pending the ruling. B would be a later widening, and C is the widest. The ruling is recorded on #24738 when given.

Landing: through the merge queue by this seat once:

  1. every check on this head reads success (or an expected skip);
  2. the lane's main-red main red on the full battery since 04:01Z: Dogfood 1/3 (audit-log-audit-capability, SystemWriteOrganizationRequiredError on sys_user_permission_set after the walled lift window) and Console Pin Gate (pinned objectui reads retired filterableFields) #24780 row 1 has landed (PR fix(dogfood): the audit-capability fixture's grant writes carry their holder's organization #24805 is in the queue);
  3. the maintainer has said go on the open question.

Generated by Claude Code

OS_TRUSTED_ORIGINS stays auth's own list and admits nothing to CORS. Auth
also trusts every origin listed in OS_CORS_ORIGIN, so that one variable
covers a cross-origin front-end that signs in; a list given through the
plugin's cors.origins option is not seen by auth, so such a front-end is
then also named in OS_TRUSTED_ORIGINS. Changeset and HARDENING.md § CORS.

Claude-Session: https://claude.ai/code/session_01LmjU2x1faLZ39DVTFyVca6
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: dbdd7fe6a89302192e82d4b484e0d5a1e65a9db6
Local-runs: none
Scope: packages/plugins/plugin-hono-server/src/hono-plugin.ts, packages/plugins/plugin-hono-server/src/adapter.ts, packages/plugins/plugin-hono-server/src/cors-default-admission.test.ts, packages/qa/http-conformance/src/hono-approvals-act.conformance.test.ts, packages/qa/http-conformance/src/hono-webhooks-redeliver.conformance.test.ts, packages/runtime/src/http-metrics-inbound-coverage.hono.integration.test.ts (all six blob-equal between f7cb52d15 and dbdd7fe6a; the previous record's judgments carry forward per path, ① below) + .changeset/24738-cors-default-no-cross-origin-caller.md and docs/HARDENING.md (the two faces the delta moves, read afresh sentence by sentence against the code), content/docs/deployment/environment-variables.mdx, content/docs/deployment/self-hosting.mdx, content/docs/deployment/production-readiness.mdx, packages/plugins/plugin-hono-server/README.md (blob-equal, carried forward, and re-grepped for the ruling's wording), packages/plugins/plugin-auth/src/auth-manager.ts (the trustedOrigins block, :3005-3075 on origin/main), packages/cli/src/commands/serve.ts (:3768 and :4133-4262 on origin/main), packages/plugins/plugin-hono-server/src/pattern-matcher.ts, packages/adapters/hono/src/index.ts (CORS env reads), content/docs/deployment/cli.mdx (:523-525), scripts/check-adr-0087-registration.mjs, scripts/check-changeset-no-major.mjs, .changeset/pre.json, .github/workflows/pr-automation.yml (which job carries the changeset gates).

Net diff read as merge-base 46bb5e3e5 to the head: 12 files, +394/-50. The delta f7cb52d15..dbdd7fe6a is one commit, docs(cors): which list auth reads, said precisely, 2 files, +7/-4, prose only. origin/main (5cb6e6e0a) is 27 commits past the merge-base; it moves none of the PR's 12 paths, and git merge-tree reports a clean merge.

① Derived judgments

Disclosure (security card, ruling on #9387, comment 5323310017). The one new commit changes two prose sentences, and its message restates them. It adds no request sequence, no description of what a foreign page could obtain, and no unauthenticated-surface detail. The PR keeps to the ruling. This record keeps to it as well: the conditions named below are stated as configurations and file locations only.

Check-runs on the head (latest run per name, 41 names, read 2026-10-20T09:58Z).

  • 30 are completed/success and 6 are completed/skipped (opt-in and live-DB gates, Console Pin Gate, the dogfood matrix).
  • 5 were still in_progress at render time: Test Core (1/6), (2/6), (3/6), (4/6) and (6/6). They are NOT MEASURED, never a pass. Test Core (5/6) concluded success.
  • Check Changeset concluded success. That job (pr-automation.yml changeset-check, :976-977 and :1127) is the one that runs check-adr-0087-registration.mjs --base MERGE_BASE and check-changeset-no-major.mjs --base MERGE_BASE, so the disposition and the no-major form are gate-measured on this head.
  • Governed Surface Queue Guard, Build Core, Build Docs, every Lint · and every Type Check · job concluded success.
  • The judgments below are on the diff. The landing's own all-green check stands apart from this verdict and waits on the five shards.

Blob equality, path by path (f7cb52d15 vs dbdd7fe6a). The floor is every path of the net diff outside the six prose faces, as the previous record (6285870503) listed them.

  • src/hono-plugin.ts, c4787e056acb == c4787e056acb: unchanged. These carry forward: the accept-set table's rows 1-5, the "Unchanged" list, the public-surface note (new boot refusal, debug log label) and the --dev with an exported NODE_ENV precision note.
  • src/adapter.ts, c25de589cdda == c25de589cdda: unchanged. "TSDoc only, HonoCorsOptions keeps its type" carries forward.
  • src/cors-default-admission.test.ts, 6f9aef00d1f8 == 6f9aef00d1f8: unchanged. The pin inventory (describes 1-4, kernel-bootstrap case included) carries forward.
  • hono-approvals-act.conformance.test.ts (b24207796866 == b24207796866) and hono-webhooks-redeliver.conformance.test.ts (3d4498227c3e == 3d4498227c3e): unchanged. "Door-parity pins, both doors cors: false" carries forward.
  • http-metrics-inbound-coverage.hono.integration.test.ts, 769c148678e0 == 769c148678e0: unchanged. "Names the origin its preflight carries" carries forward.
  • Of the prose faces, environment-variables.mdx (17b00ddebf92), self-hosting.mdx (73a41ee6147d), production-readiness.mdx (1494c7ef370a) and README.md (67d6acfa8a7d) are blob-equal, and their TRUE judgments carry forward. The changeset (286d47cc7 to 96203fb6f) and docs/HARDENING.md (8b3f94b98 to 064535725) moved and are judged afresh.
  • The cited code outside the diff:
    • pattern-matcher.ts and core/src/stack-auth.ts are blob-equal to origin/main.
    • auth-manager.ts and serve.ts moved on main since the merge-base, with ADR-0069 D5/D7 work: a trustProxy option (+6 lines at serve.ts:4258), and 283 lines in auth-manager.ts with no hunk naming trustedOrigins, OS_CORS_ORIGIN or OS_TRUSTED_ORIGINS.
    • So the two sentences are judged against origin/main's text, which is what the merged tree will carry.

The two new sentences, clause by clause, against the code. The same paragraph lands twice: changeset :17 and HARDENING.md :215-220. HARDENING.md § CORS opens on "the server that os serve / os start mount (HonoServerPlugin)", which scopes the paragraph.

  1. "OS_TRUSTED_ORIGINS is auth's own list, the origins it accepts for callbacks and CSRF checks, and it admits nothing to CORS." TRUE.

    • OS_TRUSTED_ORIGINS has exactly one code reader on origin/main: serve.ts:4189. The list it starts (trustedOrigins, :4188-4245) gains the base origin, the preview wildcards, dev http://localhost:* and the OS_ROOT_DOMAIN wildcard. It goes to new AuthPlugin({ ... trustedOrigins }) at :4248-4257 and nowhere else.
    • serve constructs HonoServerPlugin with port and tls only (:3768-3771). It passes no cors option and reads none.
    • No CORS door reads the variable. hono-plugin.ts reads OS_CORS_ENABLED, OS_CORS_ORIGIN, OS_CORS_CREDENTIALS and OS_CORS_MAX_AGE (:391-408), never OS_TRUSTED_ORIGINS. @objectstack/hono's createHonoApp reads OS_CORS_ORIGIN only (adapters/hono/src/index.ts:622).
    • Condition: a boot that is not os serve, or an os serve whose stack supplies its own AuthPlugin (hasAuthPlugin, :4133, skips the block), reads OS_TRUSTED_ORIGINS nowhere, because auth-manager.ts itself never reads it. There the variable is inert for auth as well.
    • "Admits nothing to CORS" stays true in every deployment. "Auth's own list" is a statement about os serve, which is the scope the section opens with. It is one boot shape short, not false. Non-blocking.
  2. "Auth also trusts every origin listed in OS_CORS_ORIGIN, so that one variable covers a cross-origin front-end that signs in." TRUE.

    • auth-manager.ts:3007-3015 reads readEnvWithDeprecation('OS_CORS_ORIGIN', 'CORS_ORIGIN', { silent: true }). When the value is set and is not exactly *, it is split on commas and trimmed, and each entry is pushed into the better-auth trustedOrigins array.
    • That is auth-manager's own read of the environment. So it holds for any boot that constructs AuthManager, os serve or not, and for the legacy CORS_ORIGIN spelling, which hono-plugin.ts:399 reads through the same helper.
    • The SSO branch does not falsify it. When isSsoWired(), trustedOrigins becomes a per-request function (:3063-3073) that starts from the same origins array and only adds public discovery origins on top.
    • Conditions under which the sentence does not hold, by configuration:
      • OS_CORS_ORIGIN exactly *: nothing is merged (corsOrigin !== '*'). With credentials on, the transport now refuses that boot (hono-plugin.ts:422-429). So the only live case is OS_CORS_CREDENTIALS=false, where no cookie crosses and no sign-in needs trust. The sentence says "every origin listed", and * is not an origin.
      • A pattern entry (https://*.example.com) is forwarded verbatim. The transport's matcher expands * to .* (pattern-matcher.ts:58-60), so it crosses labels. serve.ts:4182-4184 states that better-auth's glob does not cross dot separators (better-auth 1.7.3 is pinned; its source is not installed in this container and was not re-read). So a multi-label subdomain that CORS admits under such a pattern may not be auth-trusted. The sentence is about listed origins, and for patterns it is one edge short. Pre-existing, unchanged by this diff, a docs precision at most.
      • The CORS middleware off (cors: false, OS_CORS_ENABLED=false): auth still trusts the list, and CORS admits nothing. "Covers" presumes the middleware is mounted, which is the default the paragraph describes.
    • "That one variable covers" is the maintainer's own wording in the ruling (6288093770: "a cross-origin front-end needs only OS_CORS_ORIGIN, and is then trusted by both"), and the code bears it out for exact origins.
  3. "A list given through the plugin's cors.origins option instead is not seen by auth, so such a front-end is then also named in OS_TRUSTED_ORIGINS." TRUE.

    • hono-plugin.ts:400-405: corsOpts.origins wins over the env var when set.
    • Auth reads this.config.trustedOrigins and the env var only. serve.ts hands AuthPlugin nothing CORS-shaped, and no path carries the option to auth.
    • The option exists only when the stack config supplies its own HonoServerPlugin (configHasHonoServer, :3758-3762), since serve's own construction passes no cors.
    • Conditions:
      • An operator who sets cors.origins and also sets OS_CORS_ORIGIN to the same list gets auth trust from the env var, while the transport ignores the env var. That is an alternative remedy, not a contradiction.
      • Under a stack that supplies its own AuthPlugin, "named in OS_TRUSTED_ORIGINS" means that plugin's trustedOrigins option (the same one-boot-shape note as clause 1).

The ruling's wording, across every face. No "belongs in both" survives anywhere under docs/, content/docs/, .changeset/ or the README at the head.

② Semver level

  • The changeset still matches what the diff publishes.
    • Its frontmatter ('@objectstack/plugin-hono-server': minor), title, Clause-②: no (narrowing) line, ADR-0087 marker (not-required (no-migration-prescription)) and Before / Now / Unchanged bullets are byte-identical between f7cb52d15 and dbdd7fe6a. The delta is the one "What an operator does" line (:17).
    • The only published-source mover is still packages/plugins/plugin-hono-server (blob-equal).
    • The PR body's second line still reads Clause-②: no (narrowing).
  • Level: RIGHT, unchanged.
    • .changeset/pre.json on origin/main is still mode: exit, tag next, and the plugin is at 18.0.0-next.5.
    • scripts/check-changeset-no-major.mjs is still present, so major is still refused, and minor plus a BREAKING banner plus a disposition is still the launch-window form.
    • Check Changeset concluded success on the head.
  • ADR-0087 disposition: holds.
    • The gate's no-migration-prescription category is still defined on origin/main (check-adr-0087-registration.mjs:147-154). It is refused only when the changeset's own body carries instructions for rewriting a consumer's code.
    • The new sentences name deployment variables (OS_CORS_ORIGIN, OS_TRUSTED_ORIGINS, the cors.origins option). They carry no FROM-to-TO mapping, no spelling change and no consumer-code rewrite. "Then also named in OS_TRUSTED_ORIGINS" is an operator setting for a configuration this diff does not change.
    • They do not read as a migration prescription, and the gate that reads them statement-against-statement concluded success.
  • Clause-②: no (narrowing) is still RIGHT. The delta adds no key to any published payload and moves no accept set.

③ Boundary flags

The dev's open question is ruled: option A (6288093770, maintainer verbatim 「同意」). Do the shipped code and the new prose agree with it?

  • Code: agrees.
    • CORS admits only cors.origins, else OS_CORS_ORIGIN (hono-plugin.ts:398-405).
    • OS_TRUSTED_ORIGINS reaches AuthPlugin only (serve.ts:4189, :4257).
    • No B or C seam was added.
    • The code is blob-identical to the head the previous record judged A-safe, and main's movement on serve.ts and auth-manager.ts is disjoint from both blocks.
  • Prose: agrees.
    • The two sentences state the ruling's three bullets: OS_TRUSTED_ORIGINS is auth-only; the trust flows one way, from OS_CORS_ORIGIN into auth (auth-manager.ts:3007-3015); and OS_CORS_ORIGIN alone suffices for a cross-origin front-end that signs in.
    • They add the cors.origins case, which the ruling did not name and the code supports (clause 3 above).
    • Every face the ruling names ("the changeset, docs/HARDENING.md and any deployment page") no longer says "belongs in both". The deployment pages never did.

Flags carried from the previous record (its ③ items 1-10). Nothing in the delta touches them, and they stand as answered there. Items 3, 4 and 7 are now filed as #24809 (ACCEPT 6285885816).

New flags from this round.

  1. A * entry among others, with credentials off, reaches auth's trusted set.
  2. Wildcard semantics differ between the two matchers. pattern-matcher.ts turns * into .*, while its own TSDoc (:6) claims "the same wildcard syntax as better-auth's trustedOrigins", and serve.ts:4182-4184 records that better-auth's glob does not cross dot separators. Pre-existing and unchanged, it affects pattern entries only, and the only prose it touches is clause 2's "every origin listed". A docs-precision follow-up at most; not a card from this record.
  3. A boot that is not os serve reads OS_TRUSTED_ORIGINS nowhere (clause 1). The faces describe os serve, which HARDENING.md § CORS opens on. Non-blocking.
  4. main has moved 27 commits past the merge-base, on paths disjoint from the diff, and git merge-tree reports a clean merge. The serial constraint with docs: HARDENING.md and production-readiness.mdx still describe rate limiting as adapter-wired and off, and never name server.security.rateLimit or trustProxy #24740 on docs/HARDENING.md still holds (that file has not moved on main). A landing matter for the lane, not a contract one.
  5. Five Test Core shards were in_progress at render. NOT MEASURED. The landing's all-green check is separate from this verdict.

Landing. The 18.0.0 ruling (6284880389, 6285370999) and the OS_TRUSTED_ORIGINS ruling (6288093770) are both recorded, and this record is the narrowing face's review of record on this head. Still owed before the queue, as the card states: every check green on dbdd7fe6a (five shards outstanding), and the lane's main-red hold #24780.

Implemented-by: claude/issue-24738-cors-default-no-foreign-credentialed-origin
Reviewed-by: session_01LmjU2x1faLZ39DVTFyVca6

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT addendum (head dbdd7fe6a)

domain:cli seat 2 (#22648) · os-justin · session session_01LmjU2x1faLZ39DVTFyVca6 · 2026-10-20T10:09Z · head dbdd7fe6a89302192e82d4b484e0d5a1e65a9db6. The ACCEPT 6285885816 on f7cb52d15 carries forward to this head.


Generated by Claude Code

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.

security: the HTTP server's default CORS answer admits every origin for credentialed requests (detail withheld under the disclosure ruling)

2 participants