Skip to content

Backends are the consumer's responsibility

The sign and keys mint 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 a signing.Backend.

sign, keys mint and keys minisign build their --backend help text from signing.Names() and resolve the chosen backend with signing.Get(). Whatever backends the host binary registered are exactly the backends the commands offer — no more, 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 simply disappears from the command — 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 local PEM backend should not link it. Because the SDK is imported only by signing-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 (cmd/gtb/signing.go); a downstream that must exclude the AWS SDK omits that one blank import and rebuilds. Its companion cmd/gtb-no-aws-smoke binary exists precisely to prove the AWS SDK is not pulled in transitively by anything inside signing-cli.
  • The command surface stays universal. Because the commands discover backends at runtime, one build of signing-cli serves 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:

interface{ RegisterFlags(*pflag.FlagSet) }

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; the library never does — keeping go/signing free of any flag machinery.

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 — publishing must not need cloud credentials.
keys (the parent) none It only groups the subcommands.

See also