secret

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Apr 21, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package secret provides encrypted secrets management for bc workspaces.

Index

Constants

View Source
const PassphraseEnvVar = "BC_SECRET_PASSPHRASE" //nolint:gosec // not a credential, env var name constant

PassphraseEnvVar is the environment variable for the master passphrase.

Variables

This section is empty.

Functions

func Decrypt

func Decrypt(key []byte, encoded string) ([]byte, error)

Decrypt decrypts base64-encoded ciphertext (with prepended nonce) using AES-256-GCM.

func DeriveKey

func DeriveKey(passphrase string, salt []byte) []byte

DeriveKey derives an AES-256 key from a passphrase and salt using PBKDF2-SHA256.

func Encrypt

func Encrypt(key, plaintext []byte) (string, error)

Encrypt encrypts plaintext using AES-256-GCM with the given key. Returns base64-encoded ciphertext with the nonce prepended.

func GenerateSalt

func GenerateSalt() ([]byte, error)

GenerateSalt returns a cryptographically random salt.

func Passphrase

func Passphrase() (string, error)

Passphrase returns the passphrase for secret encryption. Priority: BC_SECRET_PASSPHRASE env var > auto-generated key file at ~/.bc/secret-key. The key file is created with 0600 permissions on first use.

Types

type LayeredStore added in v0.2.0

type LayeredStore struct {
	// contains filtered or unexported fields
}

LayeredStore composes a user-global vault with an optional workspace-local Store. Reads check workspace first, falling back to global. Writes default to the global vault; callers can target the workspace layer with the *Workspace methods or SetWithScope.

Each underlying Store keeps its own encryption key (derived from the same passphrase + a per-vault salt) so the two files stay independently decryptable. Callers close a LayeredStore with Close() which closes both underlying Stores; ownership flows through the wrapper.

func NewLayeredStore added in v0.2.0

func NewLayeredStore(global, workspace *Store) *LayeredStore

NewLayeredStore wraps a global vault and an optional workspace Store. Either argument may be nil, but at least one must be non-nil or every call will error.

func (*LayeredStore) Close added in v0.2.0

func (l *LayeredStore) Close() error

Close closes both underlying stores, returning the first error.

func (*LayeredStore) Delete added in v0.2.0

func (l *LayeredStore) Delete(name string) error

Delete removes from whichever layer owns the name, preferring workspace. Returns an error when the secret exists in neither.

func (*LayeredStore) DeleteScoped added in v0.2.0

func (l *LayeredStore) DeleteScoped(scope Scope, name string) error

DeleteScoped removes from a specific scope. Useful for "bc secret delete NAME --global" / "--workspace".

func (*LayeredStore) GetMeta added in v0.2.0

func (l *LayeredStore) GetMeta(name string) (*SecretMeta, error)

GetMeta returns the metadata from whichever layer owns the name, with the workspace override winning. The returned meta's Scope field is populated to reflect the owning layer.

func (*LayeredStore) GetValue added in v0.2.0

func (l *LayeredStore) GetValue(name string) (string, error)

GetValue returns the decrypted value, preferring the workspace scope.

func (*LayeredStore) Global added in v0.2.0

func (l *LayeredStore) Global() *Store

Global returns the underlying user-global Store (never nil when the LayeredStore was constructed correctly).

func (*LayeredStore) List added in v0.2.0

func (l *LayeredStore) List() ([]*SecretMeta, error)

List returns metadata for every secret in both layers. When a name exists in both, the workspace entry wins and scope is reported as "workspace".

func (*LayeredStore) ResolveEnv added in v0.2.0

func (l *LayeredStore) ResolveEnv(env map[string]string) map[string]string

ResolveEnv substitutes ${secret:NAME} in each value. Names are resolved with workspace-over-global precedence.

func (*LayeredStore) Set added in v0.2.0

func (l *LayeredStore) Set(name, value, description string) error

Set writes to the user-global vault (the default for bc secret add KEY=VAL). Use SetWorkspace for per-workspace overrides.

func (*LayeredStore) SetWithScope added in v0.2.0

func (l *LayeredStore) SetWithScope(scope Scope, name, value, description string) error

SetWithScope chooses a layer explicitly; empty scope defaults to global when it exists, else workspace.

func (*LayeredStore) SetWorkspace added in v0.2.0

func (l *LayeredStore) SetWorkspace(name, value, description string) error

SetWorkspace writes to the per-workspace override store.

func (*LayeredStore) Workspace added in v0.2.0

func (l *LayeredStore) Workspace() *Store

