Documentation
¶
Index ¶
- Constants
- Variables
- func LoadRef(ref string) ([]byte, error)
- func ValidateRef(ref string) error
- type MemoryStore
- type Pool
- func (p *Pool) Add(v Version) error
- func (p *Pool) List() []Version
- func (p *Pool) ListMetadata() []VersionMetadata
- func (p *Pool) MaxVersions() int
- func (p *Pool) Name() string
- func (p *Pool) PruneExpired(now time.Time) []string
- func (p *Pool) Remove(id string) (Version, bool)
- func (p *Pool) Replace(versions []Version) error
- func (p *Pool) Runtime() bool
- func (p *Pool) Size() int
- func (p *Pool) ValidAt(t time.Time) []Version
- type Record
- type Registry
- type Sealer
- type Set
- type Store
- type Version
- type VersionMetadata
Constants ¶
const DefaultMaxVersions = 32
DefaultMaxVersions caps how many versions a Pool may hold before Add returns ErrPoolFull. Operators can override per-pool via the `max_versions` DSL flag.
Variables ¶
var ( ErrSealKeyMissing = errors.New("secrets: encryption key is empty") ErrSealKeyLength = errors.New("secrets: encryption key must decode to 32 bytes") ErrSealedTruncated = errors.New("secrets: sealed payload is truncated") ErrSealedVersion = errors.New("secrets: unsupported sealed version") ErrSealedAuthFailure = errors.New("secrets: sealed payload failed authentication") )
var ( ErrDuplicateID = errors.New("secrets: version id already present in pool") ErrPoolFull = errors.New("secrets: pool is full") ErrInvalidWindow = errors.New("secrets: valid_until must be after valid_from") ErrEmptyID = errors.New("secrets: version id is empty") ErrEmptyValue = errors.New("secrets: version value is empty") ErrMissingFrom = errors.New("secrets: version valid_from is missing") )
var ( ErrPoolExists = errors.New("secrets: pool already registered") ErrPoolNotFound = errors.New("secrets: pool not found") )
var (
ErrInvalidSecret = errors.New("invalid secret")
)
var ErrSecretRef = errors.New("invalid secret reference")
Functions ¶
func LoadRef ¶
LoadRef loads a secret value from a reference string.
Supported forms: - env:NAME - file:/path/to/secret - raw:literal-value (intended for tests/dev only) - vault:secret/path[#field] (reads from Vault HTTP API using env-configured address/token)
func ValidateRef ¶
ValidateRef validates a secret reference format without loading its value.
Supported forms: - env:NAME - file:/path/to/secret - raw:literal-value - vault:secret/path[#field]
Types ¶
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an in-memory Store. It is the default when no persistent backend is configured, and it is used throughout tests.
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
func (*MemoryStore) ListAll ¶
func (m *MemoryStore) ListAll() ([]Record, error)
func (*MemoryStore) ListByPool ¶
func (m *MemoryStore) ListByPool(poolName string) ([]Record, error)
func (*MemoryStore) Upsert ¶
func (m *MemoryStore) Upsert(rec Record) error
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool holds a named, mutable, thread-safe collection of secret Versions.
Reads (ValidAt, List, ListMetadata, Size) use RLock and are cheap. Writes (Add, Remove, Replace) use Lock.
Pointer identity matters: route-level closures built in internal/app/run.go capture *Pool and call ValidAt on every inbound request. Config reloads must mutate the pool in place (Add, Remove, Replace) rather than creating a new Pool, so that in-flight verification still sees fresh state.
func NewPool ¶
NewPool constructs a pool with an initial set of versions. If maxVersions <= 0, DefaultMaxVersions is used. The seed slice is validated and copied.
func (*Pool) Add ¶
Add inserts v. Returns ErrDuplicateID, ErrPoolFull, or a validation error. If the pool is at capacity, Add first tries to prune versions whose ValidUntil is in the past (relative to time.Now()).
func (*Pool) ListMetadata ¶
func (p *Pool) ListMetadata() []VersionMetadata
ListMetadata returns id + validity window for each version, without the secret value. Admin GET handlers use this.
func (*Pool) MaxVersions ¶
func (*Pool) PruneExpired ¶
PruneExpired removes every version whose ValidUntil is non-zero and not after now, and returns the removed version IDs. Versions with zero ValidUntil (unbounded) are never pruned. Safe for concurrent callers; uses the pool's write lock.
Used by the periodic secret-GC sweeper (internal/app) so that expired-but- not-at-cap versions do not accumulate indefinitely in memory or in the persisted runtime_secrets table. Add() already prunes opportunistically when the pool is at max_versions; PruneExpired is the unconditional variant.
func (*Pool) Remove ¶
Remove deletes the version with the given id. Returns (removed, true) if found.
func (*Pool) Replace ¶
Replace swaps the entire version set. It validates each entry and enforces the maxVersions cap. Used at startup (seeding from Store) and during config reload for non-runtime pools.
type Record ¶
type Record struct {
PoolName string
ID string
Sealed []byte
NotBefore time.Time
NotAfter time.Time // zero = unbounded
CreatedAt time.Time
}
Record is the persisted shape of a single Pool entry. The Sealed field must already be AES-GCM wrapped (see Sealer); Store implementations never see the plaintext secret.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps secret names to *Pool. Route-level closures capture pointers from here, so the registry is the single source of truth for "which pool does this secret_ref resolve to".
Pointer identity of a Pool must survive config reload for runtime-mutable pools. Callers achieve this by calling Pool(name) and mutating via Pool.Add/Remove/Replace, rather than creating a fresh Pool and re-registering.
func NewRegistry ¶
func NewRegistry() *Registry
func (*Registry) Register ¶
Register inserts a pool under its name. Returns ErrPoolExists if the name is already taken by a different pool. Re-registering the exact same *Pool is a no-op (idempotent).
func (*Registry) Unregister ¶
Unregister removes a pool. Returns false if the name was not present. In-flight closures that captured the *Pool pointer continue to see the (now unreferenced-by-registry) pool until they are rebuilt on reload.
type Sealer ¶
type Sealer struct {
// contains filtered or unexported fields
}
Sealer wraps plaintext secrets in AES-256-GCM. The output layout is:
[1B version=0x01][12B nonce][N-byte ciphertext || 16B tag]
A version prefix is included so that future algorithm changes (e.g. key rotation via KMS, a different AEAD) can be rolled out without breaking stored records.
func NewSealer ¶
NewSealer decodes a base64-encoded 32-byte key and constructs an AES-256-GCM AEAD. Whitespace around the key is tolerated; both standard and URL encodings are accepted, with or without padding.
type Set ¶
type Set struct {
Versions []Version
}
func (Set) SigningAt ¶
SigningAt returns the newest (most recent ValidFrom) secret that is valid at time t.
type Store ¶
type Store interface {
// Upsert inserts or replaces the record keyed by (PoolName, ID).
Upsert(rec Record) error
// Delete removes the record with the given (poolName, id). Returns true if
// something was removed, false if not found. A missing record is not an error.
Delete(poolName, id string) (bool, error)
// ListByPool returns all records for the pool. Order is unspecified.
ListByPool(poolName string) ([]Record, error)
// ListAll returns records for every pool. Used on startup to hydrate all
// registered pools in one round trip.
ListAll() ([]Record, error)
}
Store persists runtime-added secrets so they survive process restart.
Implementations must be safe for concurrent use. Upsert is idempotent on (PoolName, ID): a second call with the same key replaces the previous record. This keeps the store tolerant of admin-API retries and reload replays.
The Store does not know about Version validity windows at the application layer — those live in Pool. The Store only stores and returns Records.
type Version ¶
Version is a single secret value with a validity window.
Semantics: - ValidFrom is inclusive. - ValidUntil is exclusive; a zero value means "no end".
type VersionMetadata ¶
type VersionMetadata struct {
ID string `json:"id"`
ValidFrom time.Time `json:"not_before"`
ValidUntil time.Time `json:"not_after,omitempty"`
}
VersionMetadata is the safe-to-expose subset of a Version: everything except the secret material itself. Admin GET endpoints return this shape.