Skip to content

What signing-cli does not do

Everything on this page is a deliberate boundary of the current release (v0.4.1), not a bug. If you are designing around these commands, read this before you design.

There is no signing-cli binary

This module is a library of Cobra command builders. go install on it produces nothing runnable. You get sign and keys by attaching the builders to a CLI you build or already own — see Attach the commands to a CLI, or the tutorial for a CLI built from scratch in one file.

No signing backend ships with this module

signing-cli imports no concrete backend, on purpose. A host binary that blank-imports none has sign and keys mint present but unusable: --backend has no valid value and --help lists none. Backends come from go/signing and its sibling modules, activated in the host's main package — Compile in signing backends.

OpenPGP signing is RSA-only

keys mint and sign --format openpgp both go through go/signing's openpgpkey package, which handles RSA keys only. Pointing either at an Ed25519 key fails:

minting armored public key: got ed25519.PublicKey: unsupported key type: only RSA is supported
computing signature: DetachSign: ed25519.PublicKey: unsupported key type: only RSA is supported

The restriction is upstream and structural: go-crypto's OpenPGP EdDSA signing branch needs a concrete *eddsa.PrivateKey — its own type, from go-crypto/openpgp/eddsa — rather than a crypto.Signer, so a KMS-held Ed25519 key could not drive it even if the type check were relaxed. ECDSA keys are refused by the local backend before they reach OpenPGP at all.

keys generate --algorithm ed25519 is the one exception, and only because it generates the key inside go-crypto rather than wrapping an external signer. The armored public half it writes is a valid OpenPGP key; you still cannot sign OpenPGP with the matching private half through the local backend.

minisign signing is Ed25519-only

The mirror of the above. sign --format minisign and keys minisign require an Ed25519 key — for AWS KMS, an ECC_NIST_EDWARDS25519 key. An RSA key is refused before the backend is asked to sign anything:

minisign requires an Ed25519 signing key, but --key-id resolved to *rsa.PublicKey; RSA keys sign the OpenPGP manifest path (--format openpgp) instead

One key cannot serve both signature formats

Taken together, the two rules above mean a project signing both a checksum manifest and release artefacts needs two keys: an RSA key for the OpenPGP path and an Ed25519 key for the minisign path. There is no key type that satisfies both, and no flag combination that avoids the split.

Nothing here verifies a signature

sign and keys produce signatures and keys. There is no verify command in this module, and none is planned here — verification belongs to whoever consumes the artefact:

  • OpenPGP detached signatures verify with gpg --verify, or programmatically via go/signing's verify package.
  • minisign signatures verify with the minisign tool, cargo-binstall, or rtb-update.

Private keys are never written encrypted

keys generate writes the private half unencrypted, whichever format you choose: an armored OpenPGP secret block, a PKCS#1 PEM, or a PKCS#8 PEM. There is no passphrase flag, and the CLI's own help text still describes this as a v0.1 limitation.

Correspondingly, the local backend cannot read an encrypted PEM. A file whose PEM header is ENCRYPTED PRIVATE KEY is refused outright:

constructing signer: encrypted PEM private keys are not supported in v0.1; decrypt out-of-band first or use the aws-kms backend

Use filesystem-level encryption (LUKS, FileVault, age) and move the file offline, or use a KMS-held key so no private half exists on disk at all.

Minted keys carry no expiry, and mint makes no subkey

A key produced by keys mint or keys generate --algorithm rsa is a single primary key with sign and certify capability, one User ID, and no expiry date. There is no flag to set one, no revocation certificate is emitted, and no encryption subkey is created. Rotation is handled by minting a new key and publishing it alongside the old one, not by expiring the old one.

keys generate --algorithm ed25519 differs, because go-crypto's NewEntity builds the whole entity: that path emits a primary Ed25519 key plus a Curve25519 encryption subkey you did not ask for and cannot suppress.

sign overwrites its output without asking

sign has no --force flag and no prompt. If --output (or the default <input>.sig / <input>.minisig) already exists, it is truncated and rewritten. The only path it refuses is one that resolves to the input file itself.

The keys subcommands behave the opposite way: mint, generate and minisign refuse to clobber an existing file and require --force. Do not generalise either behaviour to the other.

A failed keys generate can still leave a private key behind

keys generate writes the private half first, then the public half. If the public-half write is the one that fails — most often because the file already exists and --force was not passed — the command exits non-zero with the private half already on disk:

$ ls
pub.asc            # pre-existing, empty
$ mytool keys generate --algorithm rsa --name A --email a@example.com --output pub.asc
ERR: writing public-half output: "pub.asc" (pass --force to overwrite): output file already exists
$ ls -l
-rw-rw-r-- 1 user user    0 pub.asc
-rw------- 1 user user 1675 pub.pem   # written before the failure

Check for a stray private half after any failed keys generate.

keys publish stages, it does not deploy

keys publish writes into a local directory. It never uploads, never talks to a CDN or object store, and needs no credentials — that is the point of it reading a file rather than a backend. Deployment is a separate step you run yourself (wrangler pages deploy ./keys-staging, rsync, or whatever hosts the site).

The same is true of keys wkd.

keys publish rewrites the untrusted comment

The .pub file keys publish writes is not a copy of its input. Only the key material is carried across; the untrusted comment line is regenerated as:

untrusted comment: phpboyscout <project> artefact signing v<N> (minisign key <KEYID>)

The phpboyscout prefix is fixed in the code and not configurable, so a key published by a tool outside this estate still carries it. Nothing signs an untrusted comment, so this affects presentation only — but it means the file you publish differs byte-for-byte from the file keys minisign --output produced.

A published key's bytes can never change

keys publish is add-only. Re-running with identical content is a no-op; pointing the same project and generation at a different key is refused, both on disk and in the manifest:

site/minisign/demo/v1.pub already holds a different key — published keys are add-only; publish a new generation instead

Everything else in the entry — status, purpose, valid_from — can be updated by re-running with the same key. Rotation means a new --generation, never a replacement.

keys wkd overwrites and never prunes

keys wkd writes its tree with plain file writes and no no-clobber guard: an existing hu/<hash> bucket, policy, or submission-address is overwritten silently. It also never deletes anything, so a bucket for an email you have stopped publishing stays in the staging directory and will be deployed again. Build the tree in a fresh directory if you need it to be exact.

There is no config file and no environment binding for flags

Every value comes from a flag or a positional argument. This module reads no config file, defines no environment-variable prefix, and does not bind flags to environment variables — including when it is embedded in a go-tool-base host, whose own config store these commands do not consult. See Configuration and environment for the two environment variables that do affect behaviour, both of which belong to something else.

Flags are format-specific and refused rather than ignored

Three flags on sign apply to exactly one format, and passing one to the other format is an error, not a silent no-op:

Flag Valid for Refused for
--public-key --format openpgp (required) --format minisign
--append --format openpgp --format minisign
--project --format minisign --format openpgp

--append in particular has no minisign equivalent: it merges OpenPGP signature packets into one armored block, and a minisign signature file holds exactly one signature.

See also