Skip to content

Enrollment Approvals

When a workload proves its identity to a Control Trust Anchor, it produces an enrollment request. Enrollment Approvals is where that identity decision is made: you inspect the request — its verified subject, proof, trust anchor, and requested routes — and decide whether it becomes a derived client key. Every approval or rejection goes through the same-origin BFF, which signs the Control request server-side; the browser never talks to Control directly and never holds signing material.

The page is environment-scoped. Without an environment (or environment_id in the URL), the enrollment request, trust anchor, and client reads are not attempted.

The Enrollment lane status panel tells you up front whether actions are possible, with an Actions available / Actions disabled badge and two columns:

  • Operator session — the authenticated user, role, auth source, and whether the session holds the enrollment approval permission.
  • Action readiness — why actions are or are not available right now, and the Next required operator action (for example, “Sign in with a configured provider”, “Open Inspect on a pending request”, or “Select a pending enrollment request”).

Actions are disabled when there is no authenticated session, when Control denied the session, when RBAC does not grant enrollment approval (an explicit RBAC denied notice), when no environment is selected, when Control did not return the request list, or when no pending request is selected. Only a selected pending request with an authorized operator enables approve/reject.

If no session exists, a Sign in required panel discovers login providers from the BFF and starts a hosted sign-in. If Control returns no providers, the panel reports Sign-in setup needed rather than pretending sign-in works.

The panel also shows request counts: Requests, Pending, Approved, Rejected.

The Enrollment filters panel is GET-only: status, proof_kind, client_id, trust_anchor_id, subject, search, and limit (clamped to at most 50 rows). A metrics row summarizes requests plus trust anchor counts (Anchors, Active anchors), and a trust-context summary counts Anchors, Clients, Keys, and Route grants for the environment, with a Trust anchor context table grouping anchors by kind and status.

The Enrollment request inspection table lists matching requests:

Column What it shows
Request The enrollment request ID.
Subject / client The requested (verified) subject and the target client ID.
Status pending, approved, or rejected/revoked/expired.
Requested routes The route IDs the request asks for.
Proof / anchor The proof kind and the trust anchor ID it was verified against.
Updated Last update (or creation) time.
Detail An Inspect link that opens the request detail.

Empty states are explicit: “No enrollment requests returned” when Control returned an empty list, and “No enrollment requests match these filters” when filters hide everything.

The Enrollment request detail panel is read-only inspection material. It shows the request ID, subject, client, status, proof kind, trust anchor, created key (if any), requested scopes, expiry, and last update, followed by:

  • Requested route context — route, API, scopes, and status per requested route.
  • Client key context — the target client’s existing keys (KID, proof, subject, status, created).
  • Desired route grant context — the desired route grants for the created key (grant, route, status, granted, updated).
  • Sanitized proof metadata, Sanitized request metadata, Matched trust anchor, and Matched client — JSON sections with secret-shaped values redacted and long output truncated.

Before approving, verify against the trust anchor you registered — not against what the request claims:

  • The requested subject is the exact workload identity your enrollment policy expects (for a Kubernetes workload, the exact system:serviceaccount:<namespace>:<serviceaccount> value).
  • The matched trust anchor is the anchor you intended, with the expected issuer, and it is active. Compare against Trust Anchors, and confirm the source revision is the validated revision your enrollment policy binds — not a stale draft.
  • The proof kind matches the anchor’s source kind.
  • The client and requested routes/scopes match what the derived key should inherit. Approval grants the target client’s policy to the new key, so the client is the real blast radius.

For the full enrollment flow that produces these requests and derived keys, see OIDC JWKS enrollment runbook.

The Enrollment approval actions section appears only for a pending request, only when the operator session can approve enrollments, and only with environment and request context; otherwise it explains exactly which condition is missing (for example, “This enrollment request is approved. The UI only offers approve/reject actions for pending requests.”, or a denied notice when the session is read-only for enrollment administration).

Above the forms, an action-context grid restates the environment, request, client, subject, routes, scopes, trust anchor, and proof you are acting on.

  • Approve submits client_key_id and kid (both prefilled from the request’s created key when available) plus an optional reason.
  • Reject requires a rejection reason.

Submitting either opens a browser confirmation first; only then does the same-origin BFF send the signed Control mutation. On success the console reports that the action completed through signed Control M2M and refreshes state, with a link to audit evidence when available. On failure it shows a sanitized reason — for example, a missing same-origin anti-forgery guard, rejected unexpected fields, missing confirmation fields, an authorization denial, or missing Control signing configuration — and never surfaces raw upstream error bodies or secret-shaped metadata.

The Audit evidence link panel summarizes the audit decisions and events returned for the current context and links to the related audit view, pre-filtered to the selected enrollment request.