Use Case 025: Agent Identity Registration & Lifecycle

Overview

Property Value
Use Case ID UC-025
Use Case Name Agent Identity Registration & Lifecycle
Module Agent Identity — Identity Backend
Priority High
Status ✅ Implemented
Version 1.0
Last Updated June 15, 2026

Implementation status (agent-identity release, June 2026). Shipping on development. The tenant-owned agent registry (AgentIdentity), scope ceiling, allowed task-types, per-turn budget, public-key registry, and disable/revoke lifecycle exist end-to-end (Web /agents, API /api/agents) with integration tests. Canonical code names differ from parts of this original spec — the real types are AgentIdentity, RegisterAgentCommand / UpdateAgentCommand, and IAgentRepository; scope and task-type lists persist as space-delimited strings.

Description

This use case describes how an owner/admin registers, configures, and manages the lifecycle of a non-human agent identity (an AI agent or automated process) in Application Manager. Each agent is a tenant-owned, first-class identity with a required human owner for accountability, a link to the registered application(s) that bound its scope ceiling, a set of allowed task types, governance limits, and registered public keys used for keyless private_key_jwt authentication. Agents minted from this registry receive scoped, ephemeral credentials (UC-026) and may delegate on behalf of subjects (UC-027). The registry is the upper bound for everything an agent can ever do: no credential, token, or delegation may exceed the agent's scope ceiling or governance limits.

This is part of the agent-identity release. The SDK master design is riptide-sdk/docs/plans/AGENT-IDENTITY-ARCHITECTURE.md; the Application Manager-side plan is docs/internal/agent-identity-plan.md (Section 1, plus the identity items in §9 Reuse and §10 Resolved decisions). The AgentIdentity lifecycle shape (ExpiresAt/RevokedAt/RevokedReason/IsActiveAt(moment)) and the AgentPublicKey substrate (ECDSA P-256, kid rotation, validity windows, revocation) are lifted from the existing SyntheticPrincipal and AttestationPublicKey patterns — reused, not rebuilt.

Actors

Actor Description Role
Owner / Admin Human user who registers and owns the agent; holds agents.write capability Primary
AI Agent The non-human identity being registered; later authenticates with its private key Supporting
Application Manager Web Bootstrap 5 + Tabler MVC admin UI (AgentsController) Supporting
Application Manager API X-Api-Key-authenticated REST API (AgentsController) Supporting
AuditLog Append-only audit trail recording every lifecycle event Supporting
Cost Governance subsystem Validates budget headroom at registration (UC-032) Supporting
SDK AgentIdentity component Riptide.Platform.AgentIdentity — consumes the registry External

Preconditions

  1. Application Manager is running and reachable on the Web (11401) and API (11402) ports.
  2. The acting owner/admin is authenticated and holds the agents.read and agents.write capabilities.
  3. The owning tenant exists and is Active or Trial.
  4. At least one registered application exists to bind the agent's scope ceiling.
  5. A Top-tier budget cap exists for the tenant with viable headroom (UC-032); agent creation is fail-closed without it.
  6. The owner has (or will register) at least one ECDSA P-256 public key for the agent.

Postconditions

Success Postconditions

  1. An AgentIdentity row is persisted in IdentityDbContext with status Active, a required OwnerUserId, bound application link(s), scope ceiling, AllowedTaskTypes + DefaultTaskType, PerTurnBudget, governance limits, and audit fields.
  2. One or more AgentPublicKey rows are persisted with kid, validity window, and Active state.
  3. The agent appears in the registry list and is resolvable by GetAgentById.
  4. An audit entry recording the actor chain is written to AuditLog.

