6 workstreams, 119 tasks — Scale & Ecosystem: - WS1: Rust SDK - WS2: Agent-to-Agent (A2A) Authorization - WS3: Advanced Analytics Dashboard - WS4: Public API Gateway & Rate Limiting SaaS - WS5: Developer Experience (DX) improvements - WS6: AGNTCY Compliance Certification Package Awaiting CEO approval to begin implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
30 KiB
30 KiB
1. WS1: Rust SDK — Crate Setup
- 1.1 Create
sdk-rust/directory andCargo.toml— name:sentryagent-idp, version:1.0.0, edition:2021; add dependencies:tokio(features: full),reqwest(features: json, rustls-tls),serde(features: derive),serde_json,uuid(features: v4),thiserror,async-trait; add dev-dependencies:tokio-test,mockito - 1.2 Create
sdk-rust/src/lib.rs— crate root with#![deny(warnings)]; re-exportAgentIdPClient,TokenManager,AgentIdPError, and all model types from submodules; add crate-level//!doc comment describing the SDK - 1.3 Create
sdk-rust/src/error.rs— defineAgentIdPErrorenum with variants:HttpError(reqwest::Error),ApiError { status: u16, message: String, code: Option<String> },AuthError(String),NotFound(String),RateLimited { retry_after_secs: u64 },ConfigError(String),SerdeError(serde_json::Error),DelegationError(String); derivethiserror::ErrorandDebug; implementstd::error::Error - 1.4 Create
sdk-rust/src/models.rs— define all request structs (RegisterAgentRequest,UpdateAgentRequest,AuditLogFilters,MarketplaceFilters,DelegateRequest) and all response structs (Agent,AgentList,TokenResponse,Credentials,AuditLogEntry,AuditLogList,MarketplaceAgent,MarketplaceAgentList,DelegationToken,DelegationVerification); all structs deriveserde::Serialize,serde::Deserialize,Debug,Clone
2. WS1: Rust SDK — Token Manager
- 2.1 Create
sdk-rust/src/token_manager.rs— defineTokenCachestruct withaccess_token: Option<String>andexpires_at: Option<std::time::Instant>; defineTokenManagerstruct with fieldsapi_url,client_id,client_secret,cache: Arc<Mutex<TokenCache>> - 2.2 Implement
TokenManager::new(api_url: &str, client_id: &str, client_secret: &str) -> Self— initializes with empty cache - 2.3 Implement
TokenManager::get_token(&self) -> Result<String, AgentIdPError>— acquires lock, checksexpires_atagainstInstant::now() + 60s, returns cached token if valid, else callsPOST /oauth2/tokenviareqwest, updates cache, releases lock - 2.4 Write unit test
token_manager_returns_cached_token— mockPOST /oauth2/tokenusingmockito, callget_token()twice, verify mock is hit only once - 2.5 Write unit test
token_manager_refreshes_expired_token— setexpires_atto past, verifyget_token()triggers a newPOST /oauth2/tokencall - 2.6 Write concurrent safety test
token_manager_concurrent_calls_no_race— spawn 50tokio::spawntasks all callingget_token()simultaneously, verify mock is hit at most once (no thundering herd), verify all 50 tasks receive valid tokens
3. WS1: Rust SDK — Client Methods
- 3.1 Create
sdk-rust/src/client.rs— defineAgentIdPClientstruct with fieldsbase_url,client_id,client_secret,http: reqwest::Client,token_manager: Arc<Mutex<TokenManager>>; implementnew(base_url, client_id, client_secret) -> Selfandfrom_env() -> Result<Self, AgentIdPError>(readsAGENTIDP_API_URL,AGENTIDP_CLIENT_ID,AGENTIDP_CLIENT_SECRET) - 3.2 Create
sdk-rust/src/agents.rs— implement all agent methods onAgentIdPClient:register_agent,get_agent,list_agents,update_agent,delete_agent— each acquires a bearer token viatoken_manager.get_token(), makes the correct HTTP call, deserializes response, maps non-2xx responses toAgentIdPError::ApiError - 3.3 Create
sdk-rust/src/oauth2.rs— implementissue_token(&self, agent_id: &str, scopes: &[&str]) -> Result<TokenResponse, AgentIdPError>— sendsPOST /oauth2/tokenwithgrant_type=client_credentials - 3.4 Create
sdk-rust/src/credentials.rs— implementgenerate_credentials,rotate_credentials,revoke_credentials— map 404 response toAgentIdPError::NotFound, map 401 toAgentIdPError::AuthError - 3.5 Create
sdk-rust/src/audit.rs— implementlist_audit_logs(filters: AuditLogFilters)— serialize filters as query parameters; handle empty result set (return emptyVec, not error) - 3.6 Create
sdk-rust/src/marketplace.rs— implementlist_public_agents(filters)andget_public_agent(agent_id)— no auth header required for these endpoints - 3.7 Create
sdk-rust/src/delegation.rs— implementdelegate(req: DelegateRequest)andverify_delegation(token: &str) - 3.8 Implement 429 handling across all client methods — parse
Retry-Afterheader, returnAgentIdPError::RateLimited { retry_after_secs }; verify zerounwrap()calls in allsrc/files (rungrep -r 'unwrap()' sdk-rust/src/— must return empty)
4. WS1: Rust SDK — Tests, Examples, Documentation
- 4.1 Create
sdk-rust/examples/quickstart.rs— working example: createAgentIdPClient::from_env(), callregister_agent, callissue_token, print token; example must compile withcargo build --example quickstart - 4.2 Create
sdk-rust/tests/integration_test.rs— integration tests requiringAGENTIDP_API_URL,AGENTIDP_CLIENT_ID,AGENTIDP_CLIENT_SECRETenv vars; test: register agent, issue token, get agent, update agent, rotate credentials, delete agent; each test is#[tokio::test]with#[ignore]attribute (run explicitly withcargo test -- --ignored) - 4.3 Write
sdk-rust/README.md— installation viaCargo.toml, environment variable configuration, quickstart code example, full method reference table with signatures, error handling guide, link to crates.io - 4.4 Run
cargo doc --no-deps— verify docs generate without errors or warnings; verify all public items have///doc comments - 4.5 Run
cargo clippy -- -D warnings— zero warnings; runcargo test(unit tests only, no--ignored) — all pass
5. WS2: A2A Authorization — Database & Types
- 5.1 Create
src/infrastructure/migrations/008_add_delegation_chains.sql— createdelegation_chainstable with columns:id(UUID PK),tenant_id(UUID FK),delegator_agent_id(UUID FK),delegatee_agent_id(UUID FK),scopes(TEXT[]),delegation_token(TEXT UNIQUE),signature(TEXT),ttl_seconds(INTEGER CHECK 60–86400),issued_at(TIMESTAMPTZ),expires_at(TIMESTAMPTZ),revoked_at(TIMESTAMPTZ nullable),created_at(TIMESTAMPTZ DEFAULT NOW); create all four indexes as specified in spec - 5.2 Create
src/types/delegation.ts— define interfaces:DelegationChain,CreateDelegationRequest(delegateeAgentId, scopes, ttlSeconds),DelegationVerificationResult(valid, chainId, delegatorAgentId, delegateeAgentId, scopes, issuedAt, expiresAt, revokedAt),DelegationTokenPayload
6. WS2: A2A Authorization — Crypto & Service
- 6.1 Create
src/utils/delegationCrypto.ts— implementsignDelegationPayload(payload: DelegationTokenPayload, secret: string): stringusing HMAC-SHA256 (Node.jscrypto.createHmac('sha256', secret)); implementverifyDelegationSignature(payload: DelegationTokenPayload, signature: string, secret: string): boolean; implementgenerateDelegationToken(): string(UUID v4); export only these three functions — no other exports - 6.2 Create
src/services/DelegationService.ts— implementIDelegationServiceinterface;createDelegation: validate delegateeAgentId exists in same tenant, validate scopes ⊆ delegator's scopes, reject self-delegation, sign payload, insertdelegation_chainsrow, write audit log entry (delegation.created), returnDelegationChain - 6.3 Implement
DelegationService.verifyDelegation(delegationToken)— fetch chain row bydelegation_token, if not found throwNotFoundError, verify HMAC signature, checkexpires_at > NOW()andrevoked_at IS NULL, returnDelegationVerificationResultwithvalid: true/false(never throw on expired/revoked — returnvalid: false); write audit log entry (delegation.verified) - 6.4 Implement
DelegationService.revokeDelegation(chainId, requestingAgentId)— fetch chain by ID, verifydelegator_agent_id === requestingAgentId(else throwForbiddenError), check not already revoked (else throwConflictError), updaterevoked_at = NOW(), write audit log entry (delegation.revoked)
7. WS2: A2A Authorization — Controller, Routes, Tests
- 7.1 Create
src/controllers/DelegationController.ts— implementcreateDelegationhandler (POST /oauth2/token/delegate): extract authenticated agent ID from request context, callDelegationService.createDelegation, return HTTP 201; implementverifyDelegationhandler (POST /oauth2/token/verify-delegation): callDelegationService.verifyDelegation, return HTTP 200; implementrevokeDelegationhandler (DELETE /oauth2/token/delegate/:chainId): callDelegationService.revokeDelegation, return HTTP 204 - 7.2 Create
src/routes/delegation.ts— Express router registeringPOST /oauth2/token/delegate,POST /oauth2/token/verify-delegation,DELETE /oauth2/token/delegate/:chainIdwith authentication middleware on all three routes - 7.3 Register delegation router in
src/routes/index.tsbehindA2A_ENABLEDfeature flag — return HTTP 404 on all delegation routes whenA2A_ENABLED=false - 7.4 Add delegation Prometheus metrics:
agentidp_delegations_created_total,agentidp_delegations_verified_total(labels: result),agentidp_delegations_revoked_total— increment inDelegationControllerhandlers - 7.5 Add delegation endpoints to
docs/openapi.yaml— include all request/response schemas, error responses, and authentication requirements as defined in spec - 7.6 Write unit tests for
delegationCrypto.ts— test sign/verify round-trip, test tampered payload fails verification, test different secrets produce different signatures - 7.7 Write unit tests for
DelegationService— mock DB and audit service; test: create delegation (valid), create delegation (scope escalation rejected), create delegation (self-delegation rejected), create delegation (delegatee in different tenant rejected), verify delegation (valid), verify delegation (expired — returns valid: false not throw), verify delegation (revoked — returns valid: false), revoke delegation (by delegator — succeeds), revoke delegation (by non-delegator — throws ForbiddenError), revoke delegation (already revoked — throws ConflictError) - 7.8 Write integration tests for delegation endpoints — test all happy paths and all error cases defined in spec; verify audit log entries are created for each delegation operation
8. WS3: Analytics — Database, Aggregation Job
- 8.1 Create
src/infrastructure/migrations/009_add_analytics_aggregates.sql— createanalytics_daily_aggregatestable with columns:id(UUID PK),tenant_id(UUID FK),agent_id(UUID nullable FK),date(DATE),metric_type(VARCHAR 64),count(BIGINT),created_at,updated_at; add unique constraint on(tenant_id, agent_id, date, metric_type); create indexes on(tenant_id, date)and(agent_id, date) WHERE agent_id IS NOT NULL - 8.2 Install
node-cronnpm package — add topackage.json - 8.3 Create
src/jobs/analyticsAggregation.ts— implementrunAnalyticsAggregation(targetDate: Date): Promise<void>: execute upsert query aggregating previous day'susage_eventsrows intoanalytics_daily_aggregates; query is idempotent (upsert on unique constraint); updateagentidp_analytics_aggregation_job_duration_msgauge andagentidp_analytics_aggregation_job_last_rungauge on completion - 8.4 Register cron job in
src/app.ts— schedulerunAnalyticsAggregationat00:05 UTCdaily usingnode-cron; log job start, completion, and any errors; do not crash the process on job failure — log error and continue
9. WS3: Analytics — Service, Controller, Routes
- 9.1 Create
src/types/analytics.ts— define interfaces:UsageSummary,AgentActivityResponse,TokenTrendsResponse,DailyAggregate,AnalyticsDateRange - 9.2 Create
src/services/AnalyticsService.ts— implementIAnalyticsService;getUsageSummary: validate date range (from <= to, max 365 days), check Redis cache (analytics:{tenantId}:summary:{hash}, TTL 60s), on miss queryanalytics_daily_aggregates, compute totals, write to cache, returnUsageSummary - 9.3 Implement
AnalyticsService.getAgentActivity(tenantId, from, to, agentId?)— validate date range (max 90 days), check Redis cache (TTL 5 min), on miss queryanalytics_daily_aggregatesgrouped byagent_idanddate, join agent names fromagentstable, write to cache, returnAgentActivityResponse - 9.4 Implement
AnalyticsService.getTokenTrends(tenantId, from, to, granularity)— supportdayandweekgranularity (weekly:date_trunc('week', date)), check Redis cache (TTL 5 min), returnTokenTrendsResponsewithsuccessfulIssuances,failedIssuances,uniqueAgentsper period - 9.5 Create
src/controllers/AnalyticsController.ts— handlers forgetUsageSummary,getAgentActivity,getTokenTrends; parse and validate query parameters; return HTTP 403 for free-tier tenants (checkANALYTICS_FREE_TIERenv and tenant subscription); emitagentidp_analytics_query_duration_mshistogram and cache hit/miss counters - 9.6 Create
src/routes/analytics.ts— Express router for/analytics/usage-summary,/analytics/agent-activity,/analytics/token-trends; all routes require authentication middleware - 9.7 Register analytics router in
src/routes/index.tsbehindANALYTICS_ENABLEDfeature flag - 9.8 Add analytics endpoints to
docs/openapi.yaml— all query parameters, response schemas, and error codes as defined in spec - 9.9 Write unit tests for
AnalyticsService— test: usage-summary (cache hit), usage-summary (cache miss → DB query), agent-activity (with agentId filter), agent-activity (no filter — all agents), token-trends (daily), token-trends (weekly), date range validation (from > to rejected), date range validation (> max days rejected), free-tier rejection - 9.10 Write integration tests for analytics endpoints — test all three endpoints with valid date ranges, verify free-tier rejection, verify invalid date range errors
10. WS3: Analytics — Dashboard UI
- 10.1 Install
rechartsanddate-fnsindashboard/package.json - 10.2 Create
dashboard/src/api/analyticsApi.ts— typed fetch functions for all three analytics endpoints:fetchUsageSummary(token, from, to),fetchAgentActivity(token, from, to, agentId?),fetchTokenTrends(token, from, to, granularity); all functions return typed response objects; handle 403 response with a typedAnalyticsNotAvailableError - 10.3 Create
dashboard/src/components/charts/AgentHeatmap.tsx— renders a grid heatmap (agents × dates) usingrechartsor a custom CSS grid; color intensity representsapiCallscount; hover tooltip shows agent name, date, apiCalls, tokenIssuances, credentialRotations; acceptsagentsprop fromAgentActivityResponse - 10.4 Create
dashboard/src/components/charts/TokenTrendsChart.tsx— renders arechartsComposedChartwith aLineforsuccessfulIssuancesand aBarforfailedIssuances; X-axis is dates; tooltip shows all three metrics per period; acceptsdataPointsprop fromTokenTrendsResponse - 10.5 Create
dashboard/src/components/charts/RotationFrequencyTable.tsx— renders a sortable table of credential rotation counts per agent; columns: Agent Name, Rotations (period), Last Rotation Date; sortable by any column; acceptsagentsprop derived fromAgentActivityResponsefilteringcredentialRotations - 10.6 Create
dashboard/src/pages/Analytics.tsx— analytics tab page; renders date range picker (from/to), calls all three analytics APIs, rendersAgentHeatmap,TokenTrendsChart,RotationFrequencyTable; shows aUpgradeRequiredcomponent when API returns 403 - 10.7 Add Analytics route to
dashboard/src/App.tsx— add/analyticsroute; add "Analytics" link to dashboard navigation - 10.8 Run
npm run buildindashboard/— zero TypeScript errors, zero ESLint errors
11. WS4: API Gateway Tiers — Configuration & Middleware
- 11.1 Create
src/types/tiers.ts— define interfaces:TierName(union:'free' | 'pro' | 'enterprise'),TierLimits,TierFeatures,TierDefinition(includesid,limits,features,stripeProductId) - 11.2 Create
src/config/tiers.ts— defineTIER_CONFIG: Record<TierName, TierDefinition>with complete limit and feature definitions forfree,pro, andenterprisetiers as specified in spec; exportgetTierConfig(tier: TierName): TierDefinitionhelper - 11.3 Create
src/middleware/tierRateLimiter.ts— implementTierRateLimitermiddleware: extracttenantIdfrom authenticated request context; check Redis keytier:{tenantId}(TTL 60s) for cached tier; on miss querytenant_subscriptionsfor tenant's current tier, cache for 60s; look up rate limit config fromTIER_CONFIG; applyRateLimiterRediswith keyrl:{tier}:{tenantId}; on rejection return HTTP 429 withX-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset,Retry-Afterheaders; incrementagentidp_rate_limit_hits_totalcounter withtierandtenant_idlabels - 11.4 Replace
RateLimiterRedismiddleware on all authenticated routes insrc/routes/index.tswithTierRateLimiter; keep the flat IP-basedRateLimiterRedison unauthenticated routes unchanged; wrap replacement inTIER_RATE_LIMITING_ENABLEDfeature flag (fall back to old middleware whenfalse)
12. WS4: API Gateway Tiers — Endpoints
- 12.1 Create
src/routes/tiers.ts— Express router forGET /tiers; handler readsTIER_CONFIG, formats response as specified in spec, setsCache-Control: public, max-age=3600header; no database query; no authentication required - 12.2 Register
tiersrouter insrc/routes/index.ts - 12.3 Implement
BillingService.upgradeTier(tenantId: string, targetTier: 'pro' | 'enterprise'): Promise<{ checkoutUrl: string; sessionId: string; expiresAt: string }>— fetch current tier fromtenant_subscriptions, validate no self-upgrade or downgrade, create Stripe Checkout session withSTRIPE_PRICE_ID_PROorSTRIPE_PRICE_ID_ENTERPRISE, return checkout URL - 12.4 Add
upgradeTierhandler tosrc/controllers/BillingController.ts— validatetargetTierenum, callBillingService.upgradeTier, return HTTP 200 withcheckoutUrl,sessionId,targetTier,expiresAt - 12.5 Register
POST /billing/upgraderoute insrc/routes/billing.tswith authentication middleware - 12.6 Add
STRIPE_PRICE_ID_PRO,STRIPE_PRICE_ID_ENTERPRISE,TIER_RATE_LIMITING_ENABLEDto.env.examplewith documentation comments - 12.7 Add
GET /tiersandPOST /billing/upgradetodocs/openapi.yaml - 12.8 Write unit tests for
TierRateLimiter— test: free tier limit enforced (60 req/min), pro tier limit enforced (600 req/min), tier looked up from Redis cache (DB not called), tier fetched from DB on cache miss, rollback path (TIER_RATE_LIMITING_ENABLED=falseuses old flat limiter) - 12.9 Write unit tests for
BillingService.upgradeTier— test: upgrade free → pro (creates Stripe session), upgrade free → enterprise (creates Stripe session), already on pro (returns ALREADY_ON_TIER error), downgrade attempt (returns DOWNGRADE_NOT_SUPPORTED error) - 12.10 Write integration tests for
GET /tiers— verify response structure, verifyCache-Controlheader, verify no auth required; write integration tests forPOST /billing/upgrade— mock Stripe, verify checkout URL returned
13. WS5: Developer Experience — Scaffold Service
- 13.1 Install
archiverand@types/archiverin APIpackage.json - 13.2 Create
src/types/scaffold.ts— defineScaffoldLanguageunion ('typescript' | 'python' | 'go' | 'java' | 'rust'),ScaffoldOptionsinterface,ScaffoldTemplateinterface - 13.3 Create scaffold template files for TypeScript in
src/templates/scaffold/typescript/:package.json.tmpl,tsconfig.json.tmpl,src/index.ts.tmpl,.env.example.tmpl,.gitignore.tmpl,README.md.tmpl— each file uses{{AGENT_ID}},{{AGENT_NAME}},{{CLIENT_ID}},{{API_URL}}as template variables;.env.example.tmplMUST includeAGENTIDP_CLIENT_SECRET=<your-client-secret>placeholder (never inject real secret) - 13.4 Create scaffold template files for Python in
src/templates/scaffold/python/:requirements.txt.tmpl,main.py.tmpl,.env.example.tmpl,.gitignore.tmpl,README.md.tmpl— same template variable convention - 13.5 Create scaffold template files for Go in
src/templates/scaffold/go/:go.mod.tmpl,main.go.tmpl,.env.example.tmpl,.gitignore.tmpl,README.md.tmpl - 13.6 Create scaffold template files for Java in
src/templates/scaffold/java/:pom.xml.tmpl,src/main/java/Main.java.tmpl,.env.example.tmpl,.gitignore.tmpl,README.md.tmpl - 13.7 Create scaffold template files for Rust in
src/templates/scaffold/rust/:Cargo.toml.tmpl,src/main.rs.tmpl,.env.example.tmpl,.gitignore.tmpl,README.md.tmpl - 13.8 Create
src/services/ScaffoldService.ts— implementIScaffoldService;generateScaffold(agentId, language, apiUrl): load template files for language, inject template variables (replace{{AGENT_ID}},{{AGENT_NAME}},{{CLIENT_ID}},{{API_URL}}), build in-memory ZIP usingarchiver; return{ stream: NodeJS.ReadableStream, filename: string }; emitagentidp_scaffold_generated_totalcounter andagentidp_scaffold_generation_duration_mshistogram
14. WS5: Developer Experience — Scaffold Controller & Route
- 14.1 Create
src/controllers/ScaffoldController.ts— implementgetScaffoldhandler forGET /sdk/scaffold/:agentId: validatelanguagequery param againstScaffoldLanguageunion (HTTP 400 on invalid); fetch agent, verify agent belongs to authenticated tenant (HTTP 403 if not); callScaffoldService.generateScaffold; setContent-Type: application/zip,Content-Disposition: attachment; filename="...", pipe stream to response; write audit log entry (scaffold.generated, metadata:{ language }) - 14.2 Create
src/routes/scaffold.ts— Express router forGET /sdk/scaffold/:agentIdwith authentication middleware; apply scaffold-specific rate limiter (10 req/min per tenant, separate fromTierRateLimiter) - 14.3 Register
scaffoldrouter insrc/routes/index.ts - 14.4 Add
GET /sdk/scaffold/:agentIdtodocs/openapi.yaml— document binary response type, query parameters, all error responses - 14.5 Write unit tests for
ScaffoldService— test: generate TypeScript scaffold (verify ZIP contains all 6 files), generate Python scaffold (verify all 5 files), verify{{CLIENT_ID}}is replaced in.env.example, verify{{AGENTIDP_CLIENT_SECRET}}is placeholder not real secret, verify invalid language throwsValidationError - 14.6 Write integration tests for scaffold endpoint — test: TypeScript scaffold returns ZIP with correct
Content-TypeandContent-Disposition; Python scaffold returns ZIP; HTTP 400 on invalid language; HTTP 403 when agent belongs to different tenant; HTTP 404 when agent does not exist
15. WS5: Developer Experience — Portal & CLI
- 15.1 Install
@stoplight/elementsinportal/package.json— removeswagger-ui-react - 15.2 Rewrite
portal/app/api-explorer/page.tsx— replaceSwaggerUIcomponent with@stoplight/elements<API>component; setapiDescriptionUrl,router="hash",layout="sidebar",hideSchemas={false},tryItCredentialsPolicy="same-origin"; import Elements CSS; remove all Swagger UI imports and CSS - 15.3 Run
npm run buildinportal/— verify zero TypeScript errors and zero ESLint errors after Elements integration - 15.4 Install
unzipperand@types/unzipperincli/package.json - 15.5 Create
cli/src/commands/scaffold.ts— implementsentryagent scaffoldcommand with Commander options:--agent-id <id>(required),--language <lang>(default: typescript),--out <directory>(default:.); load config, issue Bearer token, callGET /sdk/scaffold/{agentId}?language={language}, pipe response throughunzipper.Extract({ path: outDir }), print success message and next steps; handle errors (404, 403, 400) with human-readable messages - 15.6 Register
scaffoldcommand incli/src/index.ts— add.addCommand(scaffoldCommand)to Commander program - 15.7 Run
npm run buildincli/— zero TypeScript errors; runnode dist/index.js scaffold --help— outputs correct usage
16. WS6: AGNTCY Compliance — Compliance Service
- 16.1 Create
src/types/compliance.ts— define interfaces:ComplianceRequirement(id, description, status, evidence, verifiedAt),ComplianceSection(id, name, status, requirements),ComplianceReport(reportId, generatedAt, agntcySpecVersion, tenantId, overallStatus, sections, summary),AgentCard(agntcyVersion, type, agent, issuedAt, expiresAt) - 16.2 Create
src/config/agntcyRequirements.ts— define the complete array of AGNTCY requirement objects (minimum 24 requirements), each with:id(e.g.,AI-001),description(from AGNTCY spec),section(e.g.,agent-identity), andevaluate(tenantId: string, db: Pool): Promise<RequirementEvaluation>function — each evaluator queries the live system and returns{ status, evidence } - 16.3 Create
src/services/ComplianceService.ts— implementIComplianceService;generateComplianceReport(tenantId): run all requirement evaluators fromagntcyRequirements.tsin parallel, group results by section, compute overall status (compliantif all pass,partialif any non-compliant,non-compliantif >20% fail), buildComplianceReport, write audit log entry (compliance.report_generated), emitagentidp_compliance_reports_generated_totalcounter andagentidp_compliance_report_duration_mshistogram - 16.4 Implement
ComplianceService.generateAgentCard(agentId)— fetch agent from DB, buildAgentCardper AGNTCY spec format, setexpiresAt = issuedAt + 24 hours, setcomplianceStandardsfrom system config, emitagentidp_agent_cards_served_totalcounter withvisibilitylabel
17. WS6: AGNTCY Compliance — Controller, Routes
- 17.1 Create
src/controllers/ComplianceController.ts— implementgetComplianceReporthandler: check tenant tier is pro or enterprise (HTTP 403TIER_REQUIREDfor free tier), callComplianceService.generateComplianceReport, return HTTP 200; implementgetAgentCardhandler: check agent visibility (HTTP 401 if private and unauthenticated, HTTP 403 if private and wrong tenant), callComplianceService.generateAgentCard, return HTTP 200 - 17.2 Create
src/routes/agntcy.ts— Express router forGET /agntcy/compliance-report(requires auth) andGET /agents/:id/agent-card(auth optional); register behindAGNTCY_ENABLEDfeature flag - 17.3 Register
agntcyrouter insrc/routes/index.ts - 17.4 Add
GET /agntcy/compliance-reportandGET /agents/:id/agent-cardtodocs/openapi.yaml - 17.5 Write unit tests for
ComplianceService— test:generateComplianceReport(all 24 requirements pass →compliant),generateComplianceReport(one evaluator fails →partial),generateAgentCard(public agent),generateAgentCard(private agent — verify agent data is included),generateAgentCard(non-existent agent → throws NotFoundError) - 17.6 Write integration tests for compliance endpoints — test: compliance report for pro tenant (HTTP 200, overallStatus), compliance report for free tenant (HTTP 403), agent card for public agent (no auth required), agent card for private agent (auth required, correct tenant succeeds, wrong tenant HTTP 403)
18. WS6: AGNTCY Compliance — Interoperability Tests & Docs
- 18.1 Create
tests/agntcy/interoperability.test.ts— implement all 25+ AGNTCY interoperability test cases as defined in spec: AI-001 (agent UUID uniqueness), AI-002 (W3C DID document), AUTH-001 (OAuth 2.0 token issuance), AUTH-002 (OIDC discovery), AUTHZ-001 (scope enforcement), DEL-001 through DEL-004 (delegation chain), AUDIT-001 through AUDIT-002 (immutable audit log), GOV-001 through GOV-002 (lifecycle governance), INTER-001 (agent card), COMP-001 (compliance report) - 18.2 Add
"test:agntcy": "jest --testPathPattern=tests/agntcy --forceExit"script topackage.json - 18.3 Write
docs/agntcy/certification-guide.md— complete document with all 6 sections: Overview, Requirement Mapping table, Running the Compliance Report (step-by-step), Agent Card Usage, Self-Certification Checklist, Submitting for Official AGNTCY Certification; no placeholders, no TODOs
19. QA & Release
- 19.1 Run
cargo buildandcargo clippy -- -D warningsinsdk-rust/— zero warnings; runcargo test— all unit tests pass - 19.2 Run
tsc --noEmitacross API, dashboard, portal, and CLI — zero TypeScript errors - 19.3 Run full Jest suite (
npm test) — all unit tests pass, coverage >= 80% across all new services:DelegationService,AnalyticsService,ScaffoldService,ComplianceService,TierRateLimiter - 19.4 Run
npm run buildinportal/with Elements integration — zero errors; verify/api-explorerpage renders Elements<API>component - 19.5 Run
npm run buildincli/— zero errors; runnode dist/index.js scaffold --help— shows correct options; runnode dist/index.js --help— showsscaffoldcommand listed - 19.6 Apply database migrations
008_add_delegation_chains.sqland009_add_analytics_aggregates.sqlagainst a test database — verify migrations run without errors and tables are created with correct schemas - 19.7 Run integration tests for all Phase 5 endpoints — delegation (create, verify, revoke), analytics (usage-summary, agent-activity, token-trends), tiers (GET /tiers, POST /billing/upgrade), scaffold (all 5 languages), AGNTCY (compliance-report, agent-card)
- 19.8 Run
npm run test:agntcy— all 25+ interoperability test cases pass - 19.9 Verify feature flags:
A2A_ENABLED=false→ delegation routes return 404;ANALYTICS_ENABLED=false→ analytics routes return 404;TIER_RATE_LIMITING_ENABLED=false→ flat rate limiter used;AGNTCY_ENABLED=false→ AGNTCY routes return 404 - 19.10 Verify tier rate limiting: free tenant receives 429 at 61st request/minute; pro tenant allows 600 requests/minute; tier cache refresh within 60s after Stripe webhook updates subscription
- 19.11 Verify scaffold security:
GET /sdk/scaffold/:agentIdresponse ZIP never contains a realclient_secretvalue —.env.exampleplaceholder only - 19.12 Commit all Phase 5 work on
main— one conventional commit per workstream (e.g.,feat(phase-5): WS1 — Rust SDK,feat(phase-5): WS2 — A2A Authorization, etc.)