vaultstore

package module
v0.38.0 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2026 License: AGPL-3.0 Imports: 31 Imported by: 0

README

Vault Store

Tests Status Go Report Card PkgGoDev

Vault - a secure value storage (data-at-rest) implementation for Go.

Scope

VaultStore is specifically designed as a data store component for securely storing and retrieving secrets. It is not an API or a complete secrets management system. Features such as user management, access control, and API endpoints are intentionally beyond the scope of this project.

VaultStore is meant to be integrated into your application as a library, providing the data storage layer for your secrets management needs. The application using VaultStore is responsible for implementing any additional layers such as API endpoints, user management, or access control if needed.

Documentation

Features

  • Secure storage of sensitive data
  • Token-based access to secrets
  • Password protection for stored values
  • Password rotation
  • Flexible query interface for retrieving records
  • Soft delete functionality for data recovery
  • Support for multiple database backends

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). You can find a copy of the license at https://www.gnu.org/licenses/agpl-3.0.en.html

For commercial use, please use my contact page to obtain a commercial license.

Installation

go get -u github.com/dracory/vaultstore

Technical Details

For database schema, record structure, and other technical information, please see the Technical Reference.

Setup

vault, err := NewStore(NewStoreOptions{
	VaultTableName:     "my_vault",
	DB:                 databaseInstance,
	AutomigrateEnabled: true,
})

Usage

Here are some basic examples of using VaultStore. For comprehensive documentation, see the Usage Guide.

// Create a token
token, err := vault.TokenCreate("my_value", "my_password", 20)
// token: "tk_abc123def456..."

// Check if a token exists
exists, err := vault.TokenExists(token)
// exists: true

// Read a value using a token
value, err := vault.TokenRead(token, "my_password")
// value: "my_value"

// Update a token's value
err := vault.TokenUpdate(token, "new_value", "my_password")

// Upsert a token (create if doesn't exist, update if it does)
ctx := context.Background()
existingToken := ""  // Empty to create new, or provide existing token to update
newToken, err := vault.TokenUpsert(ctx, existingToken, "my_value", "my_password")
// newToken: "tk_abc123def456..." (new token created)

// Update existing token using upsert
existingToken = newToken
updatedToken, err := vault.TokenUpsert(ctx, existingToken, "updated_value", "my_password")
// updatedToken: "tk_abc123def456..." (same token, updated value)

// Read multiple tokens at once (more efficient than individual calls)
ctx := context.Background()
tokens := []string{"token1", "token2", "token3"}
tokenValues, err := vault.TokensRead(ctx, tokens, "my_password")
// tokenValues: map[string]string{"token1": "value1", "token2": "value2", "token3": "value3"}

// Resolve multiple tokens with keys (convenience method)
keyTokenMap := map[string]string{
    "api_key":    "token1_here",
    "db_config":  "token2_here", 
    "auth_token": "token3_here",
}
resolvedMap, err := vault.TokensReadToResolvedMap(ctx, keyTokenMap, "my_password")
// resolvedMap: map[string]string{"api_key": "api_value", "db_config": "db_string", "auth_token": "auth_secret"}

// Bulk rekey all records with old password to new password
count, err := vault.BulkRekey(ctx, "old_password", "new_password")
// count: 5 (number of records rekeyed)

// Hard delete a token
err := vault.TokenDelete(token)

// Soft delete a token
err := vault.TokenSoftDelete(token)

Changelog

For a detailed version history and changes, please see the Changelog.

Documentation

Index

Constants

View Source
const (
	MAX_DATETIME = "9999-12-31 23:59:59"
	ASC          = "ASC"
	DESC         = "DESC"
)

Database constants (replaces github.com/dracory/sb dependency)

View Source
const (
	COLUMN_OBJECT_TYPE = "object_type"
	COLUMN_OBJECT_ID   = "object_id"
	COLUMN_META_KEY    = "meta_key"
	COLUMN_META_VALUE  = "meta_value"
)

Meta table column constants

