Backends are the consumer's responsibility¶
The sign, keys mint and keys minisign commands need a signing backend: AWS
KMS, an on-disk PEM key, or something else. This module ships none of them. This page
explains why, and how the commands find the backends the host compiled in.
This module imports no concrete backend¶
signing-cli never imports signing-aws-kms, signing/local, or any other
backend package. Instead it talks to the go/signing registry:
signing.Names(): the names of every backend registered in this binary.signing.Get(name): resolve one to its registered defaultsigning.Backend.signing.NewInstance(type, instance): mint a separate, named instance of a type whose package also registered a factory.
sign, keys mint and keys minisign build their --backend help text from
signing.Names(). A bare --backend aws-kms resolves through signing.Get();
a named selector such as aws-kms:prod goes through signing.NewInstance()
instead (see Selecting a backend).
Either way, the backends the host binary registered are exactly the backends
the commands offer, no more and no fewer.
Activation by blank import¶
Backends register themselves from init(). A host turns one on by
blank-importing its package in main, the same side-effect pattern as
net/http/pprof, the image/* decoders, and database drivers:
import (
_ "gitlab.com/phpboyscout/go/signing-aws-kms" // registers "aws-kms"
_ "gitlab.com/phpboyscout/go/signing/local" // registers "local"
)
With those two imports present, sign --backend aws-kms … and
sign --backend local … both work, and --help lists both names. Drop the KMS
import and rebuild, and aws-kms disappears from the command with no code change
in signing-cli or in the command wiring.
Why push backends out to the host¶
The same dependency-inversion logic that keeps go/signing light applies here:
- The AWS SDK is enormous. A tool that only ever signs with the
localPEM backend should not link it. Because the SDK is imported only bysigning-aws-kms, and that package is imported only when the host blank-imports it, linker dead-code elimination keeps the SDK out of any binary that does not opt in. - Regulated / FIPS-only builds can drop a backend cleanly. go-tool-base
isolates its backend imports in a single file (
cli/cmd/gtb/signing.go); a downstream that must exclude the AWS SDK omits that one blank import and rebuilds. Its companioncli/cmd/gtb-no-aws-smokebinary exists to prove the AWS SDK is not pulled in transitively by anything insidesigning-cli. - The command surface stays universal. Because the commands discover
backends at runtime, one build of
signing-cliserves every host regardless of which backends that host chose.
Per-backend flags¶
Some backends need their own flags. aws-kms, for example, adds
--kms-region. The signing.Backend contract is CLI-agnostic, so a backend
that needs flags implements an optional interface:
When NewCmdSign, NewCmdKeysMint and NewCmdKeysMinisign build their command,
they iterate the registered backends and type-assert for that interface, letting
each backend declare its flags onto the command's flag set. So --kms-region
appears in sign --help only when a KMS-capable backend is compiled in. The
front-end type-asserts for this and the library never does, which keeps
go/signing free of any flag machinery.
Those flags land on the registered default, because that is the object the
commands hand to RegisterFlags when they are built. A named instance is a
different object, minted after the flags have been parsed, so it gets its own
private flag set and takes its values from the selector's ?settings. That is
why --backend aws-kms:prod --kms-region eu-west-1 is refused: the flag would
configure an object the command is not going to use. Refusing it is louder than
honouring it as a default, and the louder failure is the one that cannot produce
a signature from the wrong region's key.
Which commands consult the backend registry¶
Three of the seven do, and it is worth knowing which, because the other four cannot fail for a backend reason at all.
| Command | Backend | Why |
|---|---|---|
sign |
required | It needs a crypto.Signer to sign with. |
keys mint |
required | It wraps an existing signer in OpenPGP framing. |
keys minisign |
required | It reads the signer's public half to derive the minisign key. |
keys generate |
none | It generates a fresh keypair entirely in-process. |
keys wkd |
none | It only reads already-minted armored public keys from disk. |
keys publish |
none | It reads a minisign public-key file, deliberately, because publishing must not need cloud credentials. |
keys (the parent) |
none | It only groups the subcommands. |
See also¶
- Compile in signing backends: the recipe.
- Why a separate module: the broader cycle-avoidance design.
- Dependency inversion
in the
go/signingdocs: the same pattern at the library layer.