Agent Trust & Assurance / Agent Authority ← The Life of a Uderia Agent

Architecture · P3 · the agent principal

Agent Authority

Status: implemented on main (2026-07-03). Delivers assessment requirement P3 (agent identity & permissions — only Salesforce/Microsoft were GREEN). Built on the Trust Membrane (Tier 5.2) foundation: the agent is a first-class governed principal. Design: LEAPFROG_ROADMAP.md Epic 3.3, TIER_5.2_TRUST_MEMBRANE.md §1.5.

Renamed 2026-07-04 — this document was formerly "Agent Identity Architecture" and the module was core/agent_identity.py. It was renamed to Agent Authority (core/agent_authority.py) because in Uderia an agent's identity is its Profile; this subsystem governs the agent's authority. See §0. The stored config key moved identityConfig → authorityConfig (the legacy key is still read). The internal class AgentPrincipal keeps its name — it is the correct IAM term.


0. What "Agent Identity" means in Uderia — read this first

"Agent identity" in the market is an umbrella term. In Uderia it is not a single feature — it is a composite realised across four subsystems on two orthogonal axes. Keeping them distinct is the whole point of this rename.

Concept The question it answers Where it lives in Uderia Axis
Agent identity Who is this agent? The Profile — its IFOC class, its LLM, its MCP "senses", its Memory, its Components. The @TAG profile is the agent. who
Agent assurance Is this agent verified / trustworthy? The Trust Membrane (Tier 5.2) — signed, compositional attestation. trust
Agent authoritythis document What may it do, on whose behalf, when it acts autonomously? core/agent_authority.py — a delegated, subset-of-owner scope. access
Agent audit What did it actually do? The Execution Provenance Chain — every autonomous action attributed to the agent. audit

Visual companion: The Uderia Agent — An Anatomy of Trust — the whole-agent diagram this taxonomy comes from (the five faculties inside the Trust Membrane, on the Grounding & Guardrails foundations). Its center — Agent Identity = who it is = its Profile — is exactly the "identity" row above.

Two rules follow, and they are load-bearing:

  1. Identity ≠ authority. The Profile says who the agent is; Agent Authority says what it is permitted to do. A trusted, well-defined agent (identity + assurance) can still be given a deliberately narrow authority. This is the platform's phrasing of charter #8, trust ≠ access.
  2. Authority is delegated and can only shrink. An agent has no authority of its own — when it acts autonomously it borrows a strict subset of its owner's permissions. The model can only remove; it can never grant or elevate.

If you came here looking for "the agent's identity," you want the Profile and, for how it's surfaced as an estate-wide map, the Platform Ontology. This document is specifically the access/authority face.

1. The idea

When an agent acts autonomously — a genie expert delegated by a coordinator, a scheduled task firing on a timer, a flow node, an inbound MCP ask_profile call — it is not a human issuing that request in the moment. Until now such actions ran under the owner's raw authority (the parent's auth_token, the task's user_uuid). P3 makes each autonomous action run as an Agent Principal derived from the owner whose authority is a strict subset of the owner's — and attributes every autonomous action to the agent in the audit trail.

Honest scope. This is the attainable form of P3: a scoped principal + audit attribution + fail-closed enforcement over capabilities (tools, writes, connectors, budget). The fuller vision — an agent confined to a graph sub-segment via ontology-mediated data access ("cannot query outside its slice") — depends on OBDA, which does not yet exist (Tier 5 Step 5). The shipped form scopes what the agent can do, not yet what data it can see structurally.

2. The model — pure, deterministic, subset-only

core/agent_authority.py is pure: the model + the enforcement decision. It never grants — a scope only ever removes the owner's authority (subset invariant by construction, so an agent with no config behaves exactly as today — byte-identical baseline, charter #15).

