Skip to content

Infrastructure prerequisites for Keeper

Keeper doesn’t package its external dependencies — it consumes them. Before the first start you stand up and configure the three required components yourself: PostgreSQL, Redis and Vault. The full stack is required: Keeper checks each on startup and won’t start if any one is unreachable or, in Vault’s case, sealed.

This page is requirements-first: first what’s mandatory, then tuning and scaling. The config field contract isn’t duplicated here — for that, go to keeper.yml. A minimal end-to-end demo scenario for standing up the infrastructure is in Quick Start → Step 1; production Vault setup (AppRole, persistent backend, auto-unseal) lives in the core docs, links at the end.

The cluster’s only cold store: registries of agents and operators, the service catalog, incarnation states, audit logs. Everything that survives a restart lives here.

RequirementValue
Version14+ (16 recommended — a longer LTS window). The real code floor is 13 (the built-in gen_random_uuid() is used).
Extensionsnone. Neither uuid-ossp nor pgcrypto — UUID generation is built into PostgreSQL 13+.
Driverpgx/v5 (inside Keeper, nothing to install).
Configthe postgres block with dsn_ref is required.

The DSN is passed through a Vault reference: the production convention is dsn_ref: vault:secret/keeper/postgres (Keeper reads the dsn field from KV). The parser technically accepts a plaintext DSN in keeper.yml, but the recommendation is always to go through Vault so the secret doesn’t sit in the config.

  • Connection poolpostgres.pool.min / postgres.pool.max (pgx defaults to max=4; set min ≥ 1, max ≥ min).

  • PostgreSQL max_connections — plan it from the total load: pool.max × number of Keeper instances, with ×1.5 headroom for backup tools and third-party clients.

    Example: 3 instances × pool.max: 50 = 150 connections → max_connections ≥ 225. The PostgreSQL default (100) is too small for this layout.

  • Channel encryptionsslmode=verify-full in production (a server cert from your internal PKI), sslmode=disable is acceptable only in dev.

The pool fields and their relationship to the execution worker pool (acolytes) are in keeper.yml → postgres and Sizing acolytes.

Keeper works with a single logical PostgreSQL over a single DSN.

The strategy is PostgreSQL vertical (more CPU/RAM/disk) + Keeper horizontal (stateless instances behind a load balancer) + infra HA (Patroni/managed). Sharding PG as a way to scale Keeper is not available (see above).

With many souls the audit_log table is the first to hit its limit — by INSERT rate and by volume (the target reference is the “~100k VM” invariant). Mitigations (partitioning by date / hot-cold tiering / write batching) are in the backlog, not in the current release.

Disk sizing (ballpark):

Number of soulsDB size
up to 10k VM20–50 GB
~100k VM100–200 GB (with headroom for a year of audit_log)

Operational details — backup/restore, retention, housekeeping — are in the core docs infra.md → Postgres.

The hot layer and the cluster’s coordination bus. It stores only ephemeral data: the SID lease (which instance holds an agent’s stream), Keeper-instance presence (Conclave), the leader lease for background tasks (Reaper), pub/sub channels (summons / applybus / invalidate / cancel).

RequirementValue
Version6.2+ (7.x recommended — improved ACL).
Configthe redis block with fields for the chosen mode (addr for standalone; master_name + sentinels[] for sentinel; nodes[] for cluster).
Backupnot needed. All keys are naturally recoverable: a lease is recreated on an agent’s reconnect, presence on instance startup, leadership by re-election on TTL, and pub/sub is ephemeral.

