Skip to content

[finding] after the #13279 repair, an UNRESOLVABLE data engine still answers 403 FORBIDDEN — the last surviving GRANTS-LOST disguise at the package door #13476

Description

@claude

Found while implementing the #13279 ruling (PR #13475). No severity is asserted here; this records what was driven and what came back, for triage.

The shape

#13279 ruled that a permission-store READ FAILURE must fail loud, and tryFind now raises AuthzStoreUnavailableError (503 SERVICE_UNAVAILABLE) when a read is issued and throws. That repair does NOT reach a second path to the same disguise, because that path never issues a read at all.

rest-server.ts resolves the data engine as an absorbed rejection:

  const ql = kernel
      ? await kernel.getServiceAsync('objectql').catch(NO_OP_RETURNING_UNDEFINED)
      : ...;

So an engine that cannot be RESOLVED becomes ql === undefined. resolveAuthzContext then takes tryFind's guard for an unwired engine and returns an empty grant set — correctly, for its own contract: "no engine is wired" is a legitimate embedder shape and must stay quiet.

The two conditions are not the same fact, and they arrive at the resolver as the same value:

  • an embedder that never configured a data plane — zero capabilities is TRUE;
  • a deployment whose engine resolution FAILED — zero capabilities is UNKNOWN.

What was measured

Real RestServer, real registerPackageRoutes, wired as rest-api-plugin.ts wires it. Fault class DATA_ENGINE_UNRESOLVABLE in packages/rest/src/package-door-execctx-fault-reachability.test.ts.

wiring GET /api/v1/packages
healthy store granting the capabilities 200
permission store reachable, every read throws 503 SERVICE_UNAVAILABLE (after #13279)
the data engine cannot be resolved at all 403 FORBIDDEN, unchanged

The third row still answers "Reading packages requires the studio.access or setup.access capability." to an authenticated administrator whose engine is simply gone. After #13279 it is the LAST surviving member of the GRANTS-LOST disguise on this door, and the inverted test in PR #13475 asserts that residue explicitly rather than leaving it to be rediscovered.

Why this was not folded into #13279

The #13279 ruling names tryFind / resolveUserAuthzGrants as its landing point and speaks of the permission store being unreachable. This seam is neither: it is the TRANSPORT's engine-resolution .catch, one layer out, and the correct behaviour there is a judgement rather than a mechanical consequence of that ruling — quieting it wrongly would refuse service to every embedder with no data plane, which is a supported configuration.

Related, and distinct

None of those is addressed by this card, and this card is not addressed by any of them.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions