All docs
ReferenceSpecs

Environment Variables

All of vibi's environment dependencies live in two files:

  • BFF: vibi-bff/.env (or the same keys in system env). Validated in vibi-bff/src/main/kotlin/com/vibi/bff/config/AppConfig.kt.
  • Mobile: vibi-mobile/local.properties. Gradle injects these into BuildConfig at build time.

Do not hardcode keys, secrets, or internal IPs in code — use <placeholder> only; real values must flow in from the two files above.


BFF — vibi-bff/.env

PERSO_API_KEY, PERSO_SPACE_SEQ, SEPARATION_SIGNING_SECRET, AUTH_JWT_SECRET, GOOGLE_OAUTH_CLIENT_IDS are fail-fast at boot — the server exits immediately on missing or malformed values.

Perso AI

KeyRequiredDefaultDescription
PERSO_API_KEYPerso-issued API key. Used for audio separation / STT / auto dubbing.
PERSO_SPACE_SEQPerso workspace ID. Look up via GET /portal/api/v1/spaces. Must be greater than 0.
PERSO_BASE_URLhttps://api.perso.aiUpstream base URL. Change only when fronted by your own gateway.
PERSO_POLL_INTERVAL_MS5000Progress poll interval (ms). Must be ≥ 1000.
PERSO_MAX_POLL_MINUTES30Audio separation job poll timeout (minutes). 1..120.

Server

KeyRequiredDefaultDescription
PORT8080Ktor server bind port.
BFF_BASE_URLhttp://localhost:8080Public URL used to sign download links. Change to ngrok / LAN address when exposing externally.
STORAGE_PATH./storageRoot for upload / render / separation artifacts. Use a location with sufficient disk space.
CORS_ALLOWED_ORIGINS(blank = any)Comma-separated whitelist. For Android-only deployments, set to a sentinel like android-only.invalid to block browsers.

Separation / signing

KeyRequiredDefaultDescription
SEPARATION_SIGNING_SECRETHMAC key for stem / mix download URLs. Enforced ≥ 32 chars. Generate with openssl rand -hex 32. Rotation invalidates all unexpired tokens at once.
SEPARATION_ABANDON_TTL_MS1800000Time (ms) before READY-but-uncollected separation results are cleaned up. Must be ≥ 60_000.
SEPARATION_MIX_TTL_MS600000Mix artifact retention time (ms).
SEPARATION_URL_TTL_SEC1800Stem download token lifetime (s). 60..86400.
SEPARATION_MIX_URL_TTL_SEC600Mix download token lifetime (s).

Vertex AI Gemini (subtitle translation + chat)

Boot succeeds even if these are missing, as long as subtitle translation and chat are not used — they are validated on first call.

KeyRequiredDefaultDescription
GEMINI_PROJECT_IDVertex AI project. Named GEMINI_* (not GCP_*) so the Cloud Run hosting project and the Vertex-AI-enabled project can differ.
GEMINI_LOCATIONus-central1Vertex AI region.
GEMINI_MODELgemini-2.5-flashModel ID.
GOOGLE_APPLICATION_CREDENTIALSPath to a local service account JSON. Leave blank to fall back to Application Default Credentials — on Cloud Run that resolves to the attached SA via the metadata server; locally it resolves to the gcloud auth application-default login cache.

Authentication (Google OAuth + own JWT)

KeyRequiredDefaultDescription
GOOGLE_OAUTH_CLIENT_IDSComma-separated. iOS / Android / Web client ids all allowed. Passes only when the tokeninfo aud matches one of these.
AUTH_JWT_SECRETHMAC-SHA256 signing key. Enforced ≥ 32 chars.
AUTH_JWT_EXPIRY_SECONDS604800 (7d)Issued access token lifetime (s). 60..7776000 (90 days).

Perso storage host + SSRF whitelist

