Per-provider signing backend modules¶
- Authors
- Matt Cockayne, Claude Opus 4.8 (AI drafting assistant)
- Date
- 30 June 2026
- Status
- IMPLEMENTED
- Tracking
- work item #1 (extends)
- Builds on
2026-06-30-release-signature-verification-module.md(thesigningmodule, shipped v0.1.0)
Implemented — verified 2026-07-12
Shipped as gitlab.com/phpboyscout/signing-aws-kms
v0.1.0 (package awskms). Verified: builds and tests pass, the
depfootprint_test.go guard holds (no go-tool-base, no non-AWS cloud SDK), and
it implements the signing.Backend contract with the optional RegisterFlags
seam. go-tool-base consumes it alongside signing (the four in-tree signing
trees incl. pkg/signing/kms deleted, callers + blank-imports repointed). This
establishes the per-provider backend-module pattern (signing-<provider>) that
GCP/Azure/Vault will follow.
1. Context & motivation¶
gitlab.com/phpboyscout/signing v0.1.0 defines the crypto.Signer-producing
Backend contract and ships only the light local (PEM) backend; heavy/remote
backends are injected by the consumer. The AWS KMS backend still lives inside
go-tool-base at pkg/signing/kms — 84 AWS SDK import paths — implementing the
(now external) contract.
Two forces make it time to extract it:
- Reuse. Consumers beyond gtb will want a ready-made KMS backend rather than
re-implementing one against the
Backendinterface. It should be importable without dragging in go-tool-base. - Selective weight. GCP KMS, Azure Key Vault, and HashiCorp Vault backends are planned. If each lived in one module (or in go-tool-base), every consumer would inherit every cloud SDK in its module graph. Per-provider modules let a consumer include exactly the backend(s) it needs and nothing else.
This spec establishes the per-provider backend-module pattern and delivers
its first instance, gitlab.com/phpboyscout/signing-aws-kms, following the
exact pattern used for the signing module. go-tool-base then depends on
signing and signing-aws-kms together in a single cut-over.
2. The pattern (and why a separate module per provider)¶
Each cloud/remote backend is its own module gitlab.com/phpboyscout/signing-<provider>
that:
- depends on
gitlab.com/phpboyscout/signing(for theBackendcontract +Register), - adds only its own provider SDK,
- registers itself under a stable backend name via
init()(blank-import to activate).
Why a module, not a sub-package. Go module-graph pruning propagates a
module's entire go.mod require list to every consumer that imports any of its
packages. A single backend module that bundled all clouds — or backends kept in
go-tool-base — would force AWS+GCP+Azure+Vault SDKs into the graph of a consumer
that wanted just one (or none). Separate modules quarantine each SDK: importing
signing-aws-kms pulls AWS and nothing else; not importing it pulls no AWS at
all. afmpeg (verification only) pulls neither go-tool-base nor any cloud SDK.
Consumer matrix the pattern enables:
| Consumer | Imports | Cloud SDKs in graph |
|---|---|---|
| afmpeg (verify) | signing/verify |
none |
| a KMS-signing tool | signing + signing-aws-kms |
AWS only |
| gtb (switches between) | signing + all backend modules |
all (by design) |
3. Goals / non-goals¶
Goals
- Stand up
gitlab.com/phpboyscout/signing-aws-kmswith the current KMS backend, implementingsigning.Backend, blank-import activated as backend"aws-kms". go.modcarries onlysigning+ the AWS SDK (+pflagfor the optional flag interface) — no go-tool-base, no other cloud SDK.- A dependency-footprint guard asserting the module pulls no go-tool-base and no non-AWS cloud SDK (gcp/azure/vault).
- Integrate
signingandsigning-aws-kmsinto go-tool-base in one cut-over, deleting the in-treepkg/openpgpkey,pkg/signingcore,pkg/setupverification, andpkg/signing/kms;gtb sign --backend aws-kmsandgtb updatebehave identically. - Establish the naming/structure convention so GCP/Azure/Vault modules are drop-in repeats.
Non-goals
- Implementing the GCP/Azure/Vault backends (future modules; only the pattern is set here).
- Changing the KMS signing logic or the
Backendcontract (packaging only). - A CLI (gtb is the CLI; these are mechanics).
4. Design — signing-aws-kms¶
4.1 Contents¶
Move pkg/signing/kms/{kms.go,signer.go,kms_test.go} into the new module
(module path gitlab.com/phpboyscout/signing-aws-kms, package awskms),
repointing the signing import to gitlab.com/phpboyscout/signing:
backendimplementssigning.Backend:Name() == "aws-kms";NewSigner(ctx, keyID)returns acrypto.Signerbacked by an AWS KMS RSA key (KMS RSA_4096 SIGN_VERIFY →*rsa.PublicKey, satisfyingopenpgpkey's RSA requirement and the verify package's ≥3072-bit strength policy).keyIDis the KMS key ARN or alias.init()callssigning.Register(&backend{}).
4.2 The --kms-region flag → optional interface¶
The current backend implements RegisterFlags(fs *pflag.FlagSet) to add
--kms-region. The signing.Backend contract no longer includes RegisterFlags
(it is CLI-agnostic). Per the signing spec's decision, the CLI front-end
defines its own optional flag interface and type-asserts:
- The KMS backend keeps a
RegisterFlags(fs *pflag.FlagSet)method (so it depends onpflag— acceptable; the module is heavy regardless). - gtb's
signcommand type-assertsb.(interface{ RegisterFlags(*pflag.FlagSet) })and calls it when present (replacing today's unconditional call). Region also resolves fromAWS_REGIONvia the SDK default chain, so a non-flag CLI still works.
4.3 Seams & credentials¶
Credentials/region resolve through the AWS SDK Go v2 default chain (env, profile,
web-identity/OIDC) — unchanged; the module owns no credential logic. The backend
accepts an optional *slog.Logger (nil-safe), consistent with signing's
seam conventions — for diagnostics now and forward-consistency as more backends
land.
4.4 Footprint¶
go.mod: gitlab.com/phpboyscout/signing, github.com/aws/aws-sdk-go-v2/...,
github.com/spf13/pflag. The dep-guard forbids go-tool-base, and any
cloud.google.com/gcp, azure, or hashicorp/vault import — locking the
"AWS only" boundary so a careless import can't turn this into a multi-cloud blob.
5. Combined go-tool-base integration (the cut-over)¶
Done as one change (this is the Stage 8 the roadmap referred to, now covering both modules):
- go-tool-base depends on
signingandsigning-aws-kms. - Delete from go-tool-base:
pkg/openpgpkey,pkg/signing(core +local),pkg/setup/signing*.go(verification), andpkg/signing/kms. - Repoint (no aliases — pre-1.0 clean break):
cmd/gtbblank-importsgitlab.com/phpboyscout/signing-aws-kms(aws-kms) +gitlab.com/phpboyscout/signing/local(local).internal/cmd/signresolves backends viasigning.Get(...); itsRegisterFlagscall becomes the optional-interface type-assert (§4.2).SelfUpdater/ update verification usesigning/verify; gtb injects its logger viaslog.New(<handler>)and its hardened*http.Client.- Verify
gtb sign --backend aws-kms,gtb sign --backend local,gtb update, and the full suite (incl. the existing KMS integration test, re-homed to the new module — see §6). - Migration note in
docs/reference/migration/for the moved import paths.
What shrinks: go-tool-base sheds the openpgpkey/signing/verify/kms source it no longer owns (less bespoke code to test and maintain); the shared mechanics now live in reusable modules. gtb's binary still contains AWS (it blank-imports all backends by design), but every other consumer's intersection with that code is now a versioned dependency, not a copy.
6. Development model (TDD / BDD)¶
Mirrors signing and go-tool-base:
- Test-first; table-driven +
t.Parallel();cockroachdb/errors; ≥90% coverage on unit-testable surface. - The KMS backend's real-AWS path is an integration test gated behind real
credentials (as in go-tool-base today):
INT_TEST=1/INT_TEST_SIGNING_AWS=1, skipped otherwise. Unit tests cover construction, flag/region resolution, keyID handling, and error mapping with the AWS calls faked at the SDK seam (no package-level mocking hooks). - The dependency-footprint guard (§4.4) runs as a CI gate.
- A small Godog suite is optional for a backend adapter (the signing contract
is already BDD-covered in
signing); see open question 4.
7. Documentation (Diátaxis)¶
Light for the adapter, with the parent signing repo as the canonical home
for documenting available backends:
- In
signing-aws-kms(light): README entry point; godoc + anExampleshowingsigning.Get("aws-kms")→ sign (pkg.go.dev); and two in-repo pages — a how-to sign a release with AWS KMS (key setup, IAM/OIDC,keyIDARN, region) and a short explanation (the per-provider module pattern + AWS credentials model). No separate zensical site for the adapter. - In the
signingrepo's Diátaxis site (canonical): add an "available backends" reference/how-to documentingsigning-aws-kms(and the pattern for future GCP/Azure/Vault modules), extending the existinghow-to/implement-a-custom-backendpage. This is where users discover which backends exist. - Cross-link the READMEs both ways:
signingREADME →signing-aws-kms(and future backends);signing-aws-kmsREADME →signing.
8. Repository & project framework¶
New repo gitlab.com/phpboyscout/signing-aws-kms, bootstrapped exactly like
signing (it is a library; no binaries, no goreleaser):
- cicd
go-lint/go-test/go-security; releaser-pleaser; renovate-self;zensical-pagesonly if a site is published..golangci.yaml(v2, local prefixgitlab.com/phpboyscout/signing-aws-kms),justfile,.mockery.yml,renovate.json,requirements-lock.txt(if docs site). - Same CI-runner reality as
signing: group disables shared runners → thehomelabgroup runner; MR pipelines may need a manual trigger; releaser-pleaser uses the group token; never disable the must-succeed gate (arm auto-merge + trigger a fresh pipeline). go 1.26.x; depends onsigningat its released tag.
9. Resolved decisions (carried from the signing spec)¶
- Keep
cockroachdb/errors. Registry + injection both supported. MinimalBackendcontract; per-backend CLI flags via the CLI's optional interface. Hard cut-over in gtb (no aliases, pre-1.0) + migration note. FF merges.
10. Resolved decisions¶
- Naming → module
gitlab.com/phpboyscout/signing-aws-kms, packageawskms(unambiguous vs a future GCPkms), backend name string"aws-kms". - Backend string → keep
"aws-kms"(no change togtb sign --backendor existing scripts/pipeline). - Scope → aws-kms only; the per-provider pattern is documented so GCP/Azure/Vault are drop-in repeats, specced when actually built.
- Testing → unit (≥90%, AWS faked at the SDK seam) + a gated real-AWS
integration test (
INT_TEST_SIGNING_AWS); no Godog (the sign/verify contract is already BDD-covered insigning). - Docs → light in-repo (README + pkg.go.dev
Example+ a how-to and an explanation page); no separate zensical site. The parentsigningrepo's Diátaxis site is the canonical place documenting available backends (signing-aws-kms+ the pattern); READMEs cross-link both ways. - Logging → always expose an optional nil-safe
*slog.Loggerseam, for forward-consistency withsigning. - Release ordering → cut
signing-aws-kms v0.1.0first, then do the go-tool-base cut-over so gtb depends on a released tag (not a pseudo-version).
11. Work items (once approved)¶
- Bootstrap
signing-aws-kmsrepo (packageawskms) to the standard framework (§8) — no zensical site. - Move
kms.go/signer.go/tests; repoint thesigningimport; implement the optionalRegisterFlags; add the optional*slog.Loggerseam; add the dependency-footprint guard (no go-tool-base, no non-AWS cloud SDK). - Unit tests (≥90%) + re-home the gated AWS integration test; light docs (README,
Example, how-to + explanation pages). - Docs cross-wiring: add an "available backends" page to the
signingrepo's Diátaxis site documentingsigning-aws-kms+ the pattern; cross-linksigning↔signing-aws-kmsREADMEs. - Cut
signing-aws-kms v0.1.0(before the gtb cut-over). - Combined gtb cut-over (§5): depend on
signing+signing-aws-kms; delete the four in-tree trees incl.pkg/signing/kms; repoint callers + blank-imports - the sign command's flag type-assert; verify
gtb sign/gtb update+ suite; migration note. - afmpeg + ffmpeg-wasi (the original Stage 9): afmpeg →
signing/verify; ffmpeg-wasi →gtb sign.