Signing plugin contract v1¶
A plugin reports the contract versions it speaks with
bomify-plugin-<kind> contract (see
contract versions).
The spec for a bomify-plugin-<kind> binary's signing subcommands,
signature sign, signature attest, signature verify, signature
verify-attestation, and signature supported-types, which bomify
push/save --sign and bomify pull/load --verify (or a matching
bomify trust rule) call. It's independent of the
component,
SBOM generation,
and security scanning
contracts.
Here <kind> names a signing scheme (e.g. sigstore, notation).
Go plugins should implement pkg/plugin's SigningPlugin interface and
use plugin.SignatureCommand, which provides all five subcommands,
their flags, reading --payload/--statement/--envelope, and output.
Naming and discovery¶
As for the component contract:
bomify-plugin-<kind> (.exe on Windows), installed in
<data-dir>/plugins.
What bomify does¶
A plugin only turns a payload into an envelope, and decides whether an envelope is a trusted signature over a payload. bomify does the rest:
- Pack the package as an OCI manifest, which pins the SBOM and every layer by digest, so one signature covers the whole package.
- Sign (push/save): write the payload, call
signature sign, and push the envelope as a referrer of the manifest, before tagging, so a failed signing never leaves a tag on an unsigned package. - Verify (pull/load): before fetching anything else, list the
manifest's referrers, keep those whose artifact type
signature supported-typeslists, and callsignature verifyon each until one passes. If none does, the pull fails and nothing is written. A trust rule can require several signers, each verified this way with its own options, so the same envelope may reachverifymore than once with different options: judge each call by its own flags alone. - Verify provenance (pull/load with
--verify-provenanceor a trust rule's--require-provenance): the same way, callsignature verify-attestationon each attestation referrer until one passes and bomify accepts the statement it returns.
The plugin never talks to a registry or sees the package's contents, so
it works the same for registries and bomify save tarballs.
Payload¶
--payload names a file holding the package manifest's descriptor as
compact JSON, exactly these fields in this order:
Treat it as opaque bytes: sign and verify exactly those bytes. bomify produces it identically at sign and verify time. A plugin may parse it (e.g. to embed the digest in a statement) but never needs to.
Commands¶
bomify-plugin-<kind> signature sign --payload <file> --reference <ref> [--option <key>=<value>]...
bomify-plugin-<kind> signature attest --statement <file> --reference <ref> [--option <key>=<value>]...
bomify-plugin-<kind> signature verify --payload <file> --envelope <file> --media-type <mt> --reference <ref> [--option <key>=<value>]...
bomify-plugin-<kind> signature verify-attestation --envelope <file> --media-type <mt> --subject <digest> --reference <ref> [--option <key>=<value>]...
bomify-plugin-<kind> signature supported-types
signature sign¶
| Flag | Required | Meaning |
|---|---|---|
--payload |
yes | The payload file to sign. |
--reference |
yes | The reference being published. Informational; use it to pick an identity, or ignore it. |
--option |
no, repeatable | A key=value from --sign-option. See Options. |
On success, print one SignResult and exit 0.
signature attest¶
Called with --sign when the package has
provenance.
| Flag | Required | Meaning |
|---|---|---|
--statement |
yes | An in-toto statement to attest, such as the package's build provenance. |
--reference |
yes | As for sign. |
--option |
no, repeatable | As for sign. |
Sign the statement as a DSSE
envelope with payload type application/vnd.in-toto+json, in your
ecosystem's usual form, so its own tools can verify it (e.g. a Sigstore
bundle holding a DSSE envelope, which cosign and gh attestation verify
read). On success, print one SignResult, with any
annotations those tools use to find it (e.g. Sigstore's
dev.sigstore.bundle.content and dev.sigstore.bundle.predicateType),
and exit 0. A plugin that can't produce DSSE must fail.
bomify stores an attestation as a referrer of the package like a
signature, annotated land.bomify.attestation.predicateType, and never
passes it to signature verify as a package signature, only to
signature verify-attestation.
signature verify¶
| Flag | Required | Meaning |
|---|---|---|
--payload |
yes | The payload the envelope must sign. |
--envelope |
yes | One envelope, byte for byte as some sign produced it (not necessarily this plugin's, nor a trusted signer's). |
--media-type |
yes | The envelope's media type, as its sign reported it. |
--reference |
yes | The reference being restored. Use it to scope trust, or ignore it. |
--option |
no, repeatable | A key=value from --verify-option or the matching trust rule. See Options. |
Print one VerifyResult and exit 0 only if the
envelope is a valid signature over the payload by a signer the plugin
trusts. Anything else (bad signature, untrusted or unconfigured signer,
malformed envelope, unsupported media type) exits non-zero with one
stderr message. Fail closed: if trust can't be established, it's not
trusted.
signature verify-attestation¶
| Flag | Required | Meaning |
|---|---|---|
--envelope |
yes | One attestation envelope, byte for byte as some attest produced it (not necessarily this plugin's, nor a trusted signer's). |
--media-type |
yes | The envelope's media type, as its attest reported it. |
--subject |
yes | The package manifest's digest, <algorithm>:<hex> (e.g. sha256:ae88...). |
--reference |
yes | As for verify. |
--option |
no, repeatable | As for verify. |
Print one VerifyAttestationResult and exit
0 only if the envelope is a DSSE envelope of payload type
application/vnd.in-toto+json, validly signed by a signer the plugin
trusts, whose statement names --subject among its subjects. Fail
otherwise, closed, as for verify. bomify checks the rest of the
statement itself.
signature supported-types¶
No flags. Print one
SupportedSignatureTypesResult and
exit 0. It must always report the same thing.
Options¶
--option key=value carries everything scheme-specific: a key path, a
KMS URI, a certificate identity. bomify only checks that both sides are
non-empty and never interprets them. Reject unknown keys rather than
ignoring them, since a silently ignored verification option can weaken
what's checked.
A trust rule's stored key (bomify trust create --key-option
key=<name>) arrives as an ordinary --option key=<path of bomify's
stored copy>, so a plugin always gets a file path.
The plugin owns its trust material. bomify only decides whether a
package is signed or verified, and by which plugins: --sign/--verify,
else (for verification) the most specific trust rule (see
the architecture docs).
Standard streams¶
| Stream | Reserved for |
|---|---|
| stdout | Exactly one JSON result, only on success. Nothing else, ever. |
| stderr | One short fatal message, only on failure. bomify appends it to its error (per rejected envelope, for verify). There's no --log file. |
| exit code | 0 on success; non-zero on failure, including a valid signature by an untrusted signer. |
SignResult¶
{
"artifactType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"envelope": "eyJtZWRpYVR5cGUiOi...",
"annotations": {}
}
| Field | Type | Required | Meaning |
|---|---|---|---|
artifactType |
string | yes | Artifact type of the referrer that carries the envelope. Use your ecosystem's standard type (e.g. application/vnd.cncf.notary.signature) so its tooling can find it. Must be in your supported-types. |
mediaType |
string | yes | Media type of the envelope blob, passed back to verify as --media-type. |
envelope |
string (base64) | yes, non-empty | The envelope, standard base64. bomify stores the decoded bytes and passes exactly those to verify. |
annotations |
object | no | Extra annotations for the referrer. bomify adds land.bomify.signature.plugin (<kind>). |
Schema: sign-result.schema.json.
VerifyResult¶
| Field | Type | Required | Meaning |
|---|---|---|---|
signer |
string | yes | Human-readable identity of the signer (key fingerprint, certificate subject, email). Logged only. |
Schema: verify-result.schema.json.
VerifyAttestationResult¶
| Field | Type | Required | Meaning |
|---|---|---|---|
signer |
string | yes | As for VerifyResult. |
statement |
string (base64) | yes, non-empty | The in-toto statement the envelope signs, exactly as signed, standard base64. |
Schema: verify-attestation-result.schema.json.
SupportedSignatureTypesResult¶
| Field | Type | Required | Meaning |
|---|---|---|---|
artifactTypes |
array of strings | yes, non-empty | Referrer artifact types verify and verify-attestation understand. bomify never passes them an envelope of another type. |
Schema: supported-signature-types-result.schema.json.
Where signatures live¶
Each signature is an OCI 1.1 referrer of the package manifest: a
manifest of the plugin's artifactType, subject set to the package,
with the envelope as its one layer. Registries expose it through the
Referrers API (or its tag-schema fallback); a save tarball holds it as
an untagged manifest in index.json. Re-signing adds a signature rather
than replacing one, and verification needs only one to pass.
Signatures never go in the SBOM, whose hash is the package's identity.
What bomify handles¶
Registry access, storage, choosing among a package's signatures, and
the policy on which packages need verifying are all bomify's. sign
should depend only on its flags and the plugin's keys; verify only on
its flags and trust configuration. Don't depend on the binary's other
contracts, if it has any.
Schemas¶
sign-result.schema.json¶
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/alejandro-velasco/bomify/main/plugins/contracts/signing/v1/sign-result.schema.json",
"title": "bomify plugin SignResult",
"description": "The single JSON object a bomify-plugin-<kind> binary must print to stdout on success for its \"signature sign\" subcommand, per plugins/contracts/signing/v1/CONTRACT.md. Validate a plugin's actual stdout output (one line, one object) against this schema to check it for structural conformance.",
"type": "object",
"additionalProperties": false,
"required": [
"artifactType",
"mediaType",
"envelope"
],
"properties": {
"artifactType": {
"type": "string",
"minLength": 1,
"description": "The artifact type of the referrer manifest bomify pushes to carry the envelope — ideally the signing ecosystem's own standard type (e.g. \"application/vnd.dev.sigstore.bundle.v0.3+json\"). Must also be listed by \"signature supported-types\"."
},
"mediaType": {
"type": "string",
"minLength": 1,
"description": "The media type of the envelope blob itself, passed back to \"signature verify\" as --media-type."
},
"envelope": {
"type": "string",
"minLength": 1,
"contentEncoding": "base64",
"description": "The signature envelope, standard base64-encoded. bomify stores the decoded bytes verbatim and hands exactly those bytes back to \"signature verify\"."
},
"annotations": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Optional extra annotations for the referrer manifest."
}
}
}
verify-result.schema.json¶
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/alejandro-velasco/bomify/main/plugins/contracts/signing/v1/verify-result.schema.json",
"title": "bomify plugin VerifyResult",
"description": "The single JSON object a bomify-plugin-<kind> binary must print to stdout for its \"signature verify\" subcommand when — and only when — the envelope is a trusted signature over the payload, per plugins/contracts/signing/v1/CONTRACT.md. A failed verification is reported by a non-zero exit instead.",
"type": "object",
"additionalProperties": false,
"required": [
"signer"
],
"properties": {
"signer": {
"type": "string",
"minLength": 1,
"description": "A human-readable identity of whoever made the signature (a key fingerprint, certificate subject, email address, ...). bomify only logs it."
}
}
}
verify-attestation-result.schema.json¶
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/alejandro-velasco/bomify/main/plugins/contracts/signing/v1/verify-attestation-result.schema.json",
"title": "bomify plugin VerifyAttestationResult",
"description": "The single JSON object a bomify-plugin-<kind> binary must print to stdout for its \"signature verify-attestation\" subcommand when — and only when — the envelope is a trusted attestation about the given subject, per plugins/contracts/signing/v1/CONTRACT.md. A failed verification is reported by a non-zero exit instead.",
"type": "object",
"additionalProperties": false,
"required": [
"signer",
"statement"
],
"properties": {
"signer": {
"type": "string",
"minLength": 1,
"description": "A human-readable identity of whoever signed the attestation (a key fingerprint, certificate subject, email address, ...). bomify only logs it."
},
"statement": {
"type": "string",
"contentEncoding": "base64",
"minLength": 1,
"description": "The in-toto statement the envelope signs, exactly as signed, standard base64. bomify checks it is the package's build provenance."
}
}
}
supported-signature-types-result.schema.json¶
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/alejandro-velasco/bomify/main/plugins/contracts/signing/v1/supported-signature-types-result.schema.json",
"title": "bomify plugin SupportedSignatureTypesResult",
"description": "The single JSON object a bomify-plugin-<kind> binary must print to stdout on success for its \"signature supported-types\" subcommand, per plugins/contracts/signing/v1/CONTRACT.md. Validate a plugin's actual stdout output (one line, one object) against this schema to check it for structural conformance.",
"type": "object",
"additionalProperties": false,
"required": [
"artifactTypes"
],
"properties": {
"artifactTypes": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
},
"description": "The referrer artifact types this plugin's \"signature verify\" understands. bomify never passes \"signature verify\" an envelope from a referrer of any other type."
}
}
}