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.

bash
waid build --target desktop # development (default)
waid build --target android --mode staging
waid build --target ios --mode production --release

The 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:

text
.env → .env.<mode> → .env.local → .env.<mode>.local → shell environment

Variables 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:

  • .env is the weakest file, not the switch. Put a value there only when it is identical in every deployment.
  • .env.local is 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:

properties
# .env.production
WAID_WEB_URL=https://app.example.com

With 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:

properties
# .env.staging
WAID_APP_ID=com.example.app.staging
WAID_NATIVE_URL_SCHEME=com.example.app.staging
WAID_IOS_BUNDLE_ID=com.example.app.staging
WAID_APPLE_TEAM_ID=ABCDE12345

An 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.

VariableWhat it sets
WAID_WEB_URLOrigin the web OAuth callback is built from. HTTP(S), no path.
WAID_APP_IDReverse-DNS identity for this mode: macOS bundle id, and iOS unless overridden.
WAID_NATIVE_URL_SCHEMECustom URL scheme for deep links and the native OAuth callback.
WAID_IOS_BUNDLE_IDiOS bundle id, when it must differ from WAID_APP_ID.
WAID_MACOS_BUNDLE_IDmacOS bundle id, when it must differ.
WAID_ANDROID_PACKAGEAndroid application id for this mode.
WAID_ANDROID_CERT_DIGESTSHA-256 signing-certificate digest. Required alongside the package for caller verification.
WAID_APPLE_TEAM_IDApple Developer team. Ten uppercase letters or digits. Find yours with waid signing-teams.
WAID_ASSOCIATED_DOMAINSApple associated domains, comma separated. Unset, it follows your identity host.
WAID_AUTH_REDIRECT_URIPin the web callback instead of deriving it from WAID_WEB_URL.
WAID_AUTH_REDIRECT_URI_MOBILEPin the native callback instead of deriving it.
WAID_BROKER_URL_SCHEMEScheme for the identity broker, when the app uses one.
VITE_ICE_URL · VITE_RELAY_URL · VITE_ARIN_URLService 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:

json
{
"id": "com.example.app",
"name": "app",
"displayName": "Example",
"dist": "dist",
"settings": {
"auth": {
"clientId": "example-ui",
"redirectPath": "/oauth/callback",
"scopes": ["openid"]
}
}
}
properties
# .env.development
WAID_WEB_URL=http://localhost:5173
WAID_APP_ID=com.example.app.dev
WAID_NATIVE_URL_SCHEME=com.example.app.dev
properties
# .env.production
WAID_WEB_URL=https://app.example.com
WAID_APP_ID=com.example.app
WAID_NATIVE_URL_SCHEME=com.example.app
WAID_APPLE_TEAM_ID=ABCDE12345
bash
waid build --target desktop # uses .env.development
waid build --target ios --mode production # uses .env.production

If 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.