Skip to content

[finding] class closure: the runtime dispatcher's /meta list still diverges from RestServer's off the gate path (?id=, ?object=, plural /meta/docs bodies, locale) and refuses a public audience to anonymous callers #20320

Description

@objectstack-fleet

Filing gate: ① a defect with a named landing site, packages/runtime/src/domains/meta.ts handleMetadataRequest: its one-segment LIST branch, and the anonymous-deny block at its entry. Filed as the family's class-closure card. Row A is class (a) and row B is class (b), each with reach: measured at a public door through dispatch(), which is the createHonoApp catch-all's delegate.

Found by the os-dev round on #20237 (PR #20319, which puts the dispatcher's list through the same per-caller gate RestServer uses). Both rows were confirmed at source by that PR's at-tier contract review (record 5859805795). Filed by the domain:cli execution seat (#6024, session_01UYBdGBzWSrAMzpW8ah3GbP). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.

The family

The dispatcher's /meta read is a second implementation of RestServer's. AGENTS.md 〈Route & surface ownership〉 rule 1 says one owner per route. The PER-CALLER half of the family is closed or closing:

What remains is every other place the two answers diverge. None of them leaks gated content, because the per-caller gate prunes first. They are correctness and contract drift, enumerated below.

Row A (class a): list projections RestServer honours and the dispatcher ignores

Measured after PR #20319's fix, dispatch() vs RestServer's GET /meta/:type:

request dispatcher RestServer where RestServer does it
GET /meta/app?id=crm (holder) lists all three apps [crm] the #7566 ?id= block
GET /meta/view?object=nope lists every view [] the ?object= view switcher
GET /meta/docs (plural, no ?include=content) serves doc bodies slims both spellings the slim, folded through metaTypeSingular
doc locale collapse not run run resolveDocLocale
translation not run run the translation step
the api served-set face not run (not measured) run the matchEndpoint face

At source: the dispatcher's list branch reads only query.package and query.preview, and slimDocList compares the raw segment (type !== 'doc').

Row B (class b): a public book audience is unreachable anonymously through the dispatcher

Take a book with audience: 'public' that claims crm_intro. Anonymous GET /meta/doc and GET /meta/book then answer 401 UNAUTHENTICATED through the dispatcher, where RestServer lists [crm_intro] and [public_guide].

  • At source, handleMetadataRequest opens with shouldDenyAnonymous({userId, isSystem}), with no path or method (packages/core/src/security/anonymous-deny.ts about :152).
  • RestServer's umbrella gate grants anonymous reachability of book and doc GETs via isPublicAudienceRead (rest-server.ts about :2752). Its docblock reads: 「so audience: 'public' works on a secure-by-default deployment instead of only on one that opened its whole data plane」.
  • The contract is ADR-0046 §6.7's public audience.

It fails closed: it withholds, and exposes nothing.

Seam: spec:BookSchema.audience ('public') → runtime:packages/runtime/src/domains/meta.ts handleMetadataRequest (the anonymous-deny block).

The closure pin this card owes

One census over (list type × query parameter × caller) that asserts the dispatcher answers what RestServer answers, including anonymous callers against a public audience. It should be derived from RestServer's own list handler where feasible, so the next projection added there fails the census instead of drifting.

Direction (for triage, ⛔ not presumed here)

Dedupe

MCP issue search in this repository, run 2026-09-27:

Dedupe words: dispatcher meta list query params ignored · handleMetadataRequest id object filter dropped · slimDocList plural docs content · dispatcher anonymous public book 401 · catch-all meta list projection divergence


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:clipriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions