Releasing soon Vigo is in alpha and closing in on its first stable release. Expect breaking changes between releases until then — we're looking for testing partners with meaningful fleets across diverse architectures. Learn more →

vigocli secrets

Manage the secrets vault — reversibly-encrypted-at-rest values under the master-key AES-256-GCM envelope, gated by an operator-side unlock passphrase.

The vault model

  • Storage: Every secret is a single AES-256-GCM ciphertext file under --secrets-dir (default /srv/vigo/secrets/). The wrapped value is the verbatim plaintext — no second hashing layer. Consumers (the agent's user: executor for /etc/shadow, the server's basic-auth provider for web logins) hash or compare at point of use.
  • Master key: <secrets-dir>/.master.key, mode 0600, root-owned. Generated by init. vigosrv reads this autonomously at startup to validate secret: references during config publish — the unlock gate is independent of the cryptographic envelope.
  • Unlock gate: Every gated vigocli secrets * verb refuses unless an unlock sentinel is present at /srv/vigo/.secrets-unlocked (24h sliding window). The gate is an operator-hygiene boundary — it deters accidents, not a root attacker. Anyone with read access to .master.key plus the encrypted blobs can recover plaintext without ever touching the gate.

How secrets flow to envoys

Resolved secret values never cross the agent gRPC wire as plaintext.

  1. At config load (publish or reload), the loader walks every secret:<key> reference in configcrates, resource attributes, vars, and network-device credentials. Each ref is validated against the configured secrets.Provider (missing secrets fail the load, same as before) but the resolved plaintext is never stored on the in-memory config — the loader substitutes a placeholder marker __VIGO_SECRET__<key>__END_VIGO_SECRET__ in its place.
  2. The PolicyBundle that the server returns on every CheckIn carries these placeholders, not values. The bundle also carries a secrets_epoch counter that bumps on every successful reload and on watcher-detected secret rotation.
  3. The agent receives the bundle, walks vars + configcrate attributes + when: strings for placeholder markers, batches every distinct ref into a single VigoAgent.ResolveSecrets RPC, and substitutes the resolved values in place before passing attributes to executors. Substitution failure aborts the apply with a clear error rather than letting a placeholder string land on disk.
  4. Server-side, ResolveSecrets enforces per-envoy entitlement: an envoy can only resolve refs whose placeholder appears in its own resolved bundle. Un-entitled requests fail with PermissionDenied and emit a secret.entitlement_denied audit event. Successful resolutions emit secret.epoch_resolved. Audit log records refs only, never values.
  5. The agent caches resolved values in memory for the lifetime of the process; the cache invalidates whenever secrets_epoch advances. A persistent at-rest cache (encrypted under a key derived from the agent's enrollment ed25519 identity) is a follow-up; until it lands, a cold agent restart with the server unreachable cannot apply secret-bearing resources — convergence fails loud and resumes once contact returns.

Operators don't need to change anything in their configcrates: secret:<path> keeps working the same way it always has. The placeholder pipeline is internal to vigosrv and the agent.

Subcommands

Subcommand Description
init Bootstrap — generate the master key and prompt for the initial unlock passphrase
generate-certs Generate self-signed CA and server TLS certificates
unlock Open the vault for the current operator session (24h sliding TTL)
lock Close the vault — gated verbs refuse until next unlock
rotate Replace the unlock passphrase — requires the current one
reset Break-glass: replace the passphrase by proving you can read .master.key
set raw <key> Store an arbitrary value verbatim (API keys, DSNs, certs)
set password <key> Store a human-typed credential with complexity validation
reveal <key> Decrypt and print a stored value
delete <key> Remove a stored value
list List all stored keys

init, generate-certs, and the lifecycle verbs (unlock/lock/rotate/reset) work pre-unlock. Everything else requires the vault to be unlocked.

Persistent Flags

Flag Default Description
--key-file (auto from --secrets-dir) Path to master encryption key
--secrets-dir /srv/vigo/secrets Directory for secret files

init

Bootstrap the vault. Generates a 256-bit master key, prompts for the initial unlock passphrase, writes the Argon2id verifier. Idempotent on upgrade: if the master key already exists but the verifier doesn't, only the verifier is written — existing data is preserved.

vigocli secrets init --key-file /srv/vigo/secrets/.master.key
  Master key written to /srv/vigo/secrets/.master.key (mode 0600).
  Keep this file safe — losing it means losing access to all encrypted secrets.

  Set the unlock passphrase for this vault.
  This passphrase gates every `vigocli secrets` command except init/generate-certs/unlock/lock/rotate/reset.
Enter unlock passphrase:
Confirm passphrase:
  Unlock verifier written to /srv/vigo/secrets-verifier.json.
  Run `vigocli secrets unlock` to open the vault.

Refuses to run only when both the master key AND the verifier already exist — use rotate to change the passphrase in that case.

unlock

Open the vault for the operator session. Prompts for the unlock passphrase; on match, writes a 24h-from-now expiry sentinel. Every successful gated call after this slides the expiry forward by another 24h.

vigocli secrets unlock
Enter unlock passphrase:
  Vault unlocked. Idle timeout: 24h0m0s.

lock

Close the vault immediately by removing the sentinel. Gated verbs refuse until the next unlock.

vigocli secrets lock

rotate

Replace the unlock passphrase. Prompts for the current passphrase, verifies, then prompts for a new one (with confirmation). Clears any existing sentinel — every operator must re-unlock with the new passphrase before gated verbs work again.

vigocli secrets rotate

reset

Break-glass: replace the unlock passphrase without knowing the current one. Requires read access to the master-key file — the same access level needed to decrypt secrets directly. Typed RESET confirmation prevents accidents.

vigocli secrets reset
  This will replace the unlock passphrase without verifying the old one.
  Type RESET to proceed: RESET
Enter new passphrase:
Confirm passphrase:
  Passphrase reset. Run `vigocli secrets unlock` with the new passphrase.

set raw

Store an arbitrary value verbatim under the master-key envelope. No complexity validation. Accepts the value via --from-file, stdin pipe, or interactive prompt.

vigocli secrets set raw <key> [--from-file <path>]

Examples

Interactive (prompt with input hidden):

vigocli secrets set raw vigo/db/password

From a file:

vigocli secrets set raw vigo/db/dsn --from-file /tmp/dsn.txt

From stdin:

printf 's3cret' | vigocli secrets set raw vigo/api/key

After every set, the server is notified so envoys with matching watch_secret resources converge on the new value at their next check-in.

set password

Store a human-typed credential. Interactive prompt only, with HIPAA-grade complexity validation (12+ characters, uppercase, lowercase, digit, special). The plaintext is stored verbatim under the master-key envelope; the agent's user: executor and the server's web-auth provider apply their own hashing or constant-time compare at point of use.

vigocli secrets set password <key>

Set an OS user password (consumed by the user: executor on the envoy)

vigocli secrets set password vigo/os-users/dan

Then reference it in a user: resource:

resources:
  - name: dan
    type: user
    username: dan
    password: "secret:vigo/os-users/dan"

The agent computes $6$<salt>$<hash> from the plaintext at apply time and feeds it to usermod -p / useradd -p.

Set a Vigo web UI password (consumed by the basic auth provider)

vigocli secrets set password vigo/web/auth/dan

The key must follow the pattern vigo/web/auth/<username> to match the account created with vigocli webusers create. (Admin webusers use real human OS usernames per auth setup; admin is not auto-seeded as a default.) The server's basic-auth provider resolves the stored plaintext via the secrets Provider on every login attempt and subtle.ConstantTimeCompares against the submitted password.

reveal

Decrypt and print a stored value. Each reveal is recorded in the tamper-evident audit trail as a secret_accessed event.

vigocli secrets reveal vigo/db/dsn
postgres://vigo:secret@localhost:5432/vigo?sslmode=disable

delete

Remove a stored value.

vigocli secrets delete vigo/test/key

list

List all stored keys (values are not shown).

vigocli secrets list

generate-certs

Generate a self-signed CA keypair and server TLS certificate as plain PEM files. Uses ECDSA P-256 keys.

vigocli secrets generate-certs [flags]

Flags

Flag Default Description
--tls-dir /srv/vigo/tls Directory for TLS certificate files
--san Additional SANs (DNS names or IPs), repeatable
--ca-days 3650 CA certificate validity in days
--server-days 365 Server certificate validity in days
--force false Overwrite existing certificates

Generated files

File Description
tls/ca CA certificate (PEM)
tls/ca_key CA private key (PEM)
tls/cert Server certificate (PEM)
tls/key Server private key (PEM)

The server certificate automatically includes SANs for localhost, 127.0.0.1, and the system hostname.

vigocli secrets generate-certs --san vigo.example.com --san 10.0.1.5

Related

  • webusers — manage web UI user accounts
  • doctor — verify secret resolution with health checks

Confidential — Alexander4, LLC. Not for redistribution. See license.