Files
ai-gateway/TASKS.md
T

3.2 KiB

AI Gateway — Development Tasks

Per-Plugin Module Keys (Security)

Goal: Replace the single shared AI_GATEWAY_KEY embedded in exported Live Practice plugin HTML with scoped, expiring, per-plugin keys. Existing modules and endpoints must not break.


Step 1 — DB Schema: Add expires_at and rate_limit_per_hour to Module

  • Add expires_at: DateTime, nullable to app/models/module.pyNone = no expiry (existing modules unaffected)
  • Add rate_limit_per_hour: Integer, nullable to app/models/module.pyNone = falls back to global slowapi limit
  • Create supabase/migrations/YYYYMMDD_plugin_keys.sql:
    ALTER TABLE public.modules ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ NULL;
    ALTER TABLE public.modules ADD COLUMN IF NOT EXISTS rate_limit_per_hour INTEGER NULL;
    
  • Run migration against Supabase DB

Step 2 — Enforce expiry in deps.py

  • In get_api_key() (app/api/deps.py): after DB lookup, check module.expires_at — if set and in the past, raise HTTP 403
  • Same check in get_current_module() (app/api/deps.py)
  • Document in a comment that the TTLCache (5-min TTL) means expired keys have up to a 5-minute grace period

Step 3 — Per-key rate limiting

  • In app/core/limiter.py, replace key_func=get_remote_address with a custom function that returns the X-API-Key header value when present, falling back to IP:
    def get_api_key_or_ip(request: Request) -> str:
        return request.headers.get("X-API-Key") or get_remote_address(request)
    
  • Verify existing endpoints still receive 429 responses correctly after the change

Step 4 — Provisioning endpoint

  • Create app/api/endpoints/provision.pyPOST /api/v1/modules/provision:
    • Auth: global API_KEY via X-API-Key header (reuses existing static key check)
    • Body: name, expires_in_days (int, default 365), rate_limit_per_hour (int, optional)
    • Creates a new Module record with secrets.token_hex(32) as secret_key
    • Sets expires_at = utcnow() + timedelta(days=expires_in_days)
    • Returns { secret_key, module_id, expires_at }
  • Register in app/api/router.py:
    api_router.include_router(provision.router, prefix="/modules", tags=["provisioning"])
    
  • Update api_documentation.md with the new endpoint

Step 5 — Live Practice integration (handled in Live Practice repo)

These tasks are tracked in the Live Practice TASKS.md, not here. Listed for cross-reference only.

  • Add ai_gateway_key column to plugins table
  • Export route provisions a scoped key via POST /api/v1/modules/provision (using server-side AI_GATEWAY_KEY as admin credential)
  • Scoped per-plugin key is embedded in exported HTML instead of the shared AI_GATEWAY_KEY
  • Preview-html route continues using AI_GATEWAY_KEY directly (server-side only, safe)

Backlog

  • Respect rate_limit_per_hour column per-module (currently stored but not enforced dynamically — global RATE_LIMIT setting applies uniformly)
  • Admin UI or CLI command to list/revoke/rotate plugin keys
  • Webhook or callback when a key nears expiry