Admin panel

A React + Vite + Tailwind console that drives the server only through the Admin API.
It never reads the database directly — it is just another API client, so every action it performs is
PDP-authorized, idempotent on writes, and audited.

Why API-only

No privileged back door

Because the panel goes through the same /api/iam/v1 surface as your automation, there is no path that
skips iam.can, idempotency or the audit chain. A human in the UI and a script hitting the API are governed
identically. This is ADR-8.

What you can do

Applications & manifests

Register apps, review manifest diffs, approve / apply / rollback.

Roles, permissions & policy

Browse roles and permissions; test decisions live in the policy playground.

Governance

Run access-review campaigns, triage access requests, inspect anomalies.

Audit, users & sessions

Inspect the hash-chained audit trail; manage users, sessions & tokens, events & webhooks.

The policy playground is especially useful: it calls /decisions/explain so you can see why a subject is
allowed or denied a permission, with the matched policies and failed conditions, before you ship a manifest
change.

Deployment

The panel is a static front-end deployed alongside the server. Put it behind the same TLS and auth boundary;
it authenticates as an admin caller and holds no database credentials. See
Deployment.

flowchart LR UI["React panel<br/>(static)"] -->|Bearer token| API["/api/iam/v1"] API --> PDP["PDP authorizes (iam.can)"] API --> AUD["audited"] API -.->|never| DB[("database")]
The panel needs an admin token, scoped by permission

Panel users authenticate and are authorized per action by the PDP (iam.can:iam:<permission>). Grant
panel operators only the iam:* permissions their role needs — the panel cannot do anything its token isn’t
authorized for.

Next