Blog
Automating IBM Confidential Computing contracts with contract-cli
By Sashwat Karuvayil — Senior Engineer, IBM India Systems Development Lab, Bengaluru. Leads the open-source contract tooling for IBM Confidential Computing.
Go SDK for the full contract lifecycle: build, validate, encrypt, sign.
User-facing CLI built on contract-go. Three commands replace twelve OpenSSL calls.
Declarative contracts as Terraform resources — plan, apply, destroy with the rest of your stack.
Every workload running on IBM Confidential Computing Container Runtime needs a contract. No contract, no instance — the deployment starts and then shuts down. The contract is a YAML file passed as user-data when the runtime instance is created, and it tells the runtime exactly what to run, how to log it, and how to protect the data at rest.
The first time you assemble one of these by hand — writing the YAML, base64-encoding your pod descriptors, encrypting each section with the right OpenSSL incantations, generating the signature — it’s a worthwhile exercise. The fiftieth time, it’s a bug factory. So we built tooling for it.
What a contract actually is
A contract is a YAML definition file specific to a Confidential Computing runtime instance. It’s the single input that determines what containers run, how they’re configured, where logs go, and how data volumes are encrypted. The runtime image boots, reads the contract, decrypts it if encrypted, validates the schema, checks for a signature, derives the LUKS passphrase for disk encryption, and then brings up the containers defined inside.
A contract has four top-level sections:
workload(mandatory) — defines what to run. Contains the pod descriptors (via theplaysubsection), optional container registry credentials (auths), image signing keys (images), and volume encryption seeds (volumes).env(mandatory) — defines the deployment environment. Logging configuration (syslog or IBM Cloud Logs), the deployer’s volume encryption seed, the signing key for signature validation, and optional subsections for crypto passthrough, host attestation, and retrievable secrets.attestationPublicKey(optional) — a public RSA key used to encrypt the attestation document, so only the auditor persona can read it.envWorkloadSignature(optional) — a signature over the workload and env sections, ensuring they’re used together and haven’t been tampered with.
Why two personas matter
The contract is deliberately split between two personas who don’t need to see each other’s data:
- The workload persona (app developer or ISV) provides everything about the container — the image digest, registry credentials, environment variables, the pod descriptor, and their half of the volume encryption seed.
- The deployer persona (system admin) provides everything about the environment — the logging instance, their half of the volume encryption seed, and the signing key for contract signature validation.
Each persona can encrypt their own section independently using IBM’s encryption certificate. The runtime bootloader decrypts each section at boot time. This means the deployer never sees the workload persona’s container credentials, and the workload persona never sees the deployer’s logging configuration. Separation of duty, enforced by cryptography.
What goes inside the workload section
The workload section’s most important piece is the play subsection, which defines containers using pod descriptors. There are three ways to provide them:
resources— plain YAML pod and ConfigMap descriptors, inline in the contract.archive— a base64-encoded, gzipped tar file containing the pod descriptor YAML plus any extra files (certificates, configs) needed at deployment.templates— pod descriptors with Go template expressions (like Helm), where values are filled in at deployment time from environment variables defined in the contract.
A minimal workload section looks like this:
type: workload
play:
archive: <base64-encoded tgz of pod descriptor YAML>
When a data volume is attached, both personas provide a seed — the runtime concatenates them, hashes the result (SHA-256), and uses that as the LUKS passphrase. Neither persona alone can decrypt the disk.
Why hand-rolling this is painful
Encrypting a single section of the contract manually takes six OpenSSL commands:
- Generate a random 32-byte password
- Encrypt that password with IBM’s RSA certificate
- Encrypt the YAML content with AES-256-CBC using the random password
- Base64-encode both pieces
- Concatenate them with the
hyper-protect-basic.prefix - Repeat for the other section
Signing the contract adds more steps — generate a key pair, concatenate the encrypted workload and env values (no whitespace, no trailing newline — check with hexdump), sign with SHA-256, and add the result as envWorkloadSignature.
Get any step wrong — a stray newline in the signature input, a missing pipe character in the YAML, the wrong base64 flag — and the instance either refuses to boot or shuts down silently. None of these failure modes are fun to debug.
What we shipped
- contract-go — a Go SDK that handles the entire contract lifecycle: building the workload and env sections, encrypting them, signing, and producing the final user-data YAML. The API is shaped around the workflow, not around OpenSSL.
- contract-cli — a CLI built on contract-go for platform teams who need to generate, validate, sign, and encrypt contracts from the command line. Full user documentation here.
- terraform-provider-hpcr — declarative contract generation as Terraform resources. Same SDK, same guarantees, but expressed as HCL you can plan, apply, and destroy alongside the rest of your infrastructure.
How the CLI replaces the manual workflow
The CLI supports all current IBM Confidential Computing product lines — IBM Confidential Computing Container Runtime (ccrt), IBM Confidential Computing Container Runtime for Red Hat Virtualization Solutions (ccrv), and IBM Confidential Computing Containers for Red Hat OpenShift Container Platform (ccco) — plus the legacy IBM Hyper Protect Virtual Servers (hpvs) target for older deployments. A typical workflow:
# 1. Create a base64-encoded archive of your pod descriptors
contract-cli base64-tgz --in ./pod-descriptors
# 2. Validate the contract structure before committing
contract-cli validate-contract --in contract.yaml --os ccrv
# 3. Encrypt and sign the contract in one step
contract-cli encrypt \
--in contract.yaml \
--priv private.pem \
--out user-data.yaml
Three commands instead of a dozen OpenSSL incantations. The validate-contract step catches structural issues — malformed YAML, missing mandatory sections, schema violations — before anything gets signed or encrypted. The encrypt command handles the random password generation, RSA encryption of the password, AES encryption of the content, and signature generation in a single pass.
You can also set contract expiry and decrypt attestation records:
# Encrypt with a 30-day expiry
contract-cli encrypt \
--in contract.yaml \
--priv private.pem \
--contract-expiry \
--cacert ca.crt \
--cakey ca.key \
--csr request.csr \
--expiry 30 \
--out user-data.yaml
# Decrypt and verify an attestation record
contract-cli decrypt-attestation \
--in se-checksums.txt.enc \
--priv private.pem \
--signature se-signature.bin \
--attestation-cert hpse-attestation.crt
Where Terraform fits
In CI, the CLI is usually sufficient. Where the Terraform provider earns its keep is when the contract is one piece of a larger stack — a VPC, a few VSIs, the contract that boots them, and the logging configuration that ties everything together:
resource "hpcr_contract_encrypted" "workload" {
contract = file("${path.module}/contract.yaml")
cert = file("${path.module}/encryption.pem")
}
resource "ibm_is_instance" "vsi" {
user_data = hpcr_contract_encrypted.workload.rendered
# …
}
The contract becomes a normal Terraform resource — planned, applied, destroyed alongside the rest of the infrastructure. No out-of-band scripts, no “remember to re-encrypt before you terraform apply”.
Why we open-sourced it
What started life as IBM-internal tooling now ships in the open under ibm-hyper-protect. Contracts are a trust artifact — the teams using them want to read the code that built them, and they should. Closed-source cryptography for a feature whose entire job is to make trust verifiable would have been a strange choice.
The work was recognised with IBM TCAP Significant Contributor 2025 and TCAP Leader 2026. The full case study lives on the work page.
A few things shipping it taught me
- Design the SDK around the workflow, not the cryptography. The crypto is the implementation detail. The workflow — build, validate, encrypt, sign — is the product.
- Respect the persona boundary. The contract’s split between workload and deployer isn’t bureaucracy; it’s a security property. The tooling has to make it easy to keep sections encrypted and separate.
- Make the CLI feel like a normal CLI. Real flags, real exit codes, output you can pipe. Anything else and people will stop using it and go back to shell scripts.
- Optimise for loud failure. Most of the value of this tooling is that it’s very hard to produce a quietly broken contract — the kind that encrypts cleanly but won’t boot, or boots but skips a check. Default to failing early and obviously.
What’s next
The contract surface keeps growing — new product lines, new attestation flows, crypto passthrough, retrievable secrets, multi-volume support. Most of the work now is keeping the SDK simple while the universe behind it gets more complex. If a future contract still validates, encrypts, and signs in three CLI commands, the abstraction is doing its job.
Check out the full contract-cli documentation for the complete command reference.
FAQ
Frequently asked questions
What is an IBM Confidential Computing contract?
A contract is a YAML user-data file passed to an IBM Confidential Computing Container Runtime instance at boot. It tells the runtime which containers to start, how to log them, and how to encrypt their data volumes. Without a valid contract, the instance starts and immediately shuts down.
What are the four sections of an IBM Confidential Computing contract?
workload (mandatory) defines what to run — pod descriptors, registry credentials, image-signing keys, volume encryption seeds. env (mandatory) defines the deployment environment — logging, the deployer's encryption seed, and the contract-signing key. attestationPublicKey (optional) is the public RSA key that encrypts the attestation document. envWorkloadSignature (optional) is a signature over the workload and env sections together.
Why does an IBM Confidential Computing contract use two personas?
The workload persona (app developer or ISV) and the deployer persona (system admin) each encrypt their own contract section independently using IBM's encryption certificate. Cryptography enforces separation of duty — the deployer never sees the workload persona's container credentials, and the workload persona never sees the deployer's logging configuration.
What is contract-cli?
contract-cli is an open-source CLI built on top of contract-go. It generates, validates, encrypts, and signs IBM Confidential Computing contracts in a few commands instead of a dozen OpenSSL invocations. It supports all current IBM Confidential Computing product lines: IBM Confidential Computing Container Runtime (ccrt), IBM Confidential Computing Container Runtime for Red Hat Virtualization Solutions (ccrv), and IBM Confidential Computing Containers for Red Hat OpenShift Container Platform (ccco), plus the legacy IBM Hyper Protect Virtual Servers (hpvs) target.
What is contract-go?
contract-go is a Go SDK that handles the entire IBM Confidential Computing contract lifecycle — building the workload and env sections, encrypting them with IBM's RSA certificate, signing the encrypted output, and producing the final user-data YAML. The API is shaped around the build-validate-encrypt-sign workflow rather than around OpenSSL primitives.
When should I use the Terraform provider instead of contract-cli?
Use contract-cli for one-off generation, especially in CI pipelines. Use terraform-provider-hpcr when the contract is one piece of a larger stack — VPC, virtual server instances, logging — that you plan, apply, and destroy together. The contract becomes a normal Terraform resource alongside the rest of your infrastructure.
How is the LUKS volume passphrase derived for a Confidential Computing data volume?
Both personas provide a seed for each data volume. The runtime concatenates them, hashes the result with SHA-256, and uses that hash as the LUKS passphrase. Neither persona alone holds the full key, so neither can decrypt the disk on their own.
Is the IBM Confidential Computing contract tooling open source?
Yes. contract-go, contract-cli, and terraform-provider-hpcr are all open source under the ibm-hyper-protect GitHub organization. Contracts are a trust artifact, so the cryptography that builds them is published in the open for the teams using it to audit.