View Source
const (
	TOKEN_MIN_PAYLOAD_LENGTH = 12
	TOKEN_MAX_PAYLOAD_LENGTH = 37                                           // 37 chars + tk_ prefix = 40 total
	TOKEN_MIN_TOTAL_LENGTH   = len(TOKEN_PREFIX) + TOKEN_MIN_PAYLOAD_LENGTH // 15
	TOKEN_MAX_TOTAL_LENGTH   = len(TOKEN_PREFIX) + TOKEN_MAX_PAYLOAD_LENGTH // 40
)

Token size constraints

View Source
const (
	OBJECT_TYPE_PASSWORD_IDENTITY = "password_identity"
	OBJECT_TYPE_RECORD            = "record"
	OBJECT_TYPE_VAULT_SETTINGS    = "vault"
)

Object type constants for vault_meta table

View Source
const (
	META_KEY_HASH        = "hash"
	META_KEY_PASSWORD_ID = "password_id"
	META_KEY_VERSION     = "version"
)

Meta key constants

View Source
const (
	ARGON2ID_TIME     = 1         // Minimal passes for faster verification
	ARGON2ID_MEMORY   = 16 * 1024 // 16MB - lightweight for embedded/mobile
	ARGON2ID_THREADS  = 2         // Reduced parallelism
	ARGON2ID_KEY_LEN  = 32        // Hash output length
	ARGON2ID_SALT_LEN = 16        // Salt length
)

Argon2id password hashing parameters (lightweight defaults for broad compatibility) Users can increase these via CryptoConfig for higher security requirements

View Source
const (
	ENCRYPTION_VERSION_V1 = "v1"
	ENCRYPTION_VERSION_V2 = "v2"
	ENCRYPTION_PREFIX_V1  = ENCRYPTION_VERSION_V1 + ":"
	ENCRYPTION_PREFIX_V2  = ENCRYPTION_VERSION_V2 + ":"
)

Encryption version constants for versioned encryption

View Source
const (
	V2_SALT_SIZE       = 16
	V2_NONCE_SIZE      = 12
	V2_TAG_SIZE        = 16
	ARGON2_ITERATIONS  = 3
	ARGON2_MEMORY      = 64 * 1024 // 64MB
	ARGON2_PARALLELISM = 4
	ARGON2_KEY_LENGTH  = 32
)

v2 encryption parameters (AES-GCM + Argon2id)

View Source
const BCRYPT_COST = 12

bcrypt cost for password hashing (legacy - used for backward compatibility)

View Source
const COLUMN_CREATED_AT = "created_at"
View Source
const COLUMN_EXPIRES_AT = "expires_at"
View Source
const COLUMN_ID = "id"
View Source
const COLUMN_SOFT_DELETED_AT = "soft_deleted_at"
View Source
const COLUMN_UPDATED_AT = "updated_at"
View Source
const COLUMN_VAULT_TOKEN = "vault_token"
View Source
const COLUMN_VAULT_VALUE = "vault_value"
View Source
const PASSWORD_ID_PREFIX = "p_"

Password identity ID prefix

View Source
const RECORD_META_ID_PREFIX = "r_"

Record ID prefix (used in meta table)

View Source
const TOKEN_PREFIX = "tk_"
View Source
const (
	VAULT_SETTINGS_ID = "settings"
)

Vault settings constants

Variables

View Source
var ErrPasswordInvalid = errors.New("password does not meet requirements")

ErrPasswordInvalid is returned when password does not meet requirements

View Source
var ErrTokenExpired = errors.New("token has expired")

ErrTokenExpired is returned when a token has expired

Functions

func IsToken

func IsToken(s string) bool

func IsTokenValidLength added in v0.30.0

func IsTokenValidLength(s string) bool

IsTokenValidLength checks if a token has valid format and reasonable length Returns false if token format is invalid or length is outside reasonable bounds

func NewStore

func NewStore(opts NewStoreOptions) (*storeImplementation, error)

NewStore creates a new entity store

Types

type CryptoConfig added in v0.30.0

type CryptoConfig struct {
	// Argon2id parameters
	Iterations  int
	Memory      int // in bytes
	Parallelism int
	KeyLength   int // in bytes

	// AES-GCM parameters
	SaltSize  int // in bytes
	NonceSize int // in bytes
	TagSize   int // in bytes
}

