The Browser UI
rocky serve --ui serves a browser UI for one project. It shows the project’s models and runs, the plans waiting for a human, and the policy decisions and product history the engine recorded. The UI is read-only. Every value on the page comes from a typed /api/v1 payload. Most of those payloads are the same ones the CLI prints with --output json.

The screenshots and the tour above come from the current release, which still groups the screens under a header of three lanes. The sidebar described below arrives in the next release. No address changed, so every link on this page works in both.
The UI ships inside the release binaries and the container image. A sidebar lists eleven areas. Five open a screen today:
Needs you what waits for you now Projects - Estate models, the DAG, runs and schedules Runs - Scheduler - Review plans that need a human, one plan in full Policies - Products each data product, and its journal Governance scorecard, custody, audit Agents & Clusters - Settings -The other six are not links. Each shows, under its name, what is true instead. They are listed in Areas without a screen. On a screen narrower than 1024 pixels the sidebar folds behind an Areas button.
Open the UI
Section titled “Open the UI”Start the server with a read-only token:
rocky serve --ui --token "$(openssl rand -hex 16)" --token-scope read-onlyThe server prints one address:
Rocky UI: http://127.0.0.1:8080/ui/#token=<secret>Open that address. The page reads the token from the part after #, keeps it for the browser tab, and removes it from the address bar. Browsers never send that part to a server, so the token is in no access log.
A tab opened without the token shows No token for this tab. Open the printed address again to fix it.
To run the UI somewhere other than your own machine, use the container image or the Helm chart. The chart serves the UI by default.
Needs you
Section titled “Needs you”The brief is the estate digest that rocky brief prints, for a window you pick (7 days by default). It has a card for each part of the digest. The first card, which gives the area its name, is what needs you. The rest are the agents’ policy decisions, runs, autonomy (degraded rules and active freezes), cost, drift, freshness, quality and the scheduler.

Each card says whether its data was available. A signal the ledger does not hold shows as not recorded, never as a zero.
Estate
Section titled “Estate”The printed address opens Estate, even though Needs you is first in the sidebar. It shows the project as the engine compiled it. The strip at the top names the config file, the pipelines and adapters, the compiled models with their diagnostics, and the newest run.

Below the project strip, the DAG draws every model and the edges between them. Click a model to open its detail: its columns (with their types, where the compiler inferred them) and its compiled SQL. The server caps the SQL at 256 KiB and says so when it cuts it. Further down, the estate lists recent runs and the pipelines that declare a [schedule].
Review
Section titled “Review”The Review screen lists the plans that the policy plane sent to a human, by a rule or by the default effect. The engine ranks the queue by a score: blast radius × change class × staleness. A breaking schema change weighs 3, a bare apply, promote or backfill weighs 2, and anything else weighs 1. So a wide-reaching breaking change that has waited long comes first. It is the same list rocky review --queue prints.
Open a plan to see why it waits:

The plan screen shows:
-
What it would break. The breaking-change findings against
HEAD, or why that check could not run. -
Why it needs a human. The rule, the capability, the principal and the blast radius behind the
require_reviewdecision. -
The spec it was planned against. For a product plan only: whether the product spec changed after the plan was made.
-
Sample rows. Nothing is read until you ask. The button runs the model’s query against the warehouse for up to 20 rows, and that query has a cost. The engine masks each classified column before the rows leave it, and refuses the whole sample rather than return a classified column it cannot mask. The refusal names every column and tag, and both ways out.
The workspace
[mask]block decides first. A strategy masks the column, andnonereturns it raw, which is an operator’s decision rather than a gap.[classifications.allow_unmasked]is only the fallback for a tag that block does not answer: a tag with a strategy is masked even when the list names it.What is left is refused: a tag with no
[mask]entry that the list does not name, and a tag answered only under[mask.<env>], which this path does not read.The button appears only when the plan names exactly one model to sample.
-
How to approve. The command to copy.
You approve in a terminal, not on the page. The approval marker records a git identity, and the page holds a read-only token:
rocky review <plan-id> --approveProducts
Section titled “Products”Products shows each data product: its fulfillment loop state, its working spec digest, whether its approval is recorded, and its journal. The journal lists every event the engine recorded for the product, in order.

