Verified by the sovseal team

Key Management & Custody

How the master key is generated, held in the OS keychain, and split into purpose-bound subkeys via HKDF.

sovseal uses no external KMS or cloud HSM. Custody is local-first: a single master key lives in the operating-system keychain, and every working key is derived from it on demand. The server never receives any of them.

Changed in 0.3.5

Earlier builds stored the raw 256-bit key as encryption_key_b64 inside ~/.sovseal/config.json (mode 0600). As of 0.3.5, custody moved to the OS keychain; on first run after upgrade the old field is migrated into the keychain and tombstoned in the file. If you have automation that reads encryption_key_b64, update it.


Master key custody (OS keychain)

The master key is held by the platform credential store via @napi-rs/keyring:

  • macOS — Keychain
  • Windows — Credential Manager
  • Linux — Secret Service / libsecret

It is generated once on first run with a CSPRNG and written to the keychain — never to a plaintext file:

const master = crypto.getRandomValues(new Uint8Array(32)); // 256-bit master
await keychain.setPassword("sovseal", "master", base64(master));

Opt-in file fallback

On headless machines without a keychain you may set SOVSEAL_KEY_FALLBACK=file, which stores the master at ~/.sovseal/ with 0600 permissions. If the keychain is unavailable and this flag is not set, sovseal fails closed rather than silently downgrading custody.


Subkey derivation (HKDF-SHA256)

Working keys are never used raw. The master is split by purpose using HKDF-SHA256 with domain-separation labels, so a compromise of one context cannot be replayed against another:

k_rest = HKDF-SHA256(master, info = "sovseal/at-rest/v1")   // local field encryption
k_sync = HKDF-SHA256(master, info = "sovseal/sync/v1")      // replication ciphertext
  • k_rest encrypts memory text at rest in the local LanceDB store.
  • k_sync seals the ciphertext that replicates to the server.

Both are imported as non-extractable AES-GCM CryptoKeys — they exist only inside the running process and cannot be exported back out.

Changing a label is a key-version bump

The HKDF info labels are versioned (/v1). Changing a label changes the derived key and renders existing ciphertext unreadable. Treat any label edit as a migration, not a refactor.


What the local config file holds now

~/.sovseal/config.json (0600) holds identity and routing only — no key material:

{
  "schema_version": 1,
  "project_id": "8435d886-f288-466c-8ee1-eb836e2b6912",
  "api_key": "sov_proj_8435d886-f288-466c-8ee1-eb836e2b6912",
  "endpoint": "https://<your-project>.supabase.co/functions/v1/v2-agent-state",
  "created_at": "2026-06-09T16:41:23.000Z"
}

After migration the legacy encryption_key_b64 is replaced with a tombstone marker; the live key is in the keychain.


Loss & recovery

There is no escrow and no reset. If you lose the keychain master key (or, under the file fallback, the fallback key file), every snapshot — local and replicated — becomes permanently undecryptable. Zero-knowledge means the server cannot help. Back up the master key material if continuity matters.


On this page