Skip to content

Provisioned broker receiver service

A version of the key broker that starts with no model keys at all. It receives a key only from the model owner, and only after proving with a fresh AMD hardware report which software it is running. This page is for engineers deploying it: it covers configuration, the web endpoints, and what the deployment itself must still protect.

The owner-side provisioning client is the other half: it runs on the owner's machine and sends the key.

wcm.broker_server runs the native-SNP receiver lifecycle over HTTP. It starts without model keys and accepts only an owner-authenticated sealed provisioning envelope. It uses the server package extra and does not read WCM_KEYSTORE_FILE. The original wcm.server remains a separate mounted-key reference service.

Configuration and startup

Set WCM_BROKER_CONFIG_FILE to a JSON document with exactly two top-level fields: configuration and policy. Certificate paths and the public-key path are relative to that file. All are read once at startup.

{
  "configuration": {
    "cpu_root_file": "cpu-root.pem",
    "cpu_vcek_file": "workload-vcek.pem",
    "cpu_intermediate_files": ["cpu-ask.pem"],
    "trusted_manifest_identities": ["sha256:<approved-manifest-identity>"],
    "owner_public_key_file": "owner-ed25519.pub",
    "gpu_root_file": "nvidia-device-root.pem"
  },
  "policy": {
    "measurement_hex": "<approved-broker-48-byte-SNP-measurement-as-96-hex-characters>",
    "configuration_sha256": "<approved-effective-configuration-digest>",
    "weights_hash": "sha256:<approved-weight-artifact-digest>",
    "epoch": 1,
    "guest_policy": 0,
    "minimum_tcb_le_hex": "<owner-chosen-eight-byte-TCB-floor-as-16-hex-characters>",
    "required_platform_fields": ["ciphertext_hiding_en", "alias_check_complete"],
    "forbidden_platform_fields": ["smt_en"]
  }
}

Replace every placeholder with independently approved values. guest_policy: 0 is illustrative, not a recommended hardware policy; choose the exact acceptable guest-policy word for the deployment, with DEBUG disabled. Select the TCB floor for the CPU generation. owner-ed25519.pub contains the raw 32-byte public key, not a private signing key. Certificates are PEM and normalized to DER for the effective configuration hash. Omit gpu_root_file for CPU-only workloads; GPU claims will then be refused. Unknown or duplicate JSON fields are rejected.

The owner approves BrokerEffectiveConfiguration.digest() for these actual inputs. Startup constructs the receiver from those same inputs and refuses an owner-policy digest mismatch. No model key belongs in this JSON or any receiver environment variable.

export WCM_BROKER_CONFIG_FILE=/run/wcm/broker.json
uvicorn wcm.broker_server:app_from_env --factory --workers 1 --host 127.0.0.1 --port 8080

Use one process per broker instance: each process has a different boot key and provisioning state. Run on a native SEV-SNP guest exposing /dev/sev-guest. Azure vTPM-backed SNP and software providers are not substitutes for this path.

Container target

Build the explicit receiver target; the default Docker build retains the mounted-key reference entry point:

docker build --target provisioned -f python/docker/Dockerfile -t wcm-broker .
docker run --rm --read-only --cap-drop ALL --security-opt no-new-privileges \
  -p 127.0.0.1:8080:8080 -v "$PWD/approved-config:/run/wcm:ro" \
  -e WCM_BROKER_CONFIG_FILE=/run/wcm/broker.json wcm-broker

This deliberately omits device access: on an ordinary host, health succeeds but provisioning reports fail with 503. On an approved native-SNP guest, the deployment must grant the broker user narrowly scoped access to /dev/sev-guest and expose that device to the container. Do not grant --privileged or change to root just to bypass an access failure. Device permissions, the guest kernel and launcher are part of the trusted deployment and must be approved with the image.

CI exercises the installed image using synthetic public configuration, verifies missing-hardware denial, UID 10001, no effective capabilities, no-new-privileges and read-only root configuration. It builds both targets twice and retains filesystem/runtime-configuration snapshots. These are same-runner build and container observations, not evidence of SNP-protected execution. The snapshot SHA256 is not an SNP launch measurement or an OCI digest. A measured deployment must bind the selected command, effective configuration and runtime overrides; reproducible image files alone do not supply that binding.

The owner provisioning client performs the challenge, independent report verification and sealed installation exchange: python -m wcm.owner_provision owner.json. HTTPS is required by default; --allow-loopback-http is reserved for local development. Its guide documents the separate owner configuration and effective-configuration digest command.

Protocol

Endpoint Request Response
GET /health None {"status":"ok"}; availability only
POST /provisioning/report nonce, issued_at, expires_at report_b64, transport_public_key
POST /provisioning/install nonce, ciphertext_b64, owner_signature_b64 {"installed":true}
POST /challenge None nonce, issued_at, expires_at
POST /release manifest, evidence released, sealed_key_b64, checks

The owner generates the challenge. Its nonce is 64 lowercase hex characters; times are ISO 8601 strings with UTC offsets. The report endpoint computes REPORT_DATA using the receiver's own configuration, policy and boot key, then calls the native device adapter. It cannot accept externally supplied report bytes or REPORT_DATA. Missing hardware returns HTTP 503 without fallback.

The owner independently verifies the returned report and creates the signed sealed envelope. Base64 fields use canonical padded standard base64. The install endpoint verifies the owner, pending challenge, exact policy and boot key before installing one AES key. Success returns an acknowledgment; it is not a cryptographic proof of installation. A failed cryptographic install consumes that pending request. Successful installation disables further provisioning.

Challenge/release return HTTP 409 before provisioning or after retirement. Requests in the wrong lifecycle state also return 409. Invalid wire shapes return 422; stale challenges and rejected cryptographic envelopes return 400. Validation responses omit submitted values. Model-key release returns only ciphertext bound to an attested workload key. The workload measurement and verifier profile are described in deployment trust.

Deployment boundary

This runnable service does not supply a measured image, protected process memory, secure erasure or host-independent configuration. Those must be provided by the deployment approved by the owner. Keep the receiver's code, configuration, clock and key state inside that protected boundary.

HTTP ingress authentication, TLS termination, request-size/rate limits and availability controls are deployment responsibilities. In particular, an unauthenticated caller can consume or replace pending provisioning requests; cryptographic owner authentication prevents unauthorized key installation but does not prevent denial of service. Restrict provisioning ingress to the owner. Do not log request bodies or raw attestation reports. No administrative retirement or configuration-mutation endpoint is exposed.

Tests use TestClient and synthetic signed SNP reports. They establish the HTTP and receiver behavior without a new live hardware claim.

See Provisioning lifecycle and recovery for the restart, uncertain-outcome, storage and worker-stop contract and executable software controls.