Skip to content

Configuration reference

This module has no config file, no flags and no init function you tune. Its entire externally-visible configuration is one environment variable, one advisory constant, one validation rule, and a reserved keychain namespace. They are all listed here, with what happens when each is wrong.

CI

The only environment variable this module reads.

Read by IsCI, and through it RefuseLiteralUnderCI
Recognised value the exact string true
Anything else treated as "not CI"
Unset treated as "not CI"

The comparison is exact and case-sensitive. CI=1, CI=True, CI=TRUE, CI=yes and CI=true (trailing space) all make IsCI() return false. When that happens, literal mode is offered in the menu and RefuseLiteralUnderCI returns nil — the CI protection is silently off, in the exact environment it exists for.

Not every CI system sets CI at all, and some set a different value. Do not assume; check the runner and set it explicitly in the job when it is missing:

variables:
  CI: "true"

Confirm from inside a job with printf '[%s]\n' "$CI" — the brackets make a trailing space or an empty value visible.

Nothing in the module reads CI on your behalf at a decision point. RefuseLiteralUnderCI is a check you call, and ModeChoices takes the CI state as an argument rather than consulting the environment itself.

Environment variable names

The names a user may choose for ModeEnvVar are constrained by ValidateEnvVarName:

^[A-Z][A-Z0-9_]{0,63}$
Rule Consequence
First character must be AZ a leading digit, underscore or lowercase letter is rejected
Remaining characters AZ, 09, _ hyphens, dots and lowercase are rejected
1 to 64 characters 65 or more is rejected
ASCII only non-ASCII uppercase such as É is rejected

The rule is deliberately narrower than POSIX allows, so a chosen name survives shell profiles, YAML and Docker --env files without quoting. It rejects names that would work — myTool_token is a legal environment variable — and that is the trade-off: one shape that is safe everywhere beats a permissive rule that breaks in one place.

Validation is not automatic. credentials.Store and the resolution of an env var never see the name; the check runs only where you pass ValidateEnvVarName to a prompter or a form validator.

KeychainOpTimeout

const KeychainOpTimeout = 5 * time.Second

The suggested ceiling on a single backend operation. It is a value, not a behaviour — the package never applies it. A call made with context.Background() has no deadline, and against a locked keychain that means it waits for as long as the platform takes, which on a headless Linux host waiting for an unlock prompt is indefinitely.

Derive a context from it at every call site that runs during setup or startup:

ctx, cancel := context.WithTimeout(ctx, credentials.KeychainOpTimeout)
defer cancel()

Five seconds is chosen to be longer than any healthy local keychain round-trip and short enough that a wedged one does not read as a hung program. Use your own value if you have a remote backend that is legitimately slower.

The reserved credentials-keychain-probe service

Probe writes its canary under the keychain service name credentials-keychain-probe, with an account of the form probe-<pid>-<8 hex digits> and the value probe.

Do not store anything of your own under that service name. The account is randomised per invocation so concurrent probes from any number of processes cannot collide, but the service name is fixed and the entries under it are the module's.

Service and account names you choose

Store, Retrieve and Delete take a service and an account and pass them to the backend unchanged. The module imposes no rules on them at all: no length limit, no character restrictions, no rejection of empty strings. Any limits are the platform's — macOS Keychain, the Secret Service and Windows Credential Manager each have their own.

The convention used across the toolkit is the tool name as the service and a dotted purpose as the account, e.g. mytool / github.auth. Both are safe to log: the Backend contract forbids an implementation from logging them as secrets, and no shipped backend puts either in a place a secret would not be allowed.

Where the default prompter reads and writes

DefaultPrompter is fixed to:

Input os.Stdin
Prompt output os.Stderr — so prompts never pollute a piped stdout
Masking golang.org/x/term, only when stdin is a terminal

There is no exported constructor for a prompter over different streams. If you need one — a test harness, an embedded shell — implement Prompter yourself; it is three methods.

Build-time requirements

Go 1.26.5 or newer (go.mod declares go 1.26.5)
Core dependencies github.com/cockroachdb/errors, golang.org/x/term
Added by credentials/keychain github.com/zalando/go-keyring, and transitively github.com/godbus/dbus/v5 (Linux) and github.com/danieljoos/wincred (Windows)
Added by credentials/test none

The core carries no config framework, no CLI framework and no TUI. A depfootprint_test.go guard fails the build if go-tool-base, cobra, pflag, viper, charmbracelet, OpenTelemetry or a cloud SDK enters the graph — and separately if go-keyring ever reaches the core or test packages, or ever leaves the keychain subpackage.