VMP is a Cloudflare-based video subscription platform with a Worker API, Nuxt web app, and shared TypeScript types.
@vmp/api(packages/api) — Cloudflare Worker API + D1/R2/KV integrations.@vmp/web(packages/web) — Nuxt 4 frontend deployed to Cloudflare Pages.@vmp/shared(packages/shared) — shared TypeScript contracts.@vmp/podcast-host(packages/podcast-host) — Node TypeScript media pipeline/supervisor for video processing and podcast preview jobs.@vmp/offloading(packages/offloading) — Node TypeScript offloading service for R2↔Garage hot/cold tier orchestration.
- Pushes to
mainrun staging deploy in.github/workflows/deploy.yml. - Version tags (
v*.*.*) run production deploy in.github/workflows/deploy.yml. - Deploy pipeline fails fast on type-checking before build/deploy:
@vmp/sharedtsc --noEmit@vmp/apitsc --noEmit@vmp/webnuxi prepare && nuxi typecheck
- Node.js 20+ and npm 10+.
- Cloudflare account with:
- Worker/API service
- Pages project(s)
- D1 database
- R2 bucket
- KV namespaces
- Repository secrets configured for CI deploy:
CLOUDFLARE_API_TOKEN_STAGINGCLOUDFLARE_ACCOUNT_ID_STAGINGCLOUDFLARE_API_TOKEN_PRODCLOUDFLARE_ACCOUNT_ID_PRODCF_PAGES_PROJECT_NAME(staging web deploy)
- Install dependencies:
npm ci
- Build service worker helper from TS source:
npm run build:sw-push --workspace=@vmp/web
- Type-check everything:
npm run typecheck
These are useful for controlled/manual rollouts outside GitHub Actions.
- API deploy:
npm run deploy:api
- Web deploy:
npm run deploy:web
Use Wrangler secrets for sensitive values (never commit secrets). Required values are documented in AGENTS.md and DEPLOYMENT.md (JWT, Stripe, Brevo, VAPID, RSS, TOTP encryption, etc.).
The existing local/watchfolder/rclone pipeline remains the default and is unchanged.
An optional cloud path can be enabled for direct source uploads + AWS Elemental MediaConvert.
Required API env vars (Worker):
MEDIA_CONVERT_ENABLED=1AWS_REGION(for S3 + MediaConvert, e.g.eu-central-1)AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKEN(optional, only when using temporary credentials)MEDIA_CONVERT_ENDPOINT(account-specific endpoint from MediaConvert console)MEDIA_CONVERT_ROLE_ARN(IAM role MediaConvert assumes)MEDIA_CONVERT_INPUT_BUCKET(S3 source uploads)MEDIA_CONVERT_OUTPUT_BUCKET(S3 transcode outputs)MEDIA_CONVERT_INPUT_PREFIX(optional, defaultmediaconvert-input)MEDIA_CONVERT_OUTPUT_PREFIX(optional, defaultmediaconvert-output)MEDIA_CONVERT_MAX_UPLOAD_MB(optional, default4096)MEDIA_CONVERT_PRICE_HD_PER_MIN(optional, default0.015; rough estimator)
AWS setup checklist:
- Create/choose two S3 prefixes/buckets for input and output.
- Create a MediaConvert IAM service role (
MEDIA_CONVERT_ROLE_ARN) with read access to input and write access to output. - Create an IAM principal for the Worker credentials with:
mediaconvert:CreateJob,mediaconvert:GetJobiam:PassRolescoped toMEDIA_CONVERT_ROLE_ARNs3:PutObjecton input prefixs3:GetObjecton output object prefixs3:ListBucketon output bucket (bucket-level action), scoped bys3:prefixcondition to the output prefix
- In MediaConvert console, copy the account endpoint into
MEDIA_CONVERT_ENDPOINT. - Run migration
packages/api/migrations/0017_media_convert_jobs.sql.
Notes:
- Current cloud profile is H.264/HLS with 720p only, fps capped at 30.
- Architecture is rendition-based and supports future expansion (480p/1080p/4K, alternate codecs).
- Usage/cost values are approximate normalized-minute estimates, not billing-grade accounting.
- API entrypoint is TypeScript (
packages/api/src/index.ts) referenced bypackages/api/wrangler.json. - Worker/service scripts that must remain JavaScript at runtime (for browser/service-worker execution) are generated from TypeScript sources during build.