KMS configuration is regular Elixir application config plus runtime environment variables.

Core settings

SettingDefaultPurpose
config :kms, :clientKMS.Client.LocalClient backend used by the KMS facade.
config :kms, :components:allOTP children to start. Use a subset of :repo, :cache, :sessions, :fallbacks, and :api for narrow embedded use.
config :kms, KMS.Repo, repo:KMS.Repo.SQLiteActive KMS repo module.
config :kms, :rmk_providerKMS.RMK.LocalRMK provider for persisted KMS secret material.
config :kms, :rmk_readiness_timeout_ms5000Maximum duration of startup and health RMK checks.
config :kms, :session_hash_keydev/test fallback onlyHMAC key for session bearer verifiers. Required in prod.
config :kms, :require_crash_dump_disabledtrue in prodFail prod startup unless crash dumps are disabled.

Database variables

Env varDefaultPurpose
KMS_DATABASE_BACKENDsqlitesqlite, sqlite3, postgres, or postgresql.
KMS_DATA_DIRdev: tmp/kms; prod: user data dirDirectory for default SQLite DB path.
KMS_DATABASE_PATHderived from KMS_DATA_DIRExact SQLite DB path.
KMS_DATABASE_POOL_SIZE10; SQLite test 1Repo pool size.
KMS_DATABASE_URLunsetPostgreSQL URL.
KMS_DATABASE_HOSTlocalhostPostgreSQL host when URL unset.
KMS_DATABASE_NAMEkms_<env>PostgreSQL DB when URL unset.
KMS_DATABASE_USERpostgresPostgreSQL user.
KMS_DATABASE_PASSWORDpostgresPostgreSQL password.

See Database configuration.

SQLite repo config

config :kms, KMS.Repo.SQLite,
  database: "/var/lib/kms/kms.sqlite3",
  journal_mode: :wal,
  foreign_keys: :on,
  busy_timeout: 5_000,
  pool_size: 10,
  priv: "priv/repo",
  migration_primary_key: false,
  migration_foreign_key: [type: :text],
  migration_timestamps: [type: :utc_datetime_usec]

PostgreSQL repo config

config :kms, KMS.Repo.Postgres,
  url: System.fetch_env!("KMS_DATABASE_URL"),
  pool_size: 10,
  priv: "priv/repo",
  migration_primary_key: false,
  migration_foreign_key: [type: :text],
  migration_timestamps: [type: :timestamptz]

RMK provider config

Default local provider:

config :kms, :rmk_provider, KMS.RMK.Local
config :kms, KMS.RMK.Local, key_path: System.fetch_env!("KMS_RMK_LOCAL_KEY_PATH")

External providers:

config :kms, :rmk_provider, KMS.RMK.StackIT
# config :kms, :rmk_provider, KMS.RMK.AWS
# config :kms, :rmk_provider, KMS.RMK.GCP

config :kms, :rmk_readiness_timeout_ms, 5_000

KMS checks the configured provider during application startup and fails startup if it cannot access the RMK. The same bounded check runs for readiness probes.

See provider docs:

Remote client config

config :kms, :client, KMS.Client.Remote

config :kms, KMS.Client.Remote,
  base_url: "https://kms.internal.example.com",
  api_token: System.fetch_env!("KMS_REMOTE_API_TOKEN")

Per-call overrides are also supported by the remote client:

KMS.encrypt_alias("secret", "alias", base_url: url, api_token: token)

HTTP server config

config :kms, :api,
  server: true,
  port: 4004,
  scheme: :http,
  api_token: System.fetch_env!("KMS_API_TOKEN")

All non-health routes require Authorization: Bearer <token>.

Sessions

config :kms, :session_default_ttl_ms, :timer.hours(1)
config :kms, :session_max_ttl_ms, :timer.hours(1)
config :kms, :session_idle_timeout_ms, :timer.hours(1)
config :kms, :session_max_lifetime_ms, :timer.hours(12)
config :kms, :session_cleanup_interval_ms, :timer.minutes(5)

Login rate limit:

config :kms, :session_login_rate_limit,
  principal: [limit: 5, window_ms: :timer.minutes(5)],
  source: [limit: 50, window_ms: :timer.minutes(5)]

Factor limits:

config :kms, :session_factor_attempt_limit, 5
config :kms, :session_factor_rate_limit, [limit: 5, window_ms: :timer.minutes(5)]

See Sessions.

WebAuthn

config :kms, :webauthn,
  rp_id: "example.com",
  rp_name: "My App",
  origins: ["https://example.com"],
  attestation: "none",
  user_verification: "preferred",
  challenge_ttl_ms: :timer.minutes(5)

See WebAuthn.

Cache

config :kms, :cache_ttl_ms, :timer.minutes(5)
config :kms, :rmk_cache_ttl_ms, :timer.minutes(5)
config :kms, :signing_key_cache_ttl_ms, :infinity

Signing keys default to :infinity so hot signing does not repeatedly invoke an external RMK; override :signing_key_cache_ttl_ms to accept that latency and provider dependency in exchange for shorter cache residency. Infinite entries remain until explicit eviction or cache-process restart and increase process-memory exposure.

Per-call RMK cache override:

KMS.decrypt_alias(ciphertext, "alias", rmk_cache_ttl_ms: :timer.seconds(30))

Fallback bootstrap

Fallback bootstrap and every fallback operation are local-only. The HTTP router does not compile fallback routes; use trusted in-process code or a controlled remote shell on the KMS host.

config :kms, :fallbacks_bootstrap, true
config :kms, :fallback_public_keys, [
  %{name: "ops-2026", curve: "X25519", public_key: "..."}
]

See Fallback recovery.