Spaces:
Running
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:
app/security/migrations/0001_api_key_security.sqlapp/security/migrations/0002_authoritative_tenancy_postgres.sqlapp/projects/migrations/0001_projects_foundation.sqlapp/security/migrations/0003_generation_domain_postgres.sqlapp/security/migrations/0004_generation_provider_runtime_postgres.sqlapp/projects/migrations/0002_project_resources.sqlapp/projects/migrations/0003_editor_persistence_rendering.sqlapp/projects/migrations/0004_ai_studio.sqlapp/projects/migrations/0005_ai_copilot.sqlapp/projects/migrations/0006_template_marketplace.sqlapp/social/migrations/0001_social_foundation_postgres.sqlapp/social/migrations/0002_social_rls.sqlapp/social/migrations/0003_social_integrity_postgres.sqlapp/social/migrations/0004_youtube_media_assets.sqlapp/social/migrations/0005_x_text_posts.sqlapp/social/migrations/0006_linkedin_oauth_account_type.sqlapp/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:
- builds the actual root Dockerfile;
- starts it with the supplied production environment;
- waits for Docker health;
- checks
/healthand/openapi.json; - verifies every project and project-resource operation in runtime OpenAPI;
- verifies social and generation route families;
- verifies authentication middleware; and
- 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.