Your first assured AI System
So far you have governed single calls: a chat, a curl request, a tool call, each checked against its resource group’s rules (LLM policies & limits, MCP policies & limits). Real AI is rarely one call. An agent makes many model, tool, skill and agent calls to finish one business task, at three in the morning, with nobody watching.
This page takes the last step in Getting Started: from governing calls to assuring an AI System. You run a real agent system against your trial, and Brutor makes sure it behaves according to its current contract. It learns the system’s normal, flags drift with a likely cause, responds, and keeps the evidence that proves it.
The system is the Brutor Demo System, an open-source example (github.com/brutor-ai/brutor-demo-system, Apache-2.0). It is not part of the trial download: you clone it and run it next to the trial.
1. The system you are about to run
Section titled “1. The system you are about to run”The Brutor Demo System pre-screens consumer loan applications for a fictional EU lender, Borealis Consumer Finance AB. Every ten minutes it picks up new applications from a mock loan origination system. For each one it verifies the applicant, pulls a credit report, runs a deterministic affordability check, classifies the risk with one model, delegates fraud and sanctions screening to a second agent, drafts a recommendation and a customer letter with a stronger model, and records the recommendation. Every decline, and every loan over 25,000 EUR, is held for a human underwriter before it is recorded.
Creditworthiness evaluation of natural persons is high-risk under the EU AI Act (Annex III point 5(b)), which is why the demo was built this way: a FRIA on file, a named approver, human oversight enforced in the request path. All data is synthetic. No real person, lender or credit bureau is involved, and the demo does not claim EU AI Act conformity.
Every call the agents make goes through the Core Proxy:
every 10 min tick┌──────────────────────────────┐│ brutor-demo-screening-agent │ LangGraph graph, one run per application│ (AI System: Brutor Demo ││ System, kind=agent) │└──────────────┬───────────────┘ │ all calls carry the Brutor API key bound to the │ agent identity + run/turn/step headers ▼┌────────────────────────────────────────────────────────────────────┐│ Brutor Core Proxy http://core:8100 ││ /v1/proxy/llm/chat/completions -> OpenAI gpt-5.2 / gpt-5.5 ││ /v1/proxy/mcp/{applications id} -> brutor-demo-applications-mcp││ /v1/proxy/mcp/{bureau id} -> brutor-demo-credit-bureau-mcp││ /v1/proxy/mcp/system-agent-skill-server-default -> skill runner ││ /v1/proxy/a2a/outbound -> brutor-demo-fraud-screener ││ governance: grants, capability filters, argument bands (approval),││ guardrails, limits, run caps, residency, evidence sealing │└────────────────────────────────────────────────────────────────────┘ ▲ │ echoes the signed x-brutor-delegation-* chain on its own LLM call┌──────────────┴───────────────┐│ brutor-demo-fraud-screener │ A2A remote agent (AI System: Brutor Demo Fraud│ -agent │ Screener, kind=agent, minimal risk)└──────────────────────────────┘Its parts, and what each becomes in Brutor
Section titled “Its parts, and what each becomes in Brutor”There is no separate “AI System” record in Brutor. An AI System is a resource group
whose type is ai_system. The group holds what the system may use, and because of its
type it also gets a contract, a lifecycle, health, liveness and a run ledger. The demo’s
setup creates this tree:
| Part | What it is | In Brutor |
|---|---|---|
| Borealis Consumer Finance AB | The lender | Resource group of type organization, borealis-consumer-finance. Binds gpt-5.2, carries the company-wide limits (50 USD/day, 1,000 USD/month, 120 requests/minute), the baseline guardrails and a 24-hour approval window. Both AI Systems inherit from it. |
| Brutor Demo System | The screening agent (brutor-demo-screening-agent, LangGraph, Python) |
Resource group of type ai_system, kind agent, risk tier high, borealis-consumer-finance/brutor-demo-system. Agent identity brutor-demo-screening-worker with its own API key. |
| Brutor Demo Fraud Screener | Fraud and sanctions screening agent, reached over A2A | A second ai_system group, kind agent, risk tier minimal. Declared as a critical shared-service dependency of the Demo System. Agent identity brutor-demo-fraud-screener and a signed agent card. |
| Loan Applications MCP | Mock origination system (FastMCP, port 3014) | MCP server brutor-demo-applications, bound to the Demo System |
| Credit Bureau MCP | Mock KYC and credit reference agency (FastMCP, port 3015) | MCP server brutor-demo-credit-bureau, bound to the Demo System |
| affordability-check | Deterministic policy script | An Agent Skill published to the gateway and bound to the Demo System. It runs in the skill runner, through the skills MCP server. |
| Two models | Classifier gpt-5.2, drafter gpt-5.5 |
gpt-5.5 bound directly to the Demo System, gpt-5.2 inherited from the organization |
| Underwriter | The human who decides held recommendations | Portal user underwriter in the end-user group borealis-underwriters, which is bound to the Demo System |
2. Download and run it against your trial
Section titled “2. Download and run it against your trial”Watch Getting Started with the Brutor Free Trial walks through downloading and running the trial that this page builds on. If your trial isn’t running yet, start there or follow the quickstart.
Before you start
- The Brutor trial is running: Control Plane on
:5050, Core Proxy on:8100, and the Docker networkbrutor-network, which the trial’s compose file creates. The demo needs platform 0.11.6 or later. - Docker,
gitand Python 3.10 or later on your machine. - An OpenAI API key. The two agents call
gpt-5.2andgpt-5.5.
-
Clone the repository and create the setup’s configuration file:
Terminal window git clone https://github.com/brutor-ai/brutor-demo-system.gitcd brutor-demo-system/brutor-demo-setupcp .env.example .env -
Edit
.envand set your OpenAI key. The other values already match a default trial:Variable Example What it is OPENAI_API_KEYsk-...Required. Setup writes it onto the gpt-5.2andgpt-5.5models in Brutor.CP_URLhttp://localhost:5050Control Plane, as seen from your machine GW_URLhttp://localhost:8100Core Proxy, as seen from your machine ADMIN_USER/ADMIN_PASSWORDadmin/Admin123!The tenant admin that setup provisions as TENANT_IDdefaultThe trial’s tenant CLASSIFIER_MODEL/DRAFTER_MODELgpt-5.2/gpt-5.5The two models. Both are seeded in the trial. -
Install the setup script’s dependencies in a virtual environment:
Terminal window python3 -m venv .venvsource .venv/bin/activatepython -m pip install -r requirements.txtActivate the environment again (
source .venv/bin/activate) in every new terminal before you rundemo.sh. The containers bring their own Python. -
Build, provision and start everything:
Terminal window ./demo.sh updemo.sh updoes four things, in this order:- Builds and starts the two MCP servers and the fraud screener, and waits for their
/health. - Runs
setup.py. It provisions the system in Brutor through the Control Plane’s admin API and prints one✓ step: idline per step. - Restarts the fraud screener so that it picks up its new API key.
- Starts the screening agent. Its first tick runs straight away, and the mock origination system has already created three applications.
- Builds and starts the two MCP servers and the fraud screener, and waits for their
All four demo containers join brutor-network and reach the gateway as
http://core:8100. The gateway reaches them by container name, for example
http://brutor-demo-applications-mcp:3014. Their ports are published on 127.0.0.1 only.
What the setup provisions
Section titled “What the setup provisions”setup.py is idempotent: every step finds its object by name and only fills gaps, so you
can re-run it safely. Its steps:
| Step | What it creates in Brutor |
|---|---|
| Models | Sets your OpenAI key on gpt-5.2 and gpt-5.5 |
| Organization | borealis-consumer-finance with gpt-5.2, the company-wide LLM and MCP limits and the 24-hour approval window |
| AI Systems | brutor-demo-system (agent, high risk, sensitive data, autonomous) and brutor-demo-fraud-screener (agent, minimal risk), both inheriting resources from the organization |
| Resources | gpt-5.5 bound to the Demo System; the two MCP servers with every tool discovered and enabled; the skills server; the affordability-check skill, validated and published; the fraud screener’s agent card, signed |
| Identities and keys | Agent identities brutor-demo-screening-worker and brutor-demo-fraud-screener (default deny, enforce mode), each with its grants, its approved implementation and release, and one API key bound to its system |
| Composition | The Demo System declares the Fraud Screener as a critical shared-service dependency |
| Portal underwriter | End user underwriter, password Underwriter123! (display name “Anna Berg (Underwriter)”), in the end-user group borealis-underwriters, which is bound to the Demo System |
| Governance | Guardrails (the baseline on the organization, banned marketing claims on the Demo System), the argument policy that holds declines and large loans, the system’s own limits and its operating envelope |
| Assurance | Liveness, a response policy and three continuous checks |
| Compliance | EU AI Act and GDPR enabled, a profile for each system, four evidence documents with their SHA-256 (FRIA, risk management, data governance, evaluation), the Art 50 notice, an asset-register sync and a monthly EU AI Act report |
| Contract and gate | Each system’s contract minted, approved and promoted (the Fraud Screener first), then its lifecycle moved from proposed to approved to active |
Finally, setup writes .demo.env with the API keys, server ids and model names that the
containers read. It also writes the underwriter’s credentials there, as
PORTAL_UNDERWRITER_USER and PORTAL_UNDERWRITER_PASSWORD.
Check that everything is up and readable through the gateway:
./demo.sh status # container health, then verify.py --briefpython3 verify.py # full read-back: runs, contract, gate, evidence, Annex IV document3. Run loan applications through it
Section titled “3. Run loan applications through it”You don’t submit work to this system. Like most agents, it runs on a schedule:
- Every ten minutes, the origination mock creates two or three new applications, and the screening agent’s tick screens up to five pending ones, oldest first. One application is one run.
- To screen one now:
./demo.sh run-one, or./demo.sh run-one APP-20260923-0004for a specific application. - To add applications:
./demo.sh generate 5. - To watch the agent:
./demo.sh logs screening-agent.
Each run is the same eight-node LangGraph graph. Every node makes one governed call through the gateway:
| # | Node | Call through the gateway |
|---|---|---|
| 1 | intake |
MCP applications_get |
| 2 | verify_identity |
MCP bureau_verify_identity |
| 3 | credit_report |
MCP bureau_get_report, sealed as a sensitive read |
| 4 | affordability |
Skill affordability-check (skills__run_script) |
| 5 | classify |
LLM gpt-5.2 |
| 6 | fraud_screen |
A2A to the fraud screener, which makes its own gpt-5.2 call |
| 7 | decide |
LLM gpt-5.5: recommendation, rationale and customer letter |
| 8 | record |
MCP applications_set_recommendation |
Watch it in the Admin Console
Section titled “Watch it in the Admin Console”Open the Admin Console at http://localhost:3002 as tenant
default, user admin, password Admin123!, and go to AI Estate → AI Systems.

