kv

package
v0.16.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 25, 2025 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package kv provides a secure in-memory key-value store for managing secret data. The store supports versioning of secrets, allowing operations on specific versions and tracking deleted versions. It is designed for scenarios where secrets need to be securely managed, updated, and deleted.

Package kv provides an in-memory key-value store with automatic versioning and bounded cache semantics.

Concurrency Safety

This package is NOT safe for concurrent use. All methods on KV must be externally synchronized. Callers are responsible for providing appropriate locking mechanisms (e.g., sync.RWMutex) to protect concurrent access.

Concurrent operations without synchronization will cause data races and undefined behavior.

Example of safe concurrent usage:

type SafeStore struct {
    kv *kv.KV
    mu sync.RWMutex
}

func (s *SafeStore) Put(path string, data map[string]string) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.kv.Put(path, data)
}

func (s *SafeStore) Get(path string, version int) (map[string]string, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    return s.kv.Get(path, version)
}

Use sync.RWMutex to allow concurrent reads while serializing writes for optimal performance.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config added in v0.3.2

type Config struct {
	// MaxSecretVersions is the maximum number of versions to retain per path.
	// When exceeded, older versions are automatically pruned.
	// Must be positive. A typical value is 10.
	MaxSecretVersions int
}

Config represents the configuration for a KV instance.

type KV

type KV struct {
	// contains filtered or unexported fields
}

KV represents an in-memory key-value store with automatic versioning and bounded cache semantics. Each path maintains a configurable maximum number of versions, with older versions automatically pruned when the limit is exceeded.

The store supports:

  • Versioned storage with automatic version numbering
  • Soft deletion with undelete capability
  • Bounded cache with automatic pruning of old versions
  • Version-specific retrieval and metadata tracking

func New added in v0.3.2

func New(config Config) *KV

New creates a new KV instance with the specified configuration.

The store is initialized as an empty in-memory key-value store with versioning enabled. All paths stored in this instance will retain up to MaxSecretVersions versions.

Parameters:

  • config: Configuration specifying MaxSecretVersions

Returns:

  • *KV: A new KV instance ready for use

Example:

kv := New(Config{MaxSecretVersions: 10})
kv.Put("app/config", map[string]string{"key": "value"})

func (*KV) Delete

func (kv *KV) Delete(path string, versions []int) ([]int, *sdkErrors.SDKError)

Delete marks secret versions as deleted for a given path. The deletion is performed by setting the DeletedTime to the current time.

IMPORTANT: This is a soft delete. The path remains in the store even if all versions are deleted. To completely remove a path and reclaim memory, use Destroy() after deleting all versions.

The function supports flexible version deletion with the following behavior:

  • If versions is empty, deletes only the current version
  • If versions contains specific numbers, deletes those versions
  • Version 0 in the array represents the current version
  • Non-existent versions are silently skipped without error

This idempotent behavior is useful for batch operations where you want to ensure certain versions are deleted without failing if some don't exist.

Parameters:

  • path: Path to the secret to delete
  • versions: Array of version numbers to delete (empty array deletes current version only, 0 in the array represents current version)

Returns:

  • []int: Array of version numbers that were actually modified (had their DeletedTime changed from nil to now). Already-deleted versions are not included in this list.
  • *errors.SDKError: nil on success, or one of the following sdkErrors:
  • ErrEntityNotFound: if the path doesn't exist

Example:

// Delete current version only
modified, err := kv.Delete("secret/path", []int{})
if err != nil {
    log.Printf("Failed to delete secret: %v", err)
}
log.Printf("Deleted %d version(s): %v", len(modified), modified)

// Delete specific versions
modified, err = kv.Delete("secret/path", []int{1, 2, 3})
if err != nil {
    log.Printf("Failed to delete versions: %v", err)
}
log.Printf("Actually deleted: %v", modified)

func (*KV) Destroy added in v0.15.1

func (kv *KV) Destroy(path string) *sdkErrors.SDKError

Destroy permanently removes a secret path from the store, including all versions (both active and deleted). This is a hard delete operation that cannot be undone.

Unlike Delete(), which soft-deletes versions by marking them with DeletedTime, Destroy() completely removes the path from the internal map, reclaiming the memory.

This operation is useful for:

  • Purging secrets that have all versions deleted
  • Removing obsolete paths to prevent unbounded map growth
  • Compliance requirements for data removal

Parameters:

  • path: The path to permanently remove from the store

Returns:

  • *sdkErrors.SDKError: ErrEntityNotFound if the path does not exist, nil on success

Example:

// Delete all versions first
kv.Delete("secret/path", []int{})

