Admin UI
Ontul includes a built-in web-based Admin UI for monitoring, managing, and operating the cluster.
Opening pages directly
Every page has its own URL under /admin/ (for example /admin/audit, /admin/lineage,
/admin/maintenance) and can be bookmarked, shared, or reloaded. Some page URLs are also REST API
paths — GET /admin/audit is both the Audit page and the audit-list API — so the server answers by
the kind of request: a browser opening the page gets the UI, while API calls (from the UI itself, the
CLI or scripts) get JSON as before. Paths under /admin/auth/ (login and SSO redirects) are always
handled by the server.
Who can change what
Every admin API call is checked on the server, whatever the console shows. Hiding a button is a convenience; the check is what keeps a non-administrator out.
| Area | Read | Change |
|---|---|---|
| KMS, Backup & Restore, STS | administrators only | administrators only |
IAM (users, groups, policies, keys), SSO, connections, catalogs, drivers, storage and other /admin/config/* settings, Iceberg maintenance, health scan, audit settings, cluster maintenance mode |
any signed-in user (secrets masked) | administrators only |
| SQL query history, jobs | your own entries; administrators see everyone's | your own queries and jobs; administrators any |
| Your own access key download | the key's owner or an administrator | — |
"Administrator" means a user whose policies grant AdministratorAccess. A non-administrator who calls
an administrator-only route gets 403 Forbidden. The check runs after a follower master forwards the
request to the leader, so it gives the same answer on every master. When no leader is known — during
an election — a follower answers 503 Service Unavailable rather than guessing a master.
Secrets are never sent back
Connection properties, catalog properties, job configurations, query history and SSO settings are
masked before they leave the server. Any key whose name contains secret, password, passwd,
token, credential, apikey / api_key, accesskey / access_key, private, jaas or
authheadervalue (case-insensitive) is shown as ***. The same masking is applied inside SQL text, so
CREATE USER ... PASSWORD '...' or a key=value string inside a job argument appears as
PASSWORD '***' in history.
Editing a masked value works in both forms:
- Leave
***in place — the stored secret is kept. You can rename a catalog's description or change an endpoint without re-typing its secret key. - Type a new value — it replaces the stored one.
- Clear the field (connections) — the key is removed from the connection.
KMS key status lists each key's versions with their creation time and state only. No key material is returned by any endpoint.
Access keys and tokens
Deleting an access key, or a user, revokes the key's token immediately: a request presenting it is refused from the next call on, not when a cache expires. A token is accepted only while its key exists, is Active and is not past its expiry date. An expiry date picked in the UI means to the end of that day. The download icon for a key is shown only to an administrator and to the key's owner, and the server enforces the same rule.
Pages
Dashboard
Cluster overview with real-time metrics: query throughput, latency, active queries, worker status, and JVM heap usage. All / Masters / Workers tabs break the same metrics down per node, which is where per-worker heap and active-task figures live.
Topology
The Cluster Topology page lists every node the cluster depends on, refreshed every 10 seconds:
- Masters — node ID, host, admin and internal ports, role (Leader / Follower) and status, with a Logs button that tails the node's log.
- Workers — node ID, host, internal and Flight ports, status and Logs.
Logs shows that node's own log, whichever master served the page: a follower's log is fetched from
the follower itself, and a worker is matched by host and port, so two workers on one host do not show
each other's log. The viewer reads complete lines only, follows the file as it grows, and starts again
from the end if the file is truncated or rotated. ZooKeeper servers have no log viewer.
- ZooKeeper — every server of the ensemble in ontul.zk.serverList, whether or not Ontul is
currently connected to it. Each row shows:
| Column | Meaning |
|---|---|
| Server | `host:port` from the connect string |
| Role | `leader`, `follower`, `observer` or `standalone`, as the server reports it; `unknown` when it answers but does not serve (no quorum) or has the `srvr` command disabled |
| Status | **Online** when the server answered, **Offline** when it could not be reached within `ontul.zk.admin.probeTimeoutMs` — the probe error is shown under the badge |
| Version | ZooKeeper version |
| Connections | client connections on that server |
| Latency min/avg/max | request latency the server measured, in ms |
| Znodes | number of znodes |
| Zxid | the last transaction id the server has seen; a follower far behind the leader is lagging |
The header shows the connect string, how many servers are up (`2/3 UP`), and the **ZooKeeper
session** of the master that answered — `CONNECTED`, `RECONNECTED`, `SUSPENDED` (connection lost,
session may still be alive) or `LOST` (session expired) — with its session ID.
The ZooKeeper rows come from ZooKeeper's own srvr four-letter command, sent by the master to each
server over a plain socket — the same answer echo srvr | nc host 2181 gives. Nothing is written to
ZooKeeper. srvr is in ZooKeeper's default 4lw.commands.whitelist since 3.5; an ensemble that removed
it still shows its servers as Online, with the role unknown and the server's refusal as the note.
A probe is reused for ontul.zk.admin.probeCacheTtlMs (5 s by default), so many open admin pages do not
add load to the ensemble. Any master answers on its own: unlike other admin requests, this one is not
forwarded to the leader, because finding the leader needs ZooKeeper. So the ZooKeeper section keeps
updating for an open page even when the ensemble has lost quorum and the Masters and Workers lists stall —
the case it exists for. (Logging in needs the leader, so open the page before you need it, or use the API
with a token you already hold.) The same data is available as JSON from GET /admin/nodes/zookeeper:
{
"connectString": "zk1:2181,zk2:2181,zk3:2181",
"servers": [
{"host": "zk1", "port": 2181, "status": "UP", "mode": "leader", "version": "3.9.1",
"zxid": "0x100000045", "connections": 5, "outstanding": 0,
"latencyMinMs": 0, "latencyAvgMs": 0.42, "latencyMaxMs": 7, "nodeCount": 137,
"probeRttMs": 3, "error": null},
{"host": "zk3", "port": 2181, "status": "DOWN", "mode": null, "error": "ConnectException: Connection refused", "...": "..."}
],
"session": {"state": "CONNECTED", "connected": true, "since": 1760774400000, "sessionId": "0x1000a3b2c0001"},
"reportedBy": "ontul-master-1:8080"
}
Also home to the Enter / Exit Maintenance switch. Turning it on refuses writes across every Master while leaving reads, settings and job control available; a banner stays on screen for as long as the window is open. See Cluster Maintenance Mode.
SQL Query
Built-in SQL editor with syntax highlighting, Ctrl+Enter execution, result table, and query history.
- Cancel stops the running query on the cluster, not only in the browser. The page chooses the query's ID before sending it, so it can cancel a query whose first result has not arrived yet.
- Statements that return no rows —
INSERT,CREATE,DROP,CALLand so on — show a banner with the command tag and row count instead of an empty table. - A failed statement shows the error and how long it ran.
- Integers beyond JavaScript's exact range (±253−1, for example a large
BIGINTID or an exactSUM) are sent as strings, so the page shows the digits the engine computed rather than a rounded number. - History lists your own queries (administrators see all), with their command tag. Secrets in the SQL text are masked. Clear view empties the result pane without touching history.
Catalog Browser
Point-and-click walk through a catalog: Catalogs → Schemas → Tables, then the selected table's
column list and a sample of up to 100 rows. It is the counterpart to typing SHOW SCHEMAS,
SHOW TABLES, DESCRIBE and SELECT by hand — for the times you do not yet know what to type.
It drives the same query endpoint the SQL Query page does, because catalog metadata is answered by the query engine; there is no separate introspection API, and adding one would give two code paths that could disagree about what a table looks like. A statement that fails is shown with the statement that failed rather than leaving an empty pane. A table that describes but will not read — permissions, a broken storage path — keeps its column list and reports only the preview as failed.
Catalogs
Register, unregister, and manage data source catalogs. View connector type, connection ID, table count, and configuration for each catalog.
- Register first, persist after. A new or edited catalog is only saved once its connector has started. A catalog whose endpoint or credentials are wrong is rejected with the connector's error, and editing an existing catalog with bad settings leaves the previous, working catalog in place.
- Iceberg catalogs take a REST (e.g. Polaris), Glue, JDBC or Hadoop catalog. Storage settings come
either from a connection or inline: S3 endpoint (optional for AWS), region,
access/secret key and path-style access, which is on by default for S3-compatible stores. When a
connection is chosen the inline keys are not sent, so the connection stays the only source of
credentials. The REST form sends
catalog.type=rest; a catalog that hascatalog.rest.uribut no type is also treated as REST. - Lance with Polaris takes the Polaris server root as its URI plus the warehouse name.
- A connector type the form does not know is edited as raw JSON properties, so nothing is lost by opening it in the UI.
- Masked secrets are restored from the stored catalog when you save without retyping them.
Connections
Manage physical connections (S3, JDBC, Kafka) — create, update, delete, and list. Credentials are encrypted at rest via KMS.
- Connection IDs may contain letters, digits,
_,.and-. - Secret fields are always shown as password fields and come back as
***; see Secrets are never sent back for how edits treat them. - Test really connects. For S3 it checks the configured bucket exists and can be listed
(
HeadBucket, thenListObjectsV2), or lists buckets when no bucket is set, using the same endpoint, region and path-style settings jobs use. DECIDE connections have their own check; other types answerok: falsewith "Testing is supported for S3 and DECIDE connections" instead of claiming success. - A connection type the form does not know keeps all of its properties when edited.
Semantic Layer
Register and govern semantic views — curated metrics and dimensions over a base SQL view. Each entry shows its trust badge, its metrics (a shield icon marks role-gated ones), dimensions, tags and mandatory filters. Certify signs a view off, stamping the logged-in user and a fingerprint of its semantics; the badge turns STALE (orange) if the view is edited afterwards, with the reason on hover.
Retrievers
Manage retrievers — governed multi-modal retrieval (vector / graph / full-text)
pushed down to a NeorunBase catalog. The side panel covers the SQL template, the typed parameter
contract, output columns, an optional re-rank block, synonyms and allowedRoles. An Invoke
drawer runs a retriever with structured arguments and shows the rendered SQL next to the rows.
Object Types
The ontology entity layer: typed business objects (Customer, Order) mapped onto physical tables. Each row shows the read source, the property→column bindings, the write target (system of record) and a trust badge. Certify signs the definition off; the badge shows STALE with a tooltip when the read source or a property binding changed after certification.
Link Types
Typed relationships between object types, bound either to relational join keys (JOIN) or to a
native NeorunBase graph edge (GRAPH). A link's trust is capped by both endpoint object types, so
a certified link over a draft object type displays as DRAFT with the offending endpoint named.
Action Types
Governed write-backs — the only way an agent changes data. Each action declares its target object
type, its mode (DML against the Iceberg system of record, or OPERATION calling a REST/ERP
connector), a typed parameter contract, and whether it requires approval. An invoke tester
drawer runs one with real arguments.
Action Workflows
Multi-step Sagas composed of action types, authored as JSON or kiok-style YAML (importable and exportable). On a step failure the completed steps are compensated in reverse order; the page shows the run ledger.
Certification on every ontology page
Retrievers, Link Types, Action Types and Action Workflows show the same trust badge as Object Types
and Semantic Views — DRAFT, CERTIFIED (with who signed it off), STALE (edited after
sign-off; the reason is on hover) or DEPRECATED — and administrators get the same Certify
button (POST /api/v1/<kind>/{fqn}/certify). On a STALE entry the button reads Re-certify. The
badge is the server's effectiveStatus, so a link over a draft object type still shows DRAFT.
Certifying needs administrator access or the ontology:Certify action.
Ontology Graph
Visual type-graph of object types and the link types connecting them — the schema-level map an agent traverses, rendered for humans.
Flow
The visual Ontul Flow builder and operator console: a drag-and-drop
source → transform → sink canvas backed by an editable JSON/YAML spec (both directions stay in
sync). The list view shows every registered flow with its status, Records, Rejected and
Lag columns, plus Start / Stop / Delete. Opening a flow gives Spec, Data (preview rows from a
source or sink node), Metrics (live throughput sparkline, rejected count, source lag), Logs
(auto-scrolling tail) and History (past runs with duration and rejected counts). A running flow is
read-only — stop it to edit.
The Iceberg sink's write mode (append, upsert, …) is saved in the spec as sink.write, together with
transactional and batchSize, so it survives a save and reopen. An Iceberg source starts from
latest (only new snapshots) or all (earliest is accepted as a synonym). Saving a new flow
under a name that is already taken answers 409 instead of replacing the other flow.
Data Lineage
Table-level lineage graph built from executed DML, views, started flows and edges pushed by other
engines: which sources feed which targets, each edge labelled with the operation that introduced it
(CTAS, INSERT_INTO, MERGE_INTO, VIEW, …). Enter a table, pick direction and depth; click a node
for its column-level lineage and downstream impact. See Data Lineage.
Audit
Two pages over the audit log:
- Overview & Graph — counters, the storage & retention settings (including S3 tiering), and a
user → engine → tablerelationship graph with drill-down to the related events. - Search — free text plus user, engine, table, read/write, time range and max-results filters.
Both pages take a time range (Any time, Last hour, Last 24h, Last 7 days, Last 30 days, or Custom range… with From / To pickers) and a maximum number of events, and say so when a result is cut at that maximum. They show local and S3-tiered events as one log, so turning tiering on — even with no local copy kept — does not empty the page. Denied attempts appear alongside allowed ones, so a refused certification or an IAM-blocked write is visible here.
Lance Maintenance
Compaction and cleanup for Lance datasets, mirroring the Iceberg Maintenance page for the vector/AI format.
Storage
Switch the exchange and job-log backends between local disk and S3 at runtime, cluster-wide, with no restart — the change is persisted to the replicated metadata store and pushed to every node.
Drivers
Upload JDBC driver JARs for federated catalogs. Drivers are stored durably and synced to every worker, so a new driver does not require rebuilding an image.
- An uploaded driver wins over a bundled one. Ontul ships some JDBC drivers (PostgreSQL among them).
Each uploaded JAR is loaded child-first, and a driver class found in an uploaded JAR is used before
the bundled copy, so uploading
postgresql-42.7.4.jarreally switches the version. Driver classes are read from the JAR'sMETA-INF/services/java.sql.Driver. - Uploading a newer JAR for the same driver class under a different file name supersedes the old JAR: it is removed on the master and on every worker.
- Delete removes the JAR from the master and from every worker, and answers
404for a name that does not exist. - File names must end in
.jarand may not contain path separators or...
Jobs
Monitor active and completed jobs, view real-time logs, and kill running jobs. Jobs are submitted with
the SDK or bin/submit.sh; the page is for watching and stopping them. Non-administrators see their own
jobs, and secrets in job configuration are masked. Kill stops the job on every worker running it and
reports a failure instead of silently doing nothing.
IAM
Manage users, groups, and policies. Includes a visual policy editor for creating structured IAM policies with column-level and row-level security rules.
- The policy editor covers
Allow,DenyandMaskstatements.Masktakes the columns to mask (MaskedColumns). Actions are picked from the known action list or typed by hand. - Opening a policy and saving it keeps every field, including ones the editor has no control for, so editing a policy written by the API or the CLI does not drop conditions.
- New Policy refuses a name that already exists (
409) instead of overwriting it. - The editor stays open with the server's error when a save fails.
- Groups list their members.
Single Sign-On
Configure OIDC, SAML 2.0 and LDAP / Active Directory, on tabs of their own. Every value is a named field — nothing here asks for JSON. Secrets (OIDC client secret, LDAP bind password, SAML SP private key) are never read back; each shows only whether it is set, and typing replaces it.
The SAML tab is an exchange of documents rather than a form: download this cluster's SP metadata for the provider to import, paste the provider's metadata to have the entity ID, sign-on URL and signing certificate read out of it, and generate an SP keypair when the provider needs encrypted assertions or signed requests. Each provider has a test button, so a mistake surfaces before anyone tries to log in with it.
Settings are stored in the replicated metadata store and applied on every master with no restart.
Changing them needs AdministratorAccess; everyone else sees the page read-only. See
Single Sign-On.
KMS
Key management interface for viewing and managing encryption keys: create keys, rotate them and see each key's versions. Administrators only. Key IDs are trimmed and checked for duplicates before creation. No key material is ever shown.
Iceberg Health
Continuous table-health collection for every registered Iceberg table: small-file counts, snapshot
and manifest growth, delete-file accumulation, and the findings derived from them (NO_MAINTENANCE,
STALE_MAINTENANCE, MAINTENANCE_ERRORS and friends). Includes a time series per table so you can
see whether a problem is growing, and an effects view that shows what the last maintenance run
actually changed. Collection runs on the leader only — see
High Availability for why, and how to
aggregate the matching Prometheus gauges without double-counting across a failover.
Maintenance
Configure and monitor Iceberg table maintenance — snapshot expiration, data compaction, manifest rewrite, orphan-file cleanup, and position-delete consolidation. Per-table configuration covers per-operation toggles plus Spark-aligned parameters (target file size, compaction window_hours and min_input_files, snapshot retention and retain_last, orphan safety window) and a schedule that is either a fixed interval or a 5-field UNIX cron (e.g. 0 */2 * * *) which overrides the interval. A Manual Trigger runs any single operation (or all) on demand against a wildcard table pattern, with full job history.
Settings are validated on save, and an invalid value is reported rather than stored: snapshot retention
and retain_last at least 1, the orphan-file safety window at least 24 hours (shorter windows can delete
files a running write has not committed yet), and a valid 5-field cron expression. The table field
accepts any table name and suggests tables seen in health data and maintenance history. After Run
Now, the history refreshes until the run finishes.
Backup & Restore
Backup the cluster's KMS / IAM / metadata stores to S3 and restore from any prior backup. The page exposes manual Backup Now, a fixed-interval schedule, and a 5-field UNIX cron schedule (e.g. 0 2 * * *); the next-fire timestamp is shown next to an active cron so you can see at a glance when the next run will land. See Backup & Restore for the full flow.
Backup Now needs a saved backup configuration. The backup list reports the storage error — a missing bucket, wrong credentials — instead of showing an empty list. Scheduled and cron backups run on the leader only, so a multi-master cluster takes one backup per run.
REST API
All operations available in the Admin UI are also accessible via the REST API, enabling automation and integration with external tools. The full API is documented in OpenAPI 3.0 format.
Prometheus Metrics
Ontul exposes metrics at GET /metrics in Prometheus text format:
ontul_queries_total/ontul_queries_failed/ontul_queries_activeontul_latency_ms_sumontul_workers_total/ontul_workers_ready- JVM heap and thread metrics