Failure Postconditions

  1. No AgentIdentity row is created when validation fails (missing owner, no app link, no budget headroom, requested ceiling outside the bound application's grantable scopes).
  2. A descriptive error (RFC 7807 Problem Details on the API; validation summary in the Web UI) is returned.
  3. No partial state: public keys are not orphaned if the agent insert is rolled back.

Primary Flow Sequence

sequenceDiagram
    participant Admin as Owner/Admin
    participant Web as AM Web (AgentsController)
    participant Med as MediatR
    participant Gov as Cost Governance
    participant Repo as IAgentRepository
    participant DB as IdentityDbContext
    participant Audit as AuditLog

    Admin->>Web: Submit "Register Agent" form
    Web->>Med: Send RegisterAgentCommand
    Med->>Med: FluentValidation (owner, app link, ceiling ⊆ app scopes)
    Med->>Gov: Check viable budget headroom (UC-032)
    Gov-->>Med: Headroom OK
    Med->>Repo: Add AgentIdentity (status=Active)
    Repo->>DB: INSERT AgentIdentity + AgentPublicKey
    DB-->>Repo: Saved
    Med->>Audit: Write actor-chain entry (agent.registered)
    Med-->>Web: AgentDto (Id, status, ceiling)
    Web-->>Admin: Redirect to agent detail page (success toast)

UC-025a: Register Agent

Triggers

  • Owner/admin navigates to /agents/create in the Web UI, or
  • An SDK consumer calls POST /api/agents.

Basic Flow

  1. Owner/admin opens Agents > Register Agent.
  2. System displays the registration form: name, owner (defaults to the acting admin), one or more bound applications, scope ceiling builder, task-type selection, PerTurnBudget, optional TemplateId, governance limits (max credential TTL, allowed MCP servers, reservation rate-cap override, reservation count-cap override), optional ExpiresAt, and a public-key upload field.
  3. Owner/admin sets the scope ceiling — the two-axis set (coarse {app}:{action} capabilities + fine-grained mcp:{server}:{tool} tool scopes) that bounds every credential the agent can ever mint. The ceiling must be a subset of the scopes grantable by the bound application(s).
  4. Owner/admin sets AllowedTaskTypes and DefaultTaskType. Two provisioning UX paths produce this (purpose-picker or direct-model-picker); both set AllowedTaskTypes/DefaultTaskType (detail in UC-029).
  5. Owner/admin pastes/uploads at least one ECDSA P-256 public key; the system assigns a kid and validity window.
  6. System validates: owner present, at least one app link, ceiling ⊆ app grantable scopes, task types valid, governance limits within tenant policy, public key well-formed.
  7. System checks budget headroom via the cost-governance subsystem (UC-032). Registration is blocked with a clear error if no viable budget headroom exists.
  8. System persists the AgentIdentity (status Active) and AgentPublicKey row(s) in one transaction.
  9. System writes an AuditLog entry (agent.registered) with the actor chain.
  10. System redirects to the agent detail page with a success message.

Alternative Flows

  • A1: Missing owner — Validation rejects; required OwnerUserId enforced for accountability.
  • A2: No application link — Validation rejects; ceiling cannot be bound without a registered application.
  • A3: Ceiling exceeds app scopes — Validation rejects requested scope ceiling that is not a subset of the bound application's grantable scopes.
  • A4: No budget headroom — Cost governance blocks creation; admin is directed to set/raise a cap (UC-032).
  • A5: Malformed public key — Key rejected; agent not created (or created without key if API allows deferred key registration, depending on policy).
  • A6: Duplicate name within tenant — Validation rejects to keep the registry unambiguous.

UC-025b: View / List Agents

Triggers

  • Owner/admin navigates to /agents, or calls GET /api/agents.
  • Owner/admin opens an agent detail page, or calls GET /api/agents/{id}.

Basic Flow

  1. Owner/admin clicks Agents in the navigation bar.
  2. System runs GetAgents scoped to the caller's tenant.
  3. System renders a Tabler table: name, owner, bound application(s), status badge (Active/Disabled/Revoked/Expired), DefaultTaskType, active credential count, ExpiresAt, and an action dropdown.
  4. Owner/admin selects an agent to view detail via GetAgentById.
  5. The detail page shows: identity card (name, owner, tenant, status, lifecycle dates), scope ceiling, AllowedTaskTypes/DefaultTaskType, governance limits, registered public keys (with kid, validity window, state), and minted credential summary.

Alternative Flows

  • A1: Agent not found — GetAgentById returns not found; API returns 404 Problem Details, Web shows an error.
  • A2: Cross-tenant access — A caller from another tenant cannot see the agent; result is filtered as not found.
  • A3: Status auto-derivation — An agent past its ExpiresAt displays as Expired via IsActiveAt(now) even if not yet swept.

UC-025c: Update Scope Ceiling & Allowed Task-Types / Deployments

Triggers

  • Owner/admin edits an agent and submits scope-ceiling or task-type changes.

Basic Flow

  1. Owner/admin opens Agents > > Edit.
  2. Owner/admin modifies the scope ceiling and/or AllowedTaskTypes/DefaultTaskType and/or allowed deployment set.
  3. For scope-ceiling changes, system sends UpdateAgentScopesCommand; for deployment/task-type changes, UpdateAllowedDeploymentsCommand.
  4. System validates the new ceiling ⊆ bound application grantable scopes and that DefaultTaskType ∈ AllowedTaskTypes.
  5. System persists the change and writes an AuditLog entry recording before/after values.
  6. Already-issued credentials are unaffected at issue time but constrained by the new ceiling at introspection/refresh (the ceiling is the upper bound; tightening it narrows future mints — see UC-026).

Alternative Flows

  • A1: New ceiling exceeds app scopes — Validation rejects; no change persisted.
  • A2: DefaultTaskType not in AllowedTaskTypes — Validation rejects.
  • A3: Removing a task type still referenced by active plans — Change allowed; active plans complete against prior config, new mints use the new set.

UC-025d: Disable Agent

Triggers

  • Owner/admin clicks Disable on the list or detail page, or calls a disable action.

Basic Flow

  1. Owner/admin confirms the disable action.
  2. System sends DisableAgentCommand, setting status Disabled.
  3. New credential minting and token issuance are refused while disabled; existing credentials remain valid until expiry unless separately revoked.
  4. System writes an AuditLog entry.

Alternative Flows

  • A1: Already disabled — Operation is idempotent; no error.
  • A2: Re-enable — A disabled agent may be re-enabled (status back to Active) provided budget headroom still exists.

UC-025e: Revoke Agent

Triggers

  • Owner/admin clicks Revoke on the list or detail page.

Basic Flow

  1. Owner/admin confirms revocation (irreversible) and optionally provides a RevokedReason.
  2. System sends RevokeAgentCommand, setting RevokedAt = now, RevokedReason, and status Revoked.
  3. System cascades: all active AgentCredentials for the agent are invalidated and active delegation grants revoked (UC-026, UC-027).
  4. IsActiveAt(moment) returns false for any moment ≥ RevokedAt; introspection on any minted credential returns inactive.
  5. System writes an AuditLog entry with the RevokedReason.

Alternative Flows

  • A1: Already revoked — Idempotent; no error.
  • A2: Revoke during in-flight call — Outstanding credentials fail at the next introspection; mid-stream calls hard-stop per governance.

UC-025f: Per-Agent Audit View

Triggers

  • Owner/admin opens Agents > > Audit, or calls GET /api/agents/{id}/audit.

Basic Flow

  1. Owner/admin opens the per-agent audit view.
  2. System runs GetAgentAudit filtering AuditLog to the agent.
  3. System renders a chronological list of lifecycle and credential events: registration, scope changes, deployment-set changes, credential mints/revocations, delegation grants/exchanges/revocations, disable/revoke — each with the full actor chain (agent ⇽ on-behalf-of subject where applicable) and before/after values for config changes.

Alternative Flows

  • A1: No events — Empty state shown.
  • A2: Filter by event type / date range — Query supports narrowing the result set.

API Endpoints

Method Path Auth Purpose
POST /api/agents X-Api-Key + agents.write Register a new agent identity
GET /api/agents X-Api-Key + agents.read List agents for the caller's tenant
GET /api/agents/{id} X-Api-Key + agents.read Get a single agent by id
PUT /api/agents/{id}/scopes X-Api-Key + agents.write Update scope ceiling (UpdateAgentScopes)
PUT /api/agents/{id}/deployments X-Api-Key + agents.write Update allowed task-types / deployments
POST /api/agents/{id}/keys X-Api-Key + agents.write Register / rotate an AgentPublicKey
DELETE /api/agents/{id} X-Api-Key + agents.disable Disable or revoke the agent
GET /api/agents/{id}/audit X-Api-Key + agents.read Per-agent audit trail

Representative Example — POST /api/agents

Request:

{
  "name": "invoice-triage-agent",
  "ownerUserId": "0190f3a1-2b6c-7c44-9b1e-1f2a3b4c5d6e",
  "applicationIds": ["0190e2b0-1a2b-7c33-8a0d-9e8f7a6b5c4d"],
  "scopeCeiling": {
    "capabilities": ["billing:read", "billing:export"],
    "toolScopes": ["mcp:invoices:list", "mcp:invoices:fetch"]
  },
  "allowedTaskTypes": ["analysis.financial", "summarization.document"],
  "defaultTaskType": "analysis.financial",
  "perTurnBudget": { "amount": 0.50, "currency": "USD" },
  "templateId": "finance.triage",
  "governance": {
    "maxCredentialTtlSeconds": 900,
    "allowedMcpServers": ["invoices"],
    "reservationRateCapPerMin": 60,
    "reservationCountCap": 100
  },
  "expiresAt": "2026-12-31T23:59:59Z",
  "publicKeys": [
    {
      "kid": "key-2026-06",
      "alg": "ES256",
      "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\n-----END PUBLIC KEY-----",
      "notBefore": "2026-06-09T00:00:00Z",
      "notAfter": "2027-06-09T00:00:00Z"
    }
  ]
}

Response (201 Created):

{
  "id": "0190f4c2-3d7e-7e55-ac2f-2a3b4c5d6e7f",
  "name": "invoice-triage-agent",
  "tenantId": "0190d1a0-0b1c-7a22-7f0c-8d7e6f5a4b3c",
  "ownerUserId": "0190f3a1-2b6c-7c44-9b1e-1f2a3b4c5d6e",
  "status": "Active",
  "scopeCeiling": {
    "capabilities": ["billing:read", "billing:export"],
    "toolScopes": ["mcp:invoices:list", "mcp:invoices:fetch"]
  },
  "allowedTaskTypes": ["analysis.financial", "summarization.document"],
  "defaultTaskType": "analysis.financial",
  "publicKeys": [
    { "kid": "key-2026-06", "state": "Active", "notAfter": "2027-06-09T00:00:00Z" }
  ],
  "createdAt": "2026-06-09T14:21:07Z",
  "expiresAt": "2026-12-31T23:59:59Z"
}

Business Rules

Rule ID Description
BR-1 Every agent must have a required OwnerUserId (human accountability); registration fails without it.
BR-2 An agent is tenant-owned; it is visible and manageable only within its owning tenant.
BR-3 The scope ceiling must be a subset of the scopes grantable by the bound application(s).
BR-4 The scope ceiling is the upper bound for every credential, token, and delegation the agent can ever produce.
BR-5 Agent creation is fail-closed: it is blocked unless viable budget headroom exists (UC-032).
BR-6 DefaultTaskType must be a member of AllowedTaskTypes.
BR-7 No agent secret is ever stored; authentication relies solely on registered AgentPublicKeys (private keys stay with the agent).
BR-8 Public keys support kid rotation, validity windows, and revocation; an expired or revoked key cannot authenticate.
BR-9 Revocation is irreversible and cascades to all active credentials and delegation grants.
BR-10 Governance limits (max credential TTL, allowed MCP servers, reservation rate/count caps) are enforced at credential issue time (UC-026).
BR-11 IsActiveAt(moment) returns false when the moment is past ExpiresAt, at/after RevokedAt, or while Disabled.
BR-12 Every lifecycle event is recorded in AuditLog with the full actor chain and before/after values for config changes.

Data Requirements

AgentIdentity

Field Type Constraints
Id Guid (UUIDv7) Primary key, application-generated
TenantId Guid Required, FK to Tenant
OwnerUserId Guid Required, FK to ApplicationUser (accountability)
Name string Max 256, required, unique within tenant
Status AgentStatus Active / Disabled / Revoked / Expired
ScopeCeilingJson string (JSON) Two-axis ceiling: capabilities + tool scopes; required
AllowedTaskTypesJson string (JSON) Set of task-type ids; required (replaces AllowedProviderModels)
DefaultTaskType string Max 128, required, must be in AllowedTaskTypes
PerTurnBudget decimal + currency Default per-turn budget for reactive-agent implicit plans
TemplateId string? Max 128, optional, for reporting aggregation
MaxCredentialTtlSeconds int Governance ceiling on credential TTL
AllowedMcpServersJson string (JSON) Allowed MCP servers for tool scopes
ReservationRateCapPerMin int? Override of default rate cap (default 60/min)
ReservationCountCap int? Override of default count cap (default 100)
CreatedAt DateTimeOffset Set on registration
CreatedBy string Acting principal
ExpiresAt DateTimeOffset? Null = no scheduled expiry
RevokedAt DateTimeOffset? Set on revoke
RevokedReason string? Max 1000, set on revoke

AgentPublicKey

Field Type Constraints
Id Guid (UUIDv7) Primary key
AgentId Guid Required, FK to AgentIdentity
Kid string Max 128, required, unique per agent (key id)
Algorithm string Fixed ES256 (ECDSA P-256)
PublicKeyPem string Max 4000, required (no private key ever stored)
State KeyState Active / Revoked
NotBefore DateTimeOffset Validity window start
NotAfter DateTimeOffset Validity window end
CreatedAt DateTimeOffset Set on registration
RevokedAt DateTimeOffset? Set on key revocation

Security Considerations

  • Authentication: API access uses the X-Api-Key header; the Web UI uses cookie auth. Agents themselves never get a secret here — only their public keys are registered for later private_key_jwt authentication (UC-026).
  • Authorization / capabilities: agents.read, agents.write, and agents.disable capabilities gate the registry operations; admins compose these into roles via the existing Role/Capability/RoleCapabilityMapping model (capability-based RBAC, no prescribed roles).
  • Data protection: Public keys are non-secret but integrity-protected; no private key material is ever accepted or stored. The scope ceiling and governance limits are enforced server-side, never trusted from the client at mint time.
  • Audit: Every register/update/disable/revoke/key-rotation event writes an AuditLog row with the actor chain and before/after values; heartbeats and other high-noise events are excluded by design.

Testing Scenarios

Test ID Scenario Expected Result
T-1 Register an agent with valid owner, app link, ceiling ⊆ app scopes, budget headroom 201 Created; AgentIdentity persisted Active; audit entry written
T-2 Register without OwnerUserId Rejected; required-owner validation error (BR-1)
T-3 Register without any application link Rejected; cannot bind ceiling (BR-3, A2)
T-4 Register with scope ceiling exceeding bound app's grantable scopes Rejected; ceiling-subset violation (BR-3)
T-5 Register when no viable budget headroom exists Blocked; fail-closed error directing to UC-032 (BR-5)
T-6 Register with DefaultTaskType not in AllowedTaskTypes Rejected (BR-6)
T-7 Register with malformed/non-ES256 public key Key rejected; no agent created (A5)
T-8 Register duplicate name within the same tenant Rejected (A6)
T-9 List agents as owner/admin of the tenant Returns only that tenant's agents
T-10 Get an agent belonging to another tenant 404 / filtered as not found (BR-2, A2)
T-11 Get an agent past ExpiresAt Status displays Expired; IsActiveAt(now) false (BR-11, A3)
T-12 Update scope ceiling to a valid narrower set Persisted; before/after audit recorded
T-13 Update scope ceiling exceeding app scopes Rejected (BR-3, UC-025c A1)
T-14 Update allowed task-types/deployments validly Persisted; future mints use new set (UC-025c)
T-15 Disable an active agent Status Disabled; new mints refused; existing creds valid (UC-025d)
T-16 Disable an already-disabled agent Idempotent; no error (UC-025d A1)
T-17 Re-enable a disabled agent with headroom Status back to Active (UC-025d A2)
T-18 Revoke an agent with a reason RevokedAt/RevokedReason set; status Revoked; creds + grants cascaded (UC-025e)
T-19 Revoke an already-revoked agent Idempotent; no error (UC-025e A1)
T-20 Introspect a credential after the agent is revoked Returns inactive (BR-9, UC-026)
T-21 Register a new public key with rotated kid Key persisted Active; prior key still valid within its window (BR-8)
T-22 Authenticate with a public key past NotAfter Rejected at token issue time (BR-8, UC-026)
T-23 Caller lacking agents.write attempts registration 403; no agent created (capability enforcement)
T-24 Caller lacking agents.read lists agents 403 (capability enforcement)
T-25 View per-agent audit after several lifecycle events All events listed chronologically with actor chain (UC-025f)
T-26 Concurrent registration of two agents sharing one public key PEM Both stored; key uniqueness is per-agent kid, not global PEM (BR-8)
T-27 Tighten ceiling, then attempt to mint a credential exceeding the new ceiling Mint rejected (BR-4, UC-026)
  • UC-026: Agent Credential Issuance — mints ephemeral scoped credentials bounded by this agent's ceiling and governance limits.
  • UC-027: On-Behalf-Of Delegation — delegation grants are bounded by the agent's own scope ceiling.
  • UC-029: Agent Provisioning UX — the purpose-picker and direct-model-picker paths that set AllowedTaskTypes/DefaultTaskType.
  • UC-032: Budget Cap Lattice — supplies the budget-headroom check that gates agent creation.
  • UC-033: Agent Plan Budget Lifecycle — consumes PerTurnBudget and the per-agent overlay.
  • UC-006: Role-Based Access Control — capability composition for agents.*.
  • UC-010: Activity Logging & Audit Trail — the underlying AuditLog surface.
  • UC-016: Tenant Provisioning Management — tenant ownership and Top-cap defaults.

Revision History

Version Date Author Changes
1.0 June 9, 2026 Platform Architecture Team Initial draft