Skip to content

Concepts

Why a separate, standalone CLI

The signing logic has been a reusable module family for a while (go/signing and its backends). The signing commands, however, lived inside gtb — a framework that is deliberately both a library and a CLI. That was fine while gtb was the only consumer, but it meant you could only get a sign / keys command surface by building a whole gtb application.

sigillum exists to remove that constraint. Its entire reason to be is signing and verification, so it makes sign and keys the whole tool, exposed as top-level commands rather than sub-commands of a larger app.

The practical payoff is for non-Go and non-gtb release pipelines. A Rust project, a shell-driven release, or any CI job that just needs to produce and publish OpenPGP signatures can go install (or download) one small binary and sign — with no framework build, no gtb dependency, and no bespoke signing script. It is a purpose-built utility, sized to one job.

Keeping sigillum thin is what makes this honest: it holds no signing logic of its own. All of that stays in go/signing, shared with gtb, so the two tools can never drift in how they sign. See Components & architecture for the module stack.

The build-time backend model

A signing backend (AWS KMS, local PEM, or a third party) implements go/signing's crypto.Signer-based contract and registers itself in that module's registry. The command layer never hard-codes a backend; --backend simply selects a registered one, and sigillum sign --help lists whichever backends are present.

Which backends a given binary carries is decided at build time by blank imports in sigillum's main package:

// cmd/sigillum/signing.go
import (
    _ "gitlab.com/phpboyscout/go/signing-aws-kms"
    _ "gitlab.com/phpboyscout/go/signing/local"
)

Each blank import runs the backend package's init, which registers it. Because sigillum's whole purpose is signing, it ships the full set: AWS KMS + local PEM.

This model has a security dividend. A regulated or hardened build can drop a blank import — say, remove the AWS KMS line and rebuild. The Go linker's dead-code elimination then keeps that backend's SDK (and its transitive dependencies) entirely out of the linked binary. The backend set is thus a compile-time policy decision, not a runtime flag that could be toggled by config. (sigillum uses the same pattern for OS-keychain support via cmd/sigillum/keychain.go.)