MediaRouter / docs /production-deployment-gate.md
basyx's picture
Upload 794 files
1b2323a verified
|
Raw
History Blame Contribute Delete
7.79 kB

MediaRouter production deployment gate

Deployment contract

The only backend production target is a Hugging Face Docker Space. The root README declares sdk: docker and app_port: 7860; the single production Dockerfile uses python:3.10-slim, installs the pinned requirements.txt, runs as UID 1000, exposes port 7860, and starts one Uvicorn process on 0.0.0.0:7860. Android/Termux Python is not part of this contract.

APP_ENVIRONMENT=production is set by the image. Production configuration is validated when FastAPI is imported, before a database engine starts serving requests. Local development retains the existing SQLite defaults because it uses APP_ENVIRONMENT=development.

Required Hugging Face configuration

Configure secrets in the Space settings, never in source or Docker layers. Values below are illustrative and are not usable credentials.

DATABASE_URL=postgresql+asyncpg://<security-role>:<password>@<host>/<database>
SECURITY_DATABASE_ROLE=mediarouter_security_service
SECURITY_ENFORCE_RLS=true
SECURITY_AUTO_MIGRATE=false
AUTH_ENABLED=true
CORS_ALLOWED_ORIGINS=https://<vercel-project>.vercel.app
BASE_URL=https://<owner>-<space>.hf.space
SOCIAL_AUTO_MIGRATE=false

DATABASE_URL must be external PostgreSQL/Supabase and must authenticate as the configured backend-only security role. SQLite and localhost database URLs are rejected in production. The security role must have the role attributes required by the existing forced-RLS execution-boundary check.

When SOCIAL_ENABLED=true, also configure the existing separated RLS roles:

SOCIAL_DATABASE_URL=postgresql+asyncpg://<tenant-role>:<password>@<host>/<database>
SOCIAL_TENANT_DATABASE_ROLE=mediarouter_tenant
SOCIAL_WORKER_DATABASE_URL=postgresql+asyncpg://<worker-role>:<password>@<host>/<database>
SOCIAL_WORKER_DATABASE_ROLE=mediarouter_social_worker
SOCIAL_ENFORCE_RLS=true

The tenant connection must be non-owner/non-superuser/non-BYPASSRLS; the worker connection must be the explicitly named backend-only BYPASSRLS role. Set SOCIAL_ENABLED=false when deploying only core/project APIs and those social database boundaries have not been provisioned. Missing YouTube, Meta, TikTok, X, LinkedIn, Telegram, or WhatsApp provider credentials do not prevent startup. Their adapters report controlled unavailability when invoked.

WAN and FLUX are also optional. Leaving their URL/token pairs empty keeps them unavailable. Invalid optional worker identity/readiness never makes a model available and does not stop FastAPI.

CORS_ALLOWED_ORIGINS is a comma-separated list of exact origins. Production requires HTTPS and rejects *, credentials embedded in URLs, paths, query strings, and fragments. Add every Vercel origin that is allowed to call the backend directly. The normal frontend path remains its same-origin server-side BFF, so backend keys are never exposed to browsers.

Explicit migration order

Normal application startup never applies PostgreSQL migrations. Use a controlled administrative migration connection and apply the additive files in this dependency order before starting the Space:

  1. app/security/migrations/0001_api_key_security.sql
  2. app/security/migrations/0002_authoritative_tenancy_postgres.sql
  3. app/projects/migrations/0001_projects_foundation.sql
  4. app/security/migrations/0003_generation_domain_postgres.sql
  5. app/security/migrations/0004_generation_provider_runtime_postgres.sql
  6. app/projects/migrations/0002_project_resources.sql
  7. app/projects/migrations/0003_editor_persistence_rendering.sql
  8. app/projects/migrations/0004_ai_studio.sql
  9. app/projects/migrations/0005_ai_copilot.sql
  10. app/projects/migrations/0006_template_marketplace.sql
  11. app/social/migrations/0001_social_foundation_postgres.sql
  12. app/social/migrations/0002_social_rls.sql
  13. app/social/migrations/0003_social_integrity_postgres.sql
  14. app/social/migrations/0004_youtube_media_assets.sql
  15. app/social/migrations/0005_x_text_posts.sql
  16. app/social/migrations/0006_linkedin_oauth_account_type.sql
  17. app/social/migrations/0007_tenant_asset_and_rls_hardening.sql

Keep SECURITY_AUTO_MIGRATE=false and SOCIAL_AUTO_MIGRATE=false. The security startup gate checks tables and safety indexes. It then verifies the configured PostgreSQL execution role before generation, projects, workers, or social scheduling can serve traffic.

Health, readiness, and startup failure behavior

GET /health is public and process-level. It reports only safe dependency availability and version metadata; external AI credentials are not required. The Docker health check calls it over 127.0.0.1:7860 from inside the container.

The authoritative security database and complete migrated security/project schema are required for startup. A missing connection, missing schema object, or invalid RLS role produces a clear startup failure. Social database/provider failures are contained by the existing social readiness boundary; social APIs then return controlled unavailability. Generation providers are initialized lazily by the bounded background dispatcher. Cleanup, generation, and social workers respect cancellation, catch iteration failures, and cannot enter tight retry loops.

Filesystem lifecycle

TEMP_DIR and OUTPUT_DIR contain bounded working files and expiring media outputs. They are not durable merely because their metadata is in PostgreSQL. The cleanup service removes them after CLEANUP_MINUTES, and container rebuilds may remove them sooner. If an installation needs media bytes beyond that lifecycle, attach Hugging Face persistent storage and point the directories at its mounted path, or implement/configure an authoritative object-storage adapter in a separate storage phase. The current deployment gate does not claim durable object storage.

Docker smoke gate

Create a non-committed production env file whose database has already received the migrations, then run:

scripts/docker_deployment_gate.sh /absolute/path/production.env

Optionally pass a disposable API key as the second argument to validate an authenticated GET /v1/auth/context. Without a key, the smoke test requires that endpoint to fail closed with HTTP 401. The gate:

  1. builds the actual root Dockerfile;
  2. starts it with the supplied production environment;
  3. waits for Docker health;
  4. checks /health and /openapi.json;
  5. verifies every project and project-resource operation in runtime OpenAPI;
  6. verifies social and generation route families;
  7. verifies authentication middleware; and
  8. proves that the container remains alive.

The HTTP portion can also target a deployed Space:

python3 scripts/deployment_smoke.py \
  --base-url https://<owner>-<space>.hf.space \
  --api-key '<disposable-test-key>'

The script never prints the key. Do not use a production administrator key in shared CI logs or shell history.

Vercel frontend

Set the Vercel server-only MEDIAROUTER_API_URL to the Space HTTPS origin. MEDIAROUTER_MCP_URL may use the same origin. In Vercel production, backendBaseUrl() rejects HTTP and loopback/localhost upstreams. Browser calls continue to use /api/backend/*, where the authenticated BFF attaches only a server-side role credential. OpenAPI project capability discovery therefore uses the real runtime backend and requires no frontend override.

Operator acceptance gate

Do not mark the Space ready from source inspection alone. Acceptance requires an actual Docker build, live container startup, 0.0.0.0:7860 binding, health response, and runtime OpenAPI response using production Python 3.10/Linux. A host without Docker and an accessible migrated PostgreSQL database must report these checks as blocked, not passed.