# PRIOR WebUI: Operator Guide

The PRIOR WebUI is a browser dashboard for operating a PRIOR engine: chat with the model and watch
the policy gate decide in real time, manage policy packs, issue API keys, apply licenses, and read
the compliance audit logs.

**It is a standard, unprivileged API client.** It holds an Operator API key in the browser's
`localStorage` and talks to the engine's public `/v1`, `/admin`, and `/metrics` routes exactly like
any customer integration would. There are **no UI-only privileges**, **no server-side session**, and
**no auth bypass**. Ship it as a separate, optional container; the engine image stays headless.

Stack: **Vite + React + TypeScript + Tailwind**. Typed API client generated from the engine's OpenAPI
spec. Self-hosted IBM Plex fonts (no CDN). Dark-first theme with a light toggle.

---

## Connecting (the login screen)

On first load the UI asks for two things, both stored only in your browser's `localStorage`:

| Field | Example | Notes |
|---|---|---|
| **Engine URL** | `http://localhost:8089` | The base URL of your PRIOR engine. |
| **Operator API key** | `prior_sk_…` | An **Operator** or **Admin** key (from the engine's `/admin/keys`). |

The key is validated against `GET /admin/keys` before you enter the dashboard, so a read-only
Viewer key (which lacks admin access) is rejected up front. The key is sent as
`Authorization: Bearer <key>` on every request.

> **Cross-origin + HTTPS.** The UI and engine are separate origins, so the engine must allow the
> UI's origin via `PRIOR_ALLOWED_ORIGINS`. If you point the UI at a non-`localhost` engine over
> plain `http://`, the console warns that the key travels in cleartext, so use HTTPS for any remote
> engine.

---

## The six tabs

| Tab | What you do there |
|---|---|
| **Playground** | Live chat + real-time policy X-Ray; a steering-on/off comparison. |
| **Analytics** | Traffic metrics, refusal rate, latency, traffic-by-zone. |
| **Policy & Packs** | Ingest governance documents; enable/disable/reload packs. |
| **Access** | API keys and license management. |
| **Audit Log** | Safety decisions + control-plane action log, with export. |
| **Configuration** | Steering-model params, calibration, runtime toggles, live logs. |

---

### Playground

The demonstration surface: watch the gate fire as tokens stream.

- **Chat mode.** A live chat window streaming from `/v1/chat/completions`, with an optional system
  prompt (note: the system prompt is gated too). The **X-Ray panel** reads the `x-prior-*` response
  headers live and shows the tri-state decision (🟢 GREEN passed / 🟡 YELLOW flagged / 🔴 RED
  blocked), the winning pack, and whether steering engaged.
- **Comparison mode.** Runs the *same* prompt twice, side by side: steering **off** (the raw model)
  vs steering **on** (PRIOR re-imposed), so you can prove a jailbreak that lands on the raw model
  is stopped by PRIOR. Includes preset jailbreak prompts or your own. Runs sequentially (the GPU
  serializes the two passes) and reports the zone + injection count for each.

---

### Analytics

A live dashboard polled from `/metrics` every 5 seconds.

- **System status band.** The active steering model + certification tier (Certified/Beta), loaded
  pack count, license status, and whether steering enforcement is on.
- **Traffic metrics.** Total requests, refusal rate (% RED), average gate latency (ms), and total
  injections / average completion tokens per injection.
- **Traffic by zone.** A stacked bar of GREEN / YELLOW / RED, with a live indicator when data is
  flowing.

---

### Policy & Packs

Manage what the engine enforces, all hot, no restart.

- **Ingest policy document.** Paste raw prose **or** upload a file (`.txt`, `.md`, `.pdf`, `.docx`),
  optionally name it, and PRIOR extracts the intents, generates exemplars, calibrates thresholds, and
  (if you opt in) hot-reloads the resulting `{name}_guard` + `{name}_policy` packs, typically in
  5–15 s. The panel then shows the enforcement mode, `t_low`/`t_high`, and the calibration
  diagnostics (separation, recall, specificity) so you can judge the pack before trusting it. See the
  engine's **[Policy & Packs](policy-packs.md)** guide.
- **Active packs table.** Every live pack from `/admin/packs/native`: name, encoder type, tier,
  priority, thresholds. Toggle any pack **enabled/disabled** instantly (no reload), click a name for
  a detail modal (sensitivity label, exemplar count, span-scoring status), or **hot-reload all** from
  disk.

> Exemplars and vectors are never exposed; the table shows metadata and counts only, by design.

---

### Access

Operator credentials and licensing.

- **API Keys.** Create a key with a **role** (Viewer reads dashboards, Operator manages policies,
  Admin has full control) and an optional label (e.g. `ci-pipeline`). The raw `prior_sk_…` token is
  shown **once** at creation; only a hash is stored server-side. Revoke any key (with a confirmation)
  from the table.
- **License.** An Active/Unlicensed badge plus the license details: variant, tier, capabilities,
  expiry + days remaining, customer id. **Upload** a `.bin` license file and it's hot-applied with no
  restart. Links out to the procurement page when unlicensed.

---

### Audit Log

Two compliance logs, both filterable and exportable (CSV/JSON, with a capped row export).

- **Safety decisions** (`/admin/audit/safety`) log every gated request: timestamp, zone, the pack that
  fired, match score, whether it was steered, and a **prompt hash** (SHA, never plaintext). Filter
  by zone / pack / hash. Polled every 5 s.
- **Control-plane actions** (`/admin/audit`) cover administrative operations: key create/revoke, license
  upload, pack reload, and config changes, each with timestamp, action, resource, actor role, and IP. Actor
  keys are hashed. Filter by action / resource / actor.

---

### Configuration

Engine tuning and observability.

- **Steering model.** The loaded model's name, layer count, golden-layer index (depth), alpha
  (strength), and calibration status, with a **Certified** vs **Beta** badge, plus the `.gguf` models
  available on disk (with sizes). **Auto-calibrate** runs the golden + alpha sweeps (1–4 min) and
  persists the certification; **Share calibration** downloads the payload and can submit it to the
  Eagle Logic registry. *(Loading or switching the active model is driven through the engine's model
  API; see the [API Reference](api-reference.md).)*
- **Runtime settings.** Toggle **require API key on `/v1`** live (no restart) and set the **log
  verbosity** (DEBUG/TRACE gated behind a diagnostic token). The structured-JSON-logs flag is
  boot-time and shown read-only.
- **Logs.** A live tail of the last ~300 lines from `/admin/logs`, filterable by level and color-coded
  by severity, polled every 3 s with a live toggle.

---

## Running the WebUI

### Develop

```bash
npm install
npm run dev          # http://localhost:5173 (set your engine URL + key on the login screen)
```

### Build & containerize

```bash
npm run build                     # -> dist/ (static assets)
docker build -t prior-webui .     # multi-stage: node:22-alpine build, then nginx-unprivileged
docker run --rm -p 8080:8080 prior-webui
```

The runtime image is `nginx-unprivileged` running as a **non-root** user, so it serves on **8080**
(not 80). `nginx.conf` provides the SPA `index.html` fallback for deep links and hard-caches the
hashed `/assets/` bundles. It does **not** proxy `/v1` or `/admin`, because the engine is a separate
origin set on the login screen, so **the engine must allow the UI's origin via `PRIOR_ALLOWED_ORIGINS`**.

### Deploying alongside the engine

```
  operator browser
        │
        ▼
  prior-webui  (nginx :8080, static SPA)  ──── Bearer key + CORS ────▶  PRIOR engine (:8089)
```

The UI holds no secrets of its own and keeps no server-side state; every operator authenticates with
their own engine-issued key. You can run one UI for many engines (switch the Engine URL) or one per
engine; it's stateless either way.

---

## Security notes

- Credentials (API key, engine URL, theme) live **only** in the browser's `localStorage`. Nothing
  is sent to any WebUI backend, because there isn't one.
- Give each operator their **own** scoped key; revoke from **Access → API Keys** when someone leaves.
- Use **HTTPS** and a restricted `PRIOR_ALLOWED_ORIGINS` for any non-local deployment.
- The UI cannot exceed the permissions of the key it holds: a Viewer key simply can't reach the
  admin surfaces.

---

For the engine itself, see the **[PRIOR product documentation](index.md)**.
