Component Reference

Detailed documentation for mero-kms-phala, GCP node images, and the attestation verifier

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
POST /challenge

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.

Request
{
  "peerId": string,
  "namespace": string
}
Response 200
{
  "challenge": string (hex),
  "expiresAt": string (ISO 8601)
}

Rate limited per peerId (MAX_PENDING_CHALLENGES). Challenge expires after CHALLENGE_TTL_SECS.

POST /get-key

Releases a storage encryption key after validating the node’s TDX attestation quote against the loaded policy. This is the core key-release endpoint.

Request
{
  "peerId": string,
  "namespace": string,
  "challenge": string (hex),
  "quote": string (base64),
  "signature": string (hex)
}
Response 200
{
  "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.

POST /attest

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.

Request
{
  "nonce": string (hex, optional)
}
Response 200
{
  "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.

GET /health

Health check endpoint for load balancers and monitoring. Returns the running version, profile, and policy status.

Request

No request body. Simple GET request.

Response 200
{
  "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.

// Policy fetch flow
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.

// Env policy mode — development only
USE_ENV_POLICY=true
ALLOWED_MRTD=<hex>
ALLOWED_RTMR0=<hex>
ALLOWED_RTMR1=<hex>
ALLOWED_RTMR2=<hex>
ALLOWED_RTMR3=<hex>
ALLOWED_TCB_STATUS=UpToDate,SWHardeningNeeded
Warning: USE_ENV_POLICY=true bypasses the release policy fetch and SHA-256 verification. Never use in production. CI checks block deployment with this flag enabled on production targets.

Full Environment Variable Reference

VariableTypeDefaultDescription
LISTEN_ADDRSocketAddr0.0.0.0:8080HTTP server bind address and port.
CORS_ALLOWED_ORIGINSString (CSV)*Comma-separated CORS allowed origins.
DSTACK_SOCKET_PATHPathBuf/var/run/dstack.sockUnix socket path to the dstack daemon for key derivation and quote generation.
CHALLENGE_TTL_SECSu64300Challenge nonce TTL in seconds.
MAX_PENDING_CHALLENGESusize10Max active challenges per peerId before rate-limiting.
REDIS_URLOption<String>NoneRedis URL for distributed challenge store. Required for multi-instance deployments.
ACCEPT_MOCK_ATTESTATIONboolfalseAccept synthetic TDX quotes without hardware. Dev only.
ENFORCE_MEASUREMENT_POLICYbooltrueValidate quote measurements against policy. Must be true in production.
MERO_KMS_VERSIONStringrequiredRelease tag for policy fetch (e.g. v0.3.0).
MERO_KMS_PROFILEStringrequiredImage profile: debug, debug-read-only, or locked-read-only.
MERO_KMS_POLICY_SHA256Option<String>NoneExpected SHA-256 of the policy file for supply-chain protection.
USE_ENV_POLICYboolfalseRead policy from env vars instead of GitHub releases. Dev only.
KEY_NAMESPACE_PREFIXStringcalimero/Prefix for dstack key derivation paths. Different prefixes yield different keys.
ALLOWED_MRTDStringHex-encoded expected MRTD value. Only used when USE_ENV_POLICY=true.
ALLOWED_RTMR0StringHex-encoded expected RTMR0. Only with env policy.
ALLOWED_RTMR1StringHex-encoded expected RTMR1. Only with env policy.
ALLOWED_RTMR2StringHex-encoded expected RTMR2. Only with env policy.
ALLOWED_RTMR3StringHex-encoded expected RTMR3. Only with env policy.
ALLOWED_TCB_STATUSString (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 measurementsENFORCE_MEASUREMENT_POLICY=true is the default and must not be overridden.
  • Disable mock attestationACCEPT_MOCK_ATTESTATION=false is the default. Production CI checks fail if set to true.
Development Mode

For local development without TDX hardware:

# Development environment
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

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.

debug
Full access for development. SSH enabled, debug logging, no filesystem restrictions. Nodes using this profile can only obtain keys from a KMS configured with MERO_KMS_PROFILE=debug. Not suitable for production data.
debug-read-only
Read-only root filesystem with SSH enabled for debugging. Provides stronger integrity than debug while still allowing operator access. Suitable for staging environments.
locked-read-only
Production hardened. Read-only root filesystem, SSH disabled, minimal attack surface. Strongest security posture. Required for production nodes handling real user data.
Cohort separation is cryptographic: Each profile produces a different RTMR3 value. A debug node cannot impersonate a locked-read-only node because the TDX quote would contain the wrong RTMR3 measurement. The KMS rejects any quote that doesn’t match its configured profile’s expected RTMR3.
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
VariableTypeDefaultDescription
profilestringrequiredImage profile: debug, debug-read-only, or locked-read-only.
gcp_projectstringrequiredGCP project ID to publish the image to.
gcp_zonestringus-central1-aGCP zone for the build instance.
source_image_familystringubuntu-2404-ltsBase image family to build on.
merod_binary_pathstringrequiredLocal path to the merod binary to bake into the image.
merod_versionstringrequiredVersion string for the merod binary (recorded in image metadata).
image_family_prefixstringmero-teePrefix for the GCP image family name. Final name: {prefix}-{profile}.
Build Command
# Build a locked-read-only production image
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:

1

Build images

Packer builds all three profile images in parallel.

2

Launch probe VMs

Creates temporary instances from each image to extract TDX measurements.

3

Extract measurements

Reads MRTD and RTMRs from probe VMs, generates published-mrtds.json.

4

Publish images

Promotes images to the production GCP project and updates image families.

5

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:

VariableTypeDescription
ITA_API_KEYstring (secret)Intel Trust Authority API key for submitting quote appraisal requests.
ITA_APPRAISAL_URLstringITA endpoint URL for TDX quote verification (e.g. https://api.trustauthority.intel.com/appraisal/v2/attest).
KMS_ALLOWED_HOSTSstring (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.

// compatibility-catalog.json structure
{
  "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
1

User enters KMS URL

The browser UI accepts a KMS endpoint URL (e.g. https://kms.calimero.network).

2

Browser → /api/verify

The frontend sends the KMS URL to the serverless API with a freshly generated nonce.

3

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.

4

API → ITA verification

The raw TDX quote is submitted to Intel Trust Authority for appraisal. ITA returns a signed JWT with the verification result.

5

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.

6

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.