Documentation
¶
Overview ¶
Package secret is the relay's unified secret-resolution layer: a backend-agnostic Ref pointing at a credential, and a Resolver that turns it into plaintext. It is the foundation for pluggable secret backends — today the built-in env and stored (AES-GCM in Postgres) resolvers, later vendorable subpackages for Vault, AWS Secrets Manager, etc.
Every secret the relay handles — upstream HostKey credentials AND the relay's own native secrets (e.g. object-store keys) — resolves through this one seam, so adding a backend is additive rather than a rewrite.
pkg purity: this package imports only pkg/crypto. The stored resolver's persistence is injected via the Store interface, whose Postgres implementation lives in app/ — keeping pkg/secret vendorable.
Index ¶
- type EnvResolver
- type Kind
- type MaxVersioner
- type Ref
- type Registry
- type Resolver
- type RotateResult
- type Rotator
- type Store
- type StoredResolver
- func (s *StoredResolver) Create(ctx context.Context, id string, plaintext []byte) (Ref, error)
- func (s *StoredResolver) Delete(ctx context.Context, id string) error
- func (s *StoredResolver) KeyVersion() int32
- func (s *StoredResolver) LoadKeyVersion(ctx context.Context) error
- func (s *StoredResolver) Resolve(ctx context.Context, ref Ref) ([]byte, error)
- func (s *StoredResolver) Rotate(ctx context.Context, newKey []byte) (RotateResult, error)
- func (s *StoredResolver) SetMasterKey(masterKey []byte, keyVersion int32)
- type StoredRow
- type Writer
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EnvResolver ¶
type EnvResolver struct{}
EnvResolver resolves KindEnv refs from the process environment. Pure — no persistence, no master key. An unset or empty variable is a loud error: a secret that silently resolves empty is worse than a failure.
type Kind ¶
type Kind string
Kind identifies which backend resolves a Ref.
const ( // KindEnv reads plaintext from an OS environment variable. No // ciphertext is ever persisted. KindEnv Kind = "env" // KindStored decrypts AES-GCM ciphertext held in the relay's own // store (Postgres today) using the master key. KindStored Kind = "stored" // KindAWS fetches a secret from AWS Secrets Manager. Fetch-only: the // secret lives in AWS, relay holds the resolved value in memory only. // Locator is Path = "<secretName>[:<jsonKey>]". KindAWS Kind = "aws" // KindBitwarden fetches a secret from a Bitwarden/Vaultwarden server, // decrypted client-side. Fetch-only; held in memory only. Locator is // Path = "<itemNameOrID>[/<field>]" (field defaults to "password"). KindBitwarden Kind = "bitwarden" // KindGCP fetches a secret version from GCP Secret Manager. Fetch-only, // in-memory. Locator is Path = "<secretName>[:<version>]" (version // defaults to "latest"); project comes from resolver config. KindGCP Kind = "gcp" // KindAzure fetches a secret from an Azure Key Vault. Fetch-only, // in-memory. Locator is Path = "<secretName>[/<version>]"; the vault // URL comes from resolver config. KindAzure Kind = "azure" // KindOnePassword resolves a secret via the official 1Password SDK // (service-account token). Fetch-only, in-memory. Locator is Path = // "op://<vault>/<item>/<field>". KindOnePassword Kind = "onepassword" )
type MaxVersioner ¶
MaxVersioner optionally reports the highest key_version stored, so the resolver can align its in-memory version at boot.
type Ref ¶
type Ref struct {
Kind Kind `json:"kind"`
// Env is the OS variable name when Kind == KindEnv.
Env string `json:"env,omitempty"`
// ID is the backend key when Kind == KindStored (and future
// id-addressed backends): the secret_values row id.
ID string `json:"id,omitempty"`
// Path is the locator for path-addressed external backends (KindAWS,
// KindBitwarden, …): an opaque per-backend reference string the
// resolver parses (e.g. "prod/openai-key:apiKey", "openai-key/password").
Path string `json:"path,omitempty"`
}
Ref is a backend-agnostic pointer to a secret. It is JSON-serializable so it can live inside a spec or settings JSONB blob. Exactly one locator field is meaningful per Kind.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry dispatches a Ref to the Resolver registered for its Kind. Built once at boot (Register is not safe for concurrent use with Resolve); Resolve is safe for concurrent use thereafter.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry returns an empty registry. Register backends before use.
type Resolver ¶
Resolver turns a Ref into plaintext. Implementations handle one Kind and register with a Registry. Resolution is expected off the hot path (load-time / boot-time), so implementations may do I/O.
type RotateResult ¶
RotateResult is the outcome of a successful Rotate.
type Rotator ¶
type Rotator interface {
// Rotate re-encrypts every stored value within one transaction,
// stamping newKeyVersion. reencrypt maps old (ct, nonce) → new.
Rotate(ctx context.Context, newKeyVersion int32,
reencrypt func(ct, nonce []byte) (newCt, newNonce []byte, err error)) (rotated int, err error)
}
Rotator is the optional transactional re-encryption seam. The store implements the transaction boundary; pkg/secret supplies the crypto via the reencrypt callback, so all key material stays in this package.
type Store ¶
type Store interface {
// Get returns the ciphertext, nonce, and key version for id, or an
// error if absent.
Get(ctx context.Context, id string) (ciphertext, nonce []byte, keyVersion int32, err error)
// Put writes (overwriting) the ciphertext for id.
Put(ctx context.Context, id string, ciphertext, nonce []byte, keyVersion int32) error
// Delete removes the secret at id. Deleting a missing id is not an
// error (idempotent cleanup).
Delete(ctx context.Context, id string) error
}
Store is the persistence seam for stored-secret ciphertext. Its Postgres implementation lives in app/ (keeping pkg/secret vendorable). Ciphertext + nonce are AES-GCM outputs; key_version records which master-key generation encrypted the row, for rotation.
type StoredResolver ¶
type StoredResolver struct {
// contains filtered or unexported fields
}
StoredResolver decrypts AES-GCM ciphertext from a Store using the master key. The key is held behind an RWMutex so rotation (SetMasterKey) can swap it on a live process without a restart, mirroring the prior hostkey-store behavior.
func NewStoredResolver ¶
func NewStoredResolver(store Store, masterKey []byte, keyVersion int32) *StoredResolver
NewStoredResolver constructs a resolver over store, encrypting new secrets at keyVersion with masterKey.
func (*StoredResolver) Create ¶
Create encrypts plaintext under the current master key and persists it at id, returning a stored Ref. Overwrites any existing secret at id.
func (*StoredResolver) Delete ¶
func (s *StoredResolver) Delete(ctx context.Context, id string) error
Delete removes the stored secret at id (idempotent).
func (*StoredResolver) KeyVersion ¶
func (s *StoredResolver) KeyVersion() int32
KeyVersion returns the current in-memory master-key version.
func (*StoredResolver) LoadKeyVersion ¶
func (s *StoredResolver) LoadKeyVersion(ctx context.Context) error
LoadKeyVersion aligns the in-memory version to the store's maximum, so new secrets are tagged with the generation operators last rotated to. No-op when the store doesn't report a max or the table is empty.
func (*StoredResolver) Rotate ¶
func (s *StoredResolver) Rotate(ctx context.Context, newKey []byte) (RotateResult, error)
Rotate re-encrypts every stored secret with newKey in a single store transaction, then swaps the live master key so subsequent Resolve/Create use it without a restart. The caller persists newKey to the deployment's RELAY_MASTER_KEY so future boots can decrypt.
func (*StoredResolver) SetMasterKey ¶
func (s *StoredResolver) SetMasterKey(masterKey []byte, keyVersion int32)
SetMasterKey swaps the active master key + version (post-rotation).
type Writer ¶
type Writer interface {
// Create persists plaintext under id and returns a Ref resolving to
// it. Overwrites any existing secret at id.
Create(ctx context.Context, id string, plaintext []byte) (Ref, error)
}
Writer is the optional create side: a backend that can persist a new secret and hand back a Ref addressing it. env-style backends are read-only and do not implement it.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package aws resolves secret.KindAWS refs by fetching from AWS Secrets Manager.
|
Package aws resolves secret.KindAWS refs by fetching from AWS Secrets Manager. |
|
Package azure resolves secret.KindAzure refs by fetching from Azure Key Vault.
|
Package azure resolves secret.KindAzure refs by fetching from Azure Key Vault. |
|
Package bitwarden resolves KindBitwarden secrets by fetching encrypted vault data from Vaultwarden/Bitwarden and decrypting client-side.
|
Package bitwarden resolves KindBitwarden secrets by fetching encrypted vault data from Vaultwarden/Bitwarden and decrypting client-side. |
|
Package gcp resolves secret.KindGCP refs by fetching from GCP Secret Manager.
|
Package gcp resolves secret.KindGCP refs by fetching from GCP Secret Manager. |
|
Package onepassword resolves secret.KindOnePassword refs via the official 1Password Go SDK and a service-account token.
|
Package onepassword resolves secret.KindOnePassword refs via the official 1Password Go SDK and a service-account token. |