Domain objects
The platform has ~12 domain objects that do real work. If you can name each one and explain in one sentence what it's for, you can reason about any code path. This page is that glossary.
Conventions
- Hot path = request-time, per-LLM-call.
- Control path = admin/dashboard, per-user-action.
- All paths below are rooted in
tappass/tappass/.
First-class citizens — if you remember five, remember these
Section titled “First-class citizens — if you remember five, remember these”These are the load-bearing objects. Every other domain class is either a field on one of these, or a helper that one of these consumes.
| Object | One-line purpose | Where you meet it first |
|---|---|---|
| Agent | The governed AI workload — what TapPass regulates | Every request: ctx.agent; every route: resolve_agent_or_404 |
| Pipeline | The ordered governance plan this agent runs under | Compiled once per request by the runner; defines which steps fire |
| Decision | The policy outcome (allow / block / escalate / modify / dispatch) | Emitted by OPA + the in-process dispatcher; shapes the response |
| Mandate | Signed authorization, minted when a Decision allows | Embedded JWS on Decision.mandate; AP2-compatible wire format |
| AuditEvent | The tamper-evident trail that everything decides against | Written by every step that matters; hash-chained + Ed25519-signed |
Everything else — PipelineContext, Detection, AgentSession,
AgentPact, AgentTrustProfile, MandateCapability — either
carries state between these five or provides evidence one of
them needs. Read the five, then the rest will fall into place.
flowchart LR A[Agent] -->|governed by| P[Pipeline] A -->|session of| S[AgentSession] P -->|produces findings| DT[Detection] DT -->|evaluated by OPA| DEC[Decision] DEC -->|if allow, mints| M[Mandate] A -.->|every action emits| AE[AuditEvent] P -.-> AE DEC -.-> AE M -.-> AE classDef first fill:#2a3555,stroke:#6f8bd8,stroke-width:2px,color:#fff classDef second fill:#1b2136,stroke:#394668,color:#cfd4e6 class A,P,DEC,M,AE first class S,DT second
Bold blue = the five first-class citizens. Dotted lines = "emits audit". Solid lines = data dependency.
Identity & tenancy
Section titled “Identity & tenancy”models/agent.py:43 — Pydantic model. The governed entity.
An Agent is an AI workload that makes LLM calls under our supervision.
Every agent belongs to exactly one org and has a slug (agent_id,
human-readable, mutable over time) and a UUID (id /
agent_uuid, immutable, what APIs address).
Key fields: id, agent_id, org_id, owner, framework,
declared_capabilities, active, governance_paused, metadata.
Created by /api/agents/onboard; the UUID is the stable identifier —
never key off the slug in code that crosses a rename boundary.
AgentSession
Section titled “AgentSession”models/agent.py:76 — Pydantic model. A conversation thread.
Groups consecutive LLM calls from one agent into one session so we can aggregate cost, detections, and escalation state. Bounded by inactivity timeout + explicit session-end.
Key fields: session_id, org_id, agent_id, message_count,
started_at, last_activity, max_classification, block_count,
violation_count, escalated.
One AgentSession per conversation; many AuditEvents roll up to
one session.
Governance configuration (control path)
Section titled “Governance configuration (control path)”Pipeline
Section titled “Pipeline”models/pipeline.py:50 — Pydantic model. The ordered governance
plan for an agent.
A Pipeline is a tree: categories → steps. It's compiled from the org's OPA policy, the agent's Profile, and any per-agent overrides. The runner walks it top-to-bottom for every LLM call.
Key fields: id, org_id, name, categories, assigned_agents,
enforcement_mode, version.
Stored in pipelines Postgres table. Per-agent mapping is a second
lookup in the org's policy store.
AgentPact
Section titled “AgentPact”registry/pact.py:55 — dataclass. The declared contract.
A Pact is what the operator promises about the agent: its purpose, max data classification, allowed tools, cost envelope, PII exposure posture. It's the intent. Compared at runtime against observed behaviour to produce adherence / drift signals.
Key fields: agent_id, purpose, max_classification,
allowed_data_types, allowed_tools, cost_envelope,
pii_exposure.
AgentTrustProfile
Section titled “AgentTrustProfile”verified/profile.py — dataclass. The verifiable evidence
package.
A signed (ES256) artefact summarising what an agent actually did over a window — not a judgement, just observations. Shared with partners or auditors who don't have access to our database.
Generated from the audit trail; cannot be forged without the signing key.
Hot path (per-request)
Section titled “Hot path (per-request)”PipelineContext
Section titled “PipelineContext”pipeline/context.py:106 — dataclass. The per-request flow
container.
Created once per governed call and passed through every step. Carries
the agent, session, the original + possibly modified payload, the
running list of Detection objects, token/cost counters, and the
audit-event buffer.
When you add a field to the request-time flow, it goes on
PipelineContext — not on request.state, not on a module global.
Detection
Section titled “Detection”pipeline/backends/__init__.py:28 — dataclass. Normalized finding
from any backend.
When NeMo Guardrails, LLM Guard, Azure Content Safety, or a
hand-rolled regex step sees something interesting, it emits a
Detection. Same shape for every backend so policy can reason over
all of them uniformly.
Key fields: category, severity, label, score, text,
backend, metadata.
Steps produce Detection[]; policy consumes them.
Decision
Section titled “Decision”behaviors/decisions.py:46 — frozen dataclass. The policy
outcome.
The structured answer from the policy engine: one of
allow | block | escalate | modify | dispatch_to_sandbox, plus why
and what to do next. When outcome is allow and a downstream call
is going to execute, the Decision also carries an embedded
Mandate (JWS).
Key fields: outcome, reason, behavior_id, pipeline_id,
decided_at, details, mandate (JWS string or None).
Produced by /v1/govern and by the in-process dispatcher
(behaviors/dispatch.py).
Authorization artefacts
Section titled “Authorization artefacts”Mandate
Section titled “Mandate”mandates/models.py:78 — frozen dataclass. The live authorization
primitive.
A signed, scoped, revocable authorization issued when policy allows
an action. Supersedes the older CapabilityToken for in-process
flows. Wire format is AP2-compatible so it's portable across
A2A / gateway / sandbox contexts.
Key fields: subject, issuer, capabilities (tuple of
MandateCapability), mandate_id, issued_at, expires_at,
correlation_id, signature.
Minted by dispatch_behavior() whenever a Decision resolves to
allow. Embedded in the Decision.mandate field as a JWS string so
callers can verify offline.
MandateCapability
Section titled “MandateCapability”mandates/models.py — part of Mandate. One permission slice.
Each Mandate holds a tuple of these. Structured as
action:resource:qualifier — e.g. call_llm:anthropic:claude-opus.
Downstream enforcers check this tuple against the action they're
about to perform, not the string-y outcome name.
CapabilityToken — legacy / SDK-only
Section titled “CapabilityToken — legacy / SDK-only”_sdk/capability_token.py:338 — dataclass.
Historical context: tokens supported offline PoP + delegation chains. Those features migrated into Mandate's JWS + capability tuple model.
Audit & retention
Section titled “Audit & retention”AuditEvent
Section titled “AuditEvent”models/audit.py:19 — Pydantic model. The tamper-evident log
entry.
Every governed action emits one. Hash-chained (each event's hash commits to the previous event's hash) and Ed25519-signed so the trail is provably unbroken. Retained per DPA (7 years in cold storage for compliance; see Customer data export for the retention carve-out).
Key fields: event_id, timestamp, org_id, event_type,
agent_id, session_id, details, source_framework.
Written by audit/writer.py; verified by audit/integrity.py
(runs every 4 hours in the background).
Relationships at a glance
Section titled “Relationships at a glance”Full map, split into control-path (who an Agent is and what it's promised to do) and hot-path (what happens on each LLM call).
flowchart TB
subgraph Control["Control path — who / what"]
Org[Org / Tenant]
Agent[Agent]
Pact[AgentPact
declared contract]
Profile[AgentTrustProfile
signed evidence]
Pipeline[Pipeline
governance plan]
Org --> Agent
Agent --> Pact
Agent --> Profile
Agent -->|assigned| Pipeline
end
subgraph Hot["Hot path — per request"]
Ctx[PipelineContext]
Steps[49 pipeline steps]
Det[Detection array]
Policy[OPA policy engine]
Dec[Decision]
Mand[Mandate JWS]
Ctx --> Steps
Steps --> Det
Det --> Policy
Policy --> Dec
Dec -->|if allow| Mand
end
Agent -.->|loaded into| Ctx
Pipeline -.->|drives| Steps
subgraph Audit["Audit trail — hash-chained"]
AE[AuditEvent]
end
Agent -.-> AE
Dec -.-> AE
Mand -.-> AE
Steps -.-> AE
classDef control fill:#1b2f3a,stroke:#3c6a82,color:#cfe6f2
classDef hot fill:#2a3555,stroke:#6f8bd8,color:#eef1ff
classDef audit fill:#2e2a1b,stroke:#8a7840,color:#f5eecb
class Org,Agent,Pact,Profile,Pipeline control
class Ctx,Steps,Det,Policy,Dec,Mand hot
class AE audit
Cardinalities
Section titled “Cardinalities”| Relationship | Cardinality |
|---|---|
| Org → Agent | 1 org : many agents |
| Agent → AgentSession | 1 agent : many sessions |
| Agent → AgentPact | 1 : 0-or-1 (optional) |
| Agent → Pipeline | many agents : 1 pipeline (share org defaults) |
| PipelineContext → Detection | 1 : many (one per step per finding) |
| Decision → Mandate | 1 : 0-or-1 (only if outcome == allow) |
| Any of above → AuditEvent | 1 : many (each action logs) |
When you're tempted to add a new object
Section titled “When you're tempted to add a new object”Before adding a 13th domain class, ask:
- Is it actually new, or is it a field that belongs on an existing object? (90% of the time, it's a field.)
- Does it have its own lifecycle, or is it just a shape one of these objects takes? (Prefer composition over a new class.)
- Will the audit trail need to reference it by ID? If not, consider a plain dict or tuple.
If it passes all three, add it here on the same PR that creates it.
Undocumented classes in models/ are a liability.
Also see
Section titled “Also see”- Pipeline step anatomy — how
Detectionis produced and consumed. - Security → Audit trail internals — why AuditEvent is hash-chained, and what signing key lives where.
- Data model — how these objects map onto Postgres tables.