The memory store is a fully in-memory implementation of store.Store. It keeps all data in Go maps protected by a sync.RWMutex, making it safe for concurrent access. No external dependencies are required.
Installation01
The memory store is included in the Vault module. No additional packages are needed.
import "github.com/xraph/vault/store/memory"Creating a store02
store := memory.New()memory.New() returns a *memory.Store with all internal maps initialized and ready to use.
Implemented interfaces03
The memory store satisfies all six subsystem store interfaces plus the three lifecycle methods that form the composite store.Store interface:
| Interface | Package | Methods |
secret.Store | github.com/xraph/vault/secret | GetSecret, SetSecret, DeleteSecret, ListSecrets, GetSecretVersion, ListSecretVersions |
flag.Store | github.com/xraph/vault/flag | DefineFlag, GetFlagDefinition, ListFlagDefinitions, DeleteFlagDefinition, SetFlagRules, GetFlagRules, SetFlagTenantOverride, GetFlagTenantOverride, DeleteFlagTenantOverride, ListFlagTenantOverrides |
config.Store | github.com/xraph/vault/config | GetConfig, SetConfig, DeleteConfig, ListConfig, GetConfigVersion, ListConfigVersions |
override.Store | github.com/xraph/vault/override | GetOverride, SetOverride, DeleteOverride, ListOverridesByTenant, ListOverridesByKey |
rotation.Store | github.com/xraph/vault/rotation | SaveRotationPolicy, GetRotationPolicy, ListRotationPolicies, DeleteRotationPolicy, RecordRotation, ListRotationRecords |
audit.Store | github.com/xraph/vault/audit | RecordAudit, ListAudit, ListAuditByKey |
Compile-time checks guarantee that the store satisfies every interface:
var (
_ secret.Store = (*Store)(nil)
_ flag.Store = (*Store)(nil)
_ config.Store = (*Store)(nil)
_ override.Store = (*Store)(nil)
_ rotation.Store = (*Store)(nil)
_ audit.Store = (*Store)(nil)
)Concurrency safety04
All methods acquire either a read lock (RLock) or a write lock (Lock) on the internal sync.RWMutex. Data returned from reads is deep-copied so that callers cannot mutate internal state. Byte slices (secret values) and any values (config, flag defaults) are copied via copyBytes and JSON round-trip respectively.
Lifecycle methods05
| Method | Behaviour |
Migrate(ctx) | No-op -- returns nil |
Ping(ctx) | Always returns nil |
Close() | No-op -- returns nil |
The memory store has no schema to create and no connection to check, so all lifecycle methods succeed immediately.
Auto-versioning06
Both SetSecret and SetConfig perform automatic versioning. When an entry already exists, the version number is incremented and a version record is appended to the version history. When creating a new entry, the version starts at 1.
Internal key scheme07
Entities are keyed using composite strings:
| Entity | Key format |
| Secrets, Flags, Config, Rotation | key:appID |
| Overrides, Flag Tenant Overrides | key:appID:tenantID |
This ensures that entries with the same key but different app IDs or tenant IDs remain isolated.
When to use08
Unit tests -- No setup, no teardown, zero dependencies.
Development -- Quick iteration without running a database.
CI pipelines -- Fast, deterministic tests with no external services.
Prototyping -- Explore Vault's API surface without configuring infrastructure.
When not to use09
Production -- Data is lost on process restart.
Multi-instance deployments -- Each process has its own isolated store.
Performance benchmarking -- Behaviour characteristics differ from a real database.
Complete example10
package main
import (
"context"
"fmt"
"log"
"github.com/xraph/vault/config"
"github.com/xraph/vault/crypto"
"github.com/xraph/vault/secret"
"github.com/xraph/vault/store/memory"
)
func main() {
ctx := context.Background()
// Create the in-memory store.
store := memory.New()
// Migrate is a no-op but good practice to call.
if err := store.Migrate(ctx); err != nil {
log.Fatal(err)
}
// Create an encryptor with a 32-byte key.
key := make([]byte, 32)
copy(key, "my-secret-encryption-key-32byte")
enc, err := crypto.NewEncryptor(key)
if err != nil {
log.Fatal(err)
}
// Use the store with a secret service.
secretSvc := secret.NewService(store, enc, secret.WithAppID("myapp"))
meta, err := secretSvc.Set(ctx, "database_url", []byte("postgres://localhost/mydb"), "")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Stored secret: key=%s version=%d\n", meta.Key, meta.Version)
// Retrieve and decrypt.
sec, err := secretSvc.Get(ctx, "database_url", "")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Retrieved: %s\n", string(sec.Value))
// Use the store with a config service.
cfgSvc := config.NewService(store, config.WithAppID("myapp"))
if err := cfgSvc.Set(ctx, "rate_limit", 100, ""); err != nil {
log.Fatal(err)
}
limit := cfgSvc.Int(ctx, "rate_limit", 50)
fmt.Printf("Rate limit: %d\n", limit)
}Swapping to PostgreSQL11
The memory store implements the same store.Store interface as the PostgreSQL and Grove stores. Switching to a production backend is a single-line change:
// Development
store := memory.New()
// Production
store, err := postgres.New(ctx, connString)