Headless & CI
kovra’s defaults assume a human at a Mac: the master key in the Keychain, and sensitive actions gated behind a bioProve. Unattended environments — CI runners, containers — have neither. kovra runs there too, in passphrase mode.
Passphrase mode (no keyring)
Section titled “Passphrase mode (no keyring)”Set KOVRA_PASSPHRASE and kovra stops using the OS keyring entirely: it derives
the 256-bit master key with Argon2id from your passphrase plus a stable,
non-secret salt stored next to the vault (kdf.salt). Initialize the vault once
to create the salt, then every later run derives the same key:
export KOVRA_PASSPHRASE="$CI_VAULT_PASSPHRASE" # from your CI secret storekovra initKeep the passphrase in your CI provider’s secret store — it is the only thing that unlocks the vault, so treat it like the master key it derives.
Confirmations without biometrics
Section titled “Confirmations without biometrics”A bioProve needs a person. On a host with no biometric hardware, kovra falls back to the file broker: a gated command waits and prints instructions, and a second session approves it. Force the channel explicitly in scripts:
export KOVRA_CONFIRMER=file # never attempt a biometric promptThis has a deliberate consequence: high/prod actions can’t be fully
automated — they still require a human, by design. So in CI:
low/mediumsecrets inject with no prompt — the everyday CI path.high/prodinjection still needs a human at the file broker — there’s no unattended bypass forkovra run. (The out-of-band token is a different thing: it authorizes unattended consumption of a sealed package, not aruninjection.)
The Web UI’s launch gate can be skipped for dev/CI/containers with --no-confirm
(or KOVRA_UI_NO_CONFIRM=1).
A GitHub Actions sketch
Section titled “A GitHub Actions sketch”jobs: test: runs-on: [self-hosted, macos] env: KOVRA_PASSPHRASE: ${ secrets.KOVRA_VAULT_PASSPHRASE } KOVRA_CONFIRMER: file steps: - uses: actions/checkout@v4 - name: Run tests with injected secrets run: kovra run --env dev -- npm testkovra run --env dev resolves the committed .env.refs and injects the dev
(non-high) values straight into npm test — nothing written to disk, argv, or
the log.