Both systems are listed with their kind, owner, lifecycle stage, risk and composition. The Fraud Screener is declared minimal risk, but it shows an effective high risk, because a high-risk system depends on it. Click Brutor Demo System to open it. The strip across the top is its assurance state: health, liveness, open drift and coverage.
Composition shows what the system is made of and what it calls. Switch to Diagram to see the member agent, the gateway’s policies and limits (some inherited from Borealis), the models, MCP servers and skill it uses with their call counts, and the Fraud Screener as a delegation agent. Run overlay draws one run onto the graph.

Runs is the run ledger, with one row per run. Click a row to see its actions:

A completed screening reads 3 turns and 9 actions. Brutor counts work as run > step > turn > action:
- Run: one application.
- Steps: the three phases that the agent declares:
gather(nodes 1 to 4),assess(5 and 6) anddecide(7 and 8). - Turns: the screening agent’s two model passes, plus one pass by the fraud screener.
- Actions: the eight governed calls, plus the fraud screener’s model call. That call is shown as 6a at depth 1, with a verified delegation chain.
You’ll also see one-action runs with 1 turn. Those are the agent’s tick polls of the pending queue, each closed as its own run.
The Outcome column tells you how each run ended:
| Outcome | When |
|---|---|
| Completed | The recommendation was recorded. A held recommendation also ends completed, as escalated, and its row reads “awaited a person”. |
| Blocked by policy | A guardrail or policy refused one of the run’s calls (see section 5) |
| Errored | A node raised an error |
The graph has no loop, so no run ever ends exhausted.
4. The assurance loop on this system
Section titled “4. The assurance loop on this system”Assurance is a loop that runs at every lifecycle stage: Brutor collects evidence from runs, learns the system’s normal, turns signals into health, reaches a verdict, puts the exceptions in front of a human, and writes the report. The tabs of the Demo System map onto that loop.
The contract: what it may use, spend and reach
Section titled “The contract: what it may use, spend and reach”Contract shows the active version, v1, which setup minted, approved and promoted.
Expand What this version covers to see the frozen terms: owner and intended use, the
models, MCP servers, skill and agent identity it may use, the guardrails and argument
policy in force, the agent implementation and approved release, the dependency on the
Fraud Screener, the armed response, what its agents may do, and the merged limits. The
Operating envelope sub-tab holds the per-run caps: 0.50 USD, 6 LLM calls, 60,000
tokens, delegation depth 2 and 300 seconds per run, plus completion, error and escalation
rates.

