Skip to main content

Concepts

Set rules once, group work by goal, then connect the AI your team uses.

Status: Beta Companion docs: Get started · Scopes and assets · Result

Three layers keep setup clear

Trinitite separates rules, goals, and connected systems. This keeps one setting from doing three jobs.

The three-layer model, three nouns, one rule

Scope

A goal

Declare the goal once. Every asset bound to this scope shares the goal and its stacked policies.

tr.scope("refund-flow", goal="Resolve refunds within policy")
  1. A baseline holds the common rules for governed work.
  2. A scope names a goal and groups work that shares it.
  3. An asset is the model client, MCP server, CLI, skill, connector, sandbox, or RAG system that does the work.

An asset at the root uses the baseline. An asset created from a scope also gets that scope's goal and settings.

from trinitite import Trinitite

tr = Trinitite()

tr.govern(
frameworks=["soc2"],
policies=["refund-policy"],
mode="enforce",
)

refunds = tr.scope(
"refunds",
goal="Resolve valid refunds without exposing customer data.",
)

client = refunds.client("openai", credential="cred_openai_prod")

TypeScript has the same idea, with promises and camel case:

const tr = new Trinitite();

await tr.govern({
frameworks: ["soc2"],
policies: ["refund-policy"],
mode: "enforce",
});

const refunds = await tr.scope("refunds", {
goal: "Resolve valid refunds without exposing customer data.",
});

const client = refunds.client("openai", {
credential: "cred_openai_prod",
});

Asset constellation, one binding model

Selected asset

Client

Binding
Bind at the baseline or from a scope.
Acquisition
Inline (wrap), or split-govern for a direct provider call.

An asset with no scope inherits the baseline house rules. A scoped asset also inherits the scope goal and policies.

Start small, add detail when needed

The common path is short. Add a baseline or scope when the work needs its own rules or goal.

The ladder, one line, then composable, then fully customizable

Inherited

Platform-default Guardian and policy bundle

You add

Your governed call

Drop deeper only when you need to customize.

Use the wrapped client

For a model call made by your code, use the wrapped client and normalize the response:

response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Refund order 4821."}],
)
result = tr.result(response)

The provider response and the governance result are different objects. Keep the response for model output. Use the result for the verdict and evidence.

A result can arrive three ways

The right path depends on who owns the model call.

Three ways a result reaches you, one shape on every path

  1. 01Your codeThen
  2. 02tr.clientThen
  3. 03GuardianThen
  4. 04ProviderProduces
  5. One Result shapeverdict + diff + controls+ compliance + risk + receipt
How you get ittr.result(resp)

Only acquisition differs. The result is the same, whichever path it came in on.

Inline

Your code makes a wrapped call. Use tr.result(response) right away.

Observed

Another system makes the call. Query stored decisions with tr.results.get, tr.results.list, or correlation polling.

result = tr.results.wait_for_correlation(
"refund-4821",
poll_interval_seconds=0.5,
max_wait_seconds=15,
)

TypeScript uses await tr.results.waitForCorrelation(...).

Split governance in Python

Python can check input and output around a direct provider call:

input_result = tr.govern.input(
instructions="Do not expose customer data.",
input=messages,
masking=["pii"],
)

provider_messages = input_result.masked_body or messages
provider_response = provider.chat.completions.create(
model="gpt-4o",
messages=provider_messages,
)

output_result = tr.govern.output(
flow_id=input_result.flow_id,
instructions="Do not expose customer data.",
input=provider_messages + [{
"role": "assistant",
"content": provider_response.choices[0].message.content,
}],
)

Send masked_body to the provider when it is present. Output governance checks the reply. It does not unmask it for you. Call output_result.unmask(...) inside the approved trust boundary.

Split governance is Python-only. TypeScript supports wrapped calls and stored result lookup, but it does not expose tr.govern.input(...) or tr.govern.output(...).

One result shape

Each path gives you a GovernanceResult. Common fields include verdict, diff, violations, compliance, and risk. Other fields appear only when that call produced them.

Governance result

One object, clear next steps

Selected field
explanation
explain()
Value
Structured trace
Use
Call the method when a person needs the decision steps.

Follow-up work uses methods and optional handles:

trace_response = result.explain()

if result.receipt and result.receipt.get("id"):
verification = result.receipt.verify()

In TypeScript, use await result.explain() and await result.receipt.verify().

Where to go next