How to choose a signer
Cilock supports nine signer providers. This guide is the decision tree, pick the path that matches your environment, then jump to the relevant config detail.
TL;DRโ
| Environment | Pick this |
|---|---|
| Hosted CI with OIDC (GitHub Actions, GitLab CI) | fulcio (Sigstore keyless) |
| Workloads inside a SPIFFE/SPIRE service mesh | spiffe |
| AWS-native, want an HSM-backed identity | kms/aws |
| GCP-native | kms/gcp |
| Azure-native | kms/azure |
| HashiCorp Vault user with the Transit engine | vault-transit |
| HashiCorp Vault user, simple key-stored mode | vault |
| Local dev / smoke tests / CI without OIDC | file |
| Internal cilock development | debug-signer |
The decision treeโ
Is the workload running in CI with an OIDC token available?
โโโ Yes โ Is it inside a SPIFFE/SPIRE mesh?
โ โโโ Yes โ spiffe
โ โโโ No โ fulcio (default for GitHub Actions, GitLab CI)
โโโ No โ Is it inside a cloud you already have KMS in?
โโโ AWS โ kms/aws
โโโ GCP โ kms/gcp
โโโ Azure โ kms/azure
โโโ No โ Are you using HashiCorp Vault?
โโโ Yes, with Transit engine โ vault-transit
โโโ Yes, key-stored โ vault
โโโ No โ file (and consider whether you should be in CI at all)
Why prefer keyless?โ
Both fulcio and spiffe issue short-lived certificates tied to runtime identity. There's no long-lived private key to leak, no rotation schedule to forget, no credential to exfiltrate that's useful from an attacker's laptop.
The two recent supply-chain attacks cilock catches at scale (Trivy March 2026, LiteLLM March 2026) both worked by exfiltrating exactly the kind of long-lived credentials that file/KMS signing requires you to store. Keyless signing eliminates that attack surface entirely for the signing keys themselves.
If you can do keyless, do keyless.
Fulcio specificsโ
The Sigstore CA. Most teams using GitHub Actions or GitLab CI should pick this.
cilock run --step build \
--signer-fulcio-url "$FULCIO_URL" \
--signer-fulcio-oidc-issuer "https://token.actions.githubusercontent.com" \
--signer-fulcio-oidc-client-id sigstore \
--signer-fulcio-token "$OIDC_TOKEN" \
...
In GitHub Actions, the cilock-action defaults all of this for you when enable-sigstore: true (the action's default). You only need permissions: { id-token: write } on the workflow.
For self-hosted Fulcio, set --signer-fulcio-url to your instance. The client defaults to HTTP/REST (--signer-fulcio-use-http=true); set --signer-fulcio-use-http=false to switch to gRPC.
SPIFFE/SPIRE specificsโ
For workloads that already have SVID identities from a SPIRE agent. The signer fetches an SVID from the agent's workload API socket and uses the cert as the signing identity.
cilock run --step build \
--signer-spiffe-socket-path "unix:///run/spire/sockets/agent.sock" \
...
The socket path must have a unix:// or tcp:// scheme; the bare filesystem path is not accepted.
Pair with policy certConstraint.uris containing your SPIFFE ID:
{
"type": "root",
"certConstraint": {
"commonname": "*",
"dnsnames": ["*"],
"emails": ["*"],
"organizations": ["*"],
"uris": ["spiffe://example.com/build"],
"roots": ["*"]
}
}
KMS specificsโ
KMS signers use a URI-based reference scheme borrowed from Sigstore Cosign. The same URI works as the --signer-kms-ref flag and as the publickeyid in a policy:
| Provider | URI form |
|---|---|
| AWS | awskms:///arn:aws:kms:<region>:<account>:key/<key-id> (note the triple slash; the alternative awskms://[endpoint]/[ID/ALIAS/ARN] lets you target a non-default endpoint such as LocalStack) |
| GCP | gcpkms://projects/<proj>/locations/<loc>/keyRings/<ring>/cryptoKeys/<key>/cryptoKeyVersions/<v> (/versions/<v> shorthand also accepted; the whole version suffix is optional and resolves to the key's primary version) |
| Azure | azurekms://<vault-name>.vault.azure.net/<key>[/<key-version>] |
cilock run --step build \
--signer-kms-ref "awskms:///arn:aws:kms:us-east-2:111122223333:key/1234abcd-..." \
...
Authentication uses each cloud provider's standard credential resolution, no extra cilock config needed. For full flag detail (custom endpoints, profile selection, etc.) see signing & identity.
For air-gapped verification where the KMS service isn't reachable from the verifier, embed the base64-encoded PEM public key directly in the policy alongside the publickeyid URI.
Vault specificsโ
Two distinct Vault signers, two different flag namespaces, two different Vault engines. Pick by engine:
vault-transit uses Vault's Transit secrets engine for signing-as-a-service. The key stays in Vault; cilock asks Vault to sign the payload digest. Configure via the kms-hashivault provider:
cilock run --step build \
--signer-kms-ref "hashivault://my-signing-key" \
--signer-kms-hashivault-addr "https://vault.internal:8200" \
--signer-kms-hashivault-token-file /run/secrets/vault-token \
...
This calls PUT /v1/transit/sign/<key>/<hash> under the hood (verified by tracing a failed run against a nonexistent host).
vault uses Vault's PKI secrets engine. Each invocation asks Vault to issue a short-lived X.509 certificate for the role, similar in spirit to Fulcio:
cilock run --step build \
--signer-vault-url "https://vault.internal:8200" \
--signer-vault-token "$VAULT_TOKEN" \
--signer-vault-role build-signer \
...
This calls POST /v1/pki/issue/<role> under the hood. Pick this when you want Vault-managed identity but cert-based rather than long-lived-key-based signing.
Most teams pick vault-transit. Pick vault when you already have a Vault PKI engine standing up build identities.
File specificsโ
A local PEM-format key file. Useful for local dev and CI environments without OIDC support, but not recommended for production releases: there's a long-lived private key to manage, rotate, and protect.
cilock run --step build \
--signer-file-key-path ./signing.key \
--signer-file-cert-path ./signing.crt \
...
If you find yourself using file in production CI, the question to ask is: why don't I have OIDC available here? GitHub Actions, GitLab CI, AWS, GCP, and Azure all expose OIDC tokens.
Mixing signers across pipelinesโ
Different steps can use different signers, the verifier resolves each step's collection against that step's functionaries independently. A common pattern:
| Step | Signer | Why |
|---|---|---|
build | fulcio (keyless) | Runs in CI, has OIDC |
policy-sign | kms/aws (long-lived) | Policy is signed once at policy-publish time, away from CI |
release-promotion | kms/aws (long-lived) | Promotion gate runs as a service account, not a CI workflow |
Only one signer is supported per cilock run invocation (verified from cilock/internal/cmd/run.go). To use multiple signers, run cilock multiple times.
Parity with Witnessโ
The cilock signer surface is a superset of the upstream Witness signer surface; everything Witness supports, cilock supports with the same flag namespaces. Cilock adds three:
| Provider | Witness | Cilock | Notes |
|---|---|---|---|
file | โ | โ | Cilock adds --signer-file-key-passphrase / --signer-file-key-passphrase-path for encrypted PEMs. |
fulcio | โ | โ | Cilock adds --signer-fulcio-use-http (HTTP/REST vs. gRPC; defaults to HTTP). |
spiffe | โ | โ | Identical. |
vault (PKI engine) | โ | โ | Identical. |
kms/aws | โ | โ | Identical. |
kms/gcp | โ | โ | Identical. |
kms/azure | (none) | โ | Cilock-only. |
vault-transit (kms/hashivault) | (none) | โ | Cilock-only; uses Vault's Transit engine instead of PKI. |
debug-signer | (none) | โ | Cilock-only; ephemeral keypair for development. |
For the five shared providers, the Witness docs are an authoritative reference; cilock's wire format and flag names are identical.
See alsoโ
- Signing & identity concept, full mental model
- Choose a signer for the GitHub Actions tutorial
- Witness KMS signer docs, schema-compatible upstream reference for the shared
file,fulcio,spiffe,vault,kms/{aws,gcp}providers