CryptoConfig holds configurable cryptographic parameters

func DefaultCryptoConfig added in v0.30.0

func DefaultCryptoConfig() *CryptoConfig

DefaultCryptoConfig returns secure default cryptographic parameters

func HighSecurityCryptoConfig added in v0.30.0

func HighSecurityCryptoConfig() *CryptoConfig

HighSecurityCryptoConfig returns parameters for high-security scenarios

func LightweightCryptoConfig added in v0.30.0

func LightweightCryptoConfig() *CryptoConfig

LightweightCryptoConfig returns parameters for resource-constrained environments

type MetaInterface added in v0.30.0

type MetaInterface interface {
	// Data returns the meta data as a map
	Data() map[string]string
	// DataChanged returns the changed meta data as a map
	DataChanged() map[string]string

	// Getters
	// GetID returns the meta ID
	GetID() uint
	// GetObjectType returns the object type
	GetObjectType() string
	// GetObjectID returns the object ID
	GetObjectID() string
	// GetKey returns the meta key
	GetKey() string
	// GetValue returns the meta value
	GetValue() string

	// Setters
	// SetID sets the meta ID
	SetID(id uint) MetaInterface
	// SetObjectType sets the object type
	SetObjectType(objectType string) MetaInterface
	// SetObjectID sets the object ID
	SetObjectID(objectID string) MetaInterface
	// SetKey sets the meta key
	SetKey(key string) MetaInterface
	// SetValue sets the meta value
	SetValue(value string) MetaInterface
}

MetaInterface defines the methods that a VaultMeta must implement. It provides access to metadata for vault objects including keys and values.

func NewMeta added in v0.30.0

func NewMeta() MetaInterface

NewMeta creates a new metadata entry

func NewMetaFromExistingData added in v0.30.0

func NewMetaFromExistingData(data map[string]string) MetaInterface

NewMetaFromExistingData creates a metadata entry from existing data

type NewStoreOptions

type NewStoreOptions struct {
	VaultTableName           string
	VaultMetaTableName       string
	DB                       *sql.DB
	DbDriverName             string
	AutomigrateEnabled       bool
	DebugEnabled             bool
	CryptoConfig             *CryptoConfig
	ParallelThreshold        int  // Threshold for parallel processing (0 = use default 10000)
	PasswordAllowEmpty       bool // Allow empty passwords (default: false)
	PasswordMinLength        int  // Minimum password length (default: 16)
	PasswordRequireLowercase bool // Require at least one lowercase letter (default: false)
	PasswordRequireUppercase bool // Require at least one uppercase letter (default: false)
	PasswordRequireNumbers   bool // Require at least one number (default: false)
	PasswordRequireSymbols   bool // Require at least one symbol (default: false)
}

NewStoreOptions define the options for creating a new session store

type RecordInterface

type RecordInterface interface {
	// Data returns the record data as a map
	Data() map[string]string
	// DataChanged returns the changed data as a map
	DataChanged() map[string]string

	// Getters
	// GetCreatedAt returns the created at timestamp
	GetCreatedAt() string
	// GetExpiresAt returns the expires at timestamp
	GetExpiresAt() string
	// GetSoftDeletedAt returns the soft deleted at timestamp
	GetSoftDeletedAt() string
	// GetID returns the record ID
	GetID() string
	// GetToken returns the record token
	GetToken() string
	// GetUpdatedAt returns the updated at timestamp
	GetUpdatedAt() string
	// GetValue returns the record value
	GetValue() string

	// Setters
	// SetCreatedAt sets the created at timestamp
	SetCreatedAt(createdAt string) RecordInterface
	// SetExpiresAt sets the expires at timestamp
	SetExpiresAt(expiresAt string) RecordInterface
	// SetSoftDeletedAt sets the soft deleted at timestamp
	SetSoftDeletedAt(softDeletedAt string) RecordInterface
	// SetID sets the record ID
	SetID(id string) RecordInterface
	// SetToken sets the record token
	SetToken(token string) RecordInterface
	// SetUpdatedAt sets the updated at timestamp
	SetUpdatedAt(updatedAt string) RecordInterface
	// SetValue sets the record value
	SetValue(value string) RecordInterface
}

