Packages
Key management service with embedded OTP runtime, optional HTTP API, SQLite/PostgreSQL storage, and external RMK providers
Current section
Files
Jump to
Current section
Files
docs/use_cases/remote.md
# Remote KMS service
Use this pattern when application servers should call a dedicated KMS authority process over HTTP instead of opening KMS storage or RMK material locally.
Remote KMS uses:
- optional `KMS.API` server on the KMS authority host;
- `KMS.Client.Remote` in application hosts;
- bearer API token for the backend HTTP API;
- optional KMS sessions for principal-scoped authorization.
## When to use
Good fit:
- several applications need one KMS authority;
- app servers should not have KMS database credentials;
- app servers should not have local RMK key files or cloud RMK credentials;
- operations want separate deploy/backup/access controls for KMS;
- KMS traffic stays on a private network or TLS-protected service mesh.
Examples:
- central KMS service for internal Phoenix apps;
- KMS host with external RMK credentials while app hosts only have an API token;
- tenant service encrypting/decrypting through a private KMS endpoint;
- gradual move from embedded KMS to service-owned KMS.
Poor fit:
- browser/public clients calling KMS directly;
- untrusted network without TLS;
- workloads that cannot tolerate network dependency for decrypt;
- deployments where one embedded app is simpler and sufficient.
Use [embedded KMS](embedded.md) for simple single-app deployments.
## Server configuration
On the KMS authority release, run `:kms` with the optional `KMS.API` server enabled.
```elixir
config :kms, :api,
server: true,
port: 4004,
scheme: :http,
api_token: System.fetch_env!("KMS_API_TOKEN")
```
Configure KMS persistence and RMK on the server only:
```sh
export KMS_DATABASE_BACKEND=sqlite
export KMS_DATA_DIR=/var/lib/kms
export KMS_RMK_LOCAL_KEY_PATH=/var/lib/kms/rmk.key
export KMS_SESSION_HASH_KEY=<high-entropy-secret>
export KMS_API_TOKEN=<high-entropy-admin-token>
export ERL_CRASH_DUMP_BYTES=0
```
Run migrations before serving traffic:
```sh
mix ecto.migrate
```
Health checks:
```sh
curl -fsS http://localhost:4004/health/live
curl -fsS http://localhost:4004/health/ready
```
Expose the service only on a trusted private network or behind TLS. Do not expose it directly to browsers or the public internet.
## Client configuration
On application servers:
```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")
```
Application code uses the same facade:
```elixir
{:ok, _kek} = KMS.create_alias("owner:alice")
{:ok, encrypted} =
KMS.encrypt_alias("secret", "owner:alice", aad: "document:123/body")
{:ok, "secret"} =
KMS.decrypt_alias(encrypted, "owner:alice", aad: "document:123/body")
```
The remote client sends requests to `KMS.API` and maps JSON responses back to KMS return values.
## HTTP API token vs KMS session
There are two different bearer credentials:
- HTTP API token: authenticates app-to-KMS backend calls to `KMS.API`.
- KMS session secret: optional principal-scoped authorization token used by KMS operations.
The API token protects the remote admin surface. It does not replace application authorization.
When KMS should enforce per-principal permissions, create sessions and pass `session:`:
```elixir
{:ok, session} = KMS.create_session("users", "alice", %{password: password})
{:ok, encrypted} =
KMS.encrypt_alias("secret", "tenant/acme/docs",
session: session.secret,
aad: "document:123/body"
)
```
See [Multi-user encryption](multi_user_encryption.md).
## Direct HTTP example
Most apps should use `KMS.Client.Remote`. Direct HTTP is useful for smoke tests:
```sh
curl -fsS \
-H "authorization: Bearer $KMS_API_TOKEN" \
-H "content-type: application/json" \
-d '{"alias":"owner:alice","cleartext":"secret","opts":{"aad":"document:123/body"}}' \
https://kms.internal.example.com/api/encrypt
```
## Security notes
- Treat the HTTP API as an admin backend API.
- Require a high-entropy API token.
- Use TLS or a trusted private network.
- Disable request-body logging in proxies, APM, and app logs.
- Never log API tokens, session secrets, plaintext, ciphertext payloads, passwords, factor codes, or RMK material.
- Keep RMK credentials and KMS database access only on KMS authority hosts.
- Run fallback registry and recovery operations locally on the KMS host; they are absent from the HTTP API.
- Use `/health/ready` for dependency readiness and [Observability](../observability.md) for telemetry.
## Availability notes
Remote KMS adds a network dependency to encrypt/decrypt paths. Remote `get_*` helpers
return `nil` only for an API `not_found` response; transport, HTTP, and malformed-response
failures return `{:error, reason}`. Prefer `fetch_*` when callers always want tagged results.
Design app behavior for KMS outages:
- fail closed for decrypt-sensitive operations;
- avoid decrypting during request paths when cached plaintext or async jobs are acceptable;
- set reasonable HTTP timeouts at the client/proxy layer;
- monitor KMS latency, RMK latency, cache hit rate, and readiness.