API_KEYS entries now accept key:role (back-compat: bare keys default to operator). Known roles come from ActorRole in transactionState.ts (coordinator / approver / releaser / validator / exception_manager / operator).
apiKeyAuth + optionalApiKeyAuth inject req.actorRole alongside req.apiKey, so SoD enforcement in the state machine can consult the authenticated role directly.
New requireRole(...roles) guard for per-route role gating.
Fail-closed: unknown roles are skipped during parsing, not silently promoted to operator. Cache auto-invalidates when API_KEYS changes.
9 unit tests.
Why
The middleware previously only validated that a key was present and registered — it never attached a role to the request, so the SoD layer in the state machine had to fall back on user-agent heuristics. Binding the role at the authentication layer closes that hole.
Closes gap-analysis v2 §7.7.
## What
- `API_KEYS` entries now accept `key:role` (back-compat: bare keys default to `operator`). Known roles come from `ActorRole` in `transactionState.ts` (coordinator / approver / releaser / validator / exception_manager / operator).
- `apiKeyAuth` + `optionalApiKeyAuth` inject `req.actorRole` alongside `req.apiKey`, so SoD enforcement in the state machine can consult the authenticated role directly.
- New `requireRole(...roles)` guard for per-route role gating.
- **Fail-closed**: unknown roles are skipped during parsing, not silently promoted to `operator`. Cache auto-invalidates when `API_KEYS` changes.
- 9 unit tests.
## Why
The middleware previously only validated that a key was present and registered — it never attached a role to the request, so the SoD layer in the state machine had to fall back on user-agent heuristics. Binding the role at the authentication layer closes that hole.
Closes gap-analysis v2 §7.7.
- API_KEYS entries now accept the form key:role (back-compat: bare keys
default to role=operator). Known roles come from ActorRole in
transactionState.ts (coordinator / approver / releaser / validator /
exception_manager / operator).
- apiKeyAuth + optionalApiKeyAuth inject req.actorRole alongside
req.apiKey so the SoD enforcement in the state machine can consult
the authenticated role directly.
- New requireRole(...roles) guard for per-route role gating.
- Fail-closed: unknown roles are skipped during parsing, not silently
promoted to operator. Cache auto-invalidates when API_KEYS changes.
- 9 unit tests.
This pull request is broken due to missing fork information.
This pull request can be merged automatically.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes gap-analysis v2 §7.7.
What
API_KEYSentries now acceptkey:role(back-compat: bare keys default tooperator). Known roles come fromActorRoleintransactionState.ts(coordinator / approver / releaser / validator / exception_manager / operator).apiKeyAuth+optionalApiKeyAuthinjectreq.actorRolealongsidereq.apiKey, so SoD enforcement in the state machine can consult the authenticated role directly.requireRole(...roles)guard for per-route role gating.operator. Cache auto-invalidates whenAPI_KEYSchanges.Why
The middleware previously only validated that a key was present and registered — it never attached a role to the request, so the SoD layer in the state machine had to fall back on user-agent heuristics. Binding the role at the authentication layer closes that hole.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.