Vaultx.Secrets.KV (Vaultx v0.7.0)
View SourceUnified Key-Value secrets engine interface for HashiCorp Vault.
This module provides a unified interface for both KV v1 and KV v2 engines, automatically detecting the engine version and delegating operations to the appropriate implementation. It offers a seamless experience for working with KV secrets regardless of the underlying engine version.
Features
- Automatic Version Detection: Detects KV v1 vs v2 automatically
- Unified API: Same interface works with both versions
- Version-Specific Features: Advanced features available when supported
- Graceful Degradation: Unsupported operations return clear errors
- Performance Optimized: Caches version detection results
- Convenience Functions: Additional utility functions for common operations
Supported Operations
Core Operations (Both v1 and v2)
read/2- Read secrets from any pathwrite/3- Write secrets to any pathdelete/2- Delete secrets (soft delete in v2)list/2- List secret keys at a path
KV v2 Specific Operations
read_metadata/2- Read secret metadata and version historywrite_metadata/3- Update secret metadata without creating new versiondelete_metadata/2- Permanently delete all versions and metadataundelete/2- Restore soft-deleted versionsdestroy/2- Permanently destroy specific versionslist_versions/2- List all versions of a secret
Convenience Functions
read_version/3- Read specific version of a secretwrite_cas/4- Write with Check-And-Set (CAS) supportdelete_versions/3- Delete specific versionsexists?/2- Check if a secret existskeys/2- Get field names from a secretget_field/3- Get specific field valueupdate_field/4- Update single field preserving others
Usage Examples
# Basic operations (work with both v1 and v2)
{:ok, secret} = Vaultx.Secrets.KV.read("myapp/config", mount_path: "secret")
{:ok, result} = Vaultx.Secrets.KV.write("myapp/config", %{"key" => "value"}, mount_path: "secret")
:ok = Vaultx.Secrets.KV.delete("myapp/config", mount_path: "secret")
{:ok, keys} = Vaultx.Secrets.KV.list("myapp/", mount_path: "secret")
# KV v2 specific operations (gracefully fail on v1)
{:ok, secret} = Vaultx.Secrets.KV.read_version("myapp/config", 2, mount_path: "secret")
{:ok, result} = Vaultx.Secrets.KV.write_cas("myapp/config", %{"key" => "value"}, 1, mount_path: "secret")
:ok = Vaultx.Secrets.KV.undelete("myapp/config", versions: [1, 2], mount_path: "secret")
# Metadata operations (KV v2 only)
{:ok, metadata} = Vaultx.Secrets.KV.read_metadata("myapp/config", mount_path: "secret")
:ok = Vaultx.Secrets.KV.write_metadata("myapp/config", %{"max_versions" => 5}, mount_path: "secret")
# Convenience functions
true = Vaultx.Secrets.KV.exists?("myapp/config", mount_path: "secret")
{:ok, ["username", "password"]} = Vaultx.Secrets.KV.keys("myapp/config", mount_path: "secret")
{:ok, "admin"} = Vaultx.Secrets.KV.get_field("myapp/config", "username", mount_path: "secret")Version Detection
The module automatically detects the KV engine version by:
- Checking engine mount information via
/sys/mounts - Caching the result for subsequent operations
- Falling back to API behavior analysis if needed
API Compliance
This implementation fully complies with HashiCorp Vault's official KV API:
- KV v1: https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v1
- KV v2: https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2
Configuration
# KV v1 engine
vault secrets enable -version=1 -path=kv-v1 kv
# KV v2 engine (default for new installations)
vault secrets enable -version=2 -path=secret kvError Handling
Operations return standardized errors with clear messages:
{:error, %Vaultx.Base.Error{
type: :unsupported_operation,
message: "KV v1 does not support versioning",
details: %{operation: :read, version: 2}
}}Performance Considerations
- Version detection results are cached per mount path
- Cache can be cleared with
clear_version_cache/1 - First operation per mount may be slightly slower due to detection
Summary
Functions
Initializes the KV module and sets up version detection cache. This is called automatically when the module is loaded.
Clears the version detection cache for a specific mount path or all mount paths.
Delete specific versions of a secret (KV v2 only).
Detects the KV engine version for a given mount path.
Check if a secret exists at the given path.
Get a specific field from a secret.
Get secret keys (field names) without values.
Get the latest version of a secret.
Read a secret from KV store with version support.
Update a single field in a secret (preserves other fields).
Write a secret to KV store with CAS (Check-And-Set) support.
Functions
Initializes the KV module and sets up version detection cache. This is called automatically when the module is loaded.
@spec clear_version_cache(String.t() | :all) :: :ok
Clears the version detection cache for a specific mount path or all mount paths.
Parameters
mount_path- Mount path to clear cache for, or:allto clear all
Examples
:ok = Vaultx.Secrets.KV.clear_version_cache("secret")
:ok = Vaultx.Secrets.KV.clear_version_cache(:all)
@spec delete_versions(String.t(), [pos_integer()], keyword()) :: :ok | {:error, Vaultx.Base.Error.t()}
Delete specific versions of a secret (KV v2 only).
This is an alias for delete/2 with explicit versions parameter.
@spec detect_kv_version( String.t(), keyword() ) :: {:ok, 1 | 2} | {:error, Vaultx.Base.Error.t()}
Detects the KV engine version for a given mount path.
Parameters
mount_path- The mount path to detect version foropts- Operation options
Returns
{:ok, 1}- KV v1 engine detected{:ok, 2}- KV v2 engine detected{:error, error}- Detection failed
Examples
{:ok, 1} = Vaultx.Secrets.KV.detect_kv_version("kv-v1")
{:ok, 2} = Vaultx.Secrets.KV.detect_kv_version("secret")
Check if a secret exists at the given path.
@spec get_field(String.t(), String.t(), keyword()) :: {:ok, any()} | {:error, Vaultx.Base.Error.t()}
Get a specific field from a secret.
@spec keys( String.t(), keyword() ) :: {:ok, [String.t()]} | {:error, Vaultx.Base.Error.t()}
Get secret keys (field names) without values.
@spec read_latest( String.t(), keyword() ) :: {:ok, map()} | {:error, Vaultx.Base.Error.t()}
Get the latest version of a secret.
This is a convenience function that reads the latest version.
@spec read_version(String.t(), pos_integer(), keyword()) :: {:ok, map()} | {:error, Vaultx.Base.Error.t()}
Read a secret from KV store with version support.
This is an alias for read/2 with explicit version parameter.
@spec update_field(String.t(), String.t(), any(), keyword()) :: {:ok, map()} | {:error, Vaultx.Base.Error.t()}
Update a single field in a secret (preserves other fields).
@spec write_cas(String.t(), map(), pos_integer(), keyword()) :: {:ok, map()} | {:error, Vaultx.Base.Error.t()}
Write a secret to KV store with CAS (Check-And-Set) support.
This is an alias for write/3 with explicit cas parameter.