Skip to main content
Clone the reference implementation now rather than typing the Rust code by hand — it’s a separate project from the Node/TypeScript app you built in Quickstart, so put it in its own folder alongside it, not inside it:
Below is a walkthrough of the pieces inside that repo — change the host calls and flight-specific logic to match your needs once you understand them. A TEE contract is a Rust crate compiled to a WASM component. It exports its functions through a contracts WIT interface and imports only the host capabilities it needs.

Repository Structure

The packages under wit/deps/ define the host ABI your contract links against — vendor the versions your target cluster provides (here, host-interfaces-2.1.0/ and host-tenant-1.0.0/).

Files

world.wit — declare your interface + host imports

The interfaces you import here are your contract’s entire capability set — there is no separate manifest. The host links your contract against the matching tenant world and refuses to load it if it imports an interface that world does not provide.

Cargo.toml — compile to a WASM component

lib.rs — generate bindings + dispatch to each function

The host bindings live under crate::host::* and the exported interface under crate::exports::* — both generated by the macro from wit/.

search.rs — search_offers (synchronous http, no PII)

The http interface is synchronous: the response is available before the call returns. Build a Request with a Verb, headers, and an optional payload.
Outbound HTTP is authorized by the user, not the contract — the hosts a contract may reach are resolved per-call from the calling user’s grant.

booking.rs — book_offer (PII via http-with-placeholders)

For calls that carry user PII, use http-with-placeholders. Put {{profile.<field>}} markers in the request body; the host resolves them from the calling user’s profile at dispatch time, so plaintext PII never enters WASM memory.
hwp::call returns a typed HttpError so failures never leak resolved PII — match on it for clear messages:
See Placeholders in outbound calls.

Reading secrets from the secrets KV map

The API key is read from the tenant’s secrets KV map at runtime. The key is seeded by the tenant SDK before the contract runs — there is no set-credentials host function. kv-store calls take the full z:<tid>:<map> name; build it from tenant-context at runtime (the host enforces the prefix):

Key Design Rules

  • Export functions on the contracts interface. Each takes generic-input and returns result<list<u8>, string> — JSON bytes on success, an error string on failure. There is no dispatch function and no ContractError enum.
  • kv-store calls take the full z:<tid>:<map> name. Build it at runtime from tenant_context::tenant_did() — that value is already the string form, don’t re-encode it. The host enforces the prefix. The map must exist (created and populated by the tenant SDK) before the contract reads or writes it.
  • Import only the host interfaces you use — they are your contract’s entire capability set. The host refuses to load a contract that imports an interface its tenant world does not provide.
  • http::call is synchronous; you get the response back before the function returns. Its egress is authorized per-call by the calling user’s grant.
  • For calls carrying user PII, use http-with-placeholders: put {{profile.<field>}} markers in the request and the host resolves them inside the enclave, so plaintext PII never enters your contract.