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:
@@ -6,6 +6,7 @@
|
||||
import { AgentRepository } from '../repositories/AgentRepository.js';
|
||||
import { CredentialRepository } from '../repositories/CredentialRepository.js';
|
||||
import { AuditService } from './AuditService.js';
|
||||
import { DIDService } from './DIDService.js';
|
||||
import {
|
||||
IAgent,
|
||||
ICreateAgentRequest,
|
||||
@@ -32,11 +33,15 @@ export class AgentService {
|
||||
* @param agentRepository - The agent data repository.
|
||||
* @param credentialRepository - The credential repository (for decommission cleanup).
|
||||
* @param auditService - The audit log service.
|
||||
* @param didService - Optional DIDService. When provided, a W3C DID is generated for each
|
||||
* newly registered agent. When null/undefined, DID generation is skipped
|
||||
* (backward-compatible default).
|
||||
*/
|
||||
constructor(
|
||||
private readonly agentRepository: AgentRepository,
|
||||
private readonly credentialRepository: CredentialRepository,
|
||||
private readonly auditService: AuditService,
|
||||
private readonly didService: DIDService | null = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -72,6 +77,12 @@ export class AgentService {
|
||||
|
||||
const agent = await this.agentRepository.create(data);
|
||||
|
||||
// Generate a W3C DID for the new agent when DIDService is available
|
||||
if (this.didService !== null) {
|
||||
const organizationId = data.organizationId ?? 'org_system';
|
||||
await this.didService.generateDIDForAgent(agent.agentId, organizationId);
|
||||
}
|
||||
|
||||
// Synchronous audit insert
|
||||
await this.auditService.logEvent(
|
||||
agent.agentId,
|
||||
|
||||
Reference in New Issue
Block a user