// Check if empty and destroy
secret, _ := kv.GetRawSecret("secret/path")
if secret.IsEmpty() {
    err := kv.Destroy("secret/path")
    if err != nil {
        log.Printf("Failed to destroy secret: %v", err)
    }
}

// Or destroy directly (removes regardless of deletion state)
err := kv.Destroy("secret/path")

func (*KV) Get

func (kv *KV) Get(path string, version int) (map[string]string, *sdkErrors.SDKError)

Get retrieves a versioned key-value data map from the store at the specified path.

The function supports versioned data retrieval with the following behavior:

  • If version is 0, returns the current version of the data
  • If version is specified, returns that specific version if it exists
  • Returns nil if the path doesn't exist
  • Returns nil if the specified version doesn't exist
  • Returns nil if the version has been deleted (DeletedTime is set)

Parameters:

  • path: The path to retrieve data from
  • version: The specific version to retrieve (0 for current version)

Returns:

  • map[string]string: The key-value data at the specified path and version, nil on error
  • *sdkErrors.SDKError: nil on success, or one of the following errors:
  • ErrEntityNotFound: if the path doesn't exist
  • ErrEntityDeleted: if the version doesn't exist or has been deleted

Example:

// Get current version
data, err := kv.Get("secret/myapp", 0)
if err != nil {
    log.Printf("Failed to get secret: %v", err)
    return
}

// Get specific version
historicalData, err := kv.Get("secret/myapp", 2)
if err != nil {
    log.Printf("Failed to get version 2: %v", err)
    return
}

func (*KV) GetRawSecret

func (kv *KV) GetRawSecret(path string) (*Value, *sdkErrors.SDKError)

GetRawSecret retrieves a raw secret from the store at the specified path. This function is similar to Get, but it returns the raw Value object instead of the key-value data map, providing access to all versions and metadata.

Parameters:

  • path: The path to retrieve the secret from

Returns:

  • *Value: The complete secret object with all versions and metadata, nil on error
  • *sdkErrors.SDKError: nil on success, or one of the following errors:
  • ErrEntityNotFound: if the path doesn't exist

Example:

secret, err := kv.GetRawSecret("secret/myapp")
if err != nil {
    log.Printf("Failed to get raw secret: %v", err)
    return
}
log.Printf("Current version: %d", secret.Metadata.CurrentVersion)

func (*KV) ImportSecrets added in v0.3.3

func (kv *KV) ImportSecrets(secrets map[string]*Value)

ImportSecrets hydrates the key-value store with secrets loaded from persistent storage or a similar medium. It takes a map of path to secret values and adds them to the in-memory store. This is typically used during initialization or recovery after a system crash.

The method performs a deep copy of all imported secrets to avoid sharing memory between the source data and the KV store. If a secret already exists in the store, it will be overwritten with the imported value. All version history and metadata from the imported secrets are preserved.

Parameters:

  • secrets: Map of secret paths to their complete Value objects (including all versions and metadata)

Returns:

  • None

Example:

secrets, err := persistentStore.LoadAllSecrets(context.Background())
if err != nil {
    log.Fatalf("Failed to load secrets: %v", err)
}
kv.ImportSecrets(secrets)
log.Printf("Imported %d secrets", len(secrets))

func (*KV) List

func (kv *KV) List() []string

List returns a slice containing all paths stored in the key-value store. The order of paths in the returned slice is not guaranteed to be stable between calls.

Note: List returns all paths regardless of whether their versions have been deleted. A path is only removed from the store when all of its data is explicitly removed, not when versions are soft-deleted.

Returns:

  • []string: A slice containing all paths present in the store

Example:

kv := New(Config{MaxSecretVersions: 10})
kv.Put("app/config", map[string]string{"key": "value"})
kv.Put("app/database", map[string]string{"host": "localhost"})

paths := kv.List()
// Returns: ["app/config", "app/database"] (order not guaranteed)

func (*KV) Put

func (kv *KV) Put(path string, values map[string]string)

Put stores a new version of key-value pairs at the specified path in the store. It implements automatic versioning as a bounded cache with a configurable maximum number of versions per path.

When storing values:

  • If the path doesn't exist, it creates new data with initial metadata
  • Each put operation creates a new version with an incremented version number
  • Old versions are automatically pruned when they fall outside the version window (CurrentVersion - MaxVersions)
  • All versions exceeding MaxVersions are pruned in a single operation, maintaining the most recent MaxVersions versions
  • Timestamps are updated for both creation and modification times

Version pruning behavior (bounded cache):

  • Pruning occurs on each Put when versions exceed MaxVersions
  • All versions older than (CurrentVersion - MaxVersions) are deleted
  • Example: If CurrentVersion=15 and MaxVersions=10, versions 1-5 are deleted, keeping versions 6-15
  • This ensures O(n) pruning where n is the number of excess versions, providing predictable performance

