Use Case 038: Agent Cost Governance RBAC

Overview

Property Value
Use Case ID UC-038
Use Case Name Agent Cost Governance RBAC
Module Agent Identity — Cost Governance
Priority High
Status ✅ Implemented
Version 1.0
Last Updated June 15, 2026

Implementation status (agent-identity release, June 2026). Implemented: 14 seeded platform capabilities (7 cost-governance + 7 agent/gateway) enforced by pure capability-claim policies (CapabilityAuthorizationPolicies) with no Administrator-role bypass — the only path to a feature is a logged RoleCapabilityMapping, bootstrapped once for the Administrator role by the first-startup seeder.

Description

This use case describes capability-based RBAC for the entire agent-identity, LLM-gateway, and cost-governance surface area introduced in the agent-identity release. AM exposes a set of granular capabilities; administrators compose them into roles using the existing Role / Capability / RoleCapabilityMapping model (the same model documented in UC-006). AM deliberately does not prescribe specific roles such as "FinanceAdmin" or "AiPlatformAdmin" — implementers compose roles to fit their own segregation-of-duties model. As an MVP practical default, anyone holding an existing AM admin role inherits all of the new capabilities; finer-grained roles can be layered on after the fact without code changes.

Every governance-config change — capability edit, provider-account credential rotation, model-deployment edit, and plan-extension approval — is recorded with before/after values via the respective *AuditEntry rows (BudgetCapAuditEntry, ProviderAccountAuditEntry, ModelDeploymentAuditEntry) folded into the existing AuditLog (see UC-010), on the same actor-chain pipeline as identity events. The SDK master design is riptide-sdk/docs/plans/AGENT-IDENTITY-ARCHITECTURE.md; the Application Manager plan is docs/internal/agent-identity-plan.md (Section 6). These are planned features and not yet built.

Actors

Actor Description Role
AM Administrator Holds an existing AM admin role; seeds/composes roles, inherits all new capabilities at MVP Primary
Role Composer Admin who builds finer-grained roles from the new capabilities (segregation of duties) Primary
Governance Operator User assigned a composed role (e.g., cap editor, provider-account manager) acting on a gated surface Primary
Initial Migration EF migration that seeds the new Capability rows Supporting
Authorization Pipeline Capability check on Web/API actions Supporting
AuditLog Existing audit trail receiving *AuditEntry before/after records Supporting

Preconditions

  1. The existing Role / Capability / RoleCapabilityMapping model is in place (UC-006).
  2. The agent-identity / gateway / cost-governance controllers and endpoints exist and declare capability requirements.
  3. The initial cost-governance EF migration runs on startup, seeding the new Capability rows.
  4. The existing AuditLog actor-chain pipeline is operational (UC-010).
  5. At least one AM admin role exists to receive the MVP default capability grant.

Postconditions

Success Postconditions

  1. All new agent/gateway/cost capabilities are registered as Capability rows after migration.
  2. Admins can compose arbitrary roles from these capabilities via RoleCapabilityMapping.
  3. A gated API/Web action permits the call only when the caller holds the required capability.
  4. Existing AM admin roles inherit all new capabilities (MVP default), preserving day-one operability.
  5. Every governance-config change writes a before/after *AuditEntry into AuditLog with the acting principal.

Failure Postconditions

  1. A caller lacking a required capability is denied (403); no state change occurs.
  2. A migration that fails to seed capabilities leaves the prior state intact (transactional), and the app surfaces the error rather than silently running un-gated.
  3. A governance change that fails validation is not persisted and writes no *AuditEntry.

Primary Flow Sequence

sequenceDiagram
    actor Op as Governance Operator
    participant Web as Web/API action
    participant Authz as Authorization (capability check)
    participant MR as MediatR handler
    participant Repo as Cost-governance repo
    participant Audit as AuditLog (*AuditEntry)

    Op->>Web: Request gated action (e.g., edit cap)
    Web->>Authz: Require capability (e.g., cost.cap.edit)
    alt capability present
        Authz-->>Web: Authorized
        Web->>MR: Send command (UpdateBudgetCap)
        MR->>Repo: Read current values (before)
        MR->>Repo: Persist new values (after)
        MR->>Audit: Write BudgetCapAuditEntry (before/after, actor chain)
        MR-->>Web: Success
        Web-->>Op: 200 / redirect
    else capability missing
        Authz-->>Web: Denied
        Web-->>Op: 403 (no state change)
    end

