keyring

package module
v0.2.1 Latest Latest
Warning

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

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

README

keyring

Secure credential storage for Go on four environments — Linux (Secret Service), macOS (Keychain), Windows (Credential Manager) and the browser (WebCrypto + IndexedDB) — with no third-party dependencies.

The core decides nothing: it holds no backend, no build tags and no package state. The caller injects the backend it wants, so a browser build never links a line of os/exec, and a CLI never links syscall/js.

import (
    "github.com/tinywasm/keyring"
    "github.com/tinywasm/keyring/linux"
)

kr, err := keyring.NewKeyring("my-app", linux.New())
kr.SetLog(log.Printf)
kr.Set("github_token", pat)
tok, err := kr.Get("github_token")
kr.Delete("github_token")
Don't care which backend? Use auto

A multiplatform CLI just wants "whatever this machine has". keyring/auto picks it, and is the only package in the repository that chooses:

import keyring "github.com/tinywasm/keyring/auto"

kr, err := keyring.NewKeyring("my-app") // Secret Service, Keychain,
                                        // Credential Manager or browser

A browser application does not import auto — it knows where it runs:

import "github.com/tinywasm/keyring/browser"

kr := keyring.OpenKeyring("my-app", browser.New())
The browser backend

Secrets are AES-GCM encrypted in IndexedDB under a data key that is itself wrapped by one or more key-encryption keys:

KEK Unlock Status
device none — a non-extractable CryptoKey in IndexedDB always present
passkey WebAuthn PRF + biometric opt-in, EnrollPasskey
recovery passphrase designed, not built

Because the key is a non-extractable CryptoKey, script running in the origin can ask it to decrypt but cannot read the key material out of the page. That is unreachable from a Go implementation, where any key is bytes in wasm linear memory that JS can read — which is why this backend calls WebCrypto instead of encrypting in Go. Full threat model, including what it does not protect against: docs/BROWSER_SECURITY.md.

Set/Get/Delete keep their synchronous signatures by blocking on github.com/tinywasm/await, so they must be called from a goroutine, never from the wasm main function or directly inside a JS event callback.

No keyring on the machine?

NewKeyring probes the backend on startup, and asks it to repair itself when it knows how. The Linux backend implements that: it installs gnome-keyring + libsecret with the distro package manager and starts gnome-keyring-daemon. OpenKeyring never probes — use it when the store is only needed conditionally, or in CI where GH_TOKEN may stand in.

Testing

There is no package-level provider to swap, so tests inject an in-memory one through the constructor and can run in parallel:

kr := keyring.OpenKeyring("test", tests.NewMemProvider())

tests/conformance.go holds the contract every backend must satisfy — multi-line values, non-ASCII, hex-looking passwords, the empty-service guard on DeleteAll — and each backend runs it.

Documentation

Documentation

Index

Constants

View Source
const (
	ServiceName   = "updater-cicd"
	HMACSecretKey = "hmac-secret"
	GitHubPATKey  = "github-pat"
)

Variables

This section is empty.

Functions

func Wrap added in v0.2.0

func Wrap(prefix string, err error) error

Wrap returns an error whose message is prefix + ": " + err.Error(), and whose Unwrap returns err so errors.Is still matches the sentinel. Exported because the backend packages use it.

Types

type Ensurer added in v0.2.0

type Ensurer interface {
	Ensure(log func(...any)) error
}

Ensurer is implemented by backends that can repair their own prerequisites (install packages, start a daemon). NewKeyring calls it when the initial probe fails. Backends that cannot self-repair simply do not implement it.

type Error added in v0.2.0

type Error string

Error is the error type of this package. It is a comparable string so callers can use errors.Is / == without this package importing "errors".

const (
	// ErrNotFound is returned when the key has no value in this service.
	ErrNotFound Error = "keyring: secret not found"
	// ErrUnsupported is returned by the fallback backend on platforms with no
	// credential store.
	ErrUnsupported Error = "keyring: no credential store on this platform"
	// ErrTooBig is returned when the value exceeds the backend's limit.
	ErrTooBig Error = "keyring: value too large for the platform credential store"
	// ErrUnavailable is returned when a backend exists but cannot be reached
	// (no D-Bus session, locked keychain, storage blocked by the browser).
	ErrUnavailable Error = "keyring: credential store unavailable"
	// ErrNoProvider is returned when a Keyring is built with a nil provider.
	ErrNoProvider Error = "keyring: no provider injected"
)

