You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] the runtime dispatcher's /meta item reads apply NO per-caller read gate: through a catch-all host, GET /meta/doc/:name serves a permission-set-gated doc body to a non-holder, and /meta/app/:name serves requiredPermissions-gated entries #20193
Filing gate: ① a defect with a named landing site, packages/runtime/src/domains/meta.tshandleMetadataRequest (the two-segment item branch and the /published branch). Finding class (a), with the security exception: it exposes data.
Found by the os-dev round on #20156 (PR #20190). Verified at source and filed by the domain:cli execution seat (#6024, session_01UYBdGBzWSrAMzpW8ah3GbP). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens (measured in-process by the #20156 dev)
The dev drove handleMetadataRequest with an authenticated caller who does not hold crm_admin, on the same fixtures #20156's census uses:
200, a requiredPermissions-gated nav entry included
pruned
Reach:packages/runtime/src/domains/meta-verb-fallthrough.test.ts pins that a real createHonoApp host (a ${prefix}/* catch-all → dispatch() → the domain registry → this handler) answers GET /api/v1/meta/object/account from this read path. So a host that mounts only the catch-all serves these reads through the dispatcher. That leg is an existing in-repo measurement. The dev did not re-drive the doc/app requests through a composed host.
The seat's reading at source (origin/main, 2026-09-27)
packages/runtime/src/domains/meta.ts carries no spelling of any of the per-caller gates RestServer applies: audience, filterApp, requiredPermissions, admitsBook, docReader, permissionSet all count 0. The control handleMetadataRequest counts 2 in the same file.
Its caller packages/runtime/src/http-dispatcher.ts mentions audience only in a docblock about permission-set suggestion bindings (:2145, :2149), not a read gate.
The fall-through pin's own docblock says this read path runs the ADR-0106 object mask. So objects are masked here, and docs, books and apps are not gated.
The contract it contradicts
ADR-0046 §6.7: docs content is gated server-side at the doc and book reads.
content/docs/ui/apps.mdx, the requiredPermissions row: 「The entry is never served: it is absent from the /meta body」.
AGENTS.md "Route & surface ownership": this is a second transport answering the same /meta read with different rules.
#20156 is "an alternate door skips the plain read's gate". Here the transport has no gate at all, its plain read included. Porting the gates into packages/runtime would be a second audience resolver, which ruling 5793362670 item 1 forbids (「⛔ no second resolver」). The #20156 dev left packages/runtime untouched and raised the remedy as a decision.
The remedy options the dev set out (⛔ not chosen here)
B: retire the dispatcher's /meta item reads so RestServer is the one owner (AGENTS.md "Route & surface ownership", rule 1), and mount REST in the catch-all-only hosts.
(C, porting the gates into packages/runtime as a second resolver, is ruled out by 5793362670.)
The dev recommends B if the catch-all-only hosts can mount REST, otherwise A. That depends on a measurement this card owes first: which shipped hosts are catch-all-only.
Who acts
The domain:cli execution seat (packages/runtime and packages/rest are both this lane's), after triage grades the card and, if the options really diverge, after the placement is decided. It builds on PR #20190 (#20156), which introduces the shared gate A would extract, so it waits for that PR to merge.
Dedupe
MCP issue search in this repository, open and closed, run 2026-09-27:
Filing gate: ① a defect with a named landing site,
packages/runtime/src/domains/meta.tshandleMetadataRequest(the two-segment item branch and the/publishedbranch). Finding class (a), with the security exception: it exposes data.Found by the
os-devround on #20156 (PR #20190). Verified at source and filed by thedomain:cliexecution seat (#6024,session_01UYBdGBzWSrAMzpW8ah3GbP). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens (measured in-process by the #20156 dev)
The dev drove
handleMetadataRequestwith an authenticated caller who does not holdcrm_admin, on the same fixtures #20156's census uses:RestServerread answers the same callerGET /meta/doc/crm_admin_runbookPERMISSION_DENIEDGET /meta/doc/crm_admin_runbook/publishedGET /meta/book/admin_guide(a set-gated book)GET /meta/app/crmand/meta/app/crm/publishedrequiredPermissions-gated nav entry includedReach:
packages/runtime/src/domains/meta-verb-fallthrough.test.tspins that a realcreateHonoApphost (a${prefix}/*catch-all →dispatch()→ the domain registry → this handler) answersGET /api/v1/meta/object/accountfrom this read path. So a host that mounts only the catch-all serves these reads through the dispatcher. That leg is an existing in-repo measurement. The dev did not re-drive the doc/app requests through a composed host.The seat's reading at source (
origin/main, 2026-09-27)packages/runtime/src/domains/meta.tscarries no spelling of any of the per-caller gatesRestServerapplies:audience,filterApp,requiredPermissions,admitsBook,docReader,permissionSetall count 0. The controlhandleMetadataRequestcounts 2 in the same file.packages/runtime/src/http-dispatcher.tsmentionsaudienceonly in a docblock about permission-set suggestion bindings (:2145, :2149), not a read gate.The contract it contradicts
content/docs/ui/apps.mdx, therequiredPermissionsrow: 「The entry is never served: it is absent from the/metabody」./metaread with different rules.Why it is not #20156's close
#20156 is "an alternate door skips the plain read's gate". Here the transport has no gate at all, its plain read included. Porting the gates into
packages/runtimewould be a second audience resolver, which ruling5793362670item 1 forbids (「⛔ no second resolver」). The #20156 dev leftpackages/runtimeuntouched and raised the remedy as a decision.The remedy options the dev set out (⛔ not chosen here)
RestServer's onemetaItemReadGate, docs-audience resolution included) to one transport-neutral seam thatRestServerandhandleMetadataRequestboth call./metaitem reads soRestServeris the one owner (AGENTS.md "Route & surface ownership", rule 1), and mount REST in the catch-all-only hosts.packages/runtimeas a second resolver, is ruled out by5793362670.)The dev recommends B if the catch-all-only hosts can mount REST, otherwise A. That depends on a measurement this card owes first: which shipped hosts are catch-all-only.
Who acts
The
domain:cliexecution seat (packages/runtimeandpackages/restare both this lane's), after triage grades the card and, if the options really diverge, after the placement is decided. It builds on PR #20190 (#20156), which introduces the shared gate A would extract, so it waits for that PR to merge.Dedupe
MCP issue search in this repository, open and closed, run 2026-09-27:
handleMetadataRequest runtime dispatcher meta audience gate docs permission set→ 3 hits, all closed: [finding] the metadata TYPE registry moves the same metadataForms bundles and is still invisible to dispatch-gates path derivation #9144 (the dispatch-gates path derivation), meta apps by-name route: answer an explicit permission-denied envelope for an unauthorized session, instead of being indistinguishable from "not published" (backend half of objectui#4252) #8013 (the RestServer by-name app envelope) and A bearer-authenticated admin metadata write is stampedactor: 'system'—req.user/req.userIdare unset on the/metaPUT path #7749 (the/metaPUT actor stamp). None is this defect.Dedupe words:
dispatcher meta audience gate·createHonoApp meta doc permissionSet leak·handleMetadataRequest app requiredPermissions unfiltered·catch-all meta ungatedGenerated by Claude Code