Skip to main content
Proposed (STA-806). It reads with 0173 (Infisical is the source, runway syncs), 0146 (secret references), 0076 (three deploy modes), 0352 (runway is the one deployment tool) and 0354 (two clouds). It answers the wave 10 runtime builder’s question 1.

Context

@stackra/config resolves every @Secret("NAME") field through the SecretStore port. Wave 10 built two drivers beside the runtime’s environment store (envSecrets in @stackra/app): InfisicalSecretStoreDriver (entry ., web fetch, universal-auth machine identity) and AwsSecretsManagerSecretStoreDriver (entry ./node, the runtime’s AWS role). Both read every named secret into memory at load() and answer get(name) synchronously. Nothing said which deployment binds which driver, and the Infisical driver needs a credential of its own (INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET) before it can read anything. 0173 already makes runway sync Infisical into Encore, Workers and EAS, so a runtime that is synced does not need to call Infisical at all.

Decision

  1. Three drivers, one per process. A server process binds exactly one SecretStore driver: env (the platform’s own store, read as the process environment or Worker bindings), infisical or aws-secrets-manager. The choice is deployment data in the environment’s runway.yaml (secrets.store), never a branch in code. Clients (web, Expo, Electron) bind no server secret store; they read only public build values.
  2. Binding per deploy mode. A synced SaaS runtime never calls Infisical at run time, so an Infisical outage cannot stop it.
  3. The bootstrap credential. A process bound to infisical reads exactly two secrets from the platform’s own store: INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET, through the env driver, and builds InfisicalConfig with them (buildConfig(InfisicalConfig, { values, secrets: platform })). They are a machine identity per environment and service, read-only on that service’s folder. Runway’s secrets sync writes them into the platform store (a Kubernetes or Compose secret on-prem, by the customer’s operator when runway does not run there); no file in a repository, image or .env holds them. Where the platform gives a workload identity (an AWS role), that identity signs the reads and there is no bootstrap secret, which is why AWS compute binds aws-secrets-manager.
  4. Load at boot, fail closed. The runtime awaits load() before it builds any configuration; a failed load stops the process and names the step and status, never a value (both drivers already do this). A rotation reaches a process on the next deploy or an explicit reload; no driver polls.
  5. Residency. Each product-region’s secrets live in that region’s account or project (0353, 0354); the sync never copies one region’s values into another.
  6. Values. None of these is seed, pack, settings or rule-set data. The store choice, Infisical site, project, environment and folder, and the AWS region and secret ids are deployment configuration in runway.yaml; secret names are in git; values exist only in Infisical and the stores it syncs to (0173 d6).

Consequences

  • SaaS keeps one rotation point (Infisical) and no run-time dependency on it.
  • On-prem and air-gapped customers can run their own Infisical without any Figentra service.
  • The AWS path needs a secret sync target per account; runway’s secrets sync gains an aws-secrets-manager provider.
  • A GCP-native Secret Manager driver is not built; GCP reads through env until one is needed.

Enforcement

planned — the runway schema refuses an environment whose secrets.store is not one of the three drivers, and refuses infisical without the two bootstrap names in the environment’s secret list; the gitleaks scan of 0173 covers the bootstrap names in files. Nothing checks it yet.

Open questions

  1. Infisical machine-identity auth on AWS. Infisical also offers AWS IAM auth, which would remove the bootstrap pair on AWS on-prem installs; the driver supports universal auth only.
  2. A GCP Secret Manager driver. Only if a GCP runtime cannot mount secrets as environment.

Supersedes and sources

Supersedes: none. Reads with 0173, 0146, 0179 and 0353. Sources: notes/handover/build-wave10-2026-10-25.md (runtime builder, question 1); packages/foundation/config/README.md (secret store drivers); packages/runtime/app/src/core/functions/env-secrets.function.ts.