Skip to content

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.

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:

Terminal window
export KOVRA_PASSPHRASE="$CI_VAULT_PASSPHRASE" # from your CI secret store
kovra init

Keep 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.

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:

Terminal window
export KOVRA_CONFIRMER=file # never attempt a biometric prompt

This has a deliberate consequence: high/prod actions can’t be fully automated — they still require a human, by design. So in CI:

  • low/medium secrets inject with no prompt — the everyday CI path.
  • high/prod injection still needs a human at the file broker — there’s no unattended bypass for kovra run. (The out-of-band token is a different thing: it authorizes unattended consumption of a sealed package, not a run injection.)

The Web UI’s launch gate can be skipped for dev/CI/containers with --no-confirm (or KOVRA_UI_NO_CONFIRM=1).

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 test

kovra 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.