> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackra.figentra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 0371 — Secret store drivers: which deploy mode binds which, and how the bootstrap credential arrives

> Proposed · 2026-10-26

> [!NOTE]
>
> Proposed (STA-806). It reads with
> [0173](https://docs.stackra.figentra.com/adr/0173-secrets-infisical-is-the-source-of-production-secrets.md) (Infisical is the source,
> runway syncs), [0146](https://docs.stackra.figentra.com/adr/0146-package-configuration-typescript-layers-and-secret-references.md)
> (secret references), [0076](https://docs.stackra.figentra.com/adr/0076-every-package-runs-saas-on-prem-and-air-gapped.md) (three
> deploy modes),
> [0352](https://docs.stackra.figentra.com/adr/0352-runway-is-the-one-deployment-tool-pulumi-is-its-infrastructure-engine-gen-is-its-build-step.md)
> (runway is the one deployment tool) and
> [0354](https://docs.stackra.figentra.com/adr/0354-two-clouds-aws-and-gcp-and-one-aws-account-per-product-region.md) (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.**

   | Where it runs | Driver | How values get there |
   | - | - | - |
   | Encore Cloud (SaaS) | `env` | `stackra runway secrets sync` copies Infisical into Encore secrets (0173 d1) |
   | Cloudflare Workers | `env` | the same sync into Worker secret bindings |
   | AWS compute in a product-region account (0354) | `aws-secrets-manager` | the sync copies Infisical into Secrets Manager in that account and region |
   | GCP compute (0354) | `env` | the sync copies into the platform's secret store, mounted as environment |
   | On-prem | `infisical` or `env` | the customer's self-hosted Infisical, or values its operator injects |
   | Air-gapped | `infisical` or `env` | Infisical self-hosted inside the enclave, or operator-injected; nothing leaves |
   | Developer machine | as 0173 d4 and 0267 | machine identity and the `/agents` vault paths |

   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](https://docs.stackra.figentra.com/adr/0173-secrets-infisical-is-the-source-of-production-secrets.md),
[0146](https://docs.stackra.figentra.com/adr/0146-package-configuration-typescript-layers-and-secret-references.md),
[0179](https://docs.stackra.figentra.com/adr/0179-on-prem-and-air-gapped-delivery.md) and
[0353](https://docs.stackra.figentra.com/adr/0353-data-residency-a-region-fixed-at-sign-up-a-login-directory-that-only-routes-and-copies-within-a-jurisdiction.md).
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`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.