feat(03-01): DB migrations, models, encryption service, and test scaffolds

- Add stripe and cryptography to shared pyproject.toml
- Add recharts, @stripe/stripe-js, stripe to portal package.json (submodule)
- Add billing fields to Tenant model (stripe_customer_id, subscription_status, agent_quota, trial_ends_at)
- Add budget_limit_usd to Agent model
- Create TenantLlmKey and StripeEvent models in billing.py (AuditBase and Base respectively)
- Create KeyEncryptionService (MultiFernet encrypt/decrypt/rotate) in crypto.py
- Create compute_budget_status helper in usage.py (threshold logic: ok/warning/exceeded)
- Add platform_encryption_key, stripe_, slack_oauth settings to config.py
- Create Alembic migration 005 with all schema changes, RLS, grants, and composite index
- All 12 tests passing (key encryption roundtrip, rotation, budget thresholds)
This commit is contained in:
2026-03-23 21:19:09 -06:00
parent ac606cf9ff
commit 215e67a7eb
9 changed files with 1085 additions and 1 deletions

View File

@@ -129,6 +129,58 @@ class Settings(BaseSettings):
orchestrator_url: str = Field(default="http://localhost:8003")
llm_pool_url: str = Field(default="http://localhost:8004")
# -------------------------------------------------------------------------
# Encryption
# -------------------------------------------------------------------------
platform_encryption_key: str = Field(
default="",
description="Fernet key for BYO API key encryption (base64-encoded 32-byte key)",
)
platform_encryption_key_previous: str = Field(
default="",
description="Previous Fernet key retained for decryption during rotation window",
)
# -------------------------------------------------------------------------
# Stripe
# -------------------------------------------------------------------------
stripe_secret_key: str = Field(
default="",
description="Stripe secret API key (sk_live_... or sk_test_...)",
)
stripe_webhook_secret: str = Field(
default="",
description="Stripe webhook endpoint signing secret (whsec_...)",
)
stripe_per_agent_price_id: str = Field(
default="",
description="Stripe Price ID for the per-agent monthly subscription plan",
)
portal_url: str = Field(
default="http://localhost:3000",
description="Portal base URL used in Stripe checkout success/cancel redirects",
)
# -------------------------------------------------------------------------
# Slack OAuth
# -------------------------------------------------------------------------
slack_client_id: str = Field(
default="",
description="Slack OAuth app client ID",
)
slack_client_secret: str = Field(
default="",
description="Slack OAuth app client secret",
)
slack_oauth_redirect_uri: str = Field(
default="http://localhost:3000/api/slack/callback",
description="Slack OAuth redirect URI (must match Slack app config)",
)
oauth_state_secret: str = Field(
default="",
description="HMAC secret for signing OAuth state parameters (CSRF protection)",
)
# -------------------------------------------------------------------------
# Application
# -------------------------------------------------------------------------