Skip to content

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:

  1. Pack the package as an OCI manifest, which pins the SBOM and every layer by digest, so one signature covers the whole package.
  2. 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.
  3. Verify (pull/load): before fetching anything else, list the manifest's referrers, keep those whose artifact type signature supported-types lists, and call signature verify on 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 reach verify more than once with different options: judge each call by its own flags alone.
  4. Verify provenance (pull/load with --verify-provenance or a trust rule's --require-provenance): the same way, call signature verify-attestation on 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:

{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:ae88...","size":1234}

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

{ "signer": "key sha256:<base64 fingerprint of the public key>" }
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

{ "signer": "key sha256:<base64 fingerprint of the public key>", "statement": "eyJfdHlwZSI6..." }
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

{ "artifactTypes": ["application/vnd.dev.sigstore.bundle.v0.3+json"] }
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."
    }
  }
}