KeyRequiredDefaultDescription
PERSO_STORAGE_BASE_URLhttps://portal-media.perso.aiAudio-separation /download responses point at this CDN host (/perso-storage/... paths), not PERSO_BASE_URL. The auth header is not sent to this host.
PERSO_DOWNLOAD_ALLOWED_HOSTSportal-media.perso.aiComma-separated SSRF whitelist. PERSO_BASE_URL and PERSO_STORAGE_BASE_URL hosts are added automatically.

Runtime tunables (process-level)

These bypass AppConfig and are read directly from the process environment — change them when running into Cloud Run cold-start timeouts or memory-tier limits. Unset values fall back to the defaults below.

KeyDefaultDescription
MAX_UPLOAD_FILE_SIZE_MB500Multipart upload ceiling. Tie to the Cloud Run --memory tier — 1Gi200, 4Gi1000. Applied to every receiveMultipart(formFieldLimit=…) call.
HTTP_CONNECT_TIMEOUT_MS120000 (120s)Ktor HTTP client connectTimeoutMillis. Extend for cold starts on the upstream.
HTTP_REQUEST_TIMEOUT_MS600000 (600s)requestTimeoutMillis. Large Perso uploads sit on this.
HTTP_SOCKET_TIMEOUT_MS600000 (600s)socketTimeoutMillis. Same situation as above.
RENDER_MAX_CONCURRENTCPU countffmpeg fan-out cap for the render pipeline.

Symptoms on missing values

Missing keyBoot result
PERSO_API_KEYIllegalArgumentException: PERSO_API_KEY must not be blank followed by immediate exit
PERSO_SPACE_SEQPERSO_SPACE_SEQ must be > 0 (got 0)
SEPARATION_SIGNING_SECRET (under 32 chars)SEPARATION_SIGNING_SECRET must be at least 32 chars (got N)
AUTH_JWT_SECRET (under 32 chars)AUTH_JWT_SECRET must be at least 32 chars (got N)
GOOGLE_OAUTH_CLIENT_IDSGOOGLE_OAUTH_CLIENT_IDS must not be empty (comma-separated)
GEMINI_PROJECT_ID missingBoot succeeds. 5xx on first call to /api/v2/subtitles or /api/v2/chat.
GOOGLE_APPLICATION_CREDENTIALS missing and no ADC cache and not on Cloud RunBoot succeeds. Gemini calls fail with Could not load credentials.
STORAGE_PATH directory missingBoot succeeds. IOException on first upload. Recommended to pre-create: mkdir -p ./storage/{uploads,render,separation}.

Mobile — vibi-mobile/local.properties

vibi-mobile/local.properties is not committed to git. A single file serves the same values to both Android and iOS.

sdk.dir=/Users/<you>/Library/Android/sdk
BFF_BASE_URL=http://10.0.2.2:8080/
KeyRequiredDescription
sdk.dir✅ (Android build)Absolute path to local Android SDK.
BFF_BASE_URLPublic address of the BFF. Trailing slash required (Ktor Client base URL join rule).
TargetValue
Android emulatorhttp://10.0.2.2:8080/
Android physical devicehttp://192.168.x.x:8080/ (Mac LAN IP)
iOS simulatorhttp://localhost:8080/ or Mac LAN IP
iOS physical devicehttp://192.168.x.x:8080/

10.0.2.2 is the Android emulator's host loopback alias — it does not work on iOS. You must update this value every time you switch between simulator / emulator / physical device.

When using a LAN IP, additional setup may be required — see ../how-to/connect-real-device.md.

Symptoms on missing values

Missing / wrongResult
sdk.dir missingGradle: SDK location not found.
BFF_BASE_URL missingKtor Client cannot resolve relative paths to absolute URLs; IllegalStateException on first call.
Missing trailing slashKtor's path-join rule may send api/v2/... to a path other than intended.
Using 10.0.2.2 from iOS simulatorAll BFF calls connect-timeout.

Code references

  • vibi-bff/src/main/kotlin/com/vibi/bff/config/AppConfig.kt — env → validated AppConfig data class.
  • vibi-bff/build.gradle.ktsapplication { mainClass.set(...) } + build output paths.
  • Workspace root README.md — BFF_BASE_URL per-target table (this document is the single source of truth; the README is an excerpt).