DocsPlatformAI Agent Governance

AI Agent Governance

Every AI agent deployed on a RootCX Core is a governed principal. It has its own identity, its own role, and its own audit trail. It acts under delegation from a human, and its effective authority is bounded by both sides. No configuration required. The governance is structural.


Identity

Each agent gets a deterministic system user:

Property Value
UUID uuid_v5(NAMESPACE, "agent:{appId}")
Email agent+{appId}@localhost
Type System user (is_system = true)

The UUID is stable across restarts, redeploys, and environment changes. The audit trail can always answer "what has this agent done over time" by querying on the agent's UUID.

The agent participates in the same RBAC model as human users. Same roles, same permission keys, same enforcement. No separate system.


Authority model

The Core computes effective authority on every action:

effective = agent_role_permissions ∩ delegator_permissions

The agent can never exceed its assigned role. The agent can never exceed the human who triggered it. Both boundaries apply simultaneously.

Agent role Delegator perms Effective authority
["app:crm:contacts.read"] ["*"] (admin) ["app:crm:contacts.read"]
["app:crm:*"] ["app:crm:contacts.read"] ["app:crm:contacts.read"]
["*"] (unrestricted) ["app:crm:*"] ["app:crm:*"]
any [] (offboarded user) [] (deny all)

The intersection is computed server-side on every tool call and data mutation. Not cached across requests. Change a role, it takes effect on the next action.


Restricting an agent

On first deploy, agents receive the admin role to work immediately. To enforce least-privilege, use the standard RBAC API:

# Create a scoped role
POST /api/v1/roles
{ "name": "invoice-reader", "permissions": ["app:billing:invoices.read", "tool:query_data"] }

# Revoke admin from the agent
POST /api/v1/roles/revoke
{ "userId": "<agent-uuid>", "role": "admin" }

# Assign the scoped role
POST /api/v1/roles/assign
{ "userId": "<agent-uuid>", "role": "invoice-reader" }

No redeploy needed. The restriction takes effect on the next action. Redeploying the agent does not overwrite the role you assigned.


Delegation

Every time an agent acts, it acts under delegation from a human.

Interactive invocation. A user clicks "run" or sends a message. Their identity is the delegator. The intersection applies immediately.

Autonomous triggers. Cron jobs, entity hooks, and webhooks fire without a human present. Every autonomous trigger carries a standing mandate: the identity of the human who created it. Before the agent dispatches, the Core validates:

  1. Does the delegation record exist?
  2. Is it still active (not revoked)?
  3. Does the delegator still have permissions?

If any check fails, the job is denied. The agent does not fire.

No delegation context means deny. No exceptions.


Deny-by-default

The agent has zero authority without explicit human authorization:

  • No invoker (trigger has no owner): deny.
  • No delegation (mandate revoked or missing): deny.
  • Delegator offboarded (permissions revoked): deny.
  • Permission not in intersection: deny.

This is enforced at the scheduler level before any agent code runs, and at the tool execution level on every action within a running agent.


Invocation ACL

Invoking an agent requires the app:{appId}:invoke permission. This key is auto-generated when the app is installed and can be granted to any role. Only users (or agents) with this permission can trigger the agent.


Delegated tokens

Internal callbacks between the agent worker and the Core use short-lived JWTs following RFC 8693 (Token Exchange):

Property Value
sub Delegator UUID (the human)
act.sub Agent UUID
aud rootcx-core (enforced on decode)
TTL 120 seconds
Signing HS256 (server-side only)

The token is identity-only. No capabilities in the token. Permissions are resolved live from the database on every check. Revoke a role, it takes effect immediately (not after token expiry).


Audit trail

Every data mutation captures dual-identity attribution:

Field Description
actor_uid Who performed the action (user or agent UUID)
delegator_uid On whose authority (NULL for direct user actions)
trigger_ref How it was triggered (api, agent_tool, or a trigger identifier)

This answers three questions during an incident: who performed it, on whose authority, and through what mechanism.

See Audit Log for the full schema and query API.


The security properties

Property How it is enforced
Agent cannot exceed its role Intersection on every tool call (RBAC)
Agent cannot exceed the delegator Intersection on every tool call
No human = no action Delegation check at scheduler + tool executor
Offboarded user kills the agent Standing mandate validated at dispatch
Every action is attributable Dual-identity audit at PostgreSQL trigger level
Tokens cannot be forged HS256 server-side signing, 120s TTL, audience-enforced
Agent cannot disable its own checks Authorization runs at the Core, outside the agent process

Compliance

Standard How RootCX addresses it
NIST SP 800-207 (Zero Trust) Never trust, always verify. Intersection computed on every action. No implicit trust from prior actions.
RFC 8693 (Token Exchange) act claim semantics for delegation. Identity-only tokens.
RFC 8707 (Audience) aud enforced on delegated tokens.
SOC2 CC6.1 / CC6.3 Logical access provisioned per-agent via RBAC API. Audit trail on every mutation.
EU AI Act Article 14 Human oversight by design. Every agent action traces back to an accountable human.

Next steps

  • RBAC for the operational how-to (creating roles, assigning, revoking).
  • Audit Log for querying the dual-identity audit trail.
  • Scheduled Jobs for cron-triggered agent invocations.
  • Webhooks for webhook-triggered agent invocations.