Submit an issue View all issues Source
MIR-1520

Runners hold a general-purpose EntityAccess API rather than purpose-shaped calls

Open runtime Improvement public
phinze phinze Opened Aug 3, 2026 Updated Sep 1, 2026

Runners authenticate with a cluster cert and then call EntityAccess.get/list/put/patch/ping_session directly. The coordinator therefore cannot distinguish a runner renewing its own lease from a runner reading arbitrary cluster state. Both are the same RPC, with the same authorization outcome.

This shows up in three places.

Authorization is coarse. A cert-method identity bypasses RBAC entirely (pkg/oidcauth/composite.go), which is safe only because the TLS layer verified the cert against the cluster CA. That makes the entity API itself the trust boundary, and it is drawn very wide.

The audit trail cannot be tightened. rpc access records are the only visibility into what a runner actually touched, so they have to stay at Info. On a two-runner cluster that is 12,000+ lines/hour and roughly a quarter of the coordinator's total log volume. This came up in MIR-1503, where demoting those records was the obvious way to cut volume and turned out to be exactly backwards: blanket entity access is the thing most worth watching precisely because it is unconstrained.

And blast radius exceeds the job. A compromised or simply buggy runner can reach far more of the cluster than anything it legitimately needs.

The direction is narrow, intention-revealing RPCs — claim a lease, report sandbox status, fetch my assignments — with the general entity API closed to runner identities. Each call then becomes authorizable on its own terms, each audit record becomes individually meaningful, and routine control-plane chatter becomes safe to demote to Debug, because "runner renewed its lease" is genuinely boring in a way that "runner read an entity" is not.

Long-term and larger than a single PR. Filing so the constraint is written down somewhere, because several smaller decisions are currently routing around it.