Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 169 additions & 49 deletions concepts/secrets-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@

```mermaid
flowchart TD
A["Developer authenticates to secret store"] --> B["flox activate runs on-activate hook"]
B --> C["Hook calls store CLI to retrieve secret"]
A["Developer authenticates to secret store"] --> B["flox activate runs the retrieval step"]
B --> C["Secrets plugin or on-activate hook calls store CLI"]
C --> D["Secret exported as env var in Flox shell"]
D --> E["Env var available while shell is active"]
E --> F["Secret gone when shell exits"]
Expand All @@ -38,37 +38,171 @@
This is the human-in-the-loop gate—it makes secret access auditable
and revocable.

### 2. Secret retrieval (`on-activate`)
### 2. Secret retrieval (plugin or hook)

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.
Two mechanisms retrieve secrets at shell activation time:

- **Secrets plugins** (flox 1.14.0 or later) — install a plugin package
for your secret store and declare which secrets you need in a
`[plugins.<name>]` table in `manifest.toml`.
The plugin bundles the store's CLI together with the retrieval script,
so one package delivers both.
- **`on-activate` hook** — for stores without a plugin, call the store's
CLI yourself from the `on-activate` hook in `.flox/env/manifest.toml`.

Either way the store validates the active session before returning
values, and the manifest carries only *references* to where secrets
live—never the values themselves.

See [Activating environments](/concepts/activation) 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.
They exist only in the active Flox shell and are gone when the shell
exits.
They are never written to the project directory, the manifest, or git.

## 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
- **Not in shell history** — retrieval runs non-interactively during
activation
- **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

## Secrets plugins

A secrets plugin is an ordinary package that ships the store's CLI plus
a script Flox runs during activation (dev and build modes—in build mode
the sandboxed script skips network lookups, and run-mode activations

Check warning on line 82 in concepts/secrets-management.mdx

View check run for this annotation

Mintlify / Mintlify Validation (flox) - vale-spellcheck

concepts/secrets-management.mdx#L82

Did you really mean 'lookups'?
don't source plugin scripts at all).
The script reads this plugin's `[plugins.<name>]` table from the
manifest and exports one environment variable per string-valued entry.

Plugins exist for 1Password, HashiCorp Vault, OpenBao, and Infisical.

Check warning on line 87 in concepts/secrets-management.mdx

View check run for this annotation

Mintlify / Mintlify Validation (flox) - vale-spellcheck

concepts/secrets-management.mdx#L87

Did you really mean 'Infisical'?
The packages will be published to the `flox` org on FloxHub, making
installation a single command:

```console
$ flox install flox/plugin-vault
```

Until they are published, build from the
[flox-plugins repository](https://github.com/flox/flox-plugins)—run
`flox build` in the plugin's directory and `flox install` the resulting
store path (each plugin's README walks through it; store paths pin the
system they were built on, so build on the machine that will use the
package):

```console
$ flox build vault
$ flox install /nix/store/<hash>-vault-0.1.0
```
Comment on lines +102 to +105

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't done this yet, but the plan is to publish all of the plugins to the flox org on FloxHub, so that users need only run flox install flox/<plugin-package>, no local builds required.

@djsauble djsauble Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8f87035, naming corrected in 901cb04 — the section now leads with the planned FloxHub distribution (flox install flox/plugin-vault; published packages use the plugin- prefix) and keeps build-from-repo as the interim path (with a note that store paths pin the build system).


<Note>
The `[plugins]` manifest section requires flox 1.14.0 or later.
A reference that fails to resolve (missing secret, expired session)
prints a warning and leaves that variable unset—activation still
succeeds, and any error output from the store CLI passes through so
you can act on it.
</Note>

## Implementation examples

<Tabs>
<Tab title="1Password">

Install the `flox/plugin-1password` package, then declare the secrets the
environment needs:

```toml
[plugins.1password]
GH_TOKEN = "op://Personal/GitHub Work Token/credential"
```

References are 1Password secret references (`vault/item/field`, with
or without the `op://` prefix).

