OpenSpec change: phase-4-developer-growth (spec-driven, 4/4 artifacts) 6 workstreams, 90 implementation tasks, delivery sequence: WS1 → WS2 + WS3 (parallel) → WS4 → WS5 → WS6 Workstreams: 1. Production Hardening — ioredis rate limiting, DB pool tuning, /health/detailed, k6 load tests 2. Developer Portal — Next.js 14, Swagger UI explorer, onboarding wizard, pricing/SDK pages 3. CLI Tool — sentryagent npm CLI, 5 commands, shell completion 4. Agent Marketplace — public searchable registry powered by existing agent/DID infrastructure 5. GitHub Actions — register-agent + issue-token Actions via OIDC (no stored secrets) 6. Billing & Usage Metering — Stripe Checkout, webhook-driven state, free tier enforcement New capabilities (8 specs): production-hardening, developer-portal, cli-tool, agent-marketplace, github-actions, billing-metering (+delta: web-dashboard, monitoring) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.4 KiB
ADDED Requirements
Requirement: Per-tenant usage is tracked for API calls, active agents, and token issuances
The system SHALL track the following usage metrics per tenant per day: api_calls (every authenticated API request), token_issuances (every successful POST /oauth2/token), active_agents (count of non-revoked agents at end of day). Usage SHALL be aggregated in memory and flushed to a usage_events PostgreSQL table every 60 seconds.
Scenario: API call increments usage counter
- WHEN an authenticated tenant makes any API request
- THEN the tenant's
api_callscounter for the current day is incremented
Scenario: Usage is persisted to database on flush interval
- WHEN 60 seconds elapse since the last flush
- THEN all in-memory counters are written to the
usage_eventstable and reset to zero
Requirement: Free tier limits are enforced per tenant
The system SHALL enforce free tier limits: 10 active agents maximum, 1,000 API calls per day. When a limit is exceeded, the offending request SHALL be rejected with HTTP 429 and a response body indicating which limit was reached and how to upgrade. Limit summaries SHALL be cached in Redis with a 60-second TTL.
Scenario: Agent registration blocked at free tier limit
- WHEN a free-tier tenant with 10 active agents calls
POST /agents - THEN the response is HTTP 429 with
{ "error": "free_tier_limit", "limit": "agents", "max": 10, "upgradeUrl": "..." }
Scenario: API call blocked after daily limit
- WHEN a free-tier tenant has made 1,000 API calls today and makes another request
- THEN the response is HTTP 429 with
{ "error": "free_tier_limit", "limit": "api_calls", "max": 1000, "upgradeUrl": "..." }
Scenario: Paid tenant is not rate limited by usage tiers
- WHEN a paid-tier tenant exceeds free tier thresholds
- THEN the request is processed normally with no usage-based rejection
Requirement: Stripe Checkout initiates paid tier subscription
The system SHALL expose POST /billing/checkout (authenticated) that creates a Stripe Checkout session for the paid tier plan and returns a checkoutUrl. The tenant is redirected to Stripe Checkout to complete payment. On success, Stripe sends a customer.subscription.created webhook event.
Scenario: Checkout session created
- WHEN an authenticated tenant calls
POST /billing/checkout - THEN the response is HTTP 200 with
{ "checkoutUrl": "https://checkout.stripe.com/..." }
Scenario: Duplicate subscription prevented
- WHEN a tenant with an active paid subscription calls
POST /billing/checkout - THEN the response is HTTP 409 with
{ "error": "already_subscribed" }
Requirement: Stripe webhooks update tenant subscription state
The system SHALL expose POST /billing/webhook (Stripe webhook endpoint) that verifies the stripe-signature header using stripe.webhooks.constructEvent() and processes: customer.subscription.created (set tenant to paid), invoice.payment_succeeded (extend subscription period), customer.subscription.deleted (revert tenant to free tier). All events without valid signatures SHALL be rejected with HTTP 400.
Scenario: Webhook without valid signature is rejected
- WHEN
POST /billing/webhookis called with an invalid or missingstripe-signatureheader - THEN the response is HTTP 400 and no state is changed
Scenario: Subscription created webhook activates paid tier
- WHEN Stripe sends a valid
customer.subscription.createdevent for a tenant - THEN the tenant's
subscriptionStatusis updated toactiveand free tier limits no longer apply
Scenario: Subscription deleted webhook reverts to free tier
- WHEN Stripe sends a valid
customer.subscription.deletedevent - THEN the tenant's
subscriptionStatusis updated tocancelledand free tier limits are re-enforced
Requirement: Billing is feature-flag gated
All billing enforcement and Stripe integration SHALL be gated behind the BILLING_ENABLED environment variable. When BILLING_ENABLED=false, free tier limits are not enforced, all tenants have paid-tier access, and Stripe webhook endpoint returns HTTP 200 without processing. Usage metering continues regardless of this flag.
Scenario: Billing disabled — no limits enforced
- WHEN
BILLING_ENABLED=falseand a free-tier tenant has 11 active agents - THEN agent registration succeeds without HTTP 429