fix(vv): resolve all 6 V&V issues — field trial unblocked

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>
This commit is contained in:
SentryAgent.ai Developer
2026-04-07 04:52:47 +00:00
parent d216096dfb
commit 7441c9f298
49 changed files with 8954 additions and 70 deletions

View File

@@ -0,0 +1,480 @@
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'