> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sengol.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Compliance and risk

> Choose regulations, write the ControlBook, and produce reports an examiner can rely on.

You decide what "compliant" means for each agent, and you answer the
examiner. In Sengol, that decision lives in one file: the
[ControlBook](https://docs.sengol.io/concepts/controlbook.md).

**1. Get the right role**

`cro` writes ControlBooks and generates reports.

**2. Pick regulations**

29 ship in the catalog. Add your own internal policies.

**3. Write the ControlBook**

Map each obligation to evaluators and a threshold.

**4. Activate it**

One active ControlBook per install drives gates and reports.

**5. Generate a report**

Per agent, per period, signed by the appliance.

**6. Hand evidence to the examiner**

A report plus a verifiable evidence pack.

## 1. Get the right role

The **cro** role can write ControlBooks, generate reports, propose
erasures, place legal holds, and approve agent intakes. It cannot write
evidence or decide review items. An **approver** can be the second pair of eyes
for erasures and intakes.

Most of this journey works in the Console. Each step also shows the
command-line equivalent. Commands use your `sk_` token in
`SENGOL_API_TOKEN`; REST calls exchange it for a session token first:

```bash theme={null}
JWT=$(curl -s -X POST "$SENGOL_API_URL/v1/auth/token" \
  -H "Content-Type: application/json" \
  -d "{\"grant_type\":\"local_user_token\",\"secret\":\"$SENGOL_API_TOKEN\"}" \
  | python -c 'import sys, json; print(json.load(sys.stdin)["access_token"])')
```

## 2. Pick regulations

In the Console, open **Evidence → Library**:

![Library page listing evaluators and regulations](https://mintcdn.com/sengol/WAzD6pTFOjHwDiMz/images/console-library.png?fit=max\&auto=format\&n=WAzD6pTFOjHwDiMz\&q=85\&s=46b5a58fb3a68c287bcb66a1074ee370)

Library: the evaluator registry and the regulation catalog.

From the CLI:

```bash theme={null}
sengol policy list --region CA
```

```text theme={null}
POLICY_ID   REGION  INDUSTRIES         VERSION  DISPLAY_NAME
----------  ------  -----------------  -------  ---------------------------------------------
FINTRAC_6F  CA      BANKING            2023     FINTRAC Part 6F — AML/ATF Reporting Entity Obligations
OSFI_B10    CA      BANKING,INSURANCE  2024     OSFI B-10 — Third-Party Risk Management
OSFI_E23    CA      BANKING,INSURANCE  2026     OSFI E-23 — Model Risk Management
PIPEDA      CA      ALL                2024     PIPEDA — Personal Information Protection and Electronic Documents Act
```

See what a regulation asks for, and which evaluators Sengol suggests:

```bash theme={null}
sengol regulation show OSFI_E23
```

```text theme={null}
Obligations
ID       TITLE                                       APPLIES_TO
-------  ------------------------------------------  ------------------------
E23-1.1  Model Inventory and Classification          LLM_AGENT,TRADITIONAL_ML
E23-3.1  Generative AI Task Adherence                LLM_AGENT
E23-3.2  PII and Data Governance Controls            LLM_AGENT,TRADITIONAL_ML
...
Evaluator Routing
MODEL_CATEGORY  EVALUATOR                THRESHOLD
--------------  -----------------------  ---------
LLM_AGENT       HallucinationEvaluator   0.9
LLM_AGENT       PIIEvaluator             0.95
...
```

All 29 policy ids are listed in [the ControlBook concept
page](https://docs.sengol.io/concepts/controlbook.md#the-policy-catalog). To add an internal policy,
see [custom policies](https://docs.sengol.io/concepts/controlbook.md#custom-policies).

## 3. Write the ControlBook

Each obligation names the regulation it serves, the evaluators that test
it, and the pass rate it needs:

```yaml controlbook.yaml theme={null}
controlbook_id: acme-advisory-agents
display_name: Acme Bank - Advisory agents
organization: Acme Bank
version: "2026.1"
policies:
  - OSFI_E23
industry: BANKING
region: CA
default_risk_tier: HIGH
approved_by: Chief Risk Officer

obligation_mappings:
  - obligation_id: E23-3.2-pii
    policy_id: OSFI_E23
    description: Responses never disclose a customer's personal identifiers.
    risk_tier: HIGH
    evaluators: [PIIEvaluator]
    thresholds:
      pass_rate: 0.95
    severity: CRITICAL
    critical_veto: true
    coverage_required: true

  - obligation_id: E23-3.1-faithful
    policy_id: OSFI_E23
    description: Answers match the bank's source documents.
    risk_tier: HIGH
    evaluators: [FaithfulnessEvaluator]
    thresholds:
      pass_rate: 0.90
    severity: HIGH

  - obligation_id: E23-3.1-disclosure
    policy_id: OSFI_E23
    description: Every answer carries the required risk disclosure.
    risk_tier: HIGH
    evaluators: [RequiredDisclosureEvaluator]
    thresholds:
      pass_rate: 0.95
    severity: HIGH

gold_score_formula:
  formula_type: weighted_average
  app_weights:
    E23-3.2-pii: 0.4
    E23-3.1-faithful: 0.3
    E23-3.1-disclosure: 0.3
  minimum_floor: 0.80
  compliant_threshold: 0.90
```

Validate it locally. This needs no appliance:

```bash theme={null}
sengol controlbook validate --file controlbook.yaml
```

```text theme={null}
VALID — acme-advisory-agents (warnings: 0)
```

The validator warns when a CRITICAL obligation lacks `critical_veto` or
`coverage_required`. Every field is explained in [ControlBook](https://docs.sengol.io/concepts/controlbook.md).

## 4. Activate it

**In the Console**, open **Settings → General**. In the **ControlBook**
card, choose the YAML file, then **Validate & apply**. The card shows the
active id and version.

**Through the API:**

```bash theme={null}
python -c 'import json; print(json.dumps({"yaml": open("controlbook.yaml").read()}))' \
  | curl -s -X POST "$SENGOL_API_URL/v1/controlbook" \
      -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" -d @-
```

```json theme={null}
{"valid":true,"controlbook_id":"acme-advisory-agents","version":"2026.1","policies":["OSFI_E23"],"errors":[],"warnings":[],"persisted":true}
```

What activation changes:

* Reports use the active ControlBook.
* A developer's gate that names a ControlBook is refused unless it is the
  active one: `ControlBook acme-advisory-agents 2026.1 is not the active ControlBook`.
* To change a ControlBook, bump `version`. Re-uploading the same id and
  version with different content is refused with `409`.

Tell your developers the file path so they can set `suite.controlbook` in
their `sengol.yaml` ([CI gate](https://docs.sengol.io/guides/ci-gate.md)).

## 5. Generate a report

A report checks one agent's records over a period against every obligation.

**In the Console**, open **Evidence → Reports** and choose **Generate**.

**Through the API:**

```bash theme={null}
FROM=$(python -c 'import datetime as d; print(d.date.today() - d.timedelta(days=14))')
TO=$(python -c 'import datetime as d; print(d.date.today())')
curl -s -X POST "$SENGOL_API_URL/v1/reports/generate" \
  -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
  -d "{\"agent_id\":\"gic-advisor\",\"period_from\":\"$FROM\",\"period_to\":\"$TO\",\"format\":\"json\"}"
curl -s "$SENGOL_API_URL/v1/console/reports/gic-advisor_${FROM}_${TO}/download" \
  -H "Authorization: Bearer $JWT" -o report.json
```

Read the result:

```python theme={null}
import json

report = json.load(open("report.json"))
print("overall_passed:", report["overall_passed"])
for o in report["obligation_results"]:
    print(f"  {o['obligation_id']}: passed={o['passed']} "
          f"rate={o['actual_pass_rate']:.2f} threshold={o['threshold']} {o['failure_modes']}")
```

```text theme={null}
overall_passed: False
  E23-3.2-pii: passed=True rate=0.96 threshold=0.95 []
  E23-3.1-faithful: passed=True rate=0.93 threshold=0.9 []
  E23-3.1-disclosure: passed=False rate=0.88 threshold=0.95 ['PASS_RATE_BELOW_THRESHOLD']
```

The demo agent misses its disclosure obligation: 88% of answers carried the
disclosure against a required 95%.

The Console's **Govern → Governance** page shows the same coverage live.
Enter the agent id and choose **Load**:

![Governance coverage table: two obligations met, disclosure below threshold](https://mintcdn.com/sengol/WAzD6pTFOjHwDiMz/images/console-governance.png?fit=max\&auto=format\&n=WAzD6pTFOjHwDiMz\&q=85\&s=d315f223e1f10b022dbb00f9c053dbb7)

Governance → Coverage for gic-advisor under the active ControlBook.

Generated reports are listed under **Evidence → Reports**, with a download
link for each:

![Reports page with the generate form and report history](https://mintcdn.com/sengol/WAzD6pTFOjHwDiMz/images/console-reports.png?fit=max\&auto=format\&n=WAzD6pTFOjHwDiMz\&q=85\&s=f3364683049b1c50d0b741926339df3c)

Reports: generate, then download the signed report.

How to read failure modes:

| Failure mode | Meaning |
| - | - |
| `PASS_RATE_BELOW_THRESHOLD` | Evidence exists, and too much of it failed. |
| `NO_RECORDS_OBSERVED` | No record in the period ran this obligation's evaluators **and** carried its `policy_id`. Fix the agent's instrumentation or the obligation's `policy_id`. |

> [!WARNING]
>
> Generate reports of record in the Console or through the API. Those are
> HMAC-signed by the appliance. `sengol report generate` runs locally and
> produces a report with an **empty** signature.

`format: "pdf"` adds a PDF beside the JSON when the appliance has the
`reports` extra installed.

## 6. Hand evidence to the examiner

Give the examiner:

1. the signed report,
2. the evidence pack for the same agent (`sengol export --agent gic-advisor --out pack.json`),
3. the appliance's public key, through a separate channel.

The [auditor journey](https://docs.sengol.io/journeys/auditor.md) shows what they will do with them.

## Approve an agent intake

A developer proposes each new agent as an intake. `cro`, `approver` and
`admin` can approve it, and approving needs an interactive sign-in: SSO, a
password, or `sengol login --device`. An `sk_` token or the bootstrap-secret
token cannot approve.

Open **Govern → Intakes** and select the intake. The page shows the
developer's answers, the **Approvals on this revision** panel and an
**Actions** card.

* **Approve** registers the agent. It issues no credential. Approval signs the decision and the agent's first risk tier.
* **Revise** saves your edits as a new revision. Earlier approvals stop counting.
* **Reject** needs a reason. The agent id stays reserved.
* **Withdraw** is for the person who submitted it.

![Intake detail page for gic-chat-advisor with the review progress card, approvals panel and Approve, Revise, Reject and Withdraw buttons](https://mintcdn.com/sengol/WAzD6pTFOjHwDiMz/images/console-intake-detail.png?fit=max\&auto=format\&n=WAzD6pTFOjHwDiMz\&q=85\&s=0986f11271198d186459094f41b91527)

An intake routed by a review policy: its answers, Review progress, Approvals on this revision and Actions.

The panel reads "Approved: N required, counting the person performing the
step." N is the approver count for the intake's risk tier. It is 1 until an
operator raises it, and then you cannot approve alone. Other approvers use
**Cast approval** on the same page first. The **Override reason** box matters
only if your organization set the same-person rule to `reason`. See [Set who
approves intakes](https://docs.sengol.io/journeys/platform-operator.md#7-set-who-approves-intakes).

If a token submitted the intake, the requester must confirm it first. Until
then approving answers `requester_unconfirmed`.

If a [review policy](https://docs.sengol.io/guides/review-workflows.md) routed the intake, its
mandatory reviews must be cleared before you can approve.

## Erasure requests

A data-subject erasure needs two people:

1. A `cro` proposes it: `POST /v1/audit/erasure/requests` with the trace ids, a subject reference and a reason. Nothing is purged yet.
2. A different person with `erasure:approve` (an `approver` or `admin`) approves it at `POST /v1/audit/erasure/requests/{id}/approve`.

Approval purges the raw trace content and stored reason text, and writes a
signed tombstone naming both people. The signed evaluation records stay and
still verify. The Console's **Settings → Erasure** tab shows the ledger.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.