credentials

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package credentials describes how a user-supplied secret (VCS token, AI-provider API key, Bitbucket dual-credential blob) is persisted by the interactive setup wizard and resolved by the runtime config chain.

Storage modes

Three modes are supported, in descending order of preference:

  1. ModeEnvVar — the config records the NAME of an environment variable. The actual secret lives outside the config file, typically in the user's shell profile or a CI platform's secret-injection mechanism. This is the recommended default and the only mode permitted under CI.
  2. ModeKeychain — the config records a keychain reference (`<service>/<account>`). The secret lives in the OS keychain (macOS Keychain, Linux Secret Service via godbus, Windows Credential Manager). Available only when the process has a keychain-capable Backend registered — typically by blank- importing the optional gitlab.com/phpboyscout/go-tool-base/pkg/credentials/keychain subpackage. Without such a registration, Store / Retrieve / Delete return ErrCredentialUnsupported and resolvers fall through.
  3. ModeLiteral — the secret is written as plaintext in the config file. Supported for backward compatibility and for air-gapped or throwaway environments. Refused under CI.

Why a dedicated package

The setup wizard, config masking, doctor checks, and migration tooling all reason about "which storage modes are available" and "how do we retrieve a credential stored in each mode". Consolidating those concerns here avoids scattering the same switch statement across eight call sites.

Separation of the keychain backend

The go-keyring-backed implementation lives in a dedicated subpackage (gitlab.com/phpboyscout/go-tool-base/pkg/credentials/ keychain) that registers itself via RegisterBackend during its init(). Downstream tools that want OS keychain support blank- import that subpackage from their cmd/main; regulated or compliance-constrained downstreams omit the import and run with the stub Backend, so linker dead-code elimination keeps go- keyring, godbus, and wincred out of their binary even though the packages exist in the module. Binary-level SBOM review (syft, cyclonedx-gomod on the linked artefact) shows the opt-out surface unambiguously.

See docs/development/specs/2026-04-02-credential-storage-hardening.md for the full threat model, trust-model guidance, and test matrix.

Index

Constants

This section is empty.

Variables

View Source
var ErrCredentialNotFound = errors.New("credential not found in keychain")

ErrCredentialNotFound is returned by Retrieve when the backend is reachable but no entry exists for the given service/account pair. Distinguished from ErrCredentialUnsupported so resolvers can decide whether to fall through to a literal config value.

View Source
var ErrCredentialUnsupported = errors.New("keychain support not compiled (import pkg/credentials/keychain or register a custom Backend)")

ErrCredentialUnsupported is returned by Store, Retrieve, and Delete when no keychain-capable Backend has been registered — either because the optional pkg/credentials/keychain subpackage was not imported, or because a custom backend has opted out. Callers that want to fall through to a literal or env-var step should errors.Is against this sentinel.

Functions

func Delete

func Delete(ctx context.Context, service, account string) error

Delete removes a secret from the registered Backend. Idempotent: returns nil when the entry is missing. Returns ErrCredentialUnsupported when no real backend is registered.

func KeychainAvailable

func KeychainAvailable() bool

KeychainAvailable reports whether a keychain-capable Backend is registered. Returns false in the default (stub-backend) process. Setup wizards use this as a first-pass gate before Probe.

func Probe

func Probe(ctx context.Context) bool

Probe reports whether the registered Backend is reachable at the time of the call — useful for setup wizards that want to hide the ModeKeychain option on hosts where the backend is compiled in but locked, unreachable (headless Linux without a Secret Service provider, Vault unreachable), or otherwise unusable. It performs a canary Set → Get → Delete round-trip under the reserved "gtb-keychain-probe" service with a per-invocation random account and discards the result.

With the stub backend, Probe short-circuits to false without touching anything. A true return therefore means both "a backend is registered" AND "the backend accepts round-trip calls right now", which is the precise guard the wizard needs before offering keychain as a storage option.

Callers SHOULD pass a context with a short timeout (a few seconds is reasonable) so a misbehaving remote backend cannot stall the setup wizard indefinitely.

func RegisterBackend

func RegisterBackend(b Backend)

RegisterBackend swaps the active backend. The keychain subpackage calls this from its init(); custom implementations (Vault, AWS SSM, …) may call it from anywhere safe, typically a tool's main() before the first credential call.

func Retrieve

func Retrieve(ctx context.Context, service, account string) (string, error)

Retrieve reads a secret from the registered Backend. Returns ErrCredentialUnsupported when no real backend is registered, ErrCredentialNotFound when the backend is present but the entry is missing, or a wrapped backend error for other failures. A cancelled context aborts the lookup.

func Store

func Store(ctx context.Context, service, account, secret string) error

Store writes a secret to the registered Backend. Returns ErrCredentialUnsupported when no real backend is registered. The context is forwarded to the backend so network-backed implementations (Vault, AWS SSM) can honour deadlines and cancellation; OS-keychain backends ignore it.

Types

type Backend

type Backend interface {
	// Store writes a secret under the service/account pair. Must
	// overwrite any existing entry with the same key. Neither argument
	// may be logged by the implementation — resolvers rely on being
	// able to pass these to DEBUG log surfaces safely. Implementations
	// SHOULD return ctx.Err() when the context is cancelled before
	// the write commits.
	Store(ctx context.Context, service, account, secret string) error

	// Retrieve reads a secret. MUST return [ErrCredentialNotFound] when
	// the backend is healthy but the entry does not exist, so resolvers
	// can distinguish "missing" from "unavailable" and fall through
	// cleanly. Other failures should wrap the underlying error. A
	// cancelled context MUST abort the call.
	Retrieve(ctx context.Context, service, account string) (string, error)

	// Delete removes a secret. Idempotent: must return nil when the
	// entry does not exist. Only surface real failures (e.g. keychain
	// locked, remote unreachable) as errors.
	Delete(ctx context.Context, service, account string) error

	// Available reports whether this backend can currently satisfy
	// Store/Retrieve/Delete without an immediate [ErrCredentialUnsupported].
	// A freshly-registered keychain backend typically returns true; the
	// default stub returns false. Available SHOULD be a cheap static
	// check — use [Probe] for a live round-trip.
	Available() bool
}

Backend is the minimal contract the credential store presents to the setup wizards, resolvers, and doctor checks. The default implementation (see default_backend.go) returns ErrCredentialUnsupported from every call, so callers that never register a real backend behave exactly as if the keychain feature were compiled out entirely.

Real backends are registered by a downstream optional module — gitlab.com/phpboyscout/go-tool-base/pkg/credentials/keychain — via RegisterBackend. Tool authors may also plug in a custom backend (Vault, AWS SSM, 1Password Connect, …) by implementing this interface and calling RegisterBackend during program init. See docs/how-to/custom-credential-backend.md for a worked example.

Every method takes a context.Context so backends that perform network I/O (remote secret stores, hardware security modules) can honour deadlines and cancellation. OS-keychain backends ignore the context — local IPC is fast — but implementers SHOULD still accept it for uniformity.

type Mode

type Mode string

Mode identifies how a credential is persisted by the setup wizard and resolved at runtime. See package doc for the full trust model.

const (
	// ModeEnvVar stores the name of an environment variable that
	// contains the credential. Recommended default; the only mode
	// permitted under CI.
	ModeEnvVar Mode = "env"

	// ModeKeychain stores the credential in the OS keychain. The
	// config records a keychain reference; the secret never hits
	// disk. Available only when the process has registered a
	// keychain-capable [Backend] — typically by importing the
	// optional pkg/credentials/keychain subpackage.
	ModeKeychain Mode = "keychain"

	// ModeLiteral stores the credential value directly in the
	// config file. Supported for backward compatibility and
	// throwaway environments. Refused under CI.
	ModeLiteral Mode = "literal"
)

func AvailableModes

func AvailableModes() []Mode

AvailableModes returns the credential storage modes supported by this process. ModeKeychain is present only when a keychain-capable Backend is registered.

Directories

Path Synopsis
Package credtest provides test-only credential backends so downstream tests can exercise keychain-mode behaviour without pulling in github.com/zalando/go-keyring.
Package credtest provides test-only credential backends so downstream tests can exercise keychain-mode behaviour without pulling in github.com/zalando/go-keyring.
Package keychain is the optional OS-keychain backend for gitlab.com/phpboyscout/go-tool-base/pkg/credentials.
Package keychain is the optional OS-keychain backend for gitlab.com/phpboyscout/go-tool-base/pkg/credentials.

Jump to

Keyboard shortcuts

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