Parameters:

  • path: The location where the data will be stored
  • values: A map of key-value pairs to store at this path

Example:

kv := New(Config{MaxSecretVersions: 10})
kv.Put("app/config", map[string]string{
    "api_key": "secret123",
    "timeout": "30s",
})
// Creates version 1 at path "app/config"

kv.Put("app/config", map[string]string{
    "api_key": "newsecret456",
    "timeout": "60s",
})
// Creates version 2, version 1 is still available

The function maintains metadata including:

  • CreatedTime: When the data at this path was first created
  • UpdatedTime: When the most recent version was added
  • CurrentVersion: The latest version number
  • OldestVersion: The oldest available version number after pruning
  • MaxVersions: Maximum number of versions to keep (configurable at KV creation)

func (*KV) Undelete

func (kv *KV) Undelete(path string, versions []int) ([]int, *sdkErrors.SDKError)

Undelete restores previously deleted versions of a secret at the specified path. It sets the DeletedTime to nil for each specified version that exists.

The function supports flexible version restoration with the following behavior:

  • If versions is empty, restores only the current version
  • If versions contains specific numbers, restores those versions
  • Version 0 in the array represents the current version
  • Non-existent versions are silently skipped without error

This idempotent behavior is useful for batch operations where you want to ensure certain versions are restored without failing if some don't exist.

Parameters:

  • path: The location of the secret in the store
  • versions: Array of version numbers to restore (empty array restores current version only, 0 in the array represents current version)

Returns:

  • []int: Array of version numbers that were actually modified (had their DeletedTime changed from non-nil to nil). Already-restored versions are not included in this list.
  • *sdkErrors.SDKError: nil on success, or one of the following errors:
  • ErrEntityNotFound: if the path doesn't exist

Example:

// Restore current version only
modified, err := kv.Undelete("secret/path", []int{})
if err != nil {
    log.Printf("Failed to undelete secret: %v", err)
}
log.Printf("Restored %d version(s): %v", len(modified), modified)

// Restore specific versions
modified, err = kv.Undelete("secret/path", []int{1, 2, 3})
if err != nil {
    log.Printf("Failed to undelete versions: %v", err)
}
log.Printf("Actually restored: %v", modified)

type Metadata

type Metadata struct {
	// CurrentVersion is the newest/latest non-deleted version number.
	// Version numbers start at 1. A value of 0 indicates that all versions
	// have been deleted (no valid version exists).
	CurrentVersion int

	// OldestVersion is the oldest available version number
	OldestVersion int

	// CreatedTime is when the data at this path was first created
	CreatedTime time.Time

	// UpdatedTime is when the data was last modified
	UpdatedTime time.Time

	// MaxVersions is the maximum number of versions to retain
	// When exceeded, older versions are automatically pruned
	MaxVersions int
}

Metadata tracks control information for versioned data stored at a path. It maintains version boundaries and timestamps for the overall data collection.

type Value added in v0.3.1

type Value struct {
	// Versions maps version numbers to their corresponding Version objects
	Versions map[int]Version

	// Metadata contains control information about this versioned data
	Metadata Metadata
}

Value represents a versioned collection of key-value pairs stored at a specific path. It maintains both the version history and metadata about the collection as a whole.

func (*Value) Empty added in v0.15.1

func (v *Value) Empty() bool

Empty returns true if the Value has no valid (non-deleted) versions. This is the inverse of HasValidVersions() and is useful for identifying secrets that can be purged from storage.

Returns:

  • true if all versions are deleted or no versions exist
  • false if at least one active version exists

Example:

if secret.IsEmpty() {
    // Safe to remove from storage
    kv.Destroy(path)
}

func (*Value) HasValidVersions added in v0.15.1

func (v *Value) HasValidVersions() bool

HasValidVersions returns true if the Value has at least one non-deleted version. It iterates through all versions to check their DeletedTime.

Returns:

  • true if any version has DeletedTime == nil (active version exists)
  • false if all versions are deleted or no versions exist

Note: This method performs a full scan of all versions. For stores where CurrentVersion is maintained correctly (like SPIKE Nexus), checking Metadata.CurrentVersion != 0 is more efficient.

type Version

type Version struct {
	// Data contains the actual key-value pairs stored in this version
	Data map[string]string

	// CreatedTime is when this version was created
	CreatedTime time.Time

	// Version is the numeric identifier for this version. Version numbers
	// start at 1 and increment with each update.
	Version int

	// DeletedTime indicates when this version was marked as deleted
	// A nil value means the version is active/not deleted
	DeletedTime *time.Time
}

Version represents a single version of versioned data along with its metadata. Each version maintains its own set of key-value pairs and tracking information.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL