First-party signing (Path A)¶
Sign images your own CI builds and publishes, at publish time.
Flow¶
$ gapsign sign -key ~/.config/gapsign/cosign.key registry.internal/app:v1
signed registry.internal/app@sha256:...
signature: registry.internal/app@sha256:...
gapsign sign:
- Resolves the reference to an immutable digest (a tag is looked up against the registry; a digest ref is used as-is).
- Builds the cosign "simple signing" payload committing to that digest and signs it with the enterprise key (ECDSA P-256 / SHA-256).
- Pushes the signature as a cosign-layout OCI artifact at
repo:sha256-<hex>.sig, next to the image.
Verify¶
$ gapsign verify -pub ~/.config/gapsign/cosign.pub registry.internal/app:v1
verified registry.internal/app@sha256:...
Verification is fail-closed: it exits non-zero for an unsigned image, a key that didn't sign it, or a signature whose payload binds a different digest or repository. Because the signature commits to the digest and the repository, a signature cannot be transplanted to a different image or repo.
Keys¶
$ gapsign keygen # writes cosign.key (0600) + cosign.pub
$ gapsign keygen -name ci-kms # a named pair
- The private key is written mode
0600under~/.config/gapsign/(or-out DIR) and is never logged or committed. - The public key is standard PKIX PEM — interchangeable with
cosign generate-key-pairoutput — and is the trust anchor you distribute to verifiers via GitOps.
cosign interoperability¶
gapsign signatures are byte-format cosign signatures. A stock cosign verifies them offline:
In CI¶
Run gapsign sign after the image is pushed. Resolve the key from a file under
~/.config/gapsign/ or from a KMS (see Configuration);
never bake a private key into the pipeline definition.