Verification paths

Every Cocoon session produces two attestation quotes — one from cocoon-bridge (the SDK-facing TEE) and one from cocoon-proxy (the TEE behind it). They complement each other rather than form a strict chain, so different audiences can pick how much they want to verify themselves vs. how much they're willing to take on transitive trust.

This page lays out four workflows. Most teams use Path 1; the remaining paths are layered in for higher-assurance environments or auditors.

At a glance

Audience

Path

What you do

What you get

Default user

SDK default policy

Use the SDK as-is.

Connection refused if cocoon-bridge's image hash isn't in the SDK's shipped allowlist. Quote signature is verified against Intel's DCAP root in production. The proxy quote is checked transitively because cocoon-bridge refuses to start without a valid one.

Custom hashes

SDK custom policy

Set explicit AllowedCocoonBridgeImageHashes and/or AllowedCocoonProxyImageHashes on the client.

Pin to the exact builds you trust — both TEEs, independently of any SDK update.

Reproducer

Standalone build + verify

Clone the open-source cocoon-bridge repo, build it deterministically, compare hashes. The Cocoon proxy build is reproducible the same way.

First-hand evidence that the published image hashes correspond to the published source.

Independent proxy check

Verify cocoon-proxy quote yourself

Read the proxy quote from the session response and recompute the image hash without relying on cocoon-bridge's RA-TLS verification.

Defence-in-depth: even if cocoon-bridge is compromised, you catch a bad proxy yourself.

You can stack paths — most teams start with the default policy in production and keep a reproducible-build pipeline in CI as ongoing evidence that the published hashes haven't drifted.

Path 1 — Default SDK policy

The SDKs ship with attestation verification on by default. There's nothing you have to opt into.

Go

client := cocoon.NewClient("wss://shroud.us", cocoon.WithAPIKey("shroud_prod_..."), ) // DefaultAttestationPolicy is applied automatically.

TypeScript

import { CocoonClient } from "@alphatoncapital/shroud-sdk"; const client = new CocoonClient("wss://shroud.us", { apiKey: "shroud_prod_...", }); // defaultAttestationPolicy is applied automatically.

What the default policy enforces, on every WebSocket session:

  • Parse the TDX quote returned by cocoon-bridge.

  • Verify report_data == SHA-512(tee_session_pubkey) — the session key was minted inside this specific TEE.

  • Recompute the image hash from the quote's measurement registers and check it against the SDK's built-in allowlist.

  • In production deployments, the gateway runs cocoon-bridge with TDX_VERIFICATION_MODE=dcap, which adds Intel DCAP signature verification of the quote against the Intel-rooted cert chain.

If any check fails, the SDK raises an attestation error before the first byte of plaintext leaves the client. No additional code is required.

Path 2 — SDK with a custom allowlist

If you've reviewed a specific build of cocoon-bridge (or reproduced it yourself — see Path 3), pin the SDK to that exact image hash so an SDK update can't silently broaden the set of accepted images:

Go

