Skip to main content

Running locally

There are three levels of local setup. Pick the smallest one that answers your question.

LevelWhat runs locallySecrets neededGood for
1. Frontend onlyOne SPAnoneUI, copy, layout, player chrome
2. One API + SPASPA + customer or admin APInon-production Firebase key, .env.*.localAPI and catalogue work
3. Full stackBoth APIs, multimedia, workerFirebase, GCS, Docker, gcsfuseuploads, transcoding, streaming

Most changes do not need level 3.

Level 1 — a frontend against the test environment​

The deployed test origin is https://test.greyscreentv.com. It already serves the catalogue, the APIs and the streaming path. No secret is required.

cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/gstv-website
npm ci
npm run dev # http://localhost:5173
cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/greyscreen-webview
npm ci
npm run dev # http://localhost:5174/webview/
cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/gstv-admin-web
npm ci
npm run dev # http://localhost:5173/admin/

The webview and admin dev servers keep their repository-specific behaviour:

  • greyscreen-webview uses base: '/webview/' and port 5174, so its URLs include the prefix.
  • gstv-admin-web's Vite dev server proxies /api/admin to https://test.greyscreentv.com by default. If you need the local admin API instead, change the proxy target in your working copy, not in a commit.
Admin env fallback

gstv-admin-web/src/config/index.js falls back to the legacy host https://greyscreentv.testapp.pro/api/admin when VITE_API_BASE_URL is absent. A build without its env file will silently talk to a deployment that is no longer current. Check the Network tab before debugging anything else.

Level 2 — an API on your machine​

1. Get the env layers​

Both APIs expect:

  • .env.template — tracked, names only. Use it as the checklist.
  • .env.nonprod.local — shared dev/test values, not tracked.
  • .env.development.local — dev overlay, not tracked.

The admin API additionally requires FIREBASE_DATABASE_URL (RTDB) because it consumes queues. The customer API does not need RTDB for most routes, though it reads it in a few places.

The service-account path is provided by FIREBASE_SERVICE_ACCOUNT. Do not rename the key file to something that implies an environment it is not; validateEnv() checks the project inside it.

2. Start the customer API​

cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/gstv-customer-api
npm ci
APP_ENV=dev npm run dev # port 3001

Expected boot order: validateEnv() → connectAllDatabases() → startCatalogSnapshot() → routes → listen. If the process exits immediately, read the validation error; it is usually a project/key mismatch or a missing payment key.

Check it:

curl -s http://localhost:3001/health | jq
# {"status":"ok","appEnv":"dev","production":false, ...}

3. Start the admin API​

cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/gstv-admin-api
npm ci
APP_ENV=dev npm run dev # port 3003

The admin API also starts:

  • the RTDB claim consumers (resolutionEvents, transcodeFailed, legacy imageEvents), unless RESOLUTION_CONSUMER_DISABLED=true;
  • the notification hub (SSE), always;
  • the VM lifecycle timer.
Run one admin API

The claim consumers are child_added queues with no lease. Running a second admin API against the same database double-processes events. If you only need to read admin routes, set RESOLUTION_CONSUMER_DISABLED=true in your local env and leave the VM lifecycle alone. Never disable the consumer in production.

4. Point the SPA at the local API​

For the website, set the API base used by src/lib/apiClient.ts to http://localhost:3001. The variable is VITE_API_BASE_URL in the repository's env files. Make sure the API's CORS origin list includes your dev origin; otherwise the browser request fails before it reaches a controller.

For the webview, the API base is constructed in src/config/env.ts from the mode. The default in development is the test origin, which is usually what you want.

Level 3 — media and uploads​

Only do this if your task touches the upload, transcode or HLS path.

Multimedia​

cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/gstv-multimedia
npm install
npm start

The container needs:

  • an output bucket mounted with gcsfuse;
  • a GCS service-account key mounted at runtime;
  • /dev/fuse and SYS_ADMIN (or privileged: true) in Docker;
  • HTTP_PORT=8080, EXPRESS_PORT=8000, RTMP_PORT=1935.

Running npm start outside the container only works if the bucket is already mounted at the path the cache expects. The Compose definition in vps-reverse-proxy/stacks/greyscreentv-test/docker-compose.yml is the reference.

Worker​

The worker is designed to be created by the admin API, not run by hand. If you must run it:

cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/gstv-vps
npm ci
npm run build
npm start

It needs RTDB_URL, the input and output buckets mounted with gcsfuse, DEFAULT_SEG_DURATION, and a Firebase key. ENABLE_VM_SELF_DELETE should be false outside GCE, otherwise its shutdown path tries to delete an instance that does not exist.

Shared queues

There is one RTDB queue per database, not per laptop. A local worker that points at the test stack's database competes with the test worker. Point it at an isolated RTDB or do not start it.

Native apps​

The phone shell and TV app are Expo projects:

cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/greyscreen-expo-webview
npm install
npm start # Expo dev server, then press a / i

cd /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/gstv-tv
npm install
npm start

Use the test variant (APP_VARIANT=test or the preview EAS profile). Production builds pick the production Firebase key from firebase_keys/, which you should not need locally.

Smoke test​

After the stack is up, verify in this order:

  1. GET /health on both APIs returns {"status":"ok","appEnv":...}.
  2. GET /healthz on each static frontend returns 200.
  3. GET /health on multimedia returns 200 (if running).
  4. The website loads the home rails; /movies shows the catalogue.
  5. A trailer page plays a trailer without signing in.
  6. Sign-in works for the website; the admin login works for the back office.
  7. Only then test the flow you changed.

If a catalogue call returns an empty list, check the Firebase project first, then the isReady flag on the title, then the API logs — not the React code.

Common local commands​

RepositoryCommandNotes
gstv-websitenpm run dev, npm run build:test, npm run typecheckVite mode picks the environment.
greyscreen-webviewnpm run dev, npm run build, npm run lintNo typecheck script.
gstv-admin-webnpm run dev, npm run build:testJavaScript project; no tsconfig.
gstv-customer-apiAPP_ENV=dev npm run dev, npm run buildNeeds .env layers.
gstv-admin-apiAPP_ENV=dev npm run dev, npm run buildSingle replica only.
gstv-vpsnpm run build, npm startContainer needs gcsfuse.
gstv-multimedianpm startContainer needs gcsfuse.
greyscreen-expo-webviewnpm start, npm run android, npm run iosEAS profiles in eas.json.
gstv-tvnpm start, npm run android, npm run build:devEAS/Play distribution.

Next: Per-repository guide → Troubleshooting.