Documentation
¶
Overview ¶
Package keychain stores and retrieves secrets in the operating system's native secret store — the macOS Keychain, the Linux/*BSD Secret Service, or the Windows Credential Manager — behind one small, cgo-free interface.
It is a successor to zalando/go-keyring that removes go-keyring's two hard limits: the ~4 KB macOS command-line cap and the 2560-byte Windows credential-blob cap. Secrets of any size round-trip on every platform (Windows chunks transparently under the hood). There is no cgo on any platform, so callers cross-compile freely, and a rebuilt binary keeps access to what it stored — the properties a headless, frequently-rebuilt daemon needs.
Usage ¶
The package-level Set, Get, and Delete cover the common case with a silent default configuration. For a logger or a non-default access mode, construct a Keychain with New and call its methods.
Semantics ¶
Set is an upsert. Get returns the exact bytes previously stored, or ErrNotFound. Delete is idempotent — removing an absent item is not an error. Service and account together form the lookup key; neither may be empty. An empty secret is allowed and is distinct from an absent item.
Logging ¶
The library is silent by default. Pass WithLogger a *slog.Logger to trace, at debug level, which backend ran and how an operation resolved. The secret value is never logged.
Security ¶
Every backend protects data-at-rest and never writes a plaintext file itself. On Linux and Windows an item is readable, without a prompt, by any process of the same user — the deliberate trade for a headless daemon. None of the backends defends against code already executing as that user.
macOS access ¶
macOS additionally gates each keychain item by an access partition keyed to the creating binary's code identity. A process of the same identity reads without a prompt across restarts and rebuilds: this holds for the same binary and for any binary code-signed with the same stable Apple Team ID. An unsigned or ad-hoc-signed binary — the default go build output — is bound to its cdhash, which changes on every rebuild, so a rebuilt copy can no longer read what it stored. WithSecurityCLI is the opt-in escape hatch for that case; a stable Team ID signature needs nothing extra.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrAccessDenied = errors.New("keychain: access denied by the store's ACL or partition")
ErrAccessDenied wraps a macOS denial where the item exists but this process is not permitted to read it — the access partition or ACL rejected it, which an unlock cannot fix. The common cause is an unsigned or ad-hoc-signed binary reading its own item after a rebuild changed its code identity; such a caller should fall back to WithSecurityCLI or another store. It does not occur on Linux or Windows. Test for it with errors.Is.
var ErrInvalidKey = errors.New("keychain: service and account must both be non-empty")
ErrInvalidKey is returned when service or account is empty. The two together form the lookup key, so neither may be blank.
var ErrLocked = errors.New("keychain: store is locked and needs an interactive unlock")
ErrLocked wraps an error from a store that is locked and would need an interactive unlock to proceed — a headless caller cannot answer the prompt. It occurs on the Linux Secret Service when the collection is locked, and on macOS when a read would need a keychain prompt (errSecInteractionNotAllowed). Test for it with errors.Is; the wrapped error carries the platform detail.
var ErrNotFound = errors.New("keychain: item not found")
ErrNotFound is returned by Get when no item matches the service and account.
ErrUnavailable wraps an error from a store that is not reachable at all: on Linux, no session bus or no default collection; on macOS, the Security framework failed to load. It signals the caller to fall back to another store rather than retry. Test for it with errors.Is.
var ErrUnsupported = errors.New("keychain: platform not supported")
ErrUnsupported is returned on a platform whose backend is not implemented.
Functions ¶
Types ¶
type AccessMode ¶
type AccessMode int
AccessMode controls read access. It is only meaningful on macOS; on Linux and Windows secrets are user-scoped and every mode behaves like TrustAll.
const ( // TrustAll lets any process of the same user read without a prompt. It // matches security -A, go-keyring, and the Linux and Windows default: // rebuild-safe and daemon-friendly. It protects data-at-rest, not against // code already running as the user. This is the default. TrustAll AccessMode = iota // TrustCurrentApp, on macOS only, lets only the creating binary read // silently; other apps are prompted. Stronger, but a rebuilt binary loses // access. TrustCurrentApp )
type Keychain ¶
type Keychain struct {
// contains filtered or unexported fields
}
Keychain is a handle to the OS secret store with a fixed configuration (a logger and a default access mode). Construct one with New; the package-level Set, Get, and Delete delegate to a default, silent instance.
func (*Keychain) Delete ¶
Delete removes the item under service and account. A missing item is not an error: Delete is idempotent.
type Option ¶
type Option func(*config)
Option configures a Keychain (via New) or a single Set call.
func WithAccessMode ¶
func WithAccessMode(mode AccessMode) Option
WithAccessMode selects the macOS read-access ACL. It defaults to TrustAll and is a no-op on Linux and Windows.
func WithLabel ¶
WithLabel sets a human-readable label where the store supports one — the macOS Keychain item label. It is ignored on Linux and Windows.
func WithLogger ¶
WithLogger routes debug-level tracing to logger. Without it the library is silent. The secret value is never logged, only its length and the lookup key.
func WithSecurityCLI ¶
func WithSecurityCLI() Option
WithSecurityCLI routes macOS operations through the /usr/bin/security tool instead of the native Security.framework API. It exists for one case: an unsigned or ad-hoc-signed binary that must read its own secret again after a rebuild, or share it with another application of the same user.
The native default binds an item to its creator's code identity (its cdhash), which changes on every rebuild, so a rebuilt unsigned binary can no longer read what it stored. Items written through /usr/bin/security instead live in the stable "apple-tool" access partition and stay readable across rebuilds and across apps. The trade is that the secret is passed as a command-line argument (briefly visible to the same user in the process list) — acceptable only because such an item is already readable by any process of the user.
It is a no-op on Linux and Windows, whose stores are user-scoped with no such partitioning. A binary code-signed with a stable Apple Team ID does not need it: the native default is already rebuild-stable for same-team readers.
Use it consistently: an item written with it must also be read with it. The two paths store the value differently, so mixing them on one item does not error — the value comes back garbled or missing. WithLabel and WithAccessMode have no effect in this mode.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
cf
Package cf is a minimal, cgo-free CoreFoundation binding for the darwin keychain backend.
|
Package cf is a minimal, cgo-free CoreFoundation binding for the darwin keychain backend. |
|
chunk
Package chunk stores an arbitrary-size secret across several fixed-capacity credential blobs, hiding the Windows Credential Manager per-blob byte cap behind a header-plus-chunks layout.
|
Package chunk stores an arbitrary-size secret across several fixed-capacity credential blobs, hiding the Windows Credential Manager per-blob byte cap behind a header-plus-chunks layout. |
|
secitem
Package secitem is the cgo-free Security.framework binding for the darwin keychain backend.
|
Package secitem is the cgo-free Security.framework binding for the darwin keychain backend. |
|
winapi
Package winapi is a dependency-free Windows Credential Manager binding: inline advapi32 CredWriteW/CredReadW/CredDeleteW/CredFree calls through the standard library's syscall package, with no golang.org/x/sys and no third-party wrapper.
|
Package winapi is a dependency-free Windows Credential Manager binding: inline advapi32 CredWriteW/CredReadW/CredDeleteW/CredFree calls through the standard library's syscall package, with no golang.org/x/sys and no third-party wrapper. |