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.
```env
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:
```env
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:
```bash
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:
```bash
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.