RecordInterface defines the methods that a Record must implement. It provides access to record data, timestamps, and metadata.

func NewRecord

func NewRecord() RecordInterface

func NewRecordFromExistingData

func NewRecordFromExistingData(data map[string]string) RecordInterface

type RecordQueryInterface

type RecordQueryInterface interface {
	// Validate validates the query parameters
	Validate() error

	// GetColumns returns the columns to select
	GetColumns() []string
	// SetColumns sets the columns to select
	SetColumns(columns []string) RecordQueryInterface
	// IsColumnsSet returns true if columns are set
	IsColumnsSet() bool

	// IsIDSet returns true if ID is set
	IsIDSet() bool
	// GetID returns the ID filter
	GetID() string
	// SetID sets the ID filter
	SetID(id string) RecordQueryInterface

	// IsIDInSet returns true if ID In filter is set
	IsIDInSet() bool
	// GetIDIn returns the ID In filter
	GetIDIn() []string
	// SetIDIn sets the ID In filter
	SetIDIn(idIn []string) RecordQueryInterface

	// IsTokenSet returns true if token is set
	IsTokenSet() bool
	// GetToken returns the token filter
	GetToken() string
	// SetToken sets the token filter
	SetToken(token string) RecordQueryInterface

	// IsTokenInSet returns true if token In filter is set
	IsTokenInSet() bool
	// GetTokenIn returns the token In filter
	GetTokenIn() []string
	// SetTokenIn sets the token In filter
	SetTokenIn(tokenIn []string) RecordQueryInterface

	// IsOffsetSet returns true if offset is set
	IsOffsetSet() bool
	// GetOffset returns the offset for pagination
	GetOffset() int
	// SetOffset sets the offset for pagination
	SetOffset(offset int) RecordQueryInterface

	// IsOrderBySet returns true if order by is set
	IsOrderBySet() bool
	// GetOrderBy returns the order by clause
	GetOrderBy() string
	// SetOrderBy sets the order by clause
	SetOrderBy(orderBy string) RecordQueryInterface

	// IsLimitSet returns true if limit is set
	IsLimitSet() bool
	// GetLimit returns the limit for pagination
	GetLimit() int
	// SetLimit sets the limit for pagination
	SetLimit(limit int) RecordQueryInterface

	// IsCountOnlySet returns true if count only is set
	IsCountOnlySet() bool
	// GetCountOnly returns the count only flag
	GetCountOnly() bool
	// SetCountOnly sets the count only flag
	SetCountOnly(countOnly bool) RecordQueryInterface

	// IsSortOrderSet returns true if sort order is set
	IsSortOrderSet() bool
	// GetSortOrder returns the sort order
	GetSortOrder() string
	// SetSortOrder sets the sort order
	SetSortOrder(sortOrder string) RecordQueryInterface

	// IsSoftDeletedIncludeSet returns true if soft deleted include is set
	IsSoftDeletedIncludeSet() bool
	// GetSoftDeletedInclude returns the soft deleted include flag
	GetSoftDeletedInclude() bool
	// SetSoftDeletedInclude sets the soft deleted include flag
	SetSoftDeletedInclude(softDeletedInclude bool) RecordQueryInterface
}

RecordQueryInterface defines methods for building and executing record queries. It provides a fluent interface for setting query parameters and filters.

func RecordQuery

func RecordQuery() RecordQueryInterface

RecordQuery creates a new record query

type StoreInterface

