Skip to main content

Build Config

Build config resolves environment, platform, developer, and runtime settings into one generated file. Every platform reads the same canonical runtime path:

pkg:/assets/meta/build-config.json

Web, Roku, Android, and Apple builds should resolve environment-specific overlays before packaging or serving the app. The running app loads the generated file from the canonical path instead of choosing an environment-specific source file at runtime.

Concepts

Hosanna separates these configuration layers:

ConceptPurpose
App configExisting app, theming, layout, rows, cells, fragments, translations, and runtime app structure. app.config.*.json selection and $extendFile inheritance remain separate from build config.
Build infoGenerated metadata about the build. This remains generated.
Build configEnvironment, platform, developer, and runtime settings needed by the app or toolchain: remote debug, MCP, service URLs, config base URL, mock HTTP, device overrides, tasks, Perfetto, live playlist flags, and similar values.
SecretsSecret overlays merged into generated build config at build or dev-server time. Source secret files are never bundled directly as secrets.json.

Do not use one layer to solve another layer's job:

LayerSelected byOwns
Run config.hosanna-tools/run.json, --app, --device, --targetwhich app/flavor, platform, target kind, device, ports, and build/deploy inputs HST uses
Build config--env, --platform, --profileruntime services, remote debug, MCP, mocks, flags, device overrides, and secret overlays
App config--app-config, expression, platform, default fileUI structure, theme, fragments, rows, cells, translations, and app-specific selectors

hst run and hst build resolve build config before running their platform actions, so daily launch commands normally do not need a separate build-config:resolve call.

Source Files

Source overlays live in build-config/. Generated outputs and local profile data stay out of git.

build-config/
base.json
dev.json
qa.json
prod.json
dev.web.json
dev.roku.json
profiles/
george.json # ignored

secrets/
dev.json # ignored or CI-provided
profiles/
george.json # ignored

.build-config/
resolved/ # ignored generated metadata

assets/meta/
build-config.json # ignored generated runtime copy

Do not put source config under .build-config/; that directory is for generated/internal output.

CI Secret Restore

CI should restore ignored secret overlays before resolving the runtime file. build-config:restore-secrets reads base64-encoded JSON environment variables and writes ignored files under secrets/:

BUILD_CONFIG_SECRETS_DEV_BASE64=... npx hst build-config:restore-secrets --env dev
npx hst build-config:restore-secrets

Supported names are:

Environment variableRestored file
BUILD_CONFIG_SECRETS_<ENV>_BASE64secrets/<env>.json
BUILD_CONFIG_PROFILE_SECRETS_<PROFILE>_BASE64secrets/profiles/<profile>.json

The values must decode to JSON objects. The restore command does not print secret values.

Merge Order

hst resolves build config in this order. Later files override earlier files:

Six ordered BuildConfig overlays merging into the canonical runtime file, with tracked and secret sources kept distinctSix ordered BuildConfig overlays merging into the canonical runtime file, with tracked and secret sources kept distinct

Profiles are never loaded automatically. Use a profile only when explicitly requested with --profile <name> or the matching dev-server environment/query setting.

Production Safety

Profiles are rejected for prod by default. Only use a production profile with an explicit danger flag, and never commit secrets or local profile files.

Resolving Config

Use hst build-config:resolve from the app repo:

npx hst build-config:resolve --env dev --platform web --format json
npx hst build-config:resolve --env prod --platform roku --out assets/meta/build-config.json
npx hst build-config:resolve --env dev --platform roku --profile george --out assets/meta/build-config.json
npx hst build-config:resolve --env dev --platform web --profile george --explain

You can also provide the target as shell environment variables before the command. CLI flags win when both forms are present.

CLI optionEnvironment fallback
--envHS_ENV or HOSANNA_BUILD_ENV
--platformHS_PLATFORM or HOSANNA_BUILD_PLATFORM
--profileHS_BUILD_PROFILE
--allow-profile-in-prodHS_ALLOW_PROFILE_IN_PROD=true
HS_ENV=dev HS_PLATFORM=roku npx hst build-config:resolve --out assets/meta/build-config.json
HS_ENV=dev HS_PLATFORM=web HS_BUILD_PROFILE=george npx hst build-config:resolve --format json

The generated config includes metadata describing how it was resolved:

{
"$buildConfig": {
"env": "dev",
"platform": "web",
"profile": "george",
"sources": [
"build-config/base.json",
"build-config/dev.json",
"build-config/dev.web.json",
"secrets/dev.json",
"build-config/profiles/george.json",
"secrets/profiles/george.json"
]
}
}

Source file names are safe to log. Secret values are not.

Web Development

Vite serves /assets/meta/build-config.json dynamically in development. The middleware calls the resolver on each request, so developers can edit build config, profiles, or secrets and refresh the browser without restarting Vite.

GET /assets/meta/build-config.json?env=dev&profile=george

Equivalent resolver command:

npx hst build-config:resolve --env dev --platform web --profile george --format json

For production web builds, resolve once during the build and package the generated assets/meta/build-config.json.

Roku And Native Builds

Roku, Android, iOS, and Apple builds resolve build config once before compile/package and bundle the canonical runtime file.

npx hst build-config:resolve --env dev --platform roku --out assets/meta/build-config.json
npm run roku:build

npx hst build-config:resolve --env dev --platform android --out assets/meta/build-config.json
npm run android:build-code

npx hst build-config:resolve --env dev --platform apple --out assets/meta/build-config.json
npm run apple:build-code

With a developer profile:

HS_ENV=dev HS_PLATFORM=roku HS_BUILD_PROFILE=george npx hst build-config:resolve --out assets/meta/build-config.json
npm run roku:build

Runtime APIs

Use build-config named APIs for new code:

@injectBuildConfig('remoteDebug.isEnabled')
private remoteDebugEnabled?: boolean;

const host = AppUtils.getBuildConfig<string>('remoteDebug.host');

configurationLoader.loadBuildConfig();
configurationValidator.validateBuildConfig();

Remote App Config

Build config can say where remote app configs are loaded from:

{
"config": {
"baseUrl": "https://cdn.example.com/hosanna/config"
}
}

config.remoteAppConfigBaseUrl is accepted as a legacy fallback. There is no defaultAppConfig build-config field; the default file name comes from the app-config candidate rules.

Do not hand-build remote config filenames. Use the build config resolver API so web, Roku, Android, Apple, local files, and remote files choose app config consistently:

const target = buildConfig.resolveRemoteAppConfigTarget({
expression: launchArgs.expression,
platform: device.platform,
appConfig: launchArgs.appConfig
});

Resolution order matches local app config resolution:

1. explicit appConfig override
2. app.config.<expression>.json
3. app.config.<platform>.json
4. app.config.json

Remote loaders should try each candidate in order until one loads successfully.

Deprecations

Deprecated Compatibility

Older projects may still contain debug-flags.*.json files or APIs such as @injectFlag, AppUtils.getDebugFlag, configurationLoader.loadDebugFlags, and configurationValidator.validateDebugFlags. These remain as temporary compatibility wrappers only. New code should use build-config/*.json, @injectBuildConfig, AppUtils.getBuildConfig, and the generated assets/meta/build-config.json runtime file.

Talk to us