Context
Phase 1b made agentgateway the sole key holder — pipelit mints a 60s ES256 JWT that the gateway swaps for the real provider key. The post-implementation review flagged two JWT weaknesses to close in Phase 4:
-
role="admin" for every workflow call. resolve_llm_for_node never derives a role, and platform/services/llm.py defaults role = user_role or "admin" (~line 248), so every workflow-minted JWT claims admin. agentgateway's CEL authorization is now the sole authz layer, and it's being fed a constant → per-role gateway policy is effectively bypassed. (Does not leak keys; it neuters authorization.)
-
Circular trust — pipelit self-signs and holds the signing key. A compromised pipelit process can mint a token for any user/role. Per the original design (docs/architecture/gateway-mcp-design.md, Phase 4), JWT issuance should move to plit-gw (identity provider) — or a dedicated issuer — so pipelit no longer holds the signing key. Until then, the boundary protects against agent output leaking keys, but not against a compromised pipelit process. Acceptable for single-operator; must be closed before untrusted multi-tenant.
Supersedes #187 (bearer→JWT migration itself shipped in Phase 1b).
Scope
- Derive a real per-user
role (and scoped allowed_credentials) instead of hardcoding admin; author CEL policies that actually discriminate by role.
- Move JWT issuance out of pipelit (to plit-gw or a dedicated issuer) so pipelit no longer holds the signing key.
Acceptance
- Workflow JWTs carry the correct per-user role; a non-admin workflow is denied admin-only gateway routes/tools by CEL.
- pipelit no longer holds the JWT signing key (issuer is a separate trust domain).
References
Context
Phase 1b made agentgateway the sole key holder — pipelit mints a 60s ES256 JWT that the gateway swaps for the real provider key. The post-implementation review flagged two JWT weaknesses to close in Phase 4:
role="admin"for every workflow call.resolve_llm_for_nodenever derives a role, andplatform/services/llm.pydefaultsrole = user_role or "admin"(~line 248), so every workflow-minted JWT claims admin. agentgateway's CEL authorization is now the sole authz layer, and it's being fed a constant → per-role gateway policy is effectively bypassed. (Does not leak keys; it neuters authorization.)Circular trust — pipelit self-signs and holds the signing key. A compromised pipelit process can mint a token for any user/role. Per the original design (
docs/architecture/gateway-mcp-design.md, Phase 4), JWT issuance should move to plit-gw (identity provider) — or a dedicated issuer — so pipelit no longer holds the signing key. Until then, the boundary protects against agent output leaking keys, but not against a compromised pipelit process. Acceptable for single-operator; must be closed before untrusted multi-tenant.Supersedes #187 (bearer→JWT migration itself shipped in Phase 1b).
Scope
role(and scopedallowed_credentials) instead of hardcoding admin; author CEL policies that actually discriminate by role.Acceptance
References
platform/services/jwt_issuer.py,platform/services/llm.py(role default ~248)docs/architecture/gateway-mcp-design.md(Phase 4 — plit-gw as identity provider)