Per-method RPC auth rows for the conformance table, generated from the
declared surface and, when given, the methods a backend live-mounts off it.
describe_rpc_attack_surface_tests walks every declared method's auth, but
only in-process against stub apps with no database, so it never reaches a
spawned backend. The hand-written conformance rows reach every backend but
cover a chosen few methods. A spine that hand-implements a method's gate can
drift on any method nobody wrote a row for, and the action-manifest parity
gate only compares what the spines *declare*. These rows close that: each
method's declared RouteAuth becomes ConformanceCase data that
describe_conformance_table_tests fires at the live gate, in-process and
cross-process alike.
Rows per method, from its declared auth:
- account or actor required —
anonymous → 401
authentication_required. The 401 body is method-independent, so every
such row joins one equivalence group: a caller with no credential learns
nothing that tells one method from another. - credential-gated (
credential_types) — each builtin channel the gate
does not admit (keeper for session, token for api_token, daemon
for daemon_token) → 403 credential_type_required, echoing the declared
required_credential_types. Each of those principals is the keeper. - role-gated (
roles) and admitting a session — fresh_non_admin →
403 insufficient_permissions, echoing required_roles. A role gate
behind a credential gate that refuses sessions is skipped: every
role-less principal would hit the credential gate first. - public — no rows.
Acting. On both spines the authorization phase resolves the acting
actor before the credential gate runs, so a keeper with a second actor
(extra_actors) would answer actor_required (400) to a request with no
acting on an actor-bound method. Every credential and role row on such a
method therefore binds acting to its principal's own actor at run time
(request.acting: 'principal'): a single-actor account resolves the same
actor with or without it, a multi-actor keeper resolves the one holding its
roles, and either way the gate under test is the first that can refuse. The
anonymous rows send none — the 401 precedes actor resolution.
Every refusal row sends schema-valid params where they can be generated
(generate_valid_body, its generated acting dropped for the binding),
so the gate is the only thing that can refuse. A backend
whose gate is missing reaches its handler and answers something else. The
rows therefore pin each gate, not the gate order against validation — the
401-before-400 rows of the security slate pin that.
Off the declared surface. live_rpc_endpoints adds the methods a
backend mounts beyond the declared surface — cell verbs, opt-in resolvers,
the _testing_* backdoors — read from their action specs the same way, a
method the declared surface already has taken from there. The rows then
need the same endpoints to resolve: pass them to the table runner's
live_rpc_endpoints too.
Skipped: the protocol actions (PROTOCOL_ACTION_METHODS): their auth is
fixed by fuz_app's shared specs rather than declared per consumer, they are
WebSocket-scoped (cancel is a notification with no reply to assert,
peer/ping is public, heartbeat rides an admitted socket), and the WS
suites cover them.