Skip to content

sign

sign <input-file> --backend <name> --key-id <id> [--format openpgp|minisign] [flags]

Constructor: signingcli.NewCmdSign(log Logger) *cobra.Command.

Produce a detached signature over a single input file using a crypto.Signer resolved from a compiled-in go/signing backend. Signatures are written with mode 0644 — they are public, non-sensitive artefacts distributed alongside the signed file.

The private key never leaves the backend; signing is one round-trip (two for minisign) through the crypto.Signer interface.

sign overwrites an existing output file without asking. There is no --force flag and no prompt; the only path it refuses is one that resolves to the input file itself.

Two formats, two audiences

--format Output Signs For
openpgp (default) <input>.sig ASCII-armored OpenPGP detached signature checksum manifests, Go self-update
minisign <input>.minisig prehashed ED minisign signature release artefacts, verified by cargo-binstall and rtb-update

The key type decides the format, not the flag

The two formats need two different key types, and neither will accept the other's:

  • --format openpgp requires an RSA key. An Ed25519 key fails with DetachSign: ed25519.PublicKey: unsupported key type: only RSA is supported. The restriction comes from go/signing's openpgpkey package and is structural — go-crypto's OpenPGP Ed25519 path cannot be driven through a crypto.Signer, so a KMS-held Ed25519 key could not work here regardless.
  • --format minisign requires an Ed25519 key — for AWS KMS, an ECC_NIST_EDWARDS25519 key. An RSA key is refused before any signing happens.

A project signing both a checksum manifest and release artefacts therefore needs two keys. See What signing-cli does not do.

How the minisign signature is produced

--format minisign hashes the artefact with BLAKE2b-512 and signs the 64-byte digest, so an HSM-held key can sign an artefact of any size despite the KMS 4096-byte message cap — custody is fully preserved. It takes no --public-key: a minisign signature carries its own key identifier, so the identity comes from the signer itself. Publish the matching public key with keys minisign.

The .minisig extension matters: rtb-update selects its parser by extension. cargo-binstall's signature URL is configurable, so one file serves both.

Recording project identity

--project appends project:<name> and key:<KEYID> to the trusted comment, after minisign's conventional timestamp/file/hashed fields so anything parsing the standard format still finds what it expects:

timestamp:1785326400    file:tool_1.2.3_linux_amd64.tar.gz  hashed  project:rtb-cli-bin key:56475AA75463474C

The trusted comment is covered by the signature's global signature, so these fields are tamper-evident: altering the project name makes verification fail. The key: field duplicates the key ID already in the signature body — the body is authoritative, and the copy exists so a signature file is greppable and self-describing under review.

Comment lengths are validated against the reference minisign implementation's limits (1024 bytes untrusted, 8192 trusted). No Rust verifier enforces these, so an over-long comment would verify in cargo-binstall and then fail against upstream minisign -V; sign refuses to emit one.

Arguments

Argument Required Description
<input-file> yes (exactly one) Path to the file to sign.

Flags

Flag Required Default Description
--backend yes Signing backend name. Valid values are the backends compiled into the binary (see Compile in signing backends); --help lists them.
--key-id yes Backend-specific key identifier. For aws-kms: a KMS key ID, ARN, or alias. For local: a PEM file path.
--format no openpgp openpgp or minisign. Any other value is an error listing the valid ones.
--project minisign only Project this artefact belongs to, recorded in the signature's signed trusted comment as project:<name> alongside key:<KEYID>. Omit to emit minisign's exact default comment. Rejected for --format openpgp, which has no trusted comment to record it in.
--public-key openpgp only Path to the armored OpenPGP public-key file (.asc, produced by keys mint). Must contain the public half of the key resolved by --backend / --key-id. Rejected for --format minisign.
--output no <input>.sig, or <input>.minisig Output file path for the detached signature. Refuses to equal the input path.
--created no now Signature creation time (RFC3339), truncated to whole seconds. Pin it to produce byte-identical signatures across re-runs. Must not be earlier than the signing key's creation time — see below. For --format minisign this sets the trusted comment's timestamp, and SOURCE_DATE_EPOCH is honoured when the flag is unset. --format openpgp ignores SOURCE_DATE_EPOCH.
--append openpgp only false Merge the new signature into an existing armored signature at --output instead of overwriting, producing one armored block with multiple signature packets. No-op when --output does not exist yet. Rejected for --format minisign, which has no equivalent.

Backends may register additional flags — e.g. the aws-kms backend adds --kms-region (default eu-west-2). These appear in --help only when a backend that declares them is compiled in.

--created must not predate the key

--created is used as go-crypto's notion of "now" while the signature is built, so a timestamp from before the signing key was created makes the key look not-yet-valid and the run fails:

Error: computing signature: computing detached signature: openpgp: invalid argument: no valid signing keys

The message does not mention the timestamp. If you see it while pinning --created, compare the value against the key's creation date — gpg --list-packets release.asc prints it as created <unix-seconds>.

Pinning --created to a valid instant does give byte-identical output: signing the same file twice with the same key and the same --created produces two identical .sig files.

--append and key rotation

--append is the dual-sign primitive for a key-rotation overlap window. Sign once with the old key, then --append the new key's signature into the same armored file:

sign --backend aws-kms --key-id alias/release-signing-v1 \
    --public-key v1.asc --output checksums.txt.sig checksums.txt
sign --backend aws-kms --key-id alias/release-signing-v2 \
    --public-key v2.asc --output checksums.txt.sig --append checksums.txt

Verifiers skip signature packets from issuers they do not know, so one armored file carrying both packets verifies for binaries that trust either key.

Examples

# Sign checksums.txt with a production KMS key
sign \
    --backend aws-kms \
    --kms-region eu-west-2 \
    --key-id alias/gtb-release-signing-v1 \
    --public-key release.asc \
    checksums.txt

# Reproducible signature: pin --created to a fixed instant
sign \
    --backend local \
    --key-id ./release.pem \
    --public-key ./release.asc \
    --created 2026-01-01T00:00:00Z \
    --output build.txt.sig \
    build.txt

Output

On success sign writes the armored .sig file and logs a structured INFO line carrying backend, key_id, public_key, input, output, sig_creation_time, and the primary-key fingerprint — so operators can confirm the signing identity without a follow-up gpg --verify.

See also