All findings from the inaugural LeadValidator audit resolved and confirmed. Release gate: PASS. VV_ISSUE_002 (BLOCKER): 15 OpenAPI specs verified present covering all 20 route groups (46 endpoints documented in docs/openapi/) VV_ISSUE_003 (MAJOR): Remove any types from src/db/pool.ts — replaced pool.query shim with unknown[] + Object.defineProperty, zero any types, eslint-disable suppressions removed VV_ISSUE_004 (MAJOR): Remove raw Pool from ScaffoldController and HealthDetailedController — injected AgentRepository/CredentialRepository and DbProbe interface respectively; added CredentialRepository.findActiveClientId() VV_ISSUE_005 (MAJOR): Add unit tests for 5 untested services — ComplianceStatusStore, EventPublisher, MarketplaceService, OIDCTrustPolicyService, UsageService VV_ISSUE_006 (MAJOR): Add integration tests for 7 missing route groups — analytics, billing, tiers, webhooks, marketplace, oidc-trust-policies, oidc-token-exchange VV_ISSUE_001 (MINOR): Create missing design.md and tasks.md in 4 OpenSpec archives — all archives now complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
481 lines
16 KiB
YAML
481 lines
16 KiB
YAML
openapi: "3.0.3"
|
|
|
|
info:
|
|
title: SentryAgent.ai — A2A Delegation (Agent-to-Agent)
|
|
version: 1.0.0
|
|
description: |
|
|
Agent-to-Agent (A2A) delegation endpoints for the SentryAgent.ai AgentIdP platform.
|
|
|
|
The delegation subsystem enables an authenticated agent (the *delegator*) to grant
|
|
a subset of its own scopes to another agent (the *delegatee*) for a limited time.
|
|
This creates a cryptographically-signed delegation chain, suitable for multi-agent
|
|
orchestration patterns.
|
|
|
|
**All endpoints require a valid Bearer JWT.**
|
|
|
|
**Feature flag:** When `A2A_ENABLED=false` these routes are not registered (return 404).
|
|
|
|
**Delegation rules:**
|
|
- The delegatee must be in the same tenant as the delegator.
|
|
- Delegated scopes must be a strict subset of the delegator's own scopes.
|
|
- TTL minimum: 60 seconds; maximum: 86400 seconds (24 hours).
|
|
- Each delegation chain has a unique `chainId` (UUID).
|
|
- Revoking a chain is idempotent — revoking an already-revoked chain succeeds.
|
|
|
|
servers:
|
|
- url: http://localhost:3000/api/v1
|
|
description: Local development server
|
|
- url: https://api.sentryagent.ai/v1
|
|
description: Production server
|
|
|
|
tags:
|
|
- name: A2A Delegation
|
|
description: Agent-to-Agent delegation chain management
|
|
|
|
components:
|
|
securitySchemes:
|
|
BearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
description: |
|
|
JWT access token obtained via `POST /token`.
|
|
Include as `Authorization: Bearer <token>`.
|
|
|
|
schemas:
|
|
DelegationChain:
|
|
type: object
|
|
description: A delegation chain record as returned by the API.
|
|
required:
|
|
- id
|
|
- tenantId
|
|
- delegatorAgentId
|
|
- delegateeAgentId
|
|
- scopes
|
|
- delegationToken
|
|
- ttlSeconds
|
|
- issuedAt
|
|
- expiresAt
|
|
- createdAt
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
description: Unique identifier of the delegation chain.
|
|
readOnly: true
|
|
example: "chain-abcd-1234-5678-ef01"
|
|
tenantId:
|
|
type: string
|
|
format: uuid
|
|
description: Organization (tenant) that owns this delegation.
|
|
readOnly: true
|
|
example: "org-1234-5678-abcd-ef01"
|
|
delegatorAgentId:
|
|
type: string
|
|
format: uuid
|
|
description: UUID of the agent granting authority.
|
|
example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
delegateeAgentId:
|
|
type: string
|
|
format: uuid
|
|
description: UUID of the agent receiving delegated authority.
|
|
example: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
|
scopes:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: OAuth 2.0 scopes granted by this delegation chain.
|
|
example:
|
|
- "agents:read"
|
|
delegationToken:
|
|
type: string
|
|
description: |
|
|
Opaque delegation token string that the delegatee presents to verify authority.
|
|
This token encodes the chain metadata and is HMAC-signed.
|
|
example: "chain-abcd-1234-5678-ef01.1743151200.1743237600"
|
|
signature:
|
|
type: string
|
|
description: HMAC-SHA256 signature of the delegation token payload.
|
|
example: "3a7f2b9c..."
|
|
ttlSeconds:
|
|
type: integer
|
|
description: Delegation lifetime in seconds.
|
|
minimum: 60
|
|
maximum: 86400
|
|
example: 3600
|
|
issuedAt:
|
|
type: string
|
|
format: date-time
|
|
readOnly: true
|
|
example: "2026-04-07T09:00:00.000Z"
|
|
expiresAt:
|
|
type: string
|
|
format: date-time
|
|
readOnly: true
|
|
example: "2026-04-07T10:00:00.000Z"
|
|
revokedAt:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
description: Timestamp when this chain was revoked. Null if still active or expired naturally.
|
|
readOnly: true
|
|
example: null
|
|
createdAt:
|
|
type: string
|
|
format: date-time
|
|
readOnly: true
|
|
example: "2026-04-07T09:00:00.000Z"
|
|
|
|
CreateDelegationRequest:
|
|
type: object
|
|
description: Request body for creating a new agent-to-agent delegation chain.
|
|
required:
|
|
- delegateeAgentId
|
|
- scopes
|
|
- ttlSeconds
|
|
properties:
|
|
delegateeAgentId:
|
|
type: string
|
|
format: uuid
|
|
description: |
|
|
UUID of the agent to receive delegated authority.
|
|
Must be in the same tenant as the delegator (caller).
|
|
example: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
|
scopes:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: |
|
|
Scopes to delegate. Must be a strict subset of the delegator's current token scopes.
|
|
At least one scope must be specified.
|
|
minItems: 1
|
|
example:
|
|
- "agents:read"
|
|
ttlSeconds:
|
|
type: integer
|
|
description: Delegation lifetime in seconds. Minimum: 60; Maximum: 86400 (24 hours).
|
|
minimum: 60
|
|
maximum: 86400
|
|
example: 3600
|
|
|
|
VerifyDelegationRequest:
|
|
type: object
|
|
description: Request body for verifying a delegation token.
|
|
required:
|
|
- delegationToken
|
|
properties:
|
|
delegationToken:
|
|
type: string
|
|
description: The delegation token string to verify.
|
|
example: "chain-abcd-1234-5678-ef01.1743151200.1743237600"
|
|
|
|
DelegationVerificationResult:
|
|
type: object
|
|
description: |
|
|
Result of verifying a delegation token.
|
|
Returns `valid: false` for expired or revoked tokens without throwing.
|
|
required:
|
|
- valid
|
|
- chainId
|
|
- delegatorAgentId
|
|
- delegateeAgentId
|
|
- scopes
|
|
- issuedAt
|
|
- expiresAt
|
|
properties:
|
|
valid:
|
|
type: boolean
|
|
description: Whether the delegation token is currently valid (active, not expired, not revoked).
|
|
example: true
|
|
chainId:
|
|
type: string
|
|
format: uuid
|
|
description: UUID of the delegation chain.
|
|
example: "chain-abcd-1234-5678-ef01"
|
|
delegatorAgentId:
|
|
type: string
|
|
format: uuid
|
|
example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
delegateeAgentId:
|
|
type: string
|
|
format: uuid
|
|
example: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
|
scopes:
|
|
type: array
|
|
items:
|
|
type: string
|
|
example:
|
|
- "agents:read"
|
|
issuedAt:
|
|
type: string
|
|
format: date-time
|
|
example: "2026-04-07T09:00:00.000Z"
|
|
expiresAt:
|
|
type: string
|
|
format: date-time
|
|
example: "2026-04-07T10:00:00.000Z"
|
|
revokedAt:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
example: null
|
|
|
|
ErrorResponse:
|
|
type: object
|
|
description: Standard error response envelope.
|
|
required:
|
|
- code
|
|
- message
|
|
properties:
|
|
code:
|
|
type: string
|
|
example: "DELEGATION_NOT_FOUND"
|
|
message:
|
|
type: string
|
|
example: "Delegation chain with the specified ID was not found."
|
|
details:
|
|
type: object
|
|
additionalProperties: true
|
|
|
|
responses:
|
|
Unauthorized:
|
|
description: Missing or invalid Bearer token.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "UNAUTHORIZED"
|
|
message: "A valid Bearer token is required to access this resource."
|
|
|
|
Forbidden:
|
|
description: Valid token but insufficient permissions.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "FORBIDDEN"
|
|
message: "You do not have permission to perform this action."
|
|
|
|
NotFound:
|
|
description: Delegation chain not found.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "DELEGATION_NOT_FOUND"
|
|
message: "Delegation chain with the specified ID was not found."
|
|
|
|
InternalServerError:
|
|
description: Unexpected server error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "INTERNAL_SERVER_ERROR"
|
|
message: "An unexpected error occurred. Please try again later."
|
|
|
|
security:
|
|
- BearerAuth: []
|
|
|
|
paths:
|
|
/oauth2/token/delegate:
|
|
post:
|
|
operationId: createDelegation
|
|
tags:
|
|
- A2A Delegation
|
|
summary: Create an A2A delegation chain
|
|
description: |
|
|
Creates a new agent-to-agent delegation chain. The authenticated agent
|
|
(the *delegator*) grants a subset of its own scopes to the `delegateeAgentId`.
|
|
|
|
A cryptographically-signed `delegationToken` is returned. The delegatee
|
|
presents this token to `POST /oauth2/token/verify-delegation` to prove
|
|
delegated authority.
|
|
|
|
**Validation:**
|
|
- Delegatee must be in the same organization as the delegator.
|
|
- `scopes` must be a strict subset of the delegator's current token scopes.
|
|
- `ttlSeconds` must be between 60 and 86400.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateDelegationRequest'
|
|
example:
|
|
delegateeAgentId: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
|
scopes:
|
|
- "agents:read"
|
|
ttlSeconds: 3600
|
|
responses:
|
|
'201':
|
|
description: Delegation chain created successfully.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/DelegationChain'
|
|
example:
|
|
id: "chain-abcd-1234-5678-ef01"
|
|
tenantId: "org-1234-5678-abcd-ef01"
|
|
delegatorAgentId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
delegateeAgentId: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
|
scopes:
|
|
- "agents:read"
|
|
delegationToken: "chain-abcd-1234-5678-ef01.1743151200.1743154800"
|
|
signature: "3a7f2b9c..."
|
|
ttlSeconds: 3600
|
|
issuedAt: "2026-04-07T09:00:00.000Z"
|
|
expiresAt: "2026-04-07T10:00:00.000Z"
|
|
revokedAt: null
|
|
createdAt: "2026-04-07T09:00:00.000Z"
|
|
'400':
|
|
description: Validation error in request body.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
examples:
|
|
scopeExceedsOwn:
|
|
summary: Requested scope exceeds delegator's own scopes
|
|
value:
|
|
code: "SCOPE_EXCEEDS_DELEGATOR"
|
|
message: "Delegated scopes must be a subset of the delegator's own token scopes."
|
|
details:
|
|
requested: ["agents:write"]
|
|
available: ["agents:read"]
|
|
invalidTtl:
|
|
summary: TTL out of range
|
|
value:
|
|
code: "VALIDATION_ERROR"
|
|
message: "ttlSeconds must be between 60 and 86400."
|
|
crossTenant:
|
|
summary: Delegatee in different tenant
|
|
value:
|
|
code: "CROSS_TENANT_DELEGATION"
|
|
message: "Delegatee agent must be in the same organization as the delegator."
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
description: Delegatee agent not found.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "AGENT_NOT_FOUND"
|
|
message: "Delegatee agent with the specified ID was not found."
|
|
'500':
|
|
$ref: '#/components/responses/InternalServerError'
|
|
|
|
/oauth2/token/verify-delegation:
|
|
post:
|
|
operationId: verifyDelegation
|
|
tags:
|
|
- A2A Delegation
|
|
summary: Verify a delegation token
|
|
description: |
|
|
Verifies a delegation token and returns the chain details if valid.
|
|
|
|
Returns `valid: true` with full chain metadata when the token is valid
|
|
(exists, not expired, not revoked).
|
|
|
|
Returns `valid: false` when the token is expired or revoked.
|
|
Does not throw an error for inactive tokens — always returns `200`.
|
|
|
|
Requires a valid Bearer JWT (any authenticated agent may verify a delegation).
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/VerifyDelegationRequest'
|
|
example:
|
|
delegationToken: "chain-abcd-1234-5678-ef01.1743151200.1743154800"
|
|
responses:
|
|
'200':
|
|
description: Delegation verification result returned.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/DelegationVerificationResult'
|
|
examples:
|
|
valid:
|
|
summary: Valid delegation token
|
|
value:
|
|
valid: true
|
|
chainId: "chain-abcd-1234-5678-ef01"
|
|
delegatorAgentId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
delegateeAgentId: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
|
scopes:
|
|
- "agents:read"
|
|
issuedAt: "2026-04-07T09:00:00.000Z"
|
|
expiresAt: "2026-04-07T10:00:00.000Z"
|
|
revokedAt: null
|
|
expired:
|
|
summary: Expired delegation token
|
|
value:
|
|
valid: false
|
|
chainId: "chain-abcd-1234-5678-ef01"
|
|
delegatorAgentId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
delegateeAgentId: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
|
scopes:
|
|
- "agents:read"
|
|
issuedAt: "2026-04-06T09:00:00.000Z"
|
|
expiresAt: "2026-04-06T10:00:00.000Z"
|
|
revokedAt: null
|
|
'400':
|
|
description: Missing or malformed `delegationToken` field.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "VALIDATION_ERROR"
|
|
message: "The 'delegationToken' field is required."
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'500':
|
|
$ref: '#/components/responses/InternalServerError'
|
|
|
|
/oauth2/token/delegate/{chainId}:
|
|
parameters:
|
|
- name: chainId
|
|
in: path
|
|
required: true
|
|
description: UUID of the delegation chain to revoke.
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
example: "chain-abcd-1234-5678-ef01"
|
|
|
|
delete:
|
|
operationId: revokeDelegation
|
|
tags:
|
|
- A2A Delegation
|
|
summary: Revoke a delegation chain
|
|
description: |
|
|
Immediately revokes a delegation chain.
|
|
|
|
After revocation, `POST /oauth2/token/verify-delegation` will return
|
|
`valid: false` for the revoked chain's token.
|
|
|
|
**Idempotent** — revoking an already-revoked chain returns `204` without error.
|
|
|
|
Only the delegator agent or an admin may revoke a chain.
|
|
Requires a valid Bearer JWT.
|
|
responses:
|
|
'204':
|
|
description: Delegation chain revoked successfully (or was already revoked). No response body.
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'500':
|
|
$ref: '#/components/responses/InternalServerError'
|