func (Error) Error added in v0.2.0

func (e Error) Error() string

type Fallback added in v0.2.0

type Fallback struct{}

Fallback answers every call with ErrUnsupported. It is what auto.Provider() returns on platforms with no credential store.

func (Fallback) Delete added in v0.2.0

func (Fallback) Delete(service, user string) error

func (Fallback) DeleteAll added in v0.2.0

func (Fallback) DeleteAll(service string) error

func (Fallback) Get added in v0.2.0

func (Fallback) Get(service, user string) (string, error)

func (Fallback) Set added in v0.2.0

func (Fallback) Set(service, user, password string) error

type KeyManager added in v0.0.2

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

KeyManager manages the service-exposed secrets (HMAC secret, GitHub PAT) of ServiceName on top of a generic Keyring. Same API as before; now backed by the shared Keyring type.

func New

func New(p Provider) *KeyManager

New creates a KeyManager over the ServiceName service with provider p.

func (*KeyManager) DeleteAll added in v0.0.2

func (m *KeyManager) DeleteAll() error

DeleteAll elimina todos los secretos (reset)

func (*KeyManager) GetGitHubPAT added in v0.0.2

func (m *KeyManager) GetGitHubPAT() (string, error)

GetGitHubPAT obtiene el GitHub PAT

func (*KeyManager) GetHMACSecret added in v0.0.2

func (m *KeyManager) GetHMACSecret() (string, error)

GetHMACSecret obtiene el HMAC secret

func (*KeyManager) IsConfigured added in v0.0.2

func (m *KeyManager) IsConfigured() bool

IsConfigured verifica si están configurados

func (*KeyManager) RotateGitHubPAT added in v0.0.2

func (m *KeyManager) RotateGitHubPAT(newPAT string) error

RotateGitHubPAT rota el GitHub PAT

func (*KeyManager) RotateHMACSecret added in v0.0.2

func (m *KeyManager) RotateHMACSecret(newSecret string) error

RotateHMACSecret rota el HMAC secret

func (*KeyManager) SetLog added in v0.0.2

func (m *KeyManager) SetLog(fn func(...any))

SetLog sets the logging function.

func (*KeyManager) Setup added in v0.0.2

func (m *KeyManager) Setup(hmacSecret, githubPAT string) error

Setup realiza el setup inicial - solo primera ejecución

type Keyring

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

Keyring provides scoped credential storage through an injected backend. The service name is a namespace: the same key under different services never collides, so one process can hold secrets for several apps.

func NewKeyring added in v0.0.2

func NewKeyring(service string, p Provider) (*Keyring, error)

NewKeyring creates a Keyring scoped to service over provider p, and verifies the backend actually works — asking it to repair itself when it implements Ensurer.

Callers that do not care which backend they get can use github.com/tinywasm/keyring/auto, which picks one for the target platform.

func OpenKeyring added in v0.0.3

func OpenKeyring(service string, p Provider) *Keyring

OpenKeyring creates a Keyring without probing the backend: nothing is touched until the first Get/Set/Delete. Use it when the store is only needed conditionally (session recovery) or in flows that may legitimately run without one (CI with GH_TOKEN).

func (*Keyring) Delete added in v0.0.2

func (k *Keyring) Delete(key string) error

Delete removes key from this service's namespace.

func (*Keyring) Get added in v0.0.2

func (k *Keyring) Get(key string) (string, error)

Get returns the value stored under key in this service's namespace.

func (*Keyring) Set added in v0.0.2

func (k *Keyring) Set(key, value string) error

Set stores value under key in this service's namespace.

func (*Keyring) SetLog added in v0.0.2

func (k *Keyring) SetLog(fn func(...any))

SetLog sets the logging function.

type Provider added in v0.0.2

type Provider interface {
	// Set stores password for user under service.
	Set(service, user, password string) error
	// Get returns the password stored for user under service.
	Get(service, user string) (string, error)
	// Delete removes the password stored for user under service.
	Delete(service, user string) error
	// DeleteAll removes every entry under service.
	DeleteAll(service string) error
}

Provider abstracts the OS keyring backend (Secret Service, Keychain, Credential Manager).

Directories

Path Synopsis
Package auto picks the credential backend for the platform it is built for.
Package auto picks the credential backend for the platform it is built for.

Jump to

Keyboard shortcuts

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