The Redis password is set in redis.password_ref and supports both forms:

  • vault-ref vault:secret/keeper/redis[#field] — Keeper resolves it from Vault on startup, the same way as the PG DSN and the JWT signing key. The default field of the KV secret is password; a different field is selected with the #field suffix (for example vault:secret/keeper/redis#sentinel for a separate password for the sentinel nodes). This is the recommended production form.
  • plaintext string — the password directly in keeper.yml (dev / tests without Vault).
  • empty — Redis with no password (dev only).

If the field in KV is missing or empty, Keeper fails fast on startup with a clear error (password field missing or empty).

There are no separate TLS fields for Redis in keeper.yml. Only what the go-redis client derives from the address itself works (the rediss:// scheme and the system trust store). Terminate TLS on the Redis / proxy side if you need an encrypted channel.

The Keeper client supports three topologies natively — selected by the redis.mode field (slot routing for cluster and master discovery for sentinel are handled by the client itself, with no external proxy). An empty/omitted mode = standalone (forward-compat for older configs).

modeRequired fieldsWhen
standalone (default)addr (host:port)dev / staging, a single node.
sentinelmaster_name + sentinels[] (addresses of the sentinel nodes)The recommended HA path for a typical on-premise setup. Single-master with automatic failover — simpler and safer than cluster. Optional sentinel_password_ref — a separate password for the sentinel nodes themselves.
clusternodes[] (node addresses for bootstrap discovery)Horizontal scaling for a large key volume. See the pub/sub caveat below.

The full field contract for the redis block is in keeper.yml → redis.

maxmemory 1gb
maxmemory-policy noeviction
  • maxmemory — for the target of 100k VM ~1gb is enough: the total size of all Soul Stack keys is on the order of 10 MB (soul:<sid>:lock × 100k ≈ 10 MB, presence is a few KB, pub/sub isn’t kept in memory). 1gb leaves a large margin.
  • maxmemory-policy noevictionstrictly noeviction, not allkeys-lru. Every key has its own TTL and a meaningful fallback in code; evicting a lease mid-life via LRU would cause split-brain (a double handler for one SID, two Reaper leaders). noeviction plus a memory margin prevents that.

By data volume, standalone Redis comfortably handles the target 100k VM — moving to an HA topology is dictated by Redis fault-tolerance requirements, not by data size.

  • For HA in a typical on-premise setup, choose sentinel (mode: sentinel): automatic failover, single-master, no sharding. This is the recommended path.
  • Choose cluster (mode: cluster) deliberately — when the key volume has outgrown a single master and you need horizontal sharding. Keep in mind the caveat about cluster pub/sub broadcast (see modes).

Both topologies are supported by the client natively — no external cluster-aware proxy is required.

Operational details (what’s stored in Redis by key, the restore procedure) are in the core docs infra.md → Redis.

The store for all installation secrets and the PKI for agents’ mTLS identity. Mandatory and hard-required: a sealed Vault → Keeper runs in fail-closed mode, and there’s no “run without Vault” flag, nor is one planned.

EngineRequiredWhy
KV (v1 or v2)requiredKeeper secrets (DSN, JWT key, …) and services’ essence secrets. The version is detected automatically; v2 is recommended (versioning + metadata; Sigil multi-anchor requires v2).
PKI (mount + role)requiredSigning CSRs during agent onboarding — without PKI a new agent won’t get its mTLS identity (SoulSeed).
SSHoptionalOnly for push mode keeper.push via the Vault SSH provider.

The Vault version in dev/CI is 1.18.

ParameterRequiredNote
vault.addrrequiredEmpty → fail-fast on startup.
vault.auth.methodoptionaltoken (default) or approle.
vault.kv_mountoptionalDefault secret.
vault.kv_versionoptionalEmpty = auto-probe; set explicitly only if ACL blocks the probe (see below).
vault.pki_mount / vault.pki_roleneeded for agent onboardingCSRs are signed through them.

The full field contract is in keeper.yml → vault.

In production Keeper authenticates to Vault via AppRole (not a static token):

vault:
addr: "https://vault.internal:8200"
auth:
method: approle
role_id: keeper-prod # NOT a secret — inline is fine
secret_id_file: /etc/keeper/vault-secret-id # mode 0400, absolute path
pki_mount: "pki/soulstack"
pki_role: "soul-seed"
  • role_idnot a secret, the role identifier, placed inline in keeper.yml.
  • secret_id — a secret; set by exactly one of: secret_id_file (a mode 0400 file, absolute path) or secret_id_env (the name of an env variable).

In dev a static root token (token: "root") with Vault in dev mode is acceptable. dev mode is unusable for production — it keeps secrets in RAM and loses them on every restart (losing the JWT key = invalidation of all operator tokens, loss of the PKI root).

The reference is examples/keeper/vault-policy.hcl in the core repository. Exactly 4 paths, each granting the minimum:

PathCapabilitiesWhy
secret/data/keeper/*readReading Keeper’s KV secrets. Read only — Keeper doesn’t write them. For KV v1 the path has no /data/: secret/keeper/*.
pki/issue/<pki_role>updateSigning a CSR during agent onboarding (the issue endpoint needs only POST).
secret/metadata/keeper/sigil-keys/*list, readReaper orphan-reconcile of Sigil keys — report-only, names + metadata only. KV v2 only.
auth/token/renew-selfupdateRenewing Keeper’s own client token.

Additionally:

  • push via Vault SSH — add a path for SSH-CA signing (ssh/sign/<role>).
  • the KV version probe requires access to sys/internal/ui/mounts/<mount>. If ACL blocks it, set kv_version in the config explicitly, and the probe won’t run.

What to seed into KV before starting Keeper

Section titled “What to seed into KV before starting Keeper”
PathFieldPurposeRequired
secret/keeper/jwt-signing-keysigning_keyHS256 key for signing operator JWTsalways mandatory (without it there’s no Archon auth)
secret/keeper/postgresdsnPostgreSQL DSNrequired if dsn_ref = vault-ref
secret/keeper/sigil-signing-key, secret/keeper/sigil-keys/<key_id>Sigil signingoptional (without it — fail-closed degradation)
secret/keeper/providers/*cloud-driver credentialsoptional (if cloud is used)
secret/keeper/toll-webhook-urlurlWebhook notification URLoptional
secret/keeper/metrics-passwordpasswordBasic auth on /metricsoptional
secret/keeper/ssh-host-caSSH host CA for pushoptional
secret/keeper/* (essence secrets)resolving ${ vault(...) } in CELoptional

PKI (not a KV secret, but an engine):

Terminal window
vault secrets enable -path=pki/soulstack pki
vault secrets tune -max-lease-ttl=87600h pki/soulstack
vault write -field=certificate pki/soulstack/root/generate/internal \
common_name="Soul Stack SoulSeed CA" ttl=87600h > /tmp/ca.crt
vault write pki/soulstack/roles/soul-seed \
allowed_domains="example.com,internal" \
allow_subdomains=true \
max_ttl=720h

Mount (pki/ or pki/soulstack/) + root cert + role (soul-seed) with allowed_domains / allow_subdomains / max_ttl. The path names in the policy and in keeper.yml::vault.pki_mount / pki_role must match.

Aspectdevprod
Authtoken: "root" (dev mode)AppRole + secret_id from a file/env
Backendin-memory (lost on restart)persistent (raft / consul)
Unsealautomatic (dev mode unseals itself)auto-unseal via KMS/HSM
TransportHTTPHTTPS

A sealed Vault → Keeper fail-closed (it resolves nothing, a new agent won’t onboard). Detailed production Vault setup is in the core docs prod-setup.md.

A summary of the key differences across all three components:

Componentdevprod
PostgreSQL sslmodedisableverify-full
PostgreSQL topologysingle instancePatroni / managed + HA (transparent to Keeper)
Redis passwordpassword_ref: "" (no password)password_ref: vault:secret/keeper/redis (resolved from Vault)
Redis topologymode: standalone (single instance)mode: sentinel (recommended HA) or mode: cluster — native, no proxy
Vault authtoken: "root"AppRole (secret_id from a file/env)
Vault backenddev mode, in-memorypersistent (raft/consul) + auto-unseal
Vault transportHTTPHTTPS (cert via the system trust store)

For details of production Vault setup (AppRole, persistent backend, auto-unseal, JWT signing-key rotation), see docs/keeper/prod-setup.md in the core repository.

  • Quick Start → Step 1 — stand up the infrastructure in the demo scenario.
  • keeper.yml — the normative config field contract (the postgres / redis / vault / auth blocks).
  • Installation — ways to ship the binaries; external infrastructure is on you.
  • Operations — scaling, monitoring, backup/restore, upgrades.
  • The core repository (not on this site): docs/operations/infra.md — the operational part of the infrastructure (backup/restore, retention); docs/keeper/prod-setup.md — production Vault (AppRole, persistent, auto-unseal); examples/keeper/vault-policy.hcl — the reference least-privilege policy.