Skip to content

sigillum

Keys you hold. Keys you cannot read.

A standalone command-line tool for the two jobs a project's OpenPGP keys actually do: signing release artefacts, and reading the encrypted vulnerability reports researchers send to your security contact. The private key never leaves your KMS, HSM or PEM file, the output is what the standard tools already accept, and nothing about it cares what language your project is written in.

go install gitlab.com/phpboyscout/sigillum/cmd/sigillum@latest

Sign

Turn any file into a detached signature — armored OpenPGP for checksum manifests, minisign for release artefacts. The key stays in the backend; only a digest is ever sent to it.

Mint

Build a real OpenPGP public key from a signer you already have, including an AWS KMS key that cannot export its private half.

Generate

Create a fresh Ed25519 or RSA keypair locally, when a hosted key is more ceremony than the job needs.

Decrypt

Read a vulnerability report encrypted to your published certificate. One kms:DeriveSharedSecret call does the only secret work; no private key exists in the process.

Certify

Assemble a publishable OpenPGP certificate whose primary and encryption subkey are both KMS keys, with both signatures made inside the service.

Publish

Lay out a Web Key Directory tree and a minisign keys site, so verifiers fetch your keys from your own domain rather than from whoever hosts your code.

Why a separate tool

The signing commands began life inside go-tool-base, which is both a library and a CLI and could therefore carry both. That works until something wants the commands without the framework: it ends up depending on the whole of gtb, which depends on the signing module, and the dependency graph stops being a graph.

sigillum is the leaf that resolves it — a pure CLI over go/signing, depending on the framework and depended on by nothing. The command surface is identical to gtb sign and gtb keys; sigillum simply makes those commands the whole tool, so a release pipeline that is not Go, and was never built with gtb, can still use them.

Sign a file in one command

sigillum sign \
    --backend aws-kms \
    --kms-region eu-west-2 \
    --key-id alias/release-signing-v1 \
    --public-key release.asc \
    checksums.txt

That writes checksums.txt.sig, an armored detached signature that gpg --verify (and every other modern OpenPGP implementation) accepts. Swap --backend aws-kms for --backend local --key-id ./release.pem to sign with an on-disk key instead.

Read an encrypted report in one command

sigillum decrypt \
    --certificate security-contact.asc \
    --key alias/security-contact-v1-encrypt \
    report.asc

A researcher fetched that certificate from your security.txt, encrypted their report to it, and opened a confidential ticket. Opening it needs exactly one call to your key service; the derivation, the key unwrap and the body decryption all happen locally. See Receive an encrypted report.

How do I verify a signature?

Not with sigillum — it signs, and there is no sigillum verify. Use gpg --verify for OpenPGP signatures and minisign -Vm for .minisig artefact signatures, or let the consumers do it: cargo-binstall and rtb-update both verify before installing. The reasoning, and the rest of the limits, are in What sigillum does not do.

Architecture at a glance

sigillum is deliberately thin. All real work lives upstream; sigillum wires the pieces together and ships the backends:

sigillum  (this CLI — attaches the commands, ships the backends)
   ├─ go/signing-cli   the sign / keys cobra command builders
   │       │
   │       └─ go/signing            the actual signing & verification logic
   │              └─ go/signing/openpgpkey, .../verify   OpenPGP + WKD helpers
   ├─ go/signing-aws-kms   AWS KMS backend   (blank-imported)
   └─ go/signing/local     local PEM backend (blank-imported)
  • go/signing holds all signing and verification logic.
  • go/signing-cli holds only the cobra command builders (sign, keys); it depends on go/signing + cobra and nothing else.
  • sigillum attaches those commands to its root and blank-imports the backends it ships (AWS KMS + local PEM). Which backends are compiled in is a build-time decision — a regulated build can drop a blank import and rebuild.

See Explanation for why this is a separate tool and how the backend model works.

Where to go next

The documentation follows the Diátaxis framework:

  • Tutorials — install, generate a key, sign a file and verify it, start to finish.
  • How-to guides — sign a release artefact, sign an artefact for Rust consumers, generate or mint a signing key, publish a WKD tree.
  • Reference — every command, flag, default, configuration key and failure mode.
  • Explanation — the architecture, the reasoning behind it, and what sigillum does not do.

Further reading

The blog carries a curated route through this subject: Signing your releases collects everything written about it, ordered so you can start at the beginning rather than newest-first.

Ask phpbotscout

phpbotscout

He answers questions about the projects over on the Discord, citing the docs where they already cover it, and offering to raise an issue where they don't. Bring a bug, an idea, or a questionable engineering decision.

Join the Discord