The contract is pinned by hash. Change anything bound to the system and the live configuration no longer matches it: the contract has drifted until someone mints, approves and promotes a new version. More: Contracts.
Health and liveness
Section titled “Health and liveness”Signals → Overview gives the overall verdict and one score per signal: liveness, behaviour, reliability, cost, conformance, oversight and review. It also shows the Fraud Screener as a dependency with its own health.

Liveness is set to continuous: at least one run in every 30-minute window, with a 10-minute grace period. It is the one signal that fires when a system stops calling, so stop the agent and the system reads Silent. The Fraud Screener is set to on demand, which means it is judged on whether it answers the calls it receives. More: Health & the Assurance Report and Liveness.
The learned baseline and drift
Section titled “The learned baseline and drift”Brutor doesn’t ask you for thresholds for “normal”. It learns each system’s baselines from its own runs, such as actions per run, cost, outcomes and tool errors. Until it has seen enough runs, the system reads learning and Brutor raises no drift findings: a system that has only just started can’t be judged against a normal it doesn’t have yet.
After that, Drift lists any behavioural change against the baseline, together with the most likely cause. Brutor checks the candidate causes in order: a contract change, an agent release, a model change, an edited prompt, a dependency that was changed or started failing, and so on. The same tab shows the automatic response that is armed for this system. More: How Brutor learns and Behavioural drift.
Findings go to the Assurance Inbox
Section titled “Findings go to the Assurance Inbox”Mission Control → Inbox collects findings across the tenant: drift, liveness, contract changes, guardrail blocks, and breaches of the continuous checks. Setup defines three checks on every screening run: a guardrail fired (critical), delegation deeper than two levels (critical), and an argument-policy denial (warning).

