Documentation
¶
Overview ¶
Package secrets implements VORTEX's encrypted secret store (build plan M3.2): a key-value store for arbitrary secret strings (database passwords, API keys, JWT secrets) encrypted at rest with XChaCha20-Poly1305. It is distinct from the vtls cert store — that holds TLS certificates; this holds secret values that are injected into managed processes at runtime and never written to config or disk in plaintext (Non-Negotiable Rule #2).
Index ¶
- Constants
- func InjectEnv(store *SecretStore, keys, existing []string) ([]string, error)
- func Resolve(store *SecretStore, keys []string) (map[string]string, error)
- func ResolveAdapter(ctx context.Context, a Adapter, keys []string) (map[string]string, error)
- func ValidateKeys(keys []string) error
- func ValidateName(name string) error
- type AWSSSMAdapter
- func (a *AWSSSMAdapter) Delete(ctx context.Context, name string) error
- func (a *AWSSSMAdapter) Get(ctx context.Context, name string) (string, error)
- func (a *AWSSSMAdapter) List(ctx context.Context) ([]string, error)
- func (a *AWSSSMAdapter) Ping(ctx context.Context) error
- func (a *AWSSSMAdapter) Set(ctx context.Context, name, value string) error
- type AWSSSMConfig
- type Adapter
- type AdapterConfig
- type GCPSMAdapter
- func (a *GCPSMAdapter) Delete(ctx context.Context, name string) error
- func (a *GCPSMAdapter) Get(ctx context.Context, name string) (string, error)
- func (a *GCPSMAdapter) List(ctx context.Context) ([]string, error)
- func (a *GCPSMAdapter) Ping(ctx context.Context) error
- func (a *GCPSMAdapter) Set(ctx context.Context, name, value string) error
- type GCPSMConfig
- type LocalAdapter
- func (a *LocalAdapter) Delete(_ context.Context, name string) error
- func (a *LocalAdapter) Get(_ context.Context, name string) (string, error)
- func (a *LocalAdapter) List(_ context.Context) ([]string, error)
- func (a *LocalAdapter) Ping(_ context.Context) error
- func (a *LocalAdapter) Set(_ context.Context, name, value string) error
- type RotationAlert
- type SecretMetadata
- type SecretStore
- func (s *SecretStore) Adapter() Adapter
- func (s *SecretStore) CanDecrypt() bool
- func (s *SecretStore) CheckRotation() ([]RotationAlert, error)
- func (s *SecretStore) Delete(name string) error
- func (s *SecretStore) DueForRotation(name string) (bool, error)
- func (s *SecretStore) Exists(name string) (bool, error)
- func (s *SecretStore) Get(name string) (string, error)
- func (s *SecretStore) GetMetadata(name string) (*SecretMetadata, error)
- func (s *SecretStore) IsExpired(name string) (bool, error)
- func (s *SecretStore) List() ([]string, error)
- func (s *SecretStore) Rekey(newKey []byte) error
- func (s *SecretStore) Set(name, value string) error
- func (s *SecretStore) SetWithMetadata(name, value string, meta SecretMetadata) error
- type VaultAdapter
- func (v *VaultAdapter) Delete(ctx context.Context, name string) error
- func (v *VaultAdapter) Get(ctx context.Context, name string) (string, error)
- func (v *VaultAdapter) List(ctx context.Context) ([]string, error)
- func (v *VaultAdapter) Ping(ctx context.Context) error
- func (v *VaultAdapter) Set(ctx context.Context, name, value string) error
- type VaultConfig
Constants ¶
const RotationWarningWindow = 7 * 24 * time.Hour
RotationWarningWindow is how far ahead of an expiry or rotation deadline DueForRotation starts reporting true.
Variables ¶
This section is empty.
Functions ¶
func InjectEnv ¶
func InjectEnv(store *SecretStore, keys, existing []string) ([]string, error)
InjectEnv resolves the secrets named by keys and merges them into a copy of existing (os.Environ format: "KEY=value"). A secret value overrides any existing entry with the same key. The input slice is not modified.
func Resolve ¶
func Resolve(store *SecretStore, keys []string) (map[string]string, error)
Resolve looks up every key in keys and returns a map of key→decrypted value. Resolution is all-or-nothing: if any key is missing (or fails to decrypt), it returns an error naming the offending key and no partial map.
func ResolveAdapter ¶
ResolveAdapter is the Adapter-based counterpart of Resolve: it looks up every key through any secret backend (local or external). Resolution is all-or-nothing, naming the first missing or failing key.
func ValidateKeys ¶
ValidateKeys checks that every name in keys is a legal secret name. It returns an error listing all invalid names, or nil if all are valid.
func ValidateName ¶
ValidateName reports whether name is a legal secret name (alphanumeric and underscore only).
Types ¶
type AWSSSMAdapter ¶
type AWSSSMAdapter struct {
// contains filtered or unexported fields
}
AWSSSMAdapter is a secret Adapter backed by AWS SSM Parameter Store, talking to the JSON-1.1 API directly with a minimal SigV4 signer (no AWS SDK).
func (*AWSSSMAdapter) Delete ¶
func (a *AWSSSMAdapter) Delete(ctx context.Context, name string) error
Delete removes the named parameter. It is idempotent (ParameterNotFound is not an error).
func (*AWSSSMAdapter) List ¶
func (a *AWSSSMAdapter) List(ctx context.Context) ([]string, error)
List returns parameter names under the prefix (with the prefix stripped).
type AWSSSMConfig ¶
type AWSSSMConfig struct {
Region string // AWS region
Prefix string // parameter path prefix; default "/vortex/"
AccessKey string // from env AWS_ACCESS_KEY_ID
SecretKey string // from env AWS_SECRET_ACCESS_KEY
}
AWSSSMConfig configures the AWS SSM Parameter Store adapter.
type Adapter ¶
type Adapter interface {
Get(ctx context.Context, name string) (string, error)
Set(ctx context.Context, name, value string) error
List(ctx context.Context) ([]string, error)
Delete(ctx context.Context, name string) error
// Ping checks connectivity to the backend, returning nil when healthy.
Ping(ctx context.Context) error
}
Adapter is a pluggable secret backend. The local store and the external providers (Vault, AWS SSM, GCP Secret Manager) all satisfy it, so the rest of VORTEX resolves secrets through one interface regardless of where they live.
func NewAWSSSMAdapter ¶
func NewAWSSSMAdapter(cfg AWSSSMConfig) (Adapter, error)
NewAWSSSMAdapter builds an AWSSSMAdapter. Region is required; Prefix defaults to "/vortex/".
func NewAdapter ¶
func NewAdapter(cfg AdapterConfig) (Adapter, error)
NewAdapter constructs the Adapter selected by cfg.Kind, validating that the fields required for that backend are present.
func NewGCPSMAdapter ¶
func NewGCPSMAdapter(cfg GCPSMConfig) (Adapter, error)
NewGCPSMAdapter builds a GCPSMAdapter. ProjectID is required; Prefix defaults to "vortex-". If CredFile is set it is loaded as a service-account JSON; otherwise the GCE metadata server is used for tokens.
func NewVaultAdapter ¶
func NewVaultAdapter(cfg VaultConfig) (Adapter, error)
NewVaultAdapter builds a VaultAdapter. Address is required; MountPath defaults to "secret" and Prefix to "vortex/".
type AdapterConfig ¶
type AdapterConfig struct {
Kind string // "local" | "vault" | "aws-ssm" | "gcp-sm"
Local *SecretStore
Vault VaultConfig
AWSSSM AWSSSMConfig
GCPSM GCPSMConfig
}
AdapterConfig selects and configures a secret backend.
type GCPSMAdapter ¶
type GCPSMAdapter struct {
// contains filtered or unexported fields
}
GCPSMAdapter is a secret Adapter backed by GCP Secret Manager's REST API, implemented directly over net/http (no google.golang.org SDK). Auth is via an OAuth2 access token obtained either from a service-account JSON (signed JWT exchanged for a token) or, when no credentials file is given, the GCE metadata server.
func (*GCPSMAdapter) Delete ¶
func (a *GCPSMAdapter) Delete(ctx context.Context, name string) error
Delete removes the secret and all its versions. It is idempotent (404 is not an error).
func (*GCPSMAdapter) List ¶
func (a *GCPSMAdapter) List(ctx context.Context) ([]string, error)
List returns secret names under the prefix (with the prefix stripped).
type GCPSMConfig ¶
type GCPSMConfig struct {
ProjectID string // GCP project ID
Prefix string // secret name prefix; default "vortex-"
CredFile string // path to a service account JSON, or "" for ADC/metadata
}
GCPSMConfig configures the GCP Secret Manager adapter.
type LocalAdapter ¶
type LocalAdapter struct {
// contains filtered or unexported fields
}
LocalAdapter adapts the on-disk encrypted SecretStore to the Adapter interface. It is always available, so Ping never fails.
func NewLocalAdapter ¶
func NewLocalAdapter(store *SecretStore) *LocalAdapter
NewLocalAdapter wraps store in an Adapter.
type RotationAlert ¶ added in v0.3.0
type RotationAlert struct {
Name string
Expired bool // true: past expiry; false: rotation due
Deadline time.Time // the expiry or rotation deadline that triggered this
}
RotationAlert describes one secret needing operator attention: already expired, or due for rotation within the warning window.
type SecretMetadata ¶ added in v0.3.0
type SecretMetadata struct {
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at,omitzero"` // zero = never expires
LastRotated time.Time `json:"last_rotated"`
RotateEvery time.Duration `json:"rotate_every,omitempty"` // 0 = manual only
Version int `json:"version"`
}
SecretMetadata tracks a secret's lifecycle for expiry and rotation alerts (build plan M19).
type SecretStore ¶
type SecretStore struct {
// contains filtered or unexported fields
}
SecretStore persists secret values on disk, each encrypted with XChaCha20-Poly1305 (24-byte nonce). The encryption key is derived from caller-supplied key material via SHA-256.
func NewSecretStore ¶
func NewSecretStore(path string, key []byte) (*SecretStore, error)
NewSecretStore opens (creating if needed) a secret store at path, deriving the 32-byte key from key material via SHA-256.
func (*SecretStore) Adapter ¶
func (s *SecretStore) Adapter() Adapter
Adapter returns this store wrapped as a local Adapter, so callers can treat the on-disk store uniformly with the external secret backends.
func (*SecretStore) CanDecrypt ¶ added in v0.3.0
func (s *SecretStore) CanDecrypt() bool
CanDecrypt reports whether at least one stored secret decrypts with this store's key — a cheap probe to detect whether a store is on this key (used by migration to decide whether re-keying is needed).
func (*SecretStore) CheckRotation ¶ added in v0.3.0
func (s *SecretStore) CheckRotation() ([]RotationAlert, error)
CheckRotation scans every stored secret's metadata and returns alerts for expired and rotation-due secrets, for the startup check and notifications.
func (*SecretStore) Delete ¶
func (s *SecretStore) Delete(name string) error
Delete removes the secret named name. It is idempotent: a missing secret is not an error.
func (*SecretStore) DueForRotation ¶ added in v0.3.0
func (s *SecretStore) DueForRotation(name string) (bool, error)
DueForRotation reports whether name is inside the warning window (7 days) of its expiry, or past LastRotated+RotateEvery minus the window when a rotation interval is set. Secrets without metadata are never due.
func (*SecretStore) Exists ¶
func (s *SecretStore) Exists(name string) (bool, error)
Exists reports whether a secret named name is set, without decrypting it.
func (*SecretStore) Get ¶
func (s *SecretStore) Get(name string) (string, error)
Get decrypts and returns the value stored under name. It returns os.ErrNotExist if no such secret is set.
func (*SecretStore) GetMetadata ¶ added in v0.3.0
func (s *SecretStore) GetMetadata(name string) (*SecretMetadata, error)
GetMetadata returns the lifecycle metadata for name, or os.ErrNotExist when the secret has none (set via plain Set, or never set).
func (*SecretStore) IsExpired ¶ added in v0.3.0
func (s *SecretStore) IsExpired(name string) (bool, error)
IsExpired reports whether name's expiry has passed. Secrets without metadata or without an ExpiresAt never expire.
func (*SecretStore) List ¶
func (s *SecretStore) List() ([]string, error)
List returns the names of all stored secrets.
func (*SecretStore) Rekey ¶ added in v0.3.0
func (s *SecretStore) Rekey(newKey []byte) error
Rekey re-encrypts every stored secret from this store's current key to newKey, used by the master-key migration (production audit C1) to move legacy cluster-name-keyed stores onto the master-derived key. On success the store's in-memory key is updated to newKey. Metadata files are plaintext JSON and are left untouched. It is best-effort atomic per file: each secret is decrypted, re-encrypted, and rewritten; a mid-run failure leaves already-converted files on the new key and the rest on the old, but the returned error names the failure so the caller can retry.
func (*SecretStore) Set ¶
func (s *SecretStore) Set(name, value string) error
Set encrypts value and writes it under name.
func (*SecretStore) SetWithMetadata ¶ added in v0.3.0
func (s *SecretStore) SetWithMetadata(name, value string, meta SecretMetadata) error
SetWithMetadata stores value under name (encrypted, like Set) and records lifecycle metadata beside it. Zero CreatedAt/LastRotated default to now; a zero Version auto-increments from the previous metadata (starting at 1).
type VaultAdapter ¶
type VaultAdapter struct {
// contains filtered or unexported fields
}
VaultAdapter is a secret Adapter backed by HashiCorp Vault's KV v2 engine, implemented directly over the HTTP API (no Vault SDK).
func (*VaultAdapter) Delete ¶
func (v *VaultAdapter) Delete(ctx context.Context, name string) error
Delete removes name. It is idempotent (404 is not an error).
func (*VaultAdapter) Get ¶
Get returns the value stored at name (the "value" field of the KV v2 entry).
func (*VaultAdapter) List ¶
func (v *VaultAdapter) List(ctx context.Context) ([]string, error)
List returns the secret names under the prefix.
type VaultConfig ¶
type VaultConfig struct {
Address string // e.g. https://vault.example.com
Token string // Vault token (typically from env VAULT_TOKEN)
MountPath string // KV mount path; default "secret"
Prefix string // key prefix; default "vortex/"
}
VaultConfig configures the HashiCorp Vault KV v2 adapter.