KMS.RMK.Local is the default RMK provider. It encrypts RMK-protected KMS secret material with a local file-backed key.

Use it when KMS is the only key authority and host-level controls are enough for your risk model.

Configuration

config :kms, :rmk_provider, KMS.RMK.Local

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

Environment:

KMS_RMK_LOCAL_KEY_PATH=/var/lib/kms/rmk.key

Development and test may auto-create the key file with 0600 permissions.

Production requires an explicit existing, non-empty key file. Production startup fails when the configured file is missing, unreadable, or empty.

When to use

Good fit:

  • local development and tests;
  • single-node or simple deployments;
  • deployments where KMS host compromise is already considered total compromise;
  • deployments where DB-only compromise is the main risk;
  • operators can protect and back up one local secret file.

Poor fit:

  • DB compromise cannot be tolerated without a separate key service;
  • KMS host and database are managed by different trust domains;
  • you need HSM-backed key custody, cloud IAM, or provider audit logs;
  • you want separation between database operators and key-custody operators.

Security notes

The local RMK key file is root key material. If an attacker gets both the KMS database and this key file, RMK-protected KMS material can be decrypted.

Protect the key file with:

  • strict file permissions (0600);
  • host disk encryption where appropriate;
  • restricted service user access;
  • backups separate from database backups;
  • restore drills.

Do not place the key path inside the repo, container image, or database backup volume for production.

Readiness

KMS.RMK.ensure_ready/1 reads and validates the key:

:ok = KMS.RMK.ensure_ready()

Provider-specific validation:

:ok = KMS.RMK.Local.validate_key_file!()

Caching

Local RMK calls are cheap. Caching still avoids repeated AES-GCM decrypt work, but the main cache concern is memory exposure.

Default:

config :kms, :rmk_cache_ttl_ms, :timer.minutes(5)

Disable per call when needed:

KMS.decrypt_alias(encrypted, "alias", rmk_cache_ttl_ms: 0)