Skip to main content
A common need is to authenticate a Flox environment to external services—for example, a GitHub account or a database password inside a project environment. Secrets should never be hardcoded in a manifest.toml file. Hard-coded secrets could end up committed to git, visible in shell history, and cannot be rotated without editing the manifest. Dotenv files have similar issues. A secure pattern is to keep secrets in a secret store and retrieve them just-in-time during flox activate, exporting them as environment variables that live only while the environment is active.

The JIT secrets pattern

The pattern has three phases:

1. Primary auth (once per session)

A human authenticates once to a secret store using a primary credential (biometric, SSO session, password, etc.). This grants a session or token that the retrieval step will use. This is the human-in-the-loop gate—it makes secret access auditable and revocable.

2. Secret retrieval (on-activate)

The on-activate hook in .flox/env/manifest.toml calls the secret store’s CLI or API to retrieve specific secrets at shell activation time. The store validates the active session before returning values. See Activating environments for more about hooks.

3. Scoped env var injection

Retrieved secrets are exported as environment variables. They exist only in the active Flox shell process and are gone when the shell exits. They never touch disk.

Key security properties

  • Not in manifest — secret values are never written to manifest.toml
  • Not committed to git — the manifest is safe to commit; it contains only retrieval instructions, not values
  • Not in dotenv files — no .env file to accidentally expose
  • Not in shell history — the on-activate hook runs non-interactively
  • Auditable — the secret store logs each access
  • Rotatable — update the value in the store; the manifest never changes
  • Scoped — credentials are per-environment, not global

Implementation examples

Store the token once:
Retrieve in on-activate:
macOS shows a Keychain access dialog on first use. Clicking Always Allow makes subsequent activations silent. In non-interactive contexts (CI, SSH without a GUI agent) the dialog cannot appear and the command will fail—use a CI-native secret mechanism instead.
security is installed by default at /usr/bin/security on all macOS versions. No Xcode or Homebrew required.

Declarative secrets with manifest plugins

Manifest plugins are experimental and under active development. The [plugins] section is not yet part of a stable manifest schema version, and Flox versions without the feature reject a manifest containing it with an “unknown field” error. Details described here may change before release.
The hook examples above hand-write the retrieval command for each secret. Flox is adding a declarative alternative: install a secrets plugin package and declare which secrets your environment needs in a [plugins] section of the manifest. The plugin performs the retrieval for you at activation. The model is the same reference-only JIT pattern: the manifest carries each secret’s location in the store, never its value, so it remains safe to commit. For example, with the 1Password plugin (currently in development):
Installing flox/1password provides the op CLI together with its Flox plugin. On flox activate, the plugin resolves each op://vault/item/field reference against your active 1Password session and exports the value as the named environment variable. Key behavior:
  • Plugin data lives under [plugins.<pkg-name>], keyed by the plugin’s package name. Each plugin defines and documents the shape of its own table; for secrets plugins the convention is a flat table of ENV_VAR = "path/to/secret".
  • Retrieval runs during activation before the on-activate hook, so hooks and [services] can use the variables.
  • Plugins run in the dev and build activation modes, but not in run mode (flox activate --mode run).
  • Primary auth still belongs to the secret store—for example an active op session or biometric unlock—exactly as in phase 1 of the JIT pattern above, and the security properties listed above apply unchanged.
The manual hook pattern keeps working and remains the way to integrate a secret store that doesn’t have a plugin.

Rotating a secret

Because the value lives in the store, not the manifest, rotation requires no manifest changes:
  1. Generate a new secret at the issuer.
  2. Update the secret store.
The next flox activate automatically uses the new value. No PR, no teammate notification, no dotenv sync required.
If a Flox shell is already active when rotation happens, the old value remains set in that running shell. The new value takes effect on the next flox activate. If rotation is in response to a credential compromise, kill active shells explicitly.
Finally, revoke the old token at the issuer once no more environments are active with the old secret value.

Secret store reference

Further reading