Skip to content

AI governance and agent principals ​

The AI gateway enforces two layers of governance over model usage. Model governance controls which model aliases each team may call through per-team allowlists, with a shadow audit recording every usage (allowed and denied) for review. Agent principals extend the consumer model with typed identities -- bot principals that carry their own tool allowlists and per-agent token budgets, with typed analytics attribution so agent traffic is identifiable, governable, and billable separately from user traffic.

When to use this ​

  • Different teams should reach different models -- a per-team allowlist decides who may call which alias, and a shadow audit records every usage for review before enforcement.
  • Bots and agents call your AI routes and need their own identity, tool allowlists, token budgets, and separate billing attribution.

Agent principals & governance ​

Agent principals extend the consumer model with typed identities, per-agent tool allowlists, per-agent token budgets, and typed analytics attribution. An agent consumer is a bot principal with its own governance rules; a user consumer (the default) is a human or application caller.

Consumer type ​

Each consumer declares a type field (user or agent, defaults user). The type threads through the authenticated Identity into analytics records (ai_spend.consumer_type, mcp_tool_calls. consumer_type) so agent traffic is identifiable, governable, and billable separately from user traffic. The gateway also injects an X-Consumer-Type upstream header (user or agent) alongside X-Consumer-Name so upstreams can distinguish agent from user traffic.

yaml
consumers:
  - name: agent-bot
    type: agent
    credentials:
      - type: api_key
        key: agent-key
    tool_allowlist: [search, fetch]
    token_budget:
      tokens_per_min: 1000
      scope: consumer
    agent:
      display_name: Search Agent
      description: Autonomous search and fetch bot
      owner: platform-team
      permissions: read_write
  - name: human-user
    type: user
    credentials:
      - type: api_key
        key: user-key

Agent principal metadata ​

Agent consumers can declare first-class principal metadata under the agent field. This carries display name, description, owner, and permission level for attribution and audit.

FieldDefaultDescription
agent.display_nameconsumer nameHuman-readable agent name.
agent.description(none)Human-readable description of the agent's purpose.
agent.owner(none)The user or team that owns this agent.
agent.permissionsread_onlyPermission level: read_only, read_write, or admin.

The permission level mirrors the MCP Permission enum and is used for agent-operable admin access control. read_only allows list, get, stats, health, and config tools. read_write adds create, update, and delete. admin adds purge and full admin tools.

Tool allowlist ​

A consumer with a non-empty tool_allowlist may only call the named tools through the MCP gateway. tools/call for a non-allowlisted tool is denied with tool_not_in_agent_allowlist; tools/list is filtered to show only allowlisted tools. A consumer with an empty allowlist (the default) has no restriction. Validation checks that every name references a configured ai.mcp.tools entry and rejects an allowlist when no ai.mcp block is configured.

Per-agent token budget ​

A consumer with a token_budget gets its own direct token budget, checked FIRST in the budget engine -- before the policy chain (the most-specific budget wins). The budget is consumer-scoped by definition; an anonymous caller cannot bind one. A consumer without a token_budget falls through to the policy chain (route, service, listener, global). Validation checks the same shape as a policy token_budget (at least one window, positive values).

Analytics ​

Analytics schema v8 adds a consumer_type column to ai_spend and mcp_tool_calls (defaults 'user', additive ALTER TABLE migration). Every spend record and tool-call record carries the consumer type for typed attribution.

Model governance ​

Per-team model allowlists control which model aliases each team may call, and a shadow audit records every model usage (allowed and denied) for review.

Configuration ​

yaml
ai:
  governance:
    team_allowlists:
      acme-ai-budget: [gpt-4o-mini, claude-haiku]  # low-cost only
      enterprise-team: [gpt-4o, claude-sonnet, gpt-4o-mini]
    audit: true

The key in team_allowlists is a POLICY name -- the same policy that attaches to consumers via policies: [...]. A consumer attaching a policy with an allowlist may only call the listed model aliases. When multiple policies with allowlists attach to a consumer, the model must be in ALL of them (deny-win, the same principle as AuthZ).

FieldDefaultDescription
team_allowlists(empty)Map of policy name to allowed model aliases.
auditfalseWhen true, record every model usage (allowed and denied) for shadow audit.
dry_runfalseWhen true, log would-be denials but do not reject the request. See Dry-run mode below.

Enforcement ​

The governance check runs AFTER the request body is parsed (the model alias is in the body) and BEFORE the provider is called. A denied request returns 403 with the OpenAI error shape:

json
{
  "error": {
    "code": "model_denied_by_policy",
    "message": "model 'gpt-4o' is not allowed for this team",
    "request_id": "req-..."
  }
}

No provider tokens are spent on a denied request. Consumers with no allowlist policy attached are allowed (fail-open).

Audit ​

When audit: true, every AI request (allowed and denied) is recorded in the governance audit log with: consumer, team (policy name), model alias, verdict (allow/deny), and reason. Audit events are queryable via the admin API:

POST /analytics/governance-audit
{
  "from_ms": 1693526400000,
  "to_ms": 1693612800000
}

Denials are counted in dwara_ai_governance_denied_total{reason}.

Dry-run mode ​

Set dry_run: true to evaluate the governance allowlists and log would-be denials without rejecting requests. This lets you measure the impact of a new allowlist against production traffic before switching to enforce mode.

yaml
ai:
  governance:
    team_allowlists:
      team-a: [gpt-4o-mini, claude-haiku]
    dry_run: true

In dry-run mode, would-be denials are:

  • Counted in dwara_policy_dry_run_total{phase="ai_governance",route}.
  • Logged with code = "policy_dry_run" and the violation details.
  • NOT returned to the client — the request proceeds to the provider.

Once you are satisfied with the false-positive rate, switch to dry_run: false (or remove the field) to enforce.

Runnable demo ​

Run a team model allowlist against a live gateway: demos/07-ai-gateway/ in the repository (test script: test-13-model-governance.sh -- a model outside the allowlist gets 403 before routing). The category README covers prerequisites and teardown.

See also ​