The backend registry¶
The root signing package holds one process-global registry mapping a backend
name to a Backend. It is guarded by a sync.RWMutex, so concurrent Get and
Names calls are safe.
The Backend contract¶
type Backend interface {
Name() string
NewSigner(ctx context.Context, keyID string) (crypto.Signer, error)
}
Name returns a stable identifier: lowercase, kebab-case, unique across the
process. NewSigner returns a crypto.Signer wrapping the held key; the
backend decides what keyID means.
The interface is deliberately CLI-agnostic — it mentions no flag library — so the core module can stay dependency-light. A backend needing its own flags implements an optional interface defined by the CLI front end, which type-asserts for it. The core neither defines nor depends on such an interface.
Backends do not resolve credentials. The caller configures the SDK, agent or environment before the signer is used.
Register(b Backend)¶
Adds b to the registry. Called from a backend package's init(), so that a
blank import activates it — the same activate-by-side-effect pattern as
net/http/pprof and the image/* decoders.
It panics, rather than returning an error, in three cases:
| Condition | Panic message |
|---|---|
b is nil |
signing: Register called with nil Backend |
b.Name() returns "" |
signing: Backend.Name() returned empty string |
| the name is already registered | signing: duplicate Backend registration: <name> |
All three are programming errors at process init. Failing fast beats a silently overridden backend, which would mean signing with an unexpected key.
There is no Unregister. Once a backend package is imported, its backend is
present for the life of the process.
Get(name string) (Backend, error)¶
Returns the registered backend, or ErrUnknownBackend wrapped with a listing
so the message says what this binary actually supports:
- with backends registered —
"gcp-kms" (available: aws-kms, local) - with an empty registry —
"local" (no backends are registered — this binary was built without any signing backends compiled in)
The second message almost always means a missing blank import.
Lookup is exact: no case folding, no aliasing, no fuzzy matching.
Names() []string¶
Returns every registered name, sorted alphabetically. It reports the backends
this binary compiled in, which is exactly the set of backend packages the
binary chose to import — useful for rendering the accepted values of a
--backend flag.
ResetForTesting()¶
Clears the registry. The ForTesting suffix is the Go-standard signal that
this is a test helper; production code must not call it. It is exported rather
than hidden behind a test-only build so that tests in other packages can
drive registry-mutating scenarios against fake backends.
What keyID means per backend¶
keyID is a single string whose format each backend defines.
| Backend name | Module | keyID is |
Key types |
|---|---|---|---|
local |
gitlab.com/phpboyscout/go/signing/local (ships in this module) |
A filesystem path to an unencrypted PEM file | RSA (PKCS#1 or PKCS#8) and Ed25519 (PKCS#8) |
aws-kms |
gitlab.com/phpboyscout/go/signing-aws-kms |
A KMS key ARN or alias | RSA_4096 SIGN_VERIFY for the OpenPGP path, ECC_NIST_EDWARDS25519 for the minisign path |
The local backend is stateless and declares no flags; the PEM path arrives
per call as keyID. It reads whatever path it is given, following symlinks,
with no canonicalisation — appropriate for an operator-supplied path, worth
knowing if the path could come from somewhere less trusted.
Registering a configured backend¶
Backends that need configuration can be constructed and registered
programmatically rather than relying on the package's init(). That is how the
AWS backend takes a region or a logger:
Doing this and blank-importing the same backend panics on the duplicate name. Pick one.
Related¶
- Implement a custom backend — the task version of this page.
- Backends and the per-provider module pattern — why each provider is its own module.
- Errors —
ErrUnknownBackendand the rest.