Modes and env files
waid.config.json describes your app in a way that is true everywhere. A mode
supplies the values that change between deployments — the host you talk to, the bundle
id you ship under, the signing team.
waid build --target desktop # development (default)waid build --target android --mode stagingwaid build --target ios --mode production --releaseThe mode names the env files WAID reads. --mode staging reads .env.staging.
The default mode is development. --release refuses to run without an explicit
--mode, so a store build always says which deployment it is.
Which files are read#
Four files, in this order, each overriding the last:
.env → .env.<mode> → .env.local → .env.<mode>.local → shell environmentVariables already set in your shell win over all of them, which is what lets CI override a value without editing a file.
Two things worth knowing:
.envis the weakest file, not the switch. Put a value there only when it is identical in every deployment..env.localis not a deployment. It loads for every mode, so a value there reaches staging and production builds too. Keep it for machine-specific overrides that are correct everywhere — a LAN address for a device build, say.
Files ending in .local are for values that should not be committed.
What belongs in a mode file#
The web origin#
settings.auth.redirectPath in waid.config.json is a path, not a URL. WAID joins it
onto WAID_WEB_URL to get the callback it registers:
# .env.productionWAID_WEB_URL=https://app.example.comWith redirectPath of /oauth/callback, that resolves to
https://app.example.com/oauth/callback. Without WAID_WEB_URL — and without a
native scheme — the build stops rather than guessing an address your login would
never return to.
WAID_WEB_URL must be an HTTP(S) origin with no path. waid dev ignores it and uses
the live Vite origin instead.
Native identity#
Native builds get their identity from the mode, so one source tree can ship a staging build and a production build without editing tracked files:
# .env.stagingWAID_APP_ID=com.example.app.stagingWAID_NATIVE_URL_SCHEME=com.example.app.stagingWAID_IOS_BUNDLE_ID=com.example.app.stagingWAID_APPLE_TEAM_ID=ABCDE12345An app using OAuth on a native target needs WAID_NATIVE_URL_SCHEME; the callback is
built from it plus redirectPath, giving com.example.app.staging:/oauth/callback.
iOS device builds additionally need a concrete bundle id and Apple team.
In production mode WAID rejects an identity containing local, dev,
development, staging, stage, pre or test, so a staging bundle id cannot
reach a production build by accident.
Reference#
Every key below is optional until something needs it.
| Variable | What it sets |
|---|---|
WAID_WEB_URL | Origin the web OAuth callback is built from. HTTP(S), no path. |
WAID_APP_ID | Reverse-DNS identity for this mode: macOS bundle id, and iOS unless overridden. |
WAID_NATIVE_URL_SCHEME | Custom URL scheme for deep links and the native OAuth callback. |
WAID_IOS_BUNDLE_ID | iOS bundle id, when it must differ from WAID_APP_ID. |
WAID_MACOS_BUNDLE_ID | macOS bundle id, when it must differ. |
WAID_ANDROID_PACKAGE | Android application id for this mode. |
WAID_ANDROID_CERT_DIGEST | SHA-256 signing-certificate digest. Required alongside the package for caller verification. |
WAID_APPLE_TEAM_ID | Apple Developer team. Ten uppercase letters or digits. Find yours with waid signing-teams. |
WAID_ASSOCIATED_DOMAINS | Apple associated domains, comma separated. Unset, it follows your identity host. |
WAID_AUTH_REDIRECT_URI | Pin the web callback instead of deriving it from WAID_WEB_URL. |
WAID_AUTH_REDIRECT_URI_MOBILE | Pin the native callback instead of deriving it. |
WAID_BROKER_URL_SCHEME | Scheme for the identity broker, when the app uses one. |
VITE_ICE_URL · VITE_RELAY_URL · VITE_ARIN_URL | Service origins your frontend talks to. |
These are not secrets — Vite bakes VITE_* values into the bundle, so they ship
inside the app either way. They live in env files because they differ per
deployment. Real secrets, like Android keystore passwords, belong in
waid.local.properties, which is never committed.
A worked example#
One app, three deployments, nothing per-deployment in waid.config.json:
{ "id": "com.example.app", "name": "app", "displayName": "Example", "dist": "dist", "settings": { "auth": { "clientId": "example-ui", "redirectPath": "/oauth/callback", "scopes": ["openid"] } }}# .env.developmentWAID_WEB_URL=http://localhost:5173WAID_APP_ID=com.example.app.devWAID_NATIVE_URL_SCHEME=com.example.app.dev# .env.productionWAID_WEB_URL=https://app.example.comWAID_APP_ID=com.example.appWAID_NATIVE_URL_SCHEME=com.example.appWAID_APPLE_TEAM_ID=ABCDE12345waid build --target desktop # uses .env.developmentwaid build --target ios --mode production # uses .env.productionIf a build stops here#
"auth.redirectPath requires WAID_WEB_URL for a web callback or nativeUrlScheme
for a native callback" — the mode you built has neither. Add WAID_WEB_URL for a
web callback, or WAID_NATIVE_URL_SCHEME for a native one, to the env file for that
mode. Remember the default mode is development, so the file is usually
.env.development.
"WAID_APPLE_TEAM_ID must contain 10 uppercase letters or digits" — a placeholder was left in the file. It is validated for any native target, so remove the line entirely if you are not signing for Apple.