Workspace returns the optional per-workspace override Store.

type PostgresStore

type PostgresStore struct {
	// contains filtered or unexported fields
}

PostgresStore provides Postgres-backed encrypted secrets storage.

func NewPostgresStore

func NewPostgresStore(db *sql.DB) *PostgresStore

NewPostgresStore creates a PostgresStore from an existing *sql.DB connection.

func (*PostgresStore) Close

func (p *PostgresStore) Close() error

Close closes the database connection. Close is a no-op — the shared DB is owned by the caller.

func (*PostgresStore) Delete

func (p *PostgresStore) Delete(name string) error

Delete removes a secret.

func (*PostgresStore) GetMeta

func (p *PostgresStore) GetMeta(name string) (*SecretMeta, error)

GetMeta returns metadata for a secret (no value).

func (*PostgresStore) GetValue

func (p *PostgresStore) GetValue(name string) (string, error)

GetValue retrieves and decrypts a secret value.

func (*PostgresStore) InitKey

func (p *PostgresStore) InitKey(passphrase string) error

InitKey derives or loads the encryption key from the passphrase.

func (*PostgresStore) InitSchema

func (p *PostgresStore) InitSchema() error

InitSchema creates the secrets tables in Postgres if they don't exist.

func (*PostgresStore) List

func (p *PostgresStore) List() ([]*SecretMeta, error)

List returns metadata for all secrets (no values).

func (*PostgresStore) ResolveEnv

func (p *PostgresStore) ResolveEnv(env map[string]string) map[string]string

ResolveEnv resolves ${secret:NAME} references in env vars.

func (*PostgresStore) Set

func (p *PostgresStore) Set(name, value, description string) error

Set creates or updates a secret with an encrypted value.

type Scope added in v0.2.0

type Scope string

Scope distinguishes which layer of a layered secret store holds a value. It is a runtime attribute reported in SecretMeta.Scope.

const (
	// ScopeGlobal is the user-global vault (~/.bc/secrets.vault).
	ScopeGlobal Scope = "global"
	// ScopeWorkspace is the per-workspace override
	// (<ws>/.bc/secrets.db).
	ScopeWorkspace Scope = "workspace"
)

type SecretMeta

type SecretMeta struct {
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	Name        string    `json:"name"`
	Description string    `json:"description,omitempty"`
	Scope       Scope     `json:"scope,omitempty"`
}

SecretMeta holds secret metadata (never includes the value).

Scope is runtime-only and reports which layer of a LayeredStore owns the secret ("global" or "workspace"). Plain-store lookups leave it empty.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store provides encrypted secrets storage backed by SQLite or Postgres. Values are encrypted with AES-256-GCM; the encryption key is derived from a master passphrase via PBKDF2.

func NewStore

func NewStore(workspacePath, passphrase string) (*Store, error)

NewStore creates a new secrets store for the given workspace path. The passphrase is used to derive the encryption key via PBKDF2.

func OpenStore

func OpenStore(workspacePath, passphrase string) (*Store, error)

OpenStore opens the secrets store using the shared workspace database. Uses the shared driver type to determine the backend (timescale or sqlite). The secret store keeps encryption isolation — its own salt and key derivation.

func OpenVaultFile added in v0.2.0

func OpenVaultFile(path, passphrase string) (*Store, error)

OpenVaultFile opens a Store using an explicit SQLite path instead of the conventional "<workspacePath>/.bc/secrets.db". Used for the user-global vault at ~/.bc/secrets.vault where there is no "workspace" to anchor against. Directory must exist.

func (*Store) Close

func (s *Store) Close() error

Close closes the database connection.

func (*Store) Delete

func (s *Store) Delete(name string) error

Delete removes a secret.

func (*Store) GetMeta

func (s *Store) GetMeta(name string) (*SecretMeta, error)

GetMeta returns metadata for a secret (no value).

func (*Store) GetValue

func (s *Store) GetValue(name string) (string, error)

GetValue retrieves and decrypts a secret value.

func (*Store) List

func (s *Store) List() ([]*SecretMeta, error)

List returns metadata for all secrets (no values).

func (*Store) ResolveEnv

func (s *Store) ResolveEnv(env map[string]string) map[string]string

ResolveEnv resolves ${secret:NAME} references in env vars, returning a new map with secret values substituted. Unresolvable refs are left as-is.

func (*Store) Set

func (s *Store) Set(name, value, description string) error

Set creates or updates a secret with an encrypted value.

Jump to

Keyboard shortcuts

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