Piece Meaning
AgentScope The restriction. autonomous (may it act at all?) · deny_writes (block mutating tools) · denied_tools (deny-list) · allowed_connectors (None = all; else an allow-list) · max_cost_per_run (USD cap). All defaults = unrestricted.
AgentPrincipal owner_uuid + profile_id + scope. principal_id = agent:<profile_id>. actor() = the audit stamp attributing an action to the agent, not the user. (Kept its name — "principal" is the correct IAM term for an entity that holds authority.)
parse_scope(authorityConfig) Build a scope from a config dict (absent/empty → baseline).
derive_principal(profile, owner_uuid) The principal for a profile, read from its stored authorityConfigfalling back to the legacy identityConfig key for profiles saved before the rename (same mechanism as every other *Config block).
is_tool_allowed(scope, tool, …) The decision: (allowed, reason). Autonomous-off → deny all; denied-list → deny; connector not in allow-list → deny; write under deny_writes → deny. TDA_* framework tools are never write-gated (they are the agent's own reporting/orchestration).
is_run_within_budget(scope, cost) Per-run USD cap (post-hoc; the cost engine owns the numbers).

3. Enforcement — a per-turn security context, fail-closed at every chokepoint

The design problem: one gate misses most engines, and threading a principal through every call site is fragile. The solution is a per-turn security context via contextvars + layered enforcement at the execution chokepoints.

Chokepoint File Scoped by
MCP data-source tools (Optimize / genie-slave / FASTPATH) mcp_adapter/adapter.py:invoke_mcp_tool denied_tools, deny_writes (heuristic). Genuine TDA_* framework tools (final report, logging, orchestration) exempt — but component tools are not (enforced in the component-routing branch, see below).
MCP data-source tools (Conversation / ReAct) agent/tool_enforcement.py:wrap_langchain_mcp_tools Same decision as invoke_mcp_tool. The ReAct engine executes MCP tools through the LangChain session, NOT invoke_mcp_tool, so each tool's coroutine is wrapped at ConversationAgentExecutor.__init__ (the one seam where the tools + session_id are both known) to reach the same fail-closed chokepoint. Also runs preflight SQL grounding there.
Component tools components/manager.py:_run_component (LangChain path) + mcp_adapter/adapter.py component-routing branch (Optimize path) denied_tools by component id OR TDA_ tool name (enforce_tool(..., aliases=…)), so denied_tools:["scheduler"] and denied_tools:["TDA_Scheduler"] both work on both paths. Capabilities, not write-gated.
Platform connectors core/platform_connector_registry.py:invoke_connector_tool allowed_connectors allow-list.

The single shared decision module agent/tool_enforcement.py backs both MCP paths so the "fail-closed at every tool chokepoint" guarantee holds on every engine, including the ReAct engine that bypasses invoke_mcp_tool.

4. Persistence & UX

5. Guarantees & proof

5a. The trust-budget circuit breaker writes here (Jul 2026)

The Behavioral Trust Score's opt-in circuit breaker (trust_behavior.breaker_mode = "freeze_autonomy" — see AGENT_OPS_EVAL_ARCHITECTURE.md §6c) acts through this model, not around it: when an agent's trailing conduct enters at_risk, the breaker sets the profile's authorityConfig.autonomous = False and stamps frozen_by_breaker: true + frozen_reason — suspending only the agent's unattended use via the exact same scope the Agent Authority card edits (interactive use untouched; visible and reversible by clearing the flag / re-enabling autonomous in the card). The stamps are plain authorityConfig keys, so export/import and the editor treat them like any other scope field. Implementation: eval/online.py:_run_trust_breaker (the freeze_autonomy branch calls config_manager.update_profile).

6. What's deferred (charter #19)

7. Files

File Role
src/trusted_data_agent/core/agent_authority.py The model + decision + contextvar context + enforce_tool + denied_tool_result (pure).
src/trusted_data_agent/agent/execution_service.py Derive/set the principal at the autonomous-turn boundary; reset + actor stamp on convergence; source=scheduler → origin.
src/trusted_data_agent/mcp_adapter/adapter.py MCP-tool chokepoint enforcement.
src/trusted_data_agent/components/manager.py Component-tool chokepoint enforcement.
src/trusted_data_agent/core/platform_connector_registry.py Connector chokepoint enforcement.
static/js/handlers/configurationHandler.js + templates/index.html The Agent Authority profile-editor card.
test/test_agent_authority.py 46 CI assertions incl. the async architectural proof and config back-compat.