client := cocoon.NewClient("wss://shroud.us", cocoon.WithAPIKey("shroud_prod_..."), cocoon.WithAttestationPolicy(&cocoon.AttestationPolicy{ AllowedCocoonProxyImageHashes: []string{ // Hash you've reproduced or reviewed. "c4f99569acaa71ae2f6b091b64ff6645b97eb7ab3d8c463dc2d7be752212008d", }, }), )

TypeScript

const client = new CocoonClient("wss://shroud.us", { apiKey: "shroud_prod_...", attestationPolicy: { allowedCocoonProxyImageHashes: [ "c4f99569acaa71ae2f6b091b64ff6645b97eb7ab3d8c463dc2d7be752212008d", ], }, });

This is the correct posture for high-assurance environments: an SDK update bumping the default allowlist won't change which builds your client trusts. When you want to accept a new image, you add its hash yourself after reviewing or reproducing it.

To accept multiple builds during a rolling deployment (old + new hash side-by-side), pass both:

cocoon.WithAttestationPolicy(&cocoon.AttestationPolicy{ AllowedCocoonProxyImageHashes: []string{ "c4f99569acaa71ae2f6b091b64ff6645b97eb7ab3d8c463dc2d7be752212008d", "<next-release-hash>", }, }),

Path 3 — Reproduce the build yourself

Reproducing the cocoon-bridge image is the strongest available form of verification: it ties the hash you trust directly to source code you can read.

git clone https://github.com/AlphaCompute/cocoon-bridge cd cocoon-bridge # Fetch all build inputs (Rust toolchain, DCAP packages, OVMF, # crate dependencies). Each input is checksum-verified. reprodebian/fetch-deps.sh # Build the production image. The build is hermetic (no network # access) and deterministic (SOURCE_DATE_EPOCH=0, fixed MachineId, # pinned Debian snapshot 20251105T150124Z, --remap-path-prefix). scripts/build-image prod # Compare the published hash with the one you just built. cat images/prod/image.hash sha256sum images/prod/cocoon-bridge.img

The two hashes will match if and only if no byte of the source you inspected differs from the byte that produced the released image.

Once you have a reproduced hash, add it to your AllowedCocoonProxyImageHashes (see Path 2) so your client only accepts that exact build. You've now closed the loop with no third-party trust:

  • The CPU signs the measurement (Intel TDX root of trust).

  • The measurement is the image hash (deterministic algorithm in the TDX spec).

  • The image hash matches a hash you produced from open source (your build, your machine).

Repeating this build in CI on every release gives you ongoing evidence that the published hashes correspond to the published source.

Path 4 — Verify the cocoon-proxy attestation independently

By default the SDK takes the proxy verification on transitive trust: cocoon-bridge refused to start unless the proxy's quote matched its TDX_ALLOWED_IMAGE_HASHES, so a running session implies a valid proxy. For auditors and high-assurance deployments that don't want to take that step on faith, the cocoon-proxy quote is exposed alongside the bridge quote in the session response — pull it and verify it directly.

The proxy quote uses the same TDX format as the bridge quote: the AttestationQuote struct in shroud-sdk-go/cocoon/attestation.go parses both. Running the same ImageHash() computation on the proxy quote yields a hash you can compare against your own AllowedCocoonProxyImageHashes list.

This is what the existing AllowedCocoonProxyImageHashes field on AttestationPolicy already does internally — set it explicitly to opt out of the SDK's shipped default and pin the proxy yourself:

client := cocoon.NewClient("wss://shroud.us", cocoon.WithAPIKey("shroud_prod_..."), cocoon.WithAttestationPolicy(&cocoon.AttestationPolicy{ AllowedCocoonProxyImageHashes: []string{ "c4f99569acaa71ae2f6b091b64ff6645b97eb7ab3d8c463dc2d7be752212008d", }, // Future SDK release: AllowedCocoonBridgeImageHashes pins the // bridge image too. Until then the bridge is verified against // the SDK's built-in default. }), )

Verifying the proxy quote independently doesn't replace bridge verification — it complements it. Both quotes go through the same DCAP signature check in production. The privacy claim holds with either quote alone; verifying both means a flaw in either TEE component is caught locally instead of cascading.

Don't do this

  • Don't skip attestation in production. A null/WithAttestationPolicy(nil) client is a plain TLS connection to a TEE-shaped service. The privacy claim does not hold.

  • Don't accept an image hash you haven't reviewed. Trusting an arbitrary hash because someone published it next to "verified TEE" is no stronger than trusting their server.

  • Don't pin against an empty allowlist. Both SDKs treat an empty AllowedCocoonProxyImageHashes slice as "every quote passes" — it is intended for unit tests with mock attestation. Always pass at least one hash, or use the default policy.

Last modified: 08 May 2026