/** * 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], });