Skip to content

Enable OS-keychain storage

Out of the box, credentials ships a no-op stub backend: Store / Retrieve / Delete return ErrCredentialUnsupported, ModeKeychain is never offered, and no keychain code is linked. This guide turns on real OS-keychain support — and shows how to turn it back off for a regulated build.

Activate it: one blank import

Add a single blank import to your tool's main package:

// cmd/mytool/main.go
import (
    _ "gitlab.com/phpboyscout/go/credentials/keychain" // registers the backend at init()
)

The subpackage's init() calls RegisterBackend with a go-keyring-backed implementation. From that point on:

The backend maps to the platform store automatically: macOS Keychain, Linux Secret Service (GNOME Keyring / KWallet) over D-Bus, Windows Credential Manager.

Gate the UI on Probe, not just availability

KeychainAvailable tells you a backend is registered; it does not tell you the keychain works right now. A headless Linux host with no Secret Service provider, or a locked macOS keychain, will reject writes at use time. Always gate the offer on a live Probe:

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

if credentials.Probe(ctx) {
    // safe to offer ModeKeychain — the canary Set→Get→Delete succeeded
}

ModeChoices already does this for you when you pass Probe(ctx) as its keychainUsable argument — see Choose a storage mode.

Store and resolve

With the backend active, the secret round-trips through the platform store and the config records only a service/account reference:

// store (setup time)
err := credentials.Store(ctx, "mytool", "github.auth", token)

// resolve (runtime)
token, err := credentials.Retrieve(ctx, "mytool", "github.auth")
switch {
case err == nil:
    // use token
case errors.Is(err, credentials.ErrCredentialNotFound):
    // reachable keychain, no entry — fall through to your next source
case errors.Is(err, credentials.ErrCredentialUnsupported):
    // no backend registered — fall through
}

Deactivate it: the regulated-build opt-out

To ship a build that touches no keychain IPC, omit the blank import. Keep it in a dedicated file so a build variant can drop it cleanly:

// cmd/mytool/keychain.go  — delete this file for a keychain-free build
package main

import _ "gitlab.com/phpboyscout/go/credentials/keychain"

With the import gone, Go's linker dead-code elimination keeps go-keyring, godbus, and wincred out of the binary. Nothing reaches a D-Bus session bus, and an SBOM taken against the linked artefact shows a clean surface. The tool still runs — it just falls back to the stub backend, so ModeKeychain is never offered and resolvers fall through env/literal as normal.

For why this is a subpackage rather than a separate module — and how to verify the opt-out — see The keychain opt-out.