Component Reference
Detailed documentation for mero-kms-phala, GCP node images, and the attestation verifier
mero-kms-phala
Rust HTTP service for attestation-gated key release via Phala dstack. 4 endpoints, profile-scoped policy.
Node Images
Packer + Ansible builds for GCP TDX Confidential VMs. Three security profiles with measured boot.
Attestation Verifier
React + serverless API for public attestation verification via Intel Trust Authority.
mero-kms-phala (KMS Service)
Rust HTTP service that validates TDX attestation quotes and releases storage encryption keys. Runs inside a Phala Cloud CVM with access to the dstack key derivation runtime.
API Endpoints
Generates a cryptographic challenge (nonce) for a node to include in its TDX attestation quote. The challenge binds the quote to this specific key-release request, preventing replay attacks.
"peerId": string,
"namespace": string
}
"challenge": string (hex),
"expiresAt": string (ISO 8601)
}
Rate limited per peerId (MAX_PENDING_CHALLENGES). Challenge expires after CHALLENGE_TTL_SECS.
Releases a storage encryption key after validating the node’s TDX attestation quote against the loaded policy. This is the core key-release endpoint.
"peerId": string,
"namespace": string,
"challenge": string (hex),
"quote": string (base64),
"signature": string (hex)
}
"key": string (hex),
"derivationPath": string
}
Validates: challenge freshness, signature over challenge, TDX quote integrity, MRTD/RTMR policy match, TCB status. Returns 403 on policy violation.
Returns the KMS’s own TDX attestation quote. Used by nodes for mutual verification — the node can verify the KMS is running in a genuine TEE before trusting it with key material.
"nonce": string (hex, optional)
}
"quote": string (base64),
"measurements": {
"mrtd": string,
"rtmr0": string,
"rtmr1": string,
"rtmr2": string,
"rtmr3": string
}
}
Optional nonce is included in the quote’s report data for freshness binding. Used by the attestation verifier and by nodes during mutual verification.
Health check endpoint for load balancers and monitoring. Returns the running version, profile, and policy status.
No request body. Simple GET request.
"status": "ok",
"version": string,
"profile": string,
"policyLoaded": boolean
}
Configuration
Release Policy Fetch (Production)
In production, the KMS fetches its attestation policy from GitHub releases at startup. This is the default and recommended mode.
MERO_KMS_VERSION + MERO_KMS_PROFILE
→ GitHub release: mero-tee-{version}
→ Download: kms-phala-attestation-policy.{profile}.json
→ Optional: verify SHA-256 against MERO_KMS_POLICY_SHA256
→ Load into AppState.policy
Env Policy (Development)
For development and testing, the policy can be loaded from environment variables instead of GitHub.
USE_ENV_POLICY=true
ALLOWED_MRTD=<hex>
ALLOWED_RTMR0=<hex>
ALLOWED_RTMR1=<hex>
ALLOWED_RTMR2=<hex>
ALLOWED_RTMR3=<hex>
ALLOWED_TCB_STATUS=UpToDate,SWHardeningNeeded
Full Environment Variable Reference
| Variable | Type | Default | Description |
|---|---|---|---|
| LISTEN_ADDR | SocketAddr | 0.0.0.0:8080 | HTTP server bind address and port. |
| CORS_ALLOWED_ORIGINS | String (CSV) | * | Comma-separated CORS allowed origins. |
| DSTACK_SOCKET_PATH | PathBuf | /var/run/dstack.sock | Unix socket path to the dstack daemon for key derivation and quote generation. |
| CHALLENGE_TTL_SECS | u64 | 300 | Challenge nonce TTL in seconds. |
| MAX_PENDING_CHALLENGES | usize | 10 | Max active challenges per peerId before rate-limiting. |
| REDIS_URL | Option<String> | None | Redis URL for distributed challenge store. Required for multi-instance deployments. |
| ACCEPT_MOCK_ATTESTATION | bool | false | Accept synthetic TDX quotes without hardware. Dev only. |
| ENFORCE_MEASUREMENT_POLICY | bool | true | Validate quote measurements against policy. Must be true in production. |
| MERO_KMS_VERSION | String | required | Release tag for policy fetch (e.g. v0.3.0). |
| MERO_KMS_PROFILE | String | required | Image profile: debug, debug-read-only, or locked-read-only. |
| MERO_KMS_POLICY_SHA256 | Option<String> | None | Expected SHA-256 of the policy file for supply-chain protection. |
| USE_ENV_POLICY | bool | false | Read policy from env vars instead of GitHub releases. Dev only. |
| KEY_NAMESPACE_PREFIX | String | calimero/ | Prefix for dstack key derivation paths. Different prefixes yield different keys. |
| ALLOWED_MRTD | String | — | Hex-encoded expected MRTD value. Only used when USE_ENV_POLICY=true. |
| ALLOWED_RTMR0 | String | — | Hex-encoded expected RTMR0. Only with env policy. |
| ALLOWED_RTMR1 | String | — | Hex-encoded expected RTMR1. Only with env policy. |
| ALLOWED_RTMR2 | String | — | Hex-encoded expected RTMR2. Only with env policy. |
| ALLOWED_RTMR3 | String | — | Hex-encoded expected RTMR3. Only with env policy. |
| ALLOWED_TCB_STATUS | String (CSV) | — | Comma-separated acceptable TCB statuses. Only with env policy. |
Production Guidance
- Always use release-fetch mode — set MERO_KMS_VERSION and MERO_KMS_PROFILE, leave USE_ENV_POLICY as false.
- Pin the policy hash — set MERO_KMS_POLICY_SHA256 to the SHA-256 of the expected policy file. This prevents MITM or supply-chain attacks on the policy download.
- Enforce measurements — ENFORCE_MEASUREMENT_POLICY=true is the default and must not be overridden.
- Disable mock attestation — ACCEPT_MOCK_ATTESTATION=false is the default. Production CI checks fail if set to true.
Development Mode
For local development without TDX hardware:
ACCEPT_MOCK_ATTESTATION=true
ENFORCE_MEASUREMENT_POLICY=false
USE_ENV_POLICY=true
ALLOWED_MRTD=0000...0000
ALLOWED_RTMR0=0000...0000
ALLOWED_RTMR1=0000...0000
ALLOWED_RTMR2=0000...0000
ALLOWED_RTMR3=0000...0000
ALLOWED_TCB_STATUS=UpToDate,SWHardeningNeeded,OutOfDate
In mock mode, the KMS accepts synthetic quotes with zeroed measurements. The challenge-response flow still executes but quote validation is skipped. This allows testing the full API contract without TDX hardware.
Use docker compose -f docker-compose.dev.yml up from the mero-kms directory for a pre-configured development stack.
Deployment Links
- Phala KMS Runbook — full deployment procedure for Phala Cloud CVMs
- Blue-Green Rollout — safe production rollout with decision gates
- Config Reference — detailed environment variable documentation
- Policy Management — attestation policy promotion workflows
mero-tee (GCP Node Images)
Packer + Ansible pipeline that builds GCP TDX Confidential VM images containing the merod binary, runtime configuration, and profile-specific security policies.
What It Builds
The image builder produces bootable GCP Confidential VM images with Intel TDX enabled. Each image includes:
- The merod binary (baked at build time, version-locked)
- A profile pin file at /etc/mero-kms/image-profile
- RTMR3 runtime event configuration matching the profile
- System hardening via Ansible roles (firewall, sshd, kernel params)
- Cloud-init configuration for first-boot setup
The resulting image is published to a GCP project as an image family, allowing instances to be created from the latest or a pinned version.
Security Profiles
Three profiles define the security posture of the built image. Each profile writes a distinct RTMR3 runtime event, creating cryptographic cohort separation.
Prerequisites
- Packer ≥ 1.9 — for building GCP images from templates
- Ansible ≥ 2.15 — for provisioning and hardening the image
- GCP access — service account with compute.images.create, compute.instances.create permissions in the target project
- merod binary — built from the calimero/core repository. The binary hash is recorded in the image metadata.
Configuration Variables
| Variable | Type | Default | Description |
|---|---|---|---|
| profile | string | required | Image profile: debug, debug-read-only, or locked-read-only. |
| gcp_project | string | required | GCP project ID to publish the image to. |
| gcp_zone | string | us-central1-a | GCP zone for the build instance. |
| source_image_family | string | ubuntu-2404-lts | Base image family to build on. |
| merod_binary_path | string | required | Local path to the merod binary to bake into the image. |
| merod_version | string | required | Version string for the merod binary (recorded in image metadata). |
| image_family_prefix | string | mero-tee | Prefix for the GCP image family name. Final name: {prefix}-{profile}. |
Build Command
packer build \
-var "profile=locked-read-only" \
-var "gcp_project=calimero-prod" \
-var "merod_binary_path=./merod" \
-var "merod_version=v0.8.0" \
mero-tee.pkr.hcl
The build takes approximately 10–15 minutes. Packer creates a temporary GCP instance, runs Ansible provisioning, captures the disk as an image, and destroys the instance.
Release Workflow
The release workflow automates the full build-probe-publish cycle:
Build images
Packer builds all three profile images in parallel.
Launch probe VMs
Creates temporary instances from each image to extract TDX measurements.
Extract measurements
Reads MRTD and RTMRs from probe VMs, generates published-mrtds.json.
Publish images
Promotes images to the production GCP project and updates image families.
Create release
Tags the release, uploads MRTD file, checksums, SBOM, and provenance to GitHub.
Attestation Verifier
React (Vite) single-page application with a serverless API backend for public, independent verification of KMS attestation quotes via Intel Trust Authority.
Architecture
The verifier enables anyone to independently confirm that a Calimero KMS instance is running genuine TEE code. It acts as an external auditor, separate from the node-KMS trust relationship.
Frontend (React + Vite)
SPA that accepts a KMS URL, sends it to the serverless API, and displays the verification result. Built with Vite for fast development and optimized production builds.
Serverless API (/api/verify)
Vercel serverless function that performs the actual verification. Fetches the KMS attestation quote, submits it to ITA, and returns the appraisal result. Keeps ITA credentials server-side.
Deployment
Deployed on Vercel with the following environment variables:
| Variable | Type | Description |
|---|---|---|
| ITA_API_KEY | string (secret) | Intel Trust Authority API key for submitting quote appraisal requests. |
| ITA_APPRAISAL_URL | string | ITA endpoint URL for TDX quote verification (e.g. https://api.trustauthority.intel.com/appraisal/v2/attest). |
| KMS_ALLOWED_HOSTS | string (CSV) | Allowlist of KMS hostnames the verifier is permitted to contact. SSRF protection. |
MDMA Integration
The verifier uses compatibility-catalog.json to map KMS versions to expected measurements. This enables the UI to show whether a KMS instance’s measurements match a known-good release.
{
"v0.3.0": {
"locked-read-only": {
"mrtd": "abc123...",
"rtmr0": "def456...",
"composeHash": "789abc..."
}
}
}
The catalog is updated alongside each release. The verifier fetches it at load time and uses it for measurement comparison in the verification result UI.
Verification Flow
User enters KMS URL
The browser UI accepts a KMS endpoint URL (e.g. https://kms.calimero.network).
Browser → /api/verify
The frontend sends the KMS URL to the serverless API with a freshly generated nonce.
API → POST /attest on KMS
The serverless function validates the URL against KMS_ALLOWED_HOSTS, then fetches the KMS attestation quote via POST /attest with the nonce.
API → ITA verification
The raw TDX quote is submitted to Intel Trust Authority for appraisal. ITA returns a signed JWT with the verification result.
API processes ITA response
The serverless function validates the ITA JWT signature, checks the appraisal result, compares measurements against the compatibility catalog, and verifies the nonce binding.
Result displayed in browser
The UI shows the verification status, measurement values, ITA appraisal details, compose hash comparison, and profile matching result.
What It Checks
ITA Appraisal
The Intel Trust Authority JWT confirms the TDX quote is genuine hardware-generated and the TCB level is acceptable. The verifier checks the JWT signature, issuer, and expiry.
Compose Hash
The Docker Compose hash from the KMS quote’s report data is compared against the known hash in the compatibility catalog. A mismatch indicates the KMS is running a different configuration.
Profile Matching
The RTMR3 value in the quote is compared against known profile values. This confirms the KMS is running the expected profile (e.g. locked-read-only for production).
Nonce Binding
The nonce sent in the verification request must appear in the quote’s report data. This prevents replay attacks where an old valid quote is reused.
Security
SSRF Protection
The KMS_ALLOWED_HOSTS environment variable contains a CSV allowlist of hostnames the verifier may contact. The serverless function validates the requested KMS URL against this list before making any outbound request. This prevents attackers from using the verifier as a proxy to reach internal services.
Nonce Binding
Each verification request generates a fresh random nonce. The nonce is included in the POST /attest request to the KMS and must appear in the resulting TDX quote’s report data. This binds the quote to the specific verification session, preventing replay of previously valid quotes.
JWT Verification
The ITA appraisal JWT is verified against Intel’s published signing keys. The verifier checks:
- JWT signature validity using ITA’s JWKS endpoint
- Issuer claim matches the expected ITA issuer
- Token is not expired
- Appraisal result indicates a valid TDX quote
Node Verification (Planned)
Future versions will extend the verifier to also verify individual merod node attestation quotes, in addition to KMS quotes. This requires integration with the GCP published-mrtds.json measurements for node image verification.