Packages

Key management service with embedded OTP runtime, optional HTTP API, SQLite/PostgreSQL storage, and external RMK providers

Current section

Files

Jump to
kms docs configuration.md
Raw

docs/configuration.md

# Configuration

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

## Core settings

| Setting | Default | Purpose |
|---|---|---|
| `config :kms, :client` | `KMS.Client.Local` | Client backend used by the `KMS` facade. |
| `config :kms, :components` | `:all` | OTP children to start. Use a subset of `:repo`, `:cache`, `:sessions`, `:fallbacks`, and `:api` for narrow embedded use. |
| `config :kms, KMS.Repo, repo:` | `KMS.Repo.SQLite` | Active KMS repo module. |
| `config :kms, :rmk_provider` | `KMS.RMK.Local` | RMK provider for persisted KMS secret material. |
| `config :kms, :rmk_readiness_timeout_ms` | `5000` | Maximum duration of startup and health RMK checks. |
| `config :kms, :session_hash_key` | dev/test fallback only | HMAC key for session bearer verifiers. Required in prod. |
| `config :kms, :require_crash_dump_disabled` | `true` in prod | Fail prod startup unless crash dumps are disabled. |

## Database variables

| Env var | Default | Purpose |
|---|---|---|
| `KMS_DATABASE_BACKEND` | `sqlite` | `sqlite`, `sqlite3`, `postgres`, or `postgresql`. |
| `KMS_DATA_DIR` | dev: `tmp/kms`; prod: user data dir | Directory for default SQLite DB path. |
| `KMS_DATABASE_PATH` | derived from `KMS_DATA_DIR` | Exact SQLite DB path. |
| `KMS_DATABASE_POOL_SIZE` | `10`; SQLite test `1` | Repo pool size. |
| `KMS_DATABASE_URL` | unset | PostgreSQL URL. |
| `KMS_DATABASE_HOST` | `localhost` | PostgreSQL host when URL unset. |
| `KMS_DATABASE_NAME` | `kms_<env>` | PostgreSQL DB when URL unset. |
| `KMS_DATABASE_USER` | `postgres` | PostgreSQL user. |
| `KMS_DATABASE_PASSWORD` | `postgres` | PostgreSQL password. |

See [Database configuration](database.md).

## SQLite repo config

```elixir
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

```elixir
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:

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

External providers:

```elixir
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:

- [Local RMK](rmk/local.md)
- [StackIT RMK](rmk/stackit.md)
- [AWS RMK](rmk/aws.md)
- [GCP RMK](rmk/gcp.md)

## Remote client config

```elixir
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:

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

## HTTP server config

```elixir
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

```elixir
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:

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

Factor limits:

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

See [Sessions](sessions.md).

## WebAuthn

```elixir
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](factors/webauthn.md).

## Cache

```elixir
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:

```elixir
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.

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

See [Fallback recovery](fallback-recovery.md).