UC-038a: Seed New Capabilities in Initial Migration

Triggers

  • The cost-governance EF migration runs on application startup.

Basic Flow

  1. The initial cost-governance migration includes seed data inserting the new Capability rows (see Data Requirements).
  2. On startup, migrations auto-run; the new capabilities become available for mapping.
  3. The seed is idempotent — re-running migrations does not duplicate capability rows.
  4. Each capability carries a stable name (e.g., cost.cap.edit) and a human-readable description used in the role-composition UI.

Alternative Flows

  • A1: Capability already present — seed skips existing rows (idempotent), no duplicates.
  • A2: Migration failure — the transaction rolls back; the app reports the failure rather than starting with partially seeded capabilities.

UC-038b: Compose a Custom Role from Capabilities (Segregation of Duties)

Triggers

  • An admin builds a new role tailored to their org's segregation-of-duties model.

Basic Flow

  1. The admin opens role management (existing Roles admin UI — Bootstrap 5 + Tabler MVC).
  2. They create a role (e.g., "Cap Approver") and select capabilities to attach.
  3. They attach only the intended capabilities (e.g., cost.cap.view + cost.cap.approve but not cost.cap.edit) via RoleCapabilityMapping.
  4. They assign the role to users.
  5. AM enforces exactly those capabilities for that role's members on the gated surfaces.

