Sealed transport
A node speaks plain HTTP. Any TLS in front of it ends outside the enclave, at whatever proxy or
ingress the operator runs. Everything that crosses that hop can be read there: the bearer token,
the JSON-RPC arguments, the results, a warrant. Sealing encrypts each request so that only the
attested TD can read it, and only the TD can produce a response the client accepts. It is a port of
mero-js src/sealed/sealed.ts. The wire format is byte-identical with core’s
crates/server/src/sealed.rs, and the tests pin it to core’s published vectors.
How it works
Section titled “How it works”- Attest.
POST /admin-api/tee/attest {bindTransportKey: true}returns the node’s X25519 transport key and a quote whose report data isnonce ‖ transportKeyBinding(0^32, key). The quote is verified on the device (TEE trust). This is the one call that needs no credential and is never sealed. - Handshake.
POST /sealed/v2/handshake(HANDSHAKE_PATH) runs NoiseNK_25519_AESGCM_SHA256to that key. The session keys come from both sides’ ephemeral keys. So once a session is gone (an hour at most), what was sent in it cannot be opened, not even with the transport key. - Exchange. Each request goes to
POST /sealed/v2(SEALED_PATH) asapplication/vnd.calimero.sealed(SEALED_CONTENT_TYPE). Its method, path, headers and body are all sealed. The response streams back in sealed frames, so server-sent events are sealed too. WebSockets cannot be sealed, and the node refuses one with a501from inside the envelope.
SealedTransport
Section titled “SealedTransport”SealedTransport is an OkHttp Interceptor. Use its client for every request to the node:
val sealed = createAttestedSealedFetch(nodeUrl, verifier) // attests, then seals everythingval mero = Mero(MeroConfig(baseUrl = nodeUrl, httpClient = sealed.client))val relay = RelayClient(relayUrl, account, proof, signer, nonces, httpClient = sealed.client)- Only the base URL. A request outside
baseUrlis refused rather than sent in the clear. - One session. Every request shares one session, concurrent ones included. It is renewed a
minute before the node would drop it. If the node no longer knows the session (
unknown_session), the session is replaced and the request sent again, which is safe because nothing in it ran. If the node isbusy, the client waits theRetry-Afterit asks for (at most 5 s) and tries once more. - A restarted node. With an attestation step
(
SealedTransport(baseUrl) { fetchAttestedTransportKey(admin, verifier) }), a node that restarted is attested again through your verifier, and the request is retried once. With fixed key bytes (SealedTransport(baseUrl, keyBytes)), the request fails withStaleTransportKeyExceptioninstead. A failed attestation is not remembered, so the next request attests again. - Errors. The node refusing the envelope surfaces as
SealedTransportException(status,code,retryAfterMs). Both exceptions areIOExceptions, so OkHttp reports them as the call’s failure, and the SDK’s HTTP layer wraps them in aNetworkException. - Streaming. The response body opens frame by frame as it arrives. A frame that does not authenticate, or a stream that stops before its end frame, fails the body, so nothing unauthenticated is passed on.
createSignedReleaseSealedFetch(SignedReleaseSealedFetchOptions(relayUrl, minReleaseVersion = "2.3.99"))
does it all for a node you know only by URL:
- It reads the release the node names from
/tee/info. For a node too old to serve that unsealed, usereleaseVersion. - It fetches that release from
releaseMirrororreleaseSource, and verifies its signature. - It attests the node against that release’s image, then seals everything to it.
Sealing the relay
Section titled “Sealing the relay”connectCloud. Every call to a hosted relay is sealed by default (sealRelays = true): the intent, the warrant and the method’s arguments. The default verifier isDcapRelayKeyVerifier().signedReleaseVerifier(relayUrl), which trusts the relay’s signed release (locked-read-only, 2.3.99 or newer). Passseal(aVerifyTransportQuote) inConnectCloudOptionsorConnectCloudWithAccountOptionsto choose another, orsealRelays = falsefor a plain test rig. Calls to the cloud are not sealed, because they carry no intent. A loopback relay is never sealed by default.- A delegated session. A
CloudAccountsession seals every request to a hosted relay: intents, admin reads, queries, the login and events. It usesrelayTransportClient(relayUrl), one transport per relay, checked by the account’sAttestingRelayKeyVerifier(by defaultDcapRelayKeyVerifier). A loopback relay is never sealed.CloudAccount(sealRelays = false)opts out, andforgetRelaySealing()drops every relay’s transport.
Some relays leave authentication to their proxy and have no [server.sealed] forward_auth. Such a
relay cannot guard a logged-in route inside the envelope, and answers 403 sealed_route_unguarded
from inside it. Only then do a delegated session’s logged-in requests go out plain. That answer is
sealed under the session, so nobody on the way can forge it to force the downgrade. Any other
failure fails the request and never falls back.