Implements scaffold ZIP generator, Stoplight Elements API explorer, and CLI scaffold command: Scaffold API: - 25 template files for TypeScript/Python/Go/Java/Rust in src/templates/scaffold/ - ScaffoldService: in-memory ZIP via archiver, variable injection (AGENT_ID/NAME/CLIENT_ID/API_URL) - ScaffoldController: tenant ownership check (403), language validation (400), ZIP stream response - Route GET /sdk/scaffold/:agentId with rate limiter (10 req/min per tenant) - Prometheus: scaffold_generated_total + scaffold_generation_duration_ms histogram Portal: - Replaced swagger-ui-react with @stoplight/elements API component - Dynamic import (ssr: false) for browser-only DOM dependency - Type declarations for @stoplight/elements and CSS module CLI: - sentryagent scaffold --agent-id <id> [--language typescript] [--out .] - Raw fetch for binary ZIP stream → unzipper.Extract() → prints next steps - Human-readable 400/403/404 error messages Tests: 19 tests (unit + integration), ScaffoldService 80%+ branch coverage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
238 lines
8.3 KiB
TypeScript
238 lines
8.3 KiB
TypeScript
/**
|
|
* Shared Prometheus metrics registry for SentryAgent.ai AgentIdP.
|
|
* All metric definitions live here. Import specific metrics in the files that use them.
|
|
* This is the ONLY file that defines metrics — all other files import from here.
|
|
*/
|
|
|
|
import { Registry, Counter, Gauge, Histogram } from 'prom-client';
|
|
|
|
/** Shared registry — do NOT use the default global registry (conflicts with tests). */
|
|
export const metricsRegistry = new Registry();
|
|
|
|
/**
|
|
* Total number of OAuth 2.0 tokens successfully issued.
|
|
* Labels: scope (space-separated scope string)
|
|
*/
|
|
export const tokensIssuedTotal = new Counter({
|
|
name: 'agentidp_tokens_issued_total',
|
|
help: 'Total number of OAuth 2.0 access tokens issued successfully.',
|
|
labelNames: ['scope'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of agents successfully registered.
|
|
* Labels: deployment_env
|
|
*/
|
|
export const agentsRegisteredTotal = new Counter({
|
|
name: 'agentidp_agents_registered_total',
|
|
help: 'Total number of AI agents registered successfully.',
|
|
labelNames: ['deployment_env'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total HTTP requests received.
|
|
* Labels: method, route (normalised path), status_code
|
|
*/
|
|
export const httpRequestsTotal = new Counter({
|
|
name: 'agentidp_http_requests_total',
|
|
help: 'Total number of HTTP requests received.',
|
|
labelNames: ['method', 'route', 'status_code'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* HTTP request duration in seconds.
|
|
* Labels: method, route, status_code
|
|
*/
|
|
export const httpRequestDurationSeconds = new Histogram({
|
|
name: 'agentidp_http_request_duration_seconds',
|
|
help: 'HTTP request duration in seconds.',
|
|
labelNames: ['method', 'route', 'status_code'] as const,
|
|
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5],
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* PostgreSQL query duration in seconds.
|
|
* Labels: operation (query/connect)
|
|
*/
|
|
export const dbQueryDurationSeconds = new Histogram({
|
|
name: 'agentidp_db_query_duration_seconds',
|
|
help: 'PostgreSQL query duration in seconds.',
|
|
labelNames: ['operation'] as const,
|
|
buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Redis command duration in seconds.
|
|
* Labels: command (get/set/incr/expire/ping/etc.)
|
|
*/
|
|
export const redisCommandDurationSeconds = new Histogram({
|
|
name: 'agentidp_redis_command_duration_seconds',
|
|
help: 'Redis command duration in seconds.',
|
|
labelNames: ['command'] as const,
|
|
buckets: [0.0005, 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25],
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of webhook deliveries that reached the dead-letter state
|
|
* (i.e. exhausted all retry attempts without a 2xx response).
|
|
* Labels: organization_id
|
|
*/
|
|
export const webhookDeadLettersTotal = new Counter({
|
|
name: 'agentidp_webhook_dead_letters_total',
|
|
help: 'Total number of webhook deliveries that exhausted all retry attempts.',
|
|
labelNames: ['organization_id'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of agent credentials detected as expiring within 7 days.
|
|
* Incremented by SecretsRotationJob on each scheduled check.
|
|
* Labels: agent_id
|
|
*
|
|
* SOC 2 CC9.2 — Secrets Rotation monitoring.
|
|
*/
|
|
export const credentialsExpiringSoonTotal = new Counter({
|
|
name: 'agentidp_credentials_expiring_soon_total',
|
|
help: 'Total number of agent credentials detected as expiring within 7 days.',
|
|
labelNames: ['agent_id'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Binary gauge indicating whether the most recent audit chain verification passed.
|
|
* Set to 1 (passing) or 0 (failing) by AuditChainVerificationJob.
|
|
* No labels.
|
|
*
|
|
* SOC 2 CC7.2 — Audit Log Integrity monitoring.
|
|
*/
|
|
export const auditChainIntegrity = new Gauge({
|
|
name: 'agentidp_audit_chain_integrity',
|
|
help: 'Binary gauge: 1 = most recent audit chain verification passed, 0 = failed.',
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of HTTP 429 responses returned by the rate limiter.
|
|
* Labels: endpoint (req.path at time of rejection)
|
|
*/
|
|
export const rateLimitHitsTotal = new Counter({
|
|
name: 'agentidp_rate_limit_hits_total',
|
|
help: 'Total number of HTTP 429 responses returned by the rate limiter.',
|
|
labelNames: ['endpoint'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Current number of active (checked-out) PostgreSQL pool connections.
|
|
* Updated on pool `acquire` and `remove` events.
|
|
*/
|
|
export const dbPoolActiveConnections = new Gauge({
|
|
name: 'agentidp_db_pool_active_connections',
|
|
help: 'Current number of active (checked-out) PostgreSQL pool connections.',
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Current number of waiting client requests in the PostgreSQL pool queue.
|
|
* Updated whenever the pool queue length changes.
|
|
*/
|
|
export const dbPoolWaitingRequests = new Gauge({
|
|
name: 'agentidp_db_pool_waiting_requests',
|
|
help: 'Current number of requests waiting for a PostgreSQL connection.',
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of authenticated API calls per tenant.
|
|
* Incremented on every request that passes auth middleware successfully.
|
|
* Labels: tenant_id (organization_id from the JWT payload)
|
|
*/
|
|
export const tenantApiCallsTotal = new Counter({
|
|
name: 'agentidp_tenant_api_calls_total',
|
|
help: 'Total number of authenticated API calls, labeled by tenant.',
|
|
labelNames: ['tenant_id'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of requests rejected due to free tier billing limits.
|
|
* Labels: tenant_id, limit_type ('agent_limit' | 'api_limit')
|
|
*
|
|
* WS6 — Billing & Usage Metering.
|
|
*/
|
|
export const billingLimitRejectionsTotal = new Counter({
|
|
name: 'agentidp_billing_limit_rejections_total',
|
|
help: 'Total number of requests rejected due to free tier billing limits.',
|
|
labelNames: ['tenant_id', 'limit_type'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
// ────────────────────────────────────────────────────────────────
|
|
// Phase 5 — WS2: A2A Delegation Metrics
|
|
// ────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Total number of A2A delegation chains created.
|
|
* Labels: tenant_id
|
|
*/
|
|
export const delegationsCreatedTotal = new Counter({
|
|
name: 'agentidp_delegations_created_total',
|
|
help: 'Total number of A2A delegation chains created.',
|
|
labelNames: ['tenant_id'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of A2A delegation verifications performed.
|
|
* Labels: tenant_id, result (valid | invalid | expired | revoked)
|
|
*/
|
|
export const delegationsVerifiedTotal = new Counter({
|
|
name: 'agentidp_delegations_verified_total',
|
|
help: 'Total number of A2A delegation verifications, labelled by outcome.',
|
|
labelNames: ['tenant_id', 'result'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Total number of A2A delegation chains revoked.
|
|
* Labels: tenant_id
|
|
*/
|
|
export const delegationsRevokedTotal = new Counter({
|
|
name: 'agentidp_delegations_revoked_total',
|
|
help: 'Total number of A2A delegation chains revoked.',
|
|
labelNames: ['tenant_id'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
// ────────────────────────────────────────────────────────────────
|
|
// Phase 5 — WS5: Scaffold Metrics
|
|
// ────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Total number of scaffold ZIPs generated, labelled by language.
|
|
*/
|
|
export const scaffoldGeneratedTotal = new Counter({
|
|
name: 'agentidp_scaffold_generated_total',
|
|
help: 'Total number of scaffold ZIPs generated by target language.',
|
|
labelNames: ['language'] as const,
|
|
registers: [metricsRegistry],
|
|
});
|
|
|
|
/**
|
|
* Duration of scaffold ZIP generation in milliseconds.
|
|
* Labels: language
|
|
*/
|
|
export const scaffoldGenerationDurationMs = new Histogram({
|
|
name: 'agentidp_scaffold_generation_duration_ms',
|
|
help: 'Time taken to generate a scaffold ZIP archive in milliseconds.',
|
|
labelNames: ['language'] as const,
|
|
buckets: [10, 50, 100, 250, 500, 1000, 2500],
|
|
registers: [metricsRegistry],
|
|
});
|