Skip to content

Attach the commands to a CLI

This guide shows how a host binary wires the sign and keys command builders onto its root command: first for a plain Cobra CLI, then for a go-tool-base tool. Both paths are real and compilable.

Every constructor returns a plain *cobra.Command and takes one signingcli.Logger. You do not need an adapter: a *slog.Logger and go-tool-base's logger.Logger both satisfy the interface structurally.

Attaching the commands does not compile in any signing backend. That is a separate, deliberate step; see Compile in signing backends.

Plain Cobra root (e.g. sigillum)

Add the module and attach the two top-level builders directly. NewCmdKeys returns the keys parent with all five subcommands (mint, generate, wkd, minisign and publish) already attached, so one AddCommand brings the whole group.

package main

import (
    "log/slog"
    "os"

    "github.com/spf13/cobra"

    signingcli "gitlab.com/phpboyscout/go/signing-cli"

    // Compile in the backends this binary should offer (see the
    // companion how-to). Without at least one, `--backend` has no
    // valid value.
    _ "gitlab.com/phpboyscout/go/signing-aws-kms" // registers "aws-kms"
    _ "gitlab.com/phpboyscout/go/signing/local"   // registers "local"
)

func main() {
    log := slog.New(slog.NewTextHandler(os.Stderr, nil))

    root := &cobra.Command{Use: "sigillum"}
    root.AddCommand(
        signingcli.NewCmdSign(log), // sign <input-file>
        signingcli.NewCmdKeys(log), // keys (mint | generate | wkd | minisign | publish)
    )

    if err := root.Execute(); err != nil {
        os.Exit(1)
    }
}

That is the entire integration. sigillum sign … and all five keys subcommands work.

Attaching a single subcommand

If you want only part of the group, say keys mint under your own parent, the subcommand builders are exported too:

myKeys := &cobra.Command{Use: "keys"}
myKeys.AddCommand(
    signingcli.NewCmdKeysMint(log),
    signingcli.NewCmdKeysGenerate(log),
    signingcli.NewCmdKeysWKD(log),
    signingcli.NewCmdKeysMinisign(log),
    signingcli.NewCmdKeysPublish(log),
)
root.AddCommand(myKeys)

NewCmdKeys(log) is the convenience that does exactly this, plus one thing a hand-built parent will not do unless you add it: it answers an unknown subcommand (keys bogus) with an error instead of printing help and exiting 0.

go-tool-base root (gtb and scaffolded tools)

go-tool-base registers subcommands through setup.Wrap, which pairs a command with the GTB feature it belongs to (an empty feature string means "always on, not gated by a feature flag"). Pass the framework logger straight in via p.GetLogger():

import (
    "gitlab.com/phpboyscout/go-tool-base/pkg/cmd/root"
    "gitlab.com/phpboyscout/go-tool-base/pkg/setup"

    signingcli "gitlab.com/phpboyscout/go/signing-cli"
)

rootCmd := root.NewCmdRoot(p,
    // … other subcommands …
    setup.Wrap("", signingcli.NewCmdKeys(p.GetLogger())),
    setup.Wrap("", signingcli.NewCmdSign(p.GetLogger())),
)

p is the GTB *props.Props; p.GetLogger() returns a logger.Logger, which satisfies signingcli.Logger directly. This is how the shipped gtb binary wires the commands, in cli/pkg/cmd/root/root.go in the go-tool-base repository.

As with the plain-Cobra path, the backends must still be blank-imported in the binary's main package. In gtb those live in a single on/off file, cli/cmd/gtb/signing.go; see Compile in signing backends.

Verify the wiring

your-tool sign --help          # shows --backend, --key-id, --public-key, …
your-tool keys --help          # lists mint, generate, wkd, minisign, publish
your-tool keys mint --help     # --backend's help lists the compiled-in backend types

If --backend lists no backends, you have attached the commands but not compiled any backend in.

See also