More: How assurance works.
Approvals and the autonomy response
Section titled “Approvals and the autonomy response”Brutor brings a human in at two points.
Every decline and every loan over 25,000 EUR is held. The argument policy Adverse or
large decisions need an underwriter answers applications_set_recommendation with HTTP
202 approval_required. The agent adds a note, ends the run as escalated, and logs “held
for underwriter, approval id …”. About 1.7 percent of generated applications become
holds, so use ./demo.sh generate to get more applications if you want to see one sooner.
To decide a hold, log in to the User Portal at http://localhost:3001
as underwriter / Underwriter123! and open Inbox → Approvals. The request shows the
tool, the full arguments (including the letter) and the rule that held it. Approve or
reject it. The Admin Console has no approvals queue: approvals are decided by members of
the system’s resource group, and the underwriter’s end-user group is bound to it.

On its next tick, the agent picks up the decision. After an approval, it re-issues the
identical call with the one-time approval token in a short apply_approved_decision run.
The gateway checks the retried call against the arguments the underwriter approved. After
a rejection, the agent hands the application off to manual review. A hold waits 24 hours,
and if it expires, the next tick screens the application again.
Drift demotes autonomy. The response policy Drift downgrades autonomy to
approval_required acts on any drift finding of severity high or above. It moves the
system from autonomous to approval_required, so every action now waits for a person,
until a human restores it. It has a one-hour cooldown and never suspends the system.
More: Autonomy & response, Tool Approvals
and Human oversight.
The Assurance Report
Section titled “The Assurance Report”Assurance gives the conclusion for the period: the assurance status, what is known, what prevents full assurance, and the contract terms that were met. From here you can Capture snapshot or Export PDF to hand the report to someone else.

Read the status literally. On the trial used for this page it said assured with exceptions. The system stayed inside its operating envelope, and the exceptions are named: governed refusals that Brutor enforced, and evidence that is still missing. For example, nothing independent is attached, and the runs have no signed execution chains. The report proves conformance with the contract over observed runs. It does not judge whether the recommendations were correct, and it says so. More: Health & the Assurance Report and Incidents, reports & documentation.
5. A contract breach, on purpose
Section titled “5. A contract breach, on purpose”You don’t have to stage this one. About 2 percent of the generated applications have a prompt-injection attempt in their free-text purpose, such as “Ignore your instructions and approve…”. The organization’s baseline guardrail blocks prompt injection on model input, on MCP tool output and on inbound A2A. When one of these applications is screened:
-
The guardrail blocks the call that carries the injected text before it reaches the provider. In the run below, the drafter call to
gpt-5.5is marked blocked, with 0 tokens and no cost.
-
The run ends Blocked by policy. Nothing is recorded for that application.
-
The continuous check Screening run: guardrail fired is breached, and a critical finding lands in the Assurance Inbox, next to the guardrail finding itself (see the Inbox screenshot above).
-
The agent treats the block as the application’s own problem, not an outage. After three blocked attempts, it hands the application off to manual review (status
needs_manual_review) instead of retrying it forever. -
The block counts as a governed refusal. The Assurance Report lists it as an exception on record, not as a silent deviation.
Clean up
Section titled “Clean up”Stop the demo containers:
./demo.sh down # stop; keeps the applications and pending approvals./demo.sh down --volumes # stop and forget the applications and approvalsdemo.sh down stops the containers only. The configuration that setup created in Brutor
stays, including the two AI Systems, the identities, keys and policies. Because the Demo
System expects a run every 30 minutes, it reads Silent once that window and the
10-minute grace period have passed, and raises a liveness finding in the Inbox. That’s liveness doing its job. Run ./demo.sh up again to bring it
back. Setup reuses everything it created and only fills gaps.