Alternative Flows

  • A1: Separation of edit vs. approve — an org composes one role with cost.cap.edit and a distinct role with cost.cap.approve, so the editor cannot self-approve.
  • A2: Read-only auditor role — composed from the *.view/*.read capabilities only.
  • A3: No prescribed roles — AM ships none of "FinanceAdmin"/"AiPlatformAdmin"; the admin names and shapes roles freely.

UC-038c: Enforce a Capability at an API/Web Action

Triggers

  • A user invokes a gated action (e.g., editing a budget cap, rotating a provider-account credential, editing a deployment).

Basic Flow

  1. The user requests the action (Web form post or API call).
  2. The authorization pipeline checks the caller's capabilities for the one the action requires (e.g., cost.cap.edit for a cap edit).
  3. If present, the request proceeds to the MediatR handler.
  4. The handler executes and writes the relevant *AuditEntry (UC-038e).
  5. The user receives success.

Alternative Flows

  • A1: Capability missing — 403; no state change; the denial may itself be logged.
  • A2: Capability present but validation fails — the command's FluentValidation rejects the input; no persistence, no audit entry.
  • A3: API vs. Web — the same capability gates both the X-Api-Key-authenticated API action and the cookie-authenticated Web action.
sequenceDiagram
    actor User
    participant Action as Web/API action
    participant Authz as Capability check
    participant Handler as MediatR handler
    participant Audit as AuditLog

    User->>Action: POST rotate provider credential
    Action->>Authz: Require llm.providers.write
    alt has capability
        Authz-->>Action: Authorized
        Action->>Handler: RotateProviderAccountCredential
        Handler->>Audit: ProviderAccountAuditEntry (before/after)
        Handler-->>Action: Success
        Action-->>User: 200
    else lacks capability
        Authz-->>Action: Denied
        Action-->>User: 403
    end

UC-038d: MVP Default — Admin Role Inherits All New Capabilities

Triggers

  • The agent-identity release is deployed onto an installation with existing AM admin roles.

Basic Flow

  1. The migration seeds the new capabilities and maps them to the existing AM admin role(s) by default.
  2. Existing admins immediately hold all agent/gateway/cost capabilities — no manual wiring needed for day-one operability.
  3. Implementers may later create finer-grained roles and reassign users off the broad admin role.
  4. Removing a capability from the admin role (or moving users to narrower roles) is a pure RoleCapabilityMapping change — no code change.

Alternative Flows

  • A1: Implementer tightens the default — they remove specific capabilities from the admin role and compose narrower roles; enforcement follows immediately.
  • A2: Multiple admin roles — each existing admin role receives the default grant unless the implementer scopes the seed otherwise.

UC-038e: Audit a Governance Config Change (Before/After, Actor Chain)

Triggers

  • A governance-config change occurs: cap edit, provider-account credential rotation, deployment edit, or plan-extension approval.

Basic Flow

  1. The handler reads the current entity state (the "before" snapshot).
  2. It applies and persists the change (the "after" state).
  3. It writes the matching *AuditEntry — BudgetCapAuditEntry, ProviderAccountAuditEntry, or ModelDeploymentAuditEntry — capturing before/after values, the acting principal, and the timestamp.
  4. The entry folds into the existing AuditLog on the same actor-chain pipeline as identity events (so delegated actions record the full chain).
  5. Plan-extension approvals (gated by cost.plan.extension.approve) are recorded the same way.

Alternative Flows

  • A1: Credential rotation — the ProviderAccountAuditEntry records the rotation event and metadata, never the secret value itself (write-only credential field).
  • A2: No-op change — if before == after, the handler may still record the attempt per audit policy, but no functional state changes.
  • A3: Failed change — validation/permission failure writes no *AuditEntry (nothing was persisted); the denial may be logged separately.

API Endpoints

These are existing cost-governance endpoints; this use case defines the capability each requires. Auth column shows the gating capability (all are X-Api-Key-authenticated at the transport layer).

Method Path Auth (capability) Purpose
PUT /api/budgets/caps/{id} cost.cap.edit Edit a budget cap
POST /api/budgets/approvals/{reservationId}/approve cost.cap.approve Approve an ApprovalRequired exhaustion
POST /api/agents/{id}/plans/{planId}/extend cost.plan.extension.approve Approve a plan budget extension
POST /api/providers/accounts/{id}/rotate llm.providers.write Rotate a provider-account credential
PUT /api/deployments/{id} llm.deployments.write Edit a model deployment
GET /api/insights/overview cost.insights.view.tenant/.agency View insights dashboard data
POST /api/insights/billing-report cost.report.generate Generate billing report
// PUT /api/budgets/caps/{id}  (requires cost.cap.edit)
// Request
{
  "limitAmount": 5000.00,
  "currency": "USD",
  "enforcementMode": "HardStop",
  "midStreamMode": "Strict",
  "alertThresholds": [0.5, 0.8, 0.95]
}

// Response 200 — change recorded as a BudgetCapAuditEntry (before/after)
{
  "id": "b1c2d3e4-f5a6-4b7c-8d90-1a2b3c4d5e6f",
  "tier": "Tenant",
  "limitAmount": 5000.00,
  "previousLimitAmount": 3000.00,
  "enforcementMode": "HardStop",
  "updatedBy": "admin@acme.gov",
  "updatedAt": "2026-06-09T15:20:00Z",
  "auditEntryId": "ae-77a1..."
}

// PUT /api/budgets/caps/{id} without cost.cap.edit -> 403 (no state change)

Business Rules

Rule Description
BR-1 AM ships granular capabilities; admins compose them into roles via Role/Capability/RoleCapabilityMapping
BR-2 AM does not prescribe specific roles ("FinanceAdmin", "AiPlatformAdmin") — implementers compose to their own SoD model
BR-3 New capabilities are seeded by the initial cost-governance EF migration (idempotent)
BR-4 MVP default: existing AM admin roles inherit all new capabilities
BR-5 Tightening the default is a pure RoleCapabilityMapping change — no code change
BR-6 A gated action requires its capability on both the API and Web surfaces
BR-7 Missing capability → 403, no state change
BR-8 Every cap edit, provider-account credential rotation, deployment edit, and plan-extension approval writes a before/after *AuditEntry into AuditLog
BR-9 *AuditEntry records the acting principal on the same actor-chain pipeline as identity events
BR-10 Credential rotation audit never records the secret value (write-only credential field)

Data Requirements

New capabilities registered by the initial migration:

Capability Permits
agents.read View agent identities
agents.write Create / update agent identities
agents.disable Disable / revoke agent identities
llm.gateway.read View the task-type registry
llm.gateway.config Edit the task-type registry
llm.providers.write Create / rotate / disable ProviderAccounts
llm.deployments.write Edit ModelDeployment rows
cost.cap.view View budget caps
cost.cap.edit Create / edit / delete budget caps
cost.cap.approve Approve ApprovalRequired cap-exhaustion events
cost.plan.extension.approve Approve plan budget extensions when policy requires
cost.insights.view.tenant View the insights dashboard scoped to one tenant
cost.insights.view.agency View the insights dashboard across tenants
cost.report.generate Generate the billing report

Capability row shape (existing entity; new rows added):

Field Type Constraints
Id Guid Primary key
Name string Stable capability key (e.g., cost.cap.edit), unique
Description string Human-readable; shown in role-composition UI

*AuditEntry (folds into AuditLog) — common shape for BudgetCapAuditEntry / ProviderAccountAuditEntry / ModelDeploymentAuditEntry:

Field Type Constraints
Id Guid Primary key
EntityId Guid The cap / provider account / deployment changed
ChangeType string e.g., CapEdit, CredentialRotation, DeploymentEdit, PlanExtensionApproval
BeforeValuesJson string (JSON) Snapshot of prior values (secrets excluded)
AfterValuesJson string (JSON) Snapshot of new values (secrets excluded)
ActingPrincipal string Acting user/agent; full actor chain for delegated calls
OccurredAt DateTimeOffset Timestamp

Security Considerations

  • Authentication: API governance actions authenticate via X-Api-Key; Web governance actions via the admin cookie scheme. Capability enforcement is layered on top of authentication.
  • Authorization / capabilities: every agent/gateway/cost action is gated by a granular capability; least privilege is achievable by composing narrow roles. The "no prescribed roles" stance lets organizations enforce separation of duties (e.g., edit vs. approve) without fighting baked-in role shapes.
  • Data protection: credential-rotation audit entries record metadata only, never secret values; the provider-account credential field is write-only. Before/after snapshots exclude secrets.
  • Audit: all governance-config changes write before/after *AuditEntry rows into AuditLog on the actor-chain pipeline, giving a complete forensic record including delegated actor chains. Denials may be logged for tamper visibility.

Testing Scenarios

ID Scenario Expected Result
T-1 Run initial migration All 14 new capabilities present as Capability rows
T-2 Re-run migration No duplicate capability rows (idempotent seed)
T-3 Migration failure mid-seed Transaction rolls back; app reports error, not partial state
T-4 Compose role with cost.cap.view + cost.cap.approve only Members can view and approve, but not edit caps
T-5 Compose separate edit and approve roles Editor cannot self-approve (SoD enforced)
T-6 Edit a cap with cost.cap.edit Succeeds; BudgetCapAuditEntry written with before/after
T-7 Edit a cap without cost.cap.edit 403; no state change; no audit entry
T-8 Rotate provider credential with llm.providers.write Succeeds; ProviderAccountAuditEntry records rotation, not the secret
T-9 Rotate provider credential without capability 403; no rotation
T-10 Edit deployment with llm.deployments.write Succeeds; ModelDeploymentAuditEntry written
T-11 Approve plan extension with cost.plan.extension.approve Extension approved; audited
T-12 Approve plan extension without capability 403
T-13 MVP default after deploy Existing admin role holds all 14 new capabilities
T-14 Remove a capability from admin role Members lose that gated action immediately; no code change
T-15 Same capability gates API and Web Both surfaces deny without the capability
T-16 Read-only auditor role from *.view/*.read Can view, cannot mutate any governance surface
T-17 Delegated action audit *AuditEntry records full actor chain
T-18 No-op change (before == after) No functional change; audit per policy
T-19 Validation failure on gated action Rejected by FluentValidation; no persistence, no audit entry
T-20 Capability description rendering Role-composition UI shows human-readable descriptions
T-21 Multiple admin roles Each receives the default grant unless scoped otherwise
T-22 cost.insights.view.tenant vs .agency Tenant scope limited to one tenant; agency scope cross-tenant
  • UC-006 — Role-Based Access Control (the Role/Capability/RoleCapabilityMapping model reused here)
  • UC-005 — Administrator User Management (admin roles that inherit the MVP default)
  • UC-010 — Activity Logging & Audit Trail (the AuditLog pipeline receiving *AuditEntry rows)
  • UC-025 — Agent Identity Registration & Lifecycle (gated by agents.read/write/disable)
  • UC-032 — Budget Cap Lattice Enforcement (gated by cost.cap.view/edit/approve)
  • UC-036 — Cost Insights Dashboard & Billing Report (gated by cost.insights.view.*, cost.report.generate)

Revision History

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