Governance
Section titled “Governance”Governance answers “what did the agents do, and was it allowed?” It is the one area with tabs of its own: Scorecard, Custody and Audit.
Scorecard
Section titled “Scorecard”The scorecard shows acceptance, review and denial rates for a window you pick. Group them by principal, by rule, or by the model decided on. It matches rocky audit --scorecard. It lists the metrics the ledger cannot support, and gives the reason for each.
Custody
Section titled “Custody”Enter a subject to trace its chain of custody. A subject is a model, a run id, a plan id, or another id the ledger records, such as freeze:global. It shows the decisions about it, the plan, the runs that applied it, any verification after apply, and its blast radius. It matches rocky audit --for <subject>.

The audit tab is the whole policy decision ledger, oldest first. Filter it to one product. It matches rocky audit and rocky audit --product <name>.
Areas without a screen
Section titled “Areas without a screen”Six of the eleven areas open nothing today. The sidebar shows each name with the reason under it, as plain text. A reason says where the same information is now, when it is somewhere:
| Area | What the sidebar says |
|---|---|
| Projects | One project for now: the one this server runs. |
| Runs | No page of its own yet. The runs table is on Estate. |
| Scheduler | No page of its own yet. The schedule status is on Estate. |
| Policies | No page yet. The engine serves the rules at /api/v1/policy. |
| Agents & Clusters | No page yet. Agent activity is on Needs you. |
| Settings | No page yet. The engine serves them at /api/v1/settings. |
A route with no page is not the same as nothing at all. The two API routes named above answer today, and the Embedding guide covers them.
What the UI cannot do
Section titled “What the UI cannot do”The page reads. It does not write.
- It cannot start a run. The UI token must be read-only. A read-only token gets
403 forbidden_read_only_tokenonPOST /api/v1/jobs/runand every other token-checked write. The one write route that ignores the token is the webhook route, which checks its own HMAC signature. The page does not hold that secret. To submit jobs over HTTP, run a secondrocky servewithout--ui, or use the CLI. - It cannot approve a plan. Review shows the command. You run it in a terminal.
- It cannot change policy. The Governance screens report decisions, and
/api/v1/policyreads the rules back. The rules themselves live in the[policy]block ofrocky.toml, androcky policy freezeandrocky policy unfreezeare the CLI’s only policy writes.
How the server protects the page
Section titled “How the server protects the page”With --ui, the server adds checks that a plain rocky serve does not run:
--uirefuses to start without a token, or with a token that is not read-only.- A request whose
Hostis not a loopback name, the bind host, or an--allowed-hostentry gets421 host_not_allowed. This defends against DNS rebinding, where an attacker’s domain is made to resolve to127.0.0.1.GET /api/v1/healthskips this check, so a load balancer probe still works. - A request whose
Originis neither the server’s own nor an--allowed-originentry gets403 origin_not_allowed. The check reads the origin’s host, not the whole origin, so anhttporhttpsorigin on an allowed host passes whatever its port. On a loopback server that includeshttp://localhost:5173, a local dev server.GET /api/v1/healthskips this check too. - Every UI file response carries a Content Security Policy. The page loads scripts and fonts from this server only, and styles from this server or inline. Nothing may frame it.
--ui --schedulerrefuses to start withoutROCKY_WEBHOOK_SECRET, because a browser can reach the webhook route.
Behind a reverse proxy, name the proxy host with --allowed-host:
rocky serve --ui --host 0.0.0.0 --token "$TOKEN" --token-scope read-only \ --allowed-host rocky.internalThe rocky serve reference lists every flag. The Embedding guide covers the HTTP API behind the page.