Requires an active `op` session: `op signin`, the
[desktop-app integration](https://developer.1password.com/docs/cli/app-integration/)
with biometric approval, or `OP_SERVICE_ACCOUNT_TOKEN` in
non-interactive contexts (CI, servers).

See the [Flox + 1Password blog post](https://flox.dev/popular-packages/adding-1password-secrets-to-flox-environments/)
for a hook-based walkthrough of the same store.
</Tab>
<Tab title="HashiCorp Vault">

Install the `flox/plugin-vault` package, then declare the secrets the
environment needs:

```toml
[plugins.vault]
GH_TOKEN = "secret/github-work#token"
```

Everything before the last `#` is the KV path, mount included; the
part after it is the field to extract.
Both KV v1 and v2 mounts work—`vault kv get` detects the mount
version and inserts the v2 `data/` path segment itself.

Requires `VAULT_ADDR` and a token, exactly as the `vault` CLI
resolves them: `VAULT_TOKEN`, or `~/.vault-token` written by
`vault login` (OIDC, LDAP, token, etc.).
</Tab>
<Tab title="OpenBao">

Install the `flox/plugin-openbao` package, then declare the secrets the
environment needs:

```toml
[plugins.openbao]
GH_TOKEN = "secret/github-work#token"
```

References work exactly as in the Vault plugin: KV path before the
last `#`, field after it, KV v1 and v2 both supported.

Requires `BAO_ADDR` and a token, exactly as the `bao` CLI resolves
them: `BAO_TOKEN`, or the token helper file written by `bao login`.
</Tab>
<Tab title="Infisical">

Check warning on line 174 in concepts/secrets-management.mdx

View check run for this annotation

Mintlify / Mintlify Validation (flox) - vale-spellcheck

concepts/secrets-management.mdx#L174

Did you really mean 'Infisical'?

Install the `flox/plugin-infisical` package, then declare the secrets the
environment needs:

```toml
[plugins.infisical]
DB_PASSWORD = "dev/DB_PASSWORD"
STRIPE_KEY = "prod/backend/payments/STRIPE_KEY"
```

The first segment is the Infisical environment slug (`dev`,
`staging`, `prod`, …), the last is the secret name, and anything
between is the folder path (defaults to `/`).

Select the project by running `infisical init` once in the project
directory, or by setting `INFISICAL_PROJECT_ID` (a plugin
convention, forwarded to the CLI as `--projectId`; required with
machine-identity auth).
Authenticate interactively with `infisical login`, or set
`INFISICAL_TOKEN` in non-interactive contexts.
Self-hosted deployments also set `INFISICAL_API_URL`.

The Infisical CLI prints nothing for a missing secret, so the
plugin's own warning is what you'll see; a secret whose value is
genuinely empty is indistinguishable from a missing one and is also
left unset.
</Tab>
<Tab title="macOS Keychain">

No plugin yet—use an `on-activate` hook.

Store the token once:

```bash
Expand Down Expand Up @@ -96,35 +230,10 @@
versions. No Xcode or Homebrew required.
</Note>
</Tab>
<Tab title="1Password">

```toml
[hook]
on-activate = '''
export GH_TOKEN=$(op read "op://Personal/GitHub Work Token/credential")
'''
```

Requires `op` CLI and an active `op signin` session.
`flox install _1password` to add the CLI to your environment.

See the [Flox + 1Password blog post](https://flox.dev/popular-packages/adding-1password-secrets-to-flox-environments/)
for a more detailed walkthrough including fewer interactive logins.
</Tab>
<Tab title="HashiCorp Vault">

```toml
[hook]
on-activate = '''
export GH_TOKEN=$(vault kv get -field=token secret/github-work)
'''
```

Requires the `vault` CLI and an active `vault login` session.
`flox install vault` to add the CLI to your environment.
</Tab>
<Tab title="AWS Secrets Manager">

No plugin yet—use an `on-activate` hook.

```toml
[hook]
on-activate = '''
Expand All @@ -140,8 +249,13 @@
</Tab>
<Tab title="Cross-platform (macOS + Linux)">

Cross-platform secret stores like 1Password, HashiCorp Vault, and AWS
Secrets Manager work the same on macOS and Linux—no conditional needed.
Cross-platform secret stores work the same on macOS and Linux—no
conditional needed.
That includes every store with a secrets plugin (1Password, Vault,
OpenBao, Infisical) as well as hook-based stores like AWS Secrets
Manager.
The plugin packages follow their store CLI's system availability—

Check warning on line 257 in concepts/secrets-management.mdx

View check run for this annotation

Mintlify / Mintlify Validation (flox) - vale-spellcheck

concepts/secrets-management.mdx#L257

Did you really mean 'CLI's'?
`flox show` lists the systems a package supports.

A conditional is only necessary when different tools are used on each
platform, such as macOS Keychain on macOS and `pass` on Linux:
Expand Down Expand Up @@ -177,7 +291,9 @@
<Warning>
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`.
The new value takes effect on the next fresh `flox activate`
(re-attaching to a still-cached activation replays the previously
fetched values).
If rotation is in response to a credential compromise, kill active
shells explicitly.
</Warning>
Expand All @@ -186,18 +302,22 @@

## Secret store reference

| Store | Primary auth | Retrieval command |
| --- | --- | --- |
| macOS Keychain | Login session / biometric | `security find-generic-password -a "$USER" -s "name" -w` |
| Linux keyring | Desktop session | `secret-tool lookup service name account user` |
| 1Password | `op signin` / biometric | `op read op://vault/item/field` |
| HashiCorp Vault | `vault login` (OIDC/LDAP/token) | `vault kv get -field=value secret/path` |
| AWS Secrets Manager | `aws sso login` | `aws secretsmanager get-secret-value --secret-id name --query SecretString --output text` |
| Doppler | `doppler login` | `doppler secrets get NAME --plain` |
| Pass | GPG key unlock | `pass show service/credential` |
| Store | Flox plugin | Primary auth | Retrieval command |
| --- | --- | --- | --- |
| macOS Keychain | — | Login session / biometric | `security find-generic-password -a "$USER" -s "name" -w` |
| Linux keyring | — | Desktop session | `secret-tool lookup service name account user` |
| 1Password | `flox/plugin-1password` | `op signin` / biometric | `op read op://vault/item/field` |
| HashiCorp Vault | `flox/plugin-vault` | `vault login` (OIDC/LDAP/token) | `vault kv get -field=value secret/path` |
| OpenBao | `flox/plugin-openbao` | `bao login` (OIDC/LDAP/token) | `bao kv get -field=value secret/path` |
| Infisical | `flox/plugin-infisical` | `infisical login` | `infisical secrets get NAME --env=dev --plain` |
| AWS Secrets Manager | — | `aws sso login` | `aws secretsmanager get-secret-value --secret-id name --query SecretString --output text` |
| Doppler | — | `doppler login` | `doppler secrets get NAME --plain` |
| Pass | — | GPG key unlock | `pass show service/credential` |

## Further reading

- [flox-plugins repository](https://github.com/flox/flox-plugins) —
the secrets plugin packages and per-plugin READMEs

Check warning on line 320 in concepts/secrets-management.mdx

View check run for this annotation

Mintlify / Mintlify Validation (flox) - vale-spellcheck

concepts/secrets-management.mdx#L320

Did you really mean 'READMEs'?
- [Cross-platform secrets blog post](https://flox.dev/blog/get-your-preferred-secrets-manager-in-a-portable-cross-platform-cli-toolkit/)
- [Flox and AWS secrets management](https://flox.dev/blog/flox-and-aws-taking-the-chaos-out-of-secrets-management/)
- [Flox + 1Password](https://flox.dev/popular-packages/adding-1password-secrets-to-flox-environments/)
Expand Down