Credential Broker
AI agents need credentials to act on external services — API keys, OAuth tokens, database passwords, service account keys. Without governance, agents end up with long-lived secrets pasted into their configuration, with no audit trail and no ability to revoke. The MITRITY credential broker fixes that by holding the secret value in an encrypted vault and giving agents only time-bound, agent-scoped, revocable access.
The three-tier model
If you take nothing else away from this page, take this. There are three objects, and they exist for different reasons:
- A credential is the secret value itself — encrypted at rest in the MITRITY vault, never visible in API responses. You create it once and rotate it when needed.
- A grant authorizes one specific agent to request leases for one specific credential. Without a grant, an agent cannot ask for the secret no matter what other permissions it has. Grants are the "who is allowed to use this".
- A lease is the time-bound checkout. An agent requests a lease, the broker validates the grant, and the agent retrieves the secret value via the gateway. Leases expire automatically; revoking a lease cuts the agent off immediately.
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Credential │──► │ Grant │──► │ Lease │
│ (the value)│ │ (who can) │ │ (a use) │
└────────────┘ └────────────┘ └────────────┘
1 per secret N agents per N leases per
credential grant
Cascading revocation: delete a credential → all grants on it are revoked → all leases under those grants are revoked. Revoke a grant → all leases under it are revoked.
Credential types
Four types are supported. The type is descriptive metadata — it doesn't change how the value is stored, but it shows up in the dashboard and audit log so you can filter and search.
| Type | Typical contents |
|---|---|
api_key | API keys and bearer tokens — OpenAI, Stripe, SendGrid, etc. |
oauth_token | OAuth 2.0 access tokens and refresh tokens |
db_password | Database connection passwords — PostgreSQL, MongoDB, Redis |
service_account | Cloud service account credentials — GCP service account JSON, AWS IAM access keys |
Storage
All credentials are stored in the MITRITY built-in vault. External vault integrations (GCP Secret Manager, AWS Secrets Manager, HashiCorp Vault) are on our roadmap — talk to us if you need this for procurement and we'll prioritize accordingly.
Defining a credential
Via the dashboard
- Open the customer portal and go to Credentials in the sidebar.
- Click Add credential.
- Fill in:
- Name — a unique identifier within your tenant, e.g.
openai-production-key. - Type — one of
api_key,oauth_token,db_password,service_account. - Value — the actual credential (never displayed again after save).
- Max TTL (minutes) — the longest single lease an agent can request against this credential.
- Max concurrent leases — how many leases can be active at once. Start at 1 if your agent does sequential calls.
- Rotation reminder (days) — optional metadata for your own rotation tracking.
- Name — a unique identifier within your tenant, e.g.
- Save.
Via the API
curl -X POST https://api.mitrity.com/api/v1/credentials \
-H "Authorization: Bearer mk_your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "openai-production-key",
"credential_type": "api_key",
"store_type": "builtin",
"credential_value": "sk-proj-abc123...",
"max_ttl_minutes": 60,
"max_concurrent_leases": 3,
"rotation_days": 90
}'
Response:
{
"id": "8b6c4a2e-...",
"tenant_id": "9f1a3d-...",
"name": "openai-production-key",
"credential_type": "api_key",
"store_type": "builtin",
"store_reference": null,
"has_value": true,
"max_ttl_minutes": 60,
"max_concurrent_leases": 3,
"rotation_days": 90,
"created_at": "2026-03-01T10:00:00Z",
"updated_at": "2026-03-01T10:00:00Z"
}
The credential value is never returned in any API response after creation — only has_value: true/false tells you whether a value is stored.
Request fields (create)
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique name within the tenant |
credential_type | enum | Yes | api_key, oauth_token, db_password, or service_account |
store_type | enum | No | Defaults to builtin. Only builtin is supported today. |
credential_value | string | Yes | The actual secret (encrypted at rest before storage) |
max_ttl_minutes | int | Yes | Maximum lease TTL allowed against this credential (1-1440) |
max_concurrent_leases | int | No | Defaults to 1. Max active leases at once (1-100) |
rotation_days | int | No | Rotation reminder interval — metadata only today (see Roadmap) |
Granting access to an agent
A grant says "agent X is allowed to lease credential Y." Without a grant the agent's lease request returns 403 Forbidden, even if the agent has every other permission.
curl -X POST https://api.mitrity.com/api/v1/credentials/{credential_id}/grants \
-H "Authorization: Bearer mk_your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "3a5e1c-...",
"allowed_operations": ["read"],
"enabled": true
}'
This call is idempotent — re-granting an agent that already has a grant updates it in place (allowed_operations + enabled) instead of erroring. The response is 201 Created on the first grant and 200 OK on a subsequent update.
Grant fields
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | UUID | Yes | The agent authorized to lease this credential |
allowed_operations | array | No | Operations granted. Defaults to ["read"]. |
enabled | bool | No | Defaults to true. Set false to suspend without deleting. |
To list grants on a credential:
curl https://api.mitrity.com/api/v1/credentials/{credential_id}/grants \
-H "Authorization: Bearer mk_your-api-key"
To revoke a single agent's grant (leaving the others in place), pass agent_id:
curl -X DELETE "https://api.mitrity.com/api/v1/credentials/{credential_id}/grants?agent_id={agent_id}" \
-H "Authorization: Bearer mk_your-api-key"
To remove all grants on a credential (cascading-revokes their active leases), omit agent_id:
curl -X DELETE https://api.mitrity.com/api/v1/credentials/{credential_id}/grants \
-H "Authorization: Bearer mk_your-api-key"
Leases — how agents actually get the secret
Leases are how MITRITY enforces the "time-bound, revocable" part. They are created at the credential-broker level (which writes the lease record and starts the clock), and the credential value itself is delivered to the agent through the Mitrity Gateway / MCP Sidecar at the moment the agent actually needs it. The lease creation endpoint never returns the secret — it returns the lease metadata only.
Creating a lease
curl -X POST https://api.mitrity.com/api/v1/credentials/{credential_id}/leases \
-H "Authorization: Bearer mk_your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "3a5e1c-...",
"ttl_minutes": 30
}'
Response:
{
"id": "5d2f7a-...",
"credential_id": "8b6c4a2e-...",
"agent_id": "3a5e1c-...",
"issued_at": "2026-03-01T10:05:00Z",
"expires_at": "2026-03-01T10:35:00Z",
"status": "active"
}
The agent then retrieves the actual value through the gateway. See Integration Modes for how the gateway resolves credentials at runtime.
Lease fields
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | UUID | Yes | The agent the lease is being issued to |
ttl_minutes | int | Yes | Lease duration. Must be ≤ the credential's max_ttl_minutes. |
Lease lifecycle
| Status | Meaning |
|---|---|
active | The lease is valid until expires_at. The agent can use it to fetch the secret. |
expired | The TTL has elapsed. The agent must request a new lease. |
revoked | The lease was cancelled before its TTL — manually, or via cascading revocation. |
Revoking a single lease
Immediate cutoff. The next gateway-side resolve fails with 403.
curl -X POST https://api.mitrity.com/api/v1/credentials/{credential_id}/leases/{lease_id}/revoke \
-H "Authorization: Bearer mk_your-api-key" \
-H "Content-Type: application/json" \
-d '{ "reason": "Credential rotation in progress" }'
Listing leases
curl "https://api.mitrity.com/api/v1/credentials/{credential_id}/leases" \
-H "Authorization: Bearer mk_your-api-key"
Updating a credential (rotation)
Replace a credential's value while keeping the same id, grants, and lease history. New leases issued after the update receive the new value; existing leases keep working until they expire or get revoked.
curl -X PUT https://api.mitrity.com/api/v1/credentials/{credential_id} \
-H "Authorization: Bearer mk_your-api-key" \
-H "Content-Type: application/json" \
-d '{ "credential_value": "sk-proj-new-key-xyz789..." }'
Updatable fields
| Field | Type | Description |
|---|---|---|
name | string | Rename the credential (doesn't affect grants) |
credential_value | string | Replace the secret value |
max_ttl_minutes | int | Tighten or loosen the per-lease max TTL |
max_concurrent_leases | int | Adjust the concurrent-lease ceiling |
rotation_days | int | Update the rotation reminder interval |
Runbook: rotating a leaked OpenAI key
A practical end-to-end. Assume cred-openai is the credential ID and you've already minted a new key in the OpenAI dashboard.
-
Revoke all active leases so no agent can use the old key after this moment. List leases with
status=active, then revoke each:curl "https://api.mitrity.com/api/v1/credentials/cred-openai/leases?status=active" \ -H "Authorization: Bearer mk_your-api-key" \ | jq -r '.data[].id' \ | xargs -I{} curl -X POST \ https://api.mitrity.com/api/v1/credentials/cred-openai/leases/{}/revoke \ -H "Authorization: Bearer mk_your-api-key" \ -H "Content-Type: application/json" \ -d '{"reason":"rotation: leaked key"}' -
Update the credential value with the new key:
curl -X PUT https://api.mitrity.com/api/v1/credentials/cred-openai \ -H "Authorization: Bearer mk_your-api-key" \ -H "Content-Type: application/json" \ -d '{"credential_value":"sk-proj-NEW..."}' -
Revoke the old key in the OpenAI dashboard so any agent that somehow still has it cached fails closed.
-
Verify by triggering an action on an affected agent and confirming it succeeds against the upstream (a normal OpenAI call returns 200). If you're running the Mitrity Gateway or Mitrity MCP Sidecar, the credential resolution will appear in that binary's own logs at the moment the agent acts.
The whole rotation is reversible up to step 3 — if you find the new key has issues, you can PUT back to the old value (assuming you still have it). After step 3, the old key is dead.
Security model
Encryption at rest
Credential values are stored using envelope encryption with GCP Cloud KMS:
- AES-256-GCM encrypts the credential value with a per-tenant data encryption key (DEK).
- The DEK is itself wrapped (encrypted) by a key in Google Cloud KMS managed in the MITRITY project. The wrapped DEK is stored alongside the credential row.
- To decrypt, MITRITY calls Cloud KMS to unwrap the DEK, decrypts the value with the DEK, and then discards both immediately. The plaintext value never persists outside of the active request scope.
- Each tenant has its own DEK — a compromise of one tenant's wrapped DEK cannot decrypt another tenant's credentials.
Access control
- Owner and Manager roles can create, update, delete credentials and grants.
- Member and Viewer roles can see credential metadata (name, type, has-value flag, grant list) but never the secret value.
- The secret value is only delivered to agents through the gateway's lease-resolve path, and only while a valid, unexpired, unrevoked lease exists.
Tenant isolation
- Every query is scoped to
tenant_id. There is no cross-tenant access path. - Future external-vault integrations will preserve this — your tenant's secret manager will only be accessible from agents in your tenant.
Roadmap (planned, not yet shipped)
Features described elsewhere in earlier drafts of this doc that we're tracking but haven't shipped yet:
- Credential audit events — every
credential.created/grant.created/lease.created/lease.revokedetc. written to the audit log with full context. Today the audit log captures most platform actions; credential operations are an explicit gap we're closing. - Grant-level conditions —
require_justification,allowed_time_window, environment scoping at the grant level. Today grants are unconditional. - Lease renewal endpoint — extend an active lease without re-issuing. Today agents request a new lease when the old one nears expiry.
- Bulk lease revocation — single API call to revoke every active lease on a credential (the runbook above is the current workaround).
- Automated rotation reminders — email/Slack notifications when
rotation_dayselapses. Today the field is stored as metadata for your own tracking but doesn't fire alerts. - External vault integrations — GCP Secret Manager, AWS Secrets Manager, HashiCorp Vault as alternatives to the built-in vault.
If any of these are blockers for your procurement or workflow, let us know — customer demand drives priority.
Related documentation
- Integration Modes — how the gateway and MCP sidecar resolve leases at agent runtime
- Injection Detection — detect attempts to exfiltrate credentials via prompt injection
- Delegation Chains — credential access across delegation hops
- Agent Permissions — tool-level permissions, complementary to credential grants
- RBAC — role-based access control for credential management