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:
14
src/app.ts
14
src/app.ts
@@ -23,12 +23,14 @@ import { AgentService } from './services/AgentService.js';
|
||||
import { CredentialService } from './services/CredentialService.js';
|
||||
import { OAuth2Service } from './services/OAuth2Service.js';
|
||||
import { OrgService } from './services/OrgService.js';
|
||||
import { DIDService } from './services/DIDService.js';
|
||||
|
||||
import { AgentController } from './controllers/AgentController.js';
|
||||
import { TokenController } from './controllers/TokenController.js';
|
||||
import { CredentialController } from './controllers/CredentialController.js';
|
||||
import { AuditController } from './controllers/AuditController.js';
|
||||
import { OrgController } from './controllers/OrgController.js';
|
||||
import { DIDController } from './controllers/DIDController.js';
|
||||
|
||||
import { createAgentsRouter } from './routes/agents.js';
|
||||
import { createTokenRouter } from './routes/token.js';
|
||||
@@ -37,11 +39,13 @@ import { createAuditRouter } from './routes/audit.js';
|
||||
import { createHealthRouter } from './routes/health.js';
|
||||
import { createMetricsRouter } from './routes/metrics.js';
|
||||
import { createOrgsRouter } from './routes/organizations.js';
|
||||
import { createDIDRouter } from './routes/did.js';
|
||||
|
||||
import { errorHandler } from './middleware/errorHandler.js';
|
||||
import { createOpaMiddleware } from './middleware/opa.js';
|
||||
import { metricsMiddleware } from './middleware/metrics.js';
|
||||
import { createOrgContextMiddleware } from './middleware/orgContext.js';
|
||||
import { authMiddleware } from './middleware/auth.js';
|
||||
import { createVaultClientFromEnv } from './vault/VaultClient.js';
|
||||
import { RedisClientType } from 'redis';
|
||||
import path from 'path';
|
||||
@@ -117,7 +121,8 @@ export async function createApp(): Promise<Application> {
|
||||
// Service layer
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
const auditService = new AuditService(auditRepo);
|
||||
const agentService = new AgentService(agentRepo, credentialRepo, auditService);
|
||||
const didService = new DIDService(pool, vaultClient, redis as RedisClientType);
|
||||
const agentService = new AgentService(agentRepo, credentialRepo, auditService, didService);
|
||||
const credentialService = new CredentialService(credentialRepo, agentRepo, auditService, vaultClient);
|
||||
const orgService = new OrgService(orgRepo, agentRepo);
|
||||
|
||||
@@ -150,6 +155,7 @@ export async function createApp(): Promise<Application> {
|
||||
const credentialController = new CredentialController(credentialService);
|
||||
const auditController = new AuditController(auditService);
|
||||
const orgController = new OrgController(orgService);
|
||||
const didController = new DIDController(didService, agentRepo);
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Org context middleware — sets PostgreSQL session variable app.organization_id
|
||||
@@ -169,7 +175,13 @@ export async function createApp(): Promise<Application> {
|
||||
// Prometheus metrics — unauthenticated, internal scraping only
|
||||
app.use('/metrics', createMetricsRouter());
|
||||
|
||||
// Well-known DID Document for the AgentIdP instance — unauthenticated
|
||||
app.get('/.well-known/did.json', (req, res, next) => {
|
||||
void didController.getInstanceDIDDocument(req, res, next);
|
||||
});
|
||||
|
||||
app.use(`${API_BASE}/agents`, createAgentsRouter(agentController, opaMiddleware));
|
||||
app.use(`${API_BASE}`, createDIDRouter(didController, authMiddleware, opaMiddleware));
|
||||
app.use(
|
||||
`${API_BASE}/agents/:agentId/credentials`,
|
||||
createCredentialsRouter(credentialController, opaMiddleware),
|
||||
|
||||
Reference in New Issue
Block a user