type StoreInterface interface {
	// AutoMigrate automatically migrates the database schema
	AutoMigrate() error
	// EnableDebug enables or disables debug mode
	EnableDebug(debug bool)

	// GetDbDriverName returns the database driver name
	GetDbDriverName() string
	// GetVaultTableName returns the vault table name
	GetVaultTableName() string
	// GetMetaTableName returns the meta table name
	GetMetaTableName() string

	// RecordCount returns the count of records matching the query
	RecordCount(ctx context.Context, query RecordQueryInterface) (int64, error)
	// RecordCreate creates a new record
	RecordCreate(ctx context.Context, record RecordInterface) error
	// RecordDeleteByID deletes a record by its ID
	RecordDeleteByID(ctx context.Context, recordID string) error
	// RecordDeleteByToken deletes a record by its token
	RecordDeleteByToken(ctx context.Context, token string) error
	// RecordFindByID finds a record by its ID
	RecordFindByID(ctx context.Context, recordID string) (RecordInterface, error)
	// RecordFindByToken finds a record by its token
	RecordFindByToken(ctx context.Context, token string) (RecordInterface, error)
	// RecordList returns a list of records matching the query
	RecordList(ctx context.Context, query RecordQueryInterface) ([]RecordInterface, error)
	// RecordSoftDelete soft deletes a record
	RecordSoftDelete(ctx context.Context, record RecordInterface) error
	// RecordSoftDeleteByID soft deletes a record by its ID
	RecordSoftDeleteByID(ctx context.Context, recordID string) error
	// RecordSoftDeleteByToken soft deletes a record by its token
	RecordSoftDeleteByToken(ctx context.Context, token string) error
	// RecordUpdate updates an existing record
	RecordUpdate(ctx context.Context, record RecordInterface) error

	// TokenCreate creates a new token and returns the token string
	TokenCreate(ctx context.Context, value string, password string, tokenLength int, options ...TokenCreateOptions) (token string, err error)
	// TokenCreateCustom creates a new token with a custom token string
	TokenCreateCustom(ctx context.Context, token string, value string, password string, options ...TokenCreateOptions) (err error)
	// TokenDelete deletes a token
	TokenDelete(ctx context.Context, token string) error
	// TokenExists checks if a token exists
	TokenExists(ctx context.Context, token string) (bool, error)
	// TokenRead reads the value of a token
	TokenRead(ctx context.Context, token string, password string) (string, error)
	// TokenRenew renews a token with a new expiration time
	TokenRenew(ctx context.Context, token string, expiresAt time.Time) error
	// TokensExpiredSoftDelete soft deletes all expired tokens
	TokensExpiredSoftDelete(ctx context.Context) (count int64, err error)
	// TokensExpiredDelete permanently deletes all expired tokens
	TokensExpiredDelete(ctx context.Context) (count int64, err error)
	// TokenSoftDelete soft deletes a token
	TokenSoftDelete(ctx context.Context, token string) error
	// TokenUpdate updates the value of a token
	TokenUpdate(ctx context.Context, token string, value string, password string) error
	// TokenUpsert updates or creates a token for a given value
	TokenUpsert(ctx context.Context, existingToken string, value string, password string) (newToken string, err error)

	// TokensRead reads multiple tokens at once with a single database query
	// This is more efficient than calling TokenRead multiple times
	TokensRead(ctx context.Context, tokens []string, password string) (map[string]string, error)

	// Token-based password management
	// TokensChangePassword changes the password for all tokens
	TokensChangePassword(ctx context.Context, oldPassword, newPassword string) (int, error)

	// TokensReadToResolvedMap accepts a map of key token pairs and returns a map of key value pairs
	// This is a convenience method that combines TokensRead and MapValues
	TokensReadToResolvedMap(ctx context.Context, keyTokenMap map[string]string, password string) (map[string]string, error)

	// Vault settings
	// GetVaultSetting gets a vault setting value
	GetVaultSetting(ctx context.Context, key string) (string, error)
	// SetVaultSetting sets a vault setting value
	SetVaultSetting(ctx context.Context, key, value string) error
}

StoreInterface defines the main interface for vault store operations. It provides methods for record management, token operations, and vault configuration.

The store supports: - Record CRUD operations with soft delete support - Token-based encrypted storage with expiration - Bulk token operations for improved performance - Vault settings and metadata management

type TokenCreateOptions added in v0.28.0

type TokenCreateOptions struct {
	// ExpiresAt is the expiration time for the token
	// If zero value, token never expires
	ExpiresAt time.Time
}

TokenCreateOptions contains optional parameters for token creation

Jump to

Keyboard shortcuts

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