Govern¶
Content safety, alerting, the audit trail, reporting, and the admin sign-in log. This is how you keep the gateway honest and prove it.
Guardrails¶
Where: Govern ▸ Guardrails · Who: everyone views;
MANAGE_GATEWAYto manage · Editions: all (regex); Pro AI for remote AI-classifier detectors
Guardrails screen content in both directions: the prompt on the way in, and the model's reply on the way out. They are per-org with nothing seeded; a fresh org starts empty and the apply recommended button plants a sensible starter set through the normal create calls.
Three building blocks:
- Category is a named bucket of sensitive content, like
PII,SECRET, or a customcompetitor-names. It is a free key you define, not a fixed list. A category can be marked an indicator (an IOC like an IP/domain/hash) so its matched value is kept on events for threat intel. - Detector is what finds the content. A
REGEXdetector runs in-process (a pattern plus a validator, like a Luhn check for card numbers). AREMOTEdetector calls an out-of-band AI classifier: a document guard (Llama Guard / Granite / ShieldGemma: one call returns a safe/unsafe verdict) or a span analyzer (Presidio: entity offsets you can redact). Remote detectors need the Pro AI sidecars. - Policy maps a category to an action for a scope and direction:
ALLOW,FLAG(record only),REDACT(mask the match), orBLOCK. Redaction has strategies (MASK,HASH, reversibleTOKEN,PARTIAL), and each policy has a fail-closed posture (block the call if a detector cannot finish, rather than proceed with partial findings).
How a screening decision is made¶
Detectors find spans; the policy for the most specific scope that covers the matched category decides what happens. The actions escalate, and REDACT carries a strategy:
flowchart LR
TXT["Text (prompt or reply)"] --> DET["Detectors for the org<br/>(regex in-process · remote AI classifier)"]
DET -->|"no match"| PASS["Pass through"]
DET -->|"match → category"| POL["Resolve policy<br/>(most-specific scope, this direction)"]
POL --> ALLOW["ALLOW → pass"]
POL --> FLAG["FLAG → pass + record"]
POL --> REDACT["REDACT → mask the span<br/>MASK · HASH · TOKEN · PARTIAL"]
POL --> BLOCK["BLOCK → stop the call"]
REDACT -. "reversible TOKEN" .-> RESTORE["restored for the client at the end"]
| Strategy | Effect | Use when |
|---|---|---|
MASK |
Replace with a fixed mask (e.g. ****) |
You never need the value back |
HASH |
Replace with a stable hash | You want to correlate occurrences without the value |
TOKEN |
Reversible placeholder, restored for the client | The model needs coherent text but the value must not reach the vendor |
PARTIAL |
Keep part (e.g. last 4 digits) | A human needs to recognise it |
Fail-open vs fail-closed
If a detector cannot finish scanning (for example a remote classifier times out), a fail-closed policy blocks the call rather than letting unscanned content through; a fail-open policy proceeds. Set this per policy by sensitivity: fail-closed for SECRET, perhaps fail-open for a low-stakes category.
The three tabs¶
- Policies (category-centric). Pick a category and set its request and response actions, the mask strategy, and the fail-closed posture. You see the detectors feeding it and a posture dot per category.
- Detectors (the catalogue). Enable, edit, or delete detectors.
REGEXtoday;REMOTEfor the AI classifiers. - Test (a dry run). A no-model pipeline that screens a prompt you type, lets you edit the model's hypothetical reply to inject what it might say, and re-screens it as the response, showing the reversible-redaction round-trip restored at the end. Nothing is sent to a model.
Tool-call arguments are never redacted
Redacting the arguments of a tool/function call would break the tool. Guardrails detect and flag or block tool-call content and surface it to the operator, but never mutate it. Plan policies accordingly.
Where guardrails run in the request
Request screening happens before routing; response screening happens after the vendor replies, including inline on streams (a match spanning two chunks is carried over, and a block stops the stream). It runs on /v1/chat/completions, /v1/responses, and /v1/messages. Internals: guardrails.
Notifications¶
Where: Govern ▸ Notifications · Who: members view;
MANAGE_GATEWAYto manage · Editions: all
Notifications deliver governance alerts off the same event stream, per-org, over three channel types. Four tabs:
- Channels. Create email, webhook (HMAC-signed, SSRF-safe), or in-app channels, each with a test-send; the list is paginated. An in-app channel targets org-wide (a broadcast to everyone's bell) or a chosen set of members / teams / projects, and delivers to the bell and/or email.
- Routing. Per-scope, most-specific-first rules that map an event category to a target. A rule can fan out to an audience (a role group) and several channels at once - each becomes its own line under the trigger, toggle-able and removable on its own. The severity control adapts to the category: a min-threshold for SIEM/detection events, fixed labelled choices (e.g. All reports / Only failures) for the dedicated ones. The list is paginated by rule. Apply recommended plants a starter set.
- Delivery log. What was sent, to which channel, and whether it succeeded.
- Preferences. Noise controls: a dedupe cooldown and a quiet-hours window with a timezone.
CRITICALalways sends regardless.
Email needs an SMTP server
Email channels send through the org's mail server, configured in Settings ▸ Mail server; its Test button sends to your signed-in email, so you confirm delivery end to end. Webhooks need no external mail, and an in-app channel only needs it when its email delivery is switched on. Internals: notifications.
Audit & usage¶
Where: Govern ▸ Audit & usage · Who: members view (scoped to their org) · Editions: all
This is the durable record of every call, and the spend rollups built from it. Two things on the page:
- Calls. A filterable, paged audit table: time range, model, and outcome. Each row is one completion attempt, with its tokens and (if the deployment was priced) its cost.
- Spend by model. A bar chart of cost / tokens / calls grouped by model.
How to query it¶
The page is a face over the analytics read API; you can hit the same endpoints directly:
| Endpoint | Returns |
|---|---|
GET /admin/usage/calls |
Filterable audit rows (time / model / outcome) |
GET /admin/usage/spend |
Cost, tokens, calls grouped by model |
GET /admin/usage/spend-series |
The same per time bucket (hour / day / week / month) |
GET /admin/usage/activity |
Call/token density by hour-of-day × day (the heatmaps) |
All of them are org-scoped and accept an optional team/project filter, so the read narrows to the selected scope. A member is forced to their own org; the platform admin may pass a scope. This same data feeds the budget explorer trends and the rate limits activity tab.
Where the audit trail physically lives (it follows the tier)
On Core / Base, the system of record is the Postgres audit_event table, month-partitioned, with opt-in retention (aim.retention.audit-months, default keep-forever). On Pro (SIEM on), OpenSearch becomes the audit system of record and these reads are served from it, with longer retention and snapshot archival. The page and its API are identical either way. Internals: analytics.
Reports¶
Where: Govern ▸ Reports · Who:
VIEW_REPORTSto view;MANAGE_REPORTS(operator and up) to author / schedule / run · Editions: all
Reports turn the usage data into shareable artifacts: branded PDF, CSV, JSON, or HTML, on demand or on a schedule. A report has a definition (saved spec) and produces runs (generated artifacts, stored with a TTL).
Anatomy of a report definition¶
- A dataset (what to summarise): usage / spend by model, activity, guardrail findings, and similar curated datasets. You pick from a typed catalogue, not raw SQL.
- A scope and time window (org / team / project; this period vs last).
- A format (PDF / CSV / JSON / HTML), an optional schedule (cron-like), and a delivery mode: link only (the secure default - a finished report notifies with just a dashboard link, no data leaves) or attach the file (email attaches it, a JSON webhook posts it). Delivery routes through Notifications; the link opens the Generated tab to download.
- An optional password that protects the downloaded artifact itself: a native-password PDF, a self-decrypting HTML page, or a
.gpgfile for CSV/JSON. (Separately, every stored artifact is always encrypted at rest by the object store; the password is the extra, recipient-facing layer.)
You pick the dataset from a typed catalogue, never raw SQL, so a report definition is portable across the Postgres and OpenSearch backends unchanged:
| Dataset | Summarises | Typical format |
|---|---|---|
| Spend by model | Cost / tokens / calls grouped by model, with period-over-period deltas | PDF / CSV |
| Usage over time | The same per time bucket (hour / day / week / month) | PDF / CSV |
| Activity | Request density by hour and day | |
| Guardrail findings | Categories flagged / redacted / blocked | CSV / JSON |
How a report is produced¶
flowchart LR
DEF["Definition (saved spec)"] -->|"Run now / schedule"| Q["Query the usage data<br/>(typed dataset + filter + group-by)"]
Q --> R["Render<br/>PDF · CSV · JSON · HTML"]
R --> SEAL["Seal if a password is set<br/>(PDF pw · self-decrypting HTML · .gpg)"]
SEAL --> STORE["Object store (encrypted at rest)<br/>+ a report_run row, with TTL"]
STORE --> DL["Gated download / delivery"]
A scheduled definition fires on its cron; an on-demand one fires on Run now. Either way it produces a run (the artifact plus its metadata), which is what you download.
Example: a weekly cost report, emailed as a PDF¶
- New report → name it "Weekly cost digest."
- Dataset: spend by model. Scope: your org. Window: last 7 days, with period-over-period deltas on the KPI tiles.
- Format: PDF (branded with your org logo and accent colour).
- Schedule: every Monday 08:00. Delivery: attach the file so the PDF rides the email (leave it on link only to send just a dashboard link instead). Recipients are whoever your Notifications routing sends the
reportcategory to. - Optionally set a password, so the emailed PDF needs it to open.
- Save. Hit Run now to generate one immediately; the card shows "Generating…", then "Last generated …" when ready, with a Download action.
# The same, via the API:
curl -X POST https://your-gateway/admin/reports/definitions \
-H "Content-Type: application/json" --data @report.json
# then trigger a run:
curl -X POST https://your-gateway/admin/reports/definitions/{id}/run
Storage, TTL, and the artifact
Generated bytes live in the object store (filesystem on Core, S3-compatible on Pro/Distributed), not the database, with a TTL (default one day) and per-org ceilings. Downloads stream through the gated controller, so tenant isolation holds. Internals: reports and object store.
Sign-in activity¶
Where: Govern ▸ Sign-in activity · Who:
VIEW_SECURITY· Editions: all
A filterable, paged log of admin sign-ins: successes and failures, with actor search. This is where you spot failed or suspicious access to the admin plane (brute-force attempts, logins from unexpected actors). It reads GET /admin/auth-events. It complements SIEM, which covers the data-plane and governance event stream.
Next: Investigate → (SIEM, UEBA, SOAR), or Settings & Profile →.