# Owners and mandates

Make sure no agent goes live without someone accountable for it, and give each agent a mandate, its purpose, success criteria, the systems it may call and its budget.

## No owner, no deploy

Publishing an agent (its card published, so people and other agents can reach it) when its registry entry names no owner is handled by `TICKETIQ_AGENT_PUBLISH_OWNER`:

| Mode | Effect |
| --- | --- |
| `assign` (default) | The person publishing becomes its owner, audited as `agent.publish.owner_assigned`. No agent is deployed unowned and nobody gains a step |
| `require` | The publish is refused with `422` until the registry entry names an owner (`agent.publish.unowned`) |
| `off` | No check at publish |

The owner is assigned only after the posture and red-team gates let the publish through, so a refused publish changes nothing. A version restore that publishes is handled the same way.

`TICKETIQ_REGISTRY_REQUIRE_OWNER` (default `false`) is separate: it refuses any registry save that leaves the entry without an owner.

## The mandate

A mandate says what the agent is for and the limits it works inside.

```json
PUT /api/governance/mandates/{agent}
{
  "purpose": "Answer IT questions from staff",
  "success_criteria": ["Resolved without a person", "Answered within 2 minutes"],
  "allowed_systems": ["jira", "servicenow"],
  "budget_usd": 200,
  "budget_period": "month"
}
```

| Field | What it does |
| --- | --- |
| `purpose`, `success_criteria` | What the agent is for and how its outcome is judged. Up to 10 criteria |
| `allowed_systems` | The MCP servers it may call. See below |
| `budget_usd`, `budget_period` | Saved as the agent's own [budget](/docs/ai-gateway/budgets) cap when it has none, or a looser one over the same period. A tighter cap, or one over another period, is kept |

`GET /api/governance/mandates/{agent}` returns the mandate and its `gaps`: which of `owner`, `mandate`, `success_criteria`, `allowed_systems` and `budget` are missing. Saving needs `agents.update` and Cedar `update` on the agent, and is audited as `agent.mandate.saved`.

### Allowed systems

When a mandate names its systems, a call by the agent to any other MCP server is handled by `TICKETIQ_AGENT_MANDATE_SYSTEMS`:

| Mode | Effect |
| --- | --- |
| `audit` (default) | The call goes ahead. `agent.mandate.outside` is recorded, at most once an hour per agent and server |
| `enforce` | The call is refused before it is sent, and the agent is told to ask an owner to add the system |
| `off` | No check |

A mandate with no `allowed_systems` limits nothing.

## Migrating existing agents

Every agent that has no owner is given the admin (`TICKETIQ_ADMIN_USER`) as its owner, and every agent without a mandate gets one seeded from what it does today: the MCP servers it is configured with and its current cap. Nothing an agent does changes, and every agent passes both checks.

This runs once by itself after an upgrade (`agent.mandate.migrated`, with the agents changed), and on demand with `POST /api/governance/mandates:migrate` (`agents.update` and `access.write`). A seeded mandate is marked `seeded: true` and has no purpose or success criteria: its owner should fill them in, and `gaps` lists them until they do.
