Skip to content

Why a separate module

This page explains why the sign and keys command builders live in their own module rather than inside go/signing or inside go-tool-base. The short answer is dependency-cycle avoidance; the longer answer is about keeping a light library light and letting more than one CLI share one command surface.

The three-way tension

Three facts pull against each other:

  1. go/signing must stay tiny. Its whole promise is a framework-free signing/verification library whose module graph is just go-crypto and cockroachdb/errors. Pulling Cobra into it would tax every verify-only consumer with a CLI framework they never invoke.

  2. More than one binary wants the same commands. go-tool-base exposes gtb sign / gtb keys; the standalone sigillum CLI wants byte-identical behaviour. Copy-pasting the command code into each host guarantees drift.

  3. The commands must not drag in go-tool-base. If the builders lived in GTB's pkg/, then sigillum — and any other tool wanting just the signing commands — would have to import the entire framework. Worse, GTB itself already depends on go/signing, so co-locating the builders with the logic would knot the graph.

The resolution: a thin command-surface module

signing-cli sits in the middle:

        go/signing            (logic; tiny; no Cobra)
            │  imported by
        signing-cli           (Cobra command builders; this module)
            ▲            ▲
   imported │            │ imported by
        by  │            │
   go-tool-base      sigillum        (host binaries)
  • It depends on go/signing (for the registry and OpenPGP helpers) and Cobra — nothing else heavy.
  • It is imported by each host binary.
  • It never imports go-tool-base.

Because the dependency arrows only ever point up — hosts → signing-cligo/signing — there is no cycle. GTB can depend on signing-cli, and signing-cli never needs to depend back on GTB. The seam that makes the last point possible is the narrow Logger interface: the builders name a four-method logging contract instead of GTB's concrete logger type, so they never have to import GTB to log.

What each layer owns

Layer Owns Deliberately does not own
go/signing (+ backends) Signing/verification logic, the backend registry, OpenPGP/WKD helpers Any CLI framework
signing-cli (this module) The sign/keys *cobra.Command surface: flags, help text, arg validation, output wiring The crypto logic; any concrete backend; go-tool-base
Host binary (gtb, sigillum) Wiring the commands onto a root; blank-importing the backends it wants compiled in Re-implementing the command surface

That last row is why backends are the consumer's job — see Backends are the consumer's responsibility.

See also