feat(phase-3): workstream 2 — W3C DIDs

Implements W3C DID Core 1.0 per-agent identity for every registered agent:

Schema:
- agent_did_keys table: stores EC P-256 public key JWK + Vault path for private key
- agents.did + agents.did_created_at columns

Key management:
- EC P-256 key pair generated on every agent registration via Node.js crypto
- Private key stored in Vault KV v2 (dev:no-vault marker when Vault not configured)
- Public key JWK stored in PostgreSQL agent_did_keys table

API (4 new endpoints):
- GET /.well-known/did.json — instance DID Document (public, cached)
- GET /api/v1/agents/:id/did — per-agent DID Document (public, 410 for decommissioned)
- GET /api/v1/agents/:id/did/resolve — W3C DID Resolution result (agents:read scope)
- GET /api/v1/agents/:id/did/card — AGNTCY agent card (public)

Implementation:
- DIDService: DID construction, key generation, Redis caching (TTL configurable)
- DIDController: 410 Gone for decommissioned agents, correct Content-Type on resolve
- AgentService: calls DIDService.generateDIDForAgent on every new registration

Tests: 429 passing, DIDService 98.93% coverage, private key absence verified in all responses

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SentryAgent.ai Developer
2026-03-30 00:47:59 +00:00
parent d252097f71
commit 3d1fff15f6
15 changed files with 2171 additions and 14 deletions

95
src/types/did.ts Normal file
View File

@@ -0,0 +1,95 @@
/**
* W3C DID Core 1.0 and AGNTCY extension types for SentryAgent.ai AgentIdP.
* All interfaces are strictly typed — no `any` usage.
*/
/** A W3C DID Core 1.0 verification method. */
export interface IVerificationMethod {
id: string;
type: string;
controller: string;
publicKeyJwk: IPublicKeyJwk;
}
/** JWK representation of a public key. */
export interface IPublicKeyJwk {
kty: string;
crv?: string;
x?: string;
y?: string;
/** RSA modulus (base64url). */
n?: string;
/** RSA public exponent (base64url). */
e?: string;
use?: string;
kid?: string;
}
/** A W3C DID Document service endpoint. */
export interface IDIDService {
id: string;
type: string;
serviceEndpoint: string;
}
/** W3C DID Core 1.0 DID Document. */
export interface IDIDDocument {
'@context': string[];
id: string;
controller: string;
verificationMethod: IVerificationMethod[];
authentication: string[];
assertionMethod?: string[];
service?: IDIDService[];
agntcy?: IAgntcyExtension;
}
/** AGNTCY extension fields on a per-agent DID Document. */
export interface IAgntcyExtension {
agentId: string;
agentType: string;
capabilities: string[];
deploymentEnv: string;
owner: string;
version: string;
}
/** W3C DID Resolution result format. */
export interface IDIDResolutionResult {
didDocument: IDIDDocument;
didDocumentMetadata: {
created: string;
updated: string;
deactivated: boolean;
};
didResolutionMetadata: {
contentType: string;
retrieved: string;
};
}
/** AGNTCY-format agent card. */
export interface IAgentCard {
did: string;
name: string;
agentType: string;
capabilities: string[];
owner: string;
version: string;
deploymentEnv: string;
identityProvider: string;
issuedAt: string;
}
/** Raw database row for agent_did_keys. */
export interface IAgentDIDKeyRow {
keyId: string;
agentId: string;
organizationId: string;
publicKeyJwk: IPublicKeyJwk;
vaultKeyPath: string;
keyType: string;
curve: string;
createdAt: Date;
rotatedAt: Date | null;
}

View File

@@ -69,6 +69,10 @@ export interface IAgent {
status: AgentStatus;
createdAt: Date;
updatedAt: Date;
/** W3C DID identifier for this agent. Populated after DID generation. */
did?: string;
/** Timestamp when the DID was first generated for this agent. */
didCreatedAt?: Date;
}
/** Request body for registering a new AI agent. */