convex/trust_fabric/ that govern every agent action before execution is permitted. It evaluates two fail-fast gate pipelines (dispatch and launch), resolves role-based authority, and produces structured decisions with machine-readable explanations and unblock hints.
This page is for engineers extending or debugging the governance system. For a higher-level overview of what each gate checks, see the Governance Pipeline flow.
Architecture Overview
The Trust Fabric follows a pure-core / impure-shell architecture. Gate functions are pure (no DB access, no side effects). Async operations like policy evaluation and concurrency counting are injected through a hooks interface. The Convex binding layer fetches data, wires hooks, and delegates to the engine.TRUST FABRIC ARCHITECTURE
bindings.tsimpure shell
~850 loc1. Fetch gateway, agent, budgets, role2. Resolve context via role_resolver3. Wire hooks as closures4. evaluateGovernance(ctx, hooks)
delegates to↓
engine.tspure orchestration
~1,150 locRuns gate sequence. Each gate: pure fn → SafetyGateResult → GateOutcome. Fail-fast on first block.
Dispatch PipelineLaunch PipelineHooks
invokes↓
gates/pure · testable
preflight
identity
safety
budget
context
policy
scope
autonomy
guardrails
two-agent
produces↓
Allow
All gates passed.
Block
Structured error + hints.
Hold
Approval required.
| File | Role | Lines |
|---|---|---|
convex/trust_fabric/engine.ts | Gate orchestration, evaluation entry points | ~1,150 |
convex/trust_fabric/bindings.ts | Convex action layer, data fetching, hook wiring | ~850 |
convex/trust_fabric/types.ts | GovernanceDecision, GateOutcome, explanations, snapshots | ~420 |
convex/trust_fabric/role_resolver.ts | Role, identity, authority, scope resolution | ~400 |
convex/trust_fabric/explanation.ts | Structured explanation builder, unblock hints | ~640 |
convex/trust_fabric/gates/index.ts | Gate sequences, decision builders, shared types | ~430 |
convex/trust_fabric/gates/gate_*.ts | Individual gate implementations | ~430 total |
Gate Anatomy
Every gate is a pure function that takes aGateContext (or a gate-specific input type) and returns a SafetyGateResult:
GateContext is the shared input shape for most gates:
concurrencyGate from gate_3_safety.ts:
- Pure function — no DB access, no side effects, trivially testable.
- Resolved bounds first — gates prefer
resolvedBounds(role-derived + agent overrides) over rawagentDocfields. This makes role authority bounds actually enforce. - Fail with structure — failures return a machine-readable
errorCode, aretryableflag, and a human-readablemessage. The engine uses all three.
Gate Composition
Two ordered sequences define which gates run for each action type. Both are fail-fast: the first blocking gate terminates evaluation and skips the rest.Dispatch Pipeline (GOVERNANCE_GATE_SEQUENCE)
Used for step_dispatch and delegated_run_dispatch. This is the full pipeline that evaluates every dispatch request before an agent executes work.
- Gate 2b:
identity— validates NHI (Non-Human Identity) credentials when present. Runs betweenagentStatusandconcurrency. - Two-Agent Rule — injected into the approval gate for Rung 4 (bounded) agents when the action is classified as
financialand estimated cost exceeds the$100threshold.
step_dispatch: concurrency gate runs normally.delegated_run_dispatch: concurrency gate is skipped (delegated runs manage their own concurrency). Budget gate checks against a guardrails cost ceiling instead of just monthly spend.
Launch Pipeline (LAUNCH_GATE_SEQUENCE)
Used for workflow_run_launch preflight. A lighter sequence because there is no specific agent yet at launch time.
POC Reduced Enforcement
Agents in thepoc lifecycle phase get reduced enforcement: gates 5—8 (budget, envelope budget, trust level, context trust) warn but do not block. The POC override downgrades a block to a pass with an audit detail noting the override:
GateOutcome.data with pocOverride: true for audit trail integrity.
The Hooks Pattern
Gates that need async data (DB queries, rate limiter mutations) cannot call the database directly — they are pure functions. The engine delegates these operations through theGovernanceEvaluationHooks interface:
ActionCtx:
- The engine (
engine.ts) is fully testable without a database — pass mock hooks. - Gates remain pure functions — the engine calls hooks at the right point in the sequence.
- The binding layer is the only place that touches Convex
ctx.
LaunchEvaluationHooks) with just policy evaluation, no concurrency or rate limiting.
Bindings
bindings.ts is the Convex action layer that wires the pure engine to the database. It exports two main internalAction handlers:
runGovernanceGates
The primary dispatch governance entry point. Steps:
- Fetch data — gateway doc, agent doc (via
resolveForExecution), budget envelopes, registry entry - Registry freshness check — blocks dispatch if agent has no registry entry (shadow IT prevention)
- Resolve governance context — calls
resolveGovernanceContext()fromrole_resolver.tsto get effective role, posture, identity, bounds, and scope - Build hooks — closes over
ctxto createGovernanceEvaluationHooks - Rate limit pre-check — token-based rate limit check before the full pipeline
- Evaluate — calls
evaluateGovernance(evalContext, hooks)from the engine - Audit — schedules governance audit log write and trace event (fire-and-forget, never blocks dispatch)
runEdgeGovernanceGates
Same as runGovernanceGates but for edge/Convex-native agents with no real gateway. Uses a synthetic healthy gateway doc. Skips gateway-scoped policies when no gatewayId is provided.
Legacy wrappers
runSafetyGates and runDelegatedSafetyGates provide backward compatibility — they call runGovernanceGates internally and convert the GovernanceDecision to the legacy SafetyGateResult format via toLegacySafetyGateResult().
Re-exports
bindings.ts re-exports all public types, gate functions, and engine entry points. Existing consumers that import from trust_fabric/bindings continue to work without import changes.
Role Resolver
role_resolver.ts contains pure functions that centralize precedence logic for role, identity, and authority resolution. The engine consumes its output (GovernanceResolutionContext) — it never fetches data itself.
resolveGovernanceContext (primary entry point)
Packages six resolution steps into a single result:
effectivePosture— the authority posture in effectpostureSource— where it came from (run_override|role_default|platform_default)roleSnapshot— resolved role with autonomy tier, authority bounds, trusted context config, scopeactorIdentity— structuredActorIdentityV1enveloperesolvedBounds— merged authority bounds (role defaults + agent overrides)resolvedTrustedContext— role’s trusted context config for the context trust gateresolvedScopeConstraints— environment, tool, and data access constraints from the role scope
Posture Precedence
advisory, retrieval, approval_required, bounded_autonomous. Invalid values are silently skipped in the precedence chain.
Authority Bounds Merging
Role-level bounds serve as defaults. Agent-level fields (maxConcurrentSteps, budgetMonthlyCents) override them:
Dispatch Context Provenance
buildDispatchContextProvenance() constructs a ContextProvenanceV1 describing the trustworthiness of the dispatch context. At dispatch time, the context is always internal_verified (Convex-managed). Freshness is derived from the run’s creation time against the role’s maxFreshnessMinutes (default: 30 minutes).
Explanation Builder
explanation.ts is a pure function that derives a structured GovernanceExplanation from a GovernanceDecision. Every decision gets an explanation attached automatically by buildGovernanceDecision().
The explanation answers three questions:
- What happened? —
outcome+summary - Why? —
reasons(one per evaluated gate, skipped gates excluded) - What could change it? —
unblockHints(only when concrete threshold data exists)
Explanation Reasons
Each gate outcome maps to anExplanationReason with:
category— one of:authority,trust,scope,policy,budget,health,approval,concurrency,readinessgate— which gate produced itoutcome—pass,block,hold, orskipsummary— human-readable descriptiontrustAspect— for trust failures:source_class,freshness, orenvironmentenforcedLimit— for budget/concurrency/trust failures: the threshold and current valuescopeAction— whether the system blocked execution or merely conveyed constraints
UnblockHints
Only emitted when the system has concrete data to make the hint honest. Each hint carries:enforcedconfidence means there is concrete threshold data backing the hint (e.g., “Agent budget exhausted: 5000/5000 cents”).advisoryconfidence means the hint is best-effort guidance without exact numbers (e.g., “Gateway is offline. Wait for it to come back online.”).
unblockHints array rather than inventing certainty.
Adding a New Gate
To add a new gate to the dispatch pipeline:Step 1: Create the gate file
Createconvex/trust_fabric/gates/gate_N_yourgate.ts following the naming convention:
Step 2: Export from the gate index
Add your gate toconvex/trust_fabric/gates/index.ts:
GOVERNANCE_GATE_SEQUENCE:
Step 3: Wire into the engine
Inconvex/trust_fabric/engine.ts, add the gate evaluation at the appropriate point in the evaluateGovernance() function:
Step 4: Add explanation support
Inconvex/trust_fabric/explanation.ts:
- Add a category mapping in
GATE_CATEGORY_MAP:
- Add a passed-gate summary in
summaryForPassedGate():
- Add unblock hints in
buildUnblockHints()if you have concrete threshold data:
Step 5: If your gate needs async data
If your gate requires DB queries, do not add DB access to the gate function. Instead:- Add a new hook to
GovernanceEvaluationHooks:
- Wire the hook in
bindings.tsinside the hooks object:
- Call the hook in the engine before your gate:
Step 6: Test
Write a unit test for your gate function. Since gates are pure functions, they can be tested without any Convex runtime:All Gate Files Reference
| File | Gate | What it checks |
|---|---|---|
gate_1_preflight.ts | preflightGate | OPA/Rego deploy-time policy (stub — enforcement in CI/CD) |
gate_2_identity.ts | identityGate | NHI credential expiry (graceful degradation when no NHI) |
gate_3_safety.ts | gatewayHealthGate | Gateway online/degraded/offline status |
gate_3_safety.ts | agentStatusGate | Agent lifecycle status (paused/terminated/error) |
gate_3_safety.ts | concurrencyGate | Running step count vs max concurrent limit |
gate_3_safety.ts | agentLifecycleGate | Agent lifecycle stage (active/inactive) |
gate_4_budget.ts | budgetGate | Agent monthly budget exhaustion |
gate_4_budget.ts | envelopeBudgetGate | Budget envelope exhaustion (per-scope, per-period) |
gate_5_context_trust.ts | contextTrustGate | Context source class, freshness, environment eligibility |
gate_6_policy.ts | (hooks) | Dispatch policy evaluation (wired through engine hooks) |
gate_7_scope.ts | scopeGate | Credential scope / network policy (stub — future Aembit integration) |
gate_8_autonomy.ts | trustLevelGate | Agent trust level vs gateway minimum |
gate_8_autonomy.ts | autonomyGate | Autonomy rung enforcement (assistive/retrieval/supervised/bounded) |
gate_9_guardrails.ts | guardrailsGate | Output guardrails (stub — Bifrost enforces externally) |
two_agent_rule.ts | twoAgentRuleCheck | Financial threshold for dual approval at Rung 4 ($100 / 10,000 cents) |
GovernanceDecision Shape
The final output of every evaluation. Consumed by the interpreter, audit trail, and explanation builder:pass— all gates passed, execution may proceed.block— a gate rejected the action.blockedByidentifies which gate and why.hold— a policy requires approval before proceeding.heldByidentifies the policy.

