Security property
The client establishes three linked facts before it sends private content:- A genuine supported AMD platform produced the report. The browser verifies the AMD SEV-SNP report signature and the ARK → ASK → chip-specific VCEK certificate chain.
- The enclave is running an expected release. The browser verifies a Sigstore DSSE bundle issued through GitHub Actions for a tagged release of the expected repository, checks Rekor consistency, and compares the signed release measurement with the measurement in the hardware report.
- The encryption key belongs to that enclave. The report data contains the enclave’s HPKE public key. Certificate verification binds the expected domain, HPKE key, and attestation-document hash together.
securityVerified becomes true only when every verification stage succeeds. getbased additionally requires a non-empty attested HPKE public key before creating the private transport.
Verification flow
- The app creates a Tinfoil
SecureClientwithtransport: 'ehbp'. - The client fetches an attestation bundle containing the SEV-SNP report, VCEK certificate, signed release data, enclave certificate, and routing domain.
- The verifier checks the AMD certificate chain, report signature, firmware and TCB minimums, production guest policy, and that debug and migration-agent modes are disabled.
- The verifier validates the Sigstore identity, tagged workflow reference, release digest, and signed code measurement for the expected repository.
- It compares the signed measurement with the live enclave measurement.
- It verifies that the enclave certificate matches the domain and embeds both the attested HPKE key and the attestation-document hash.
- getbased accepts the returned key only when
securityVerifiedis true and then binds the secure fetch implementation to the verified proxy and enclave origins.
/private attestation bundle and private chat endpoint.
Key model
Private TEE mode does not use a long-lived private key owned only by the user.- Browser: EHBP creates ephemeral HPKE sender material and retains the per-request context in browser memory.
- Enclave: The enclave holds the private key corresponding to the HPKE public key embedded in its signed hardware report.
- Proxy: The PPQ or Routstr proxy receives the encrypted request and routing headers but has neither endpoint’s body-decryption material.
Ehbp-Encapsulated-Key header. The encrypted response keys are derived from the request exporter secret, encapsulated key, and a fresh response nonce.
Request and response handling
getbased builds the normal OpenAI-compatible request before the secure fetch layer encrypts its body. For Routstr, the encrypted JSON uses the enclave model ID without thetinfoil- prefix, while the full advertised model ID remains in X-Routstr-Model for routing and billing.
The proxy must be able to read operational headers. Depending on the provider, visible information includes:
- API or session bearer credentials
- Selected private model and query-source headers
- Destination, IP address, timing, and approximate payload size
- Balance, reservation, and billing metadata
- A plaintext proxy error such as authentication, insufficient balance, or rate limiting can pass through unchanged because it was generated before enclave processing.
- A successful response must include
Ehbp-Response-Nonce. The browser rejects a plaintext success response and decrypts only a nonce-bound EHBP stream.
Failure and rotation behavior
Private selections fail closed. They never fall through to the provider’s standard plaintext request path.- Missing or failed attestation stops setup.
- An unverified or missing HPKE key stops setup.
- Requests to origins outside the verified proxy/enclave set are rejected locally.
- A successful response without the EHBP nonce is rejected.
- An EHBP key-configuration mismatch resets the client, performs one fresh attestation, and retries once.
- Changing the Routstr node clears node-scoped models and cached attestation clients.
Venice Encrypted TEE Mode
Venice uses a different protocol and a different verification level from the PPQ/Routstr Tinfoil path. Do not infer the Tinfoil guarantees above from the Venice lock icon.Encrypted and visible fields
getbased encrypts each messagecontent value with ECDH on secp256k1, HKDF-SHA256, and AES-256-GCM. The request carries the client public key, the model public key returned with the attestation evidence, and the selected Venice E2EE model. Streaming output is expected as AES-GCM-encrypted chunks with a fresh server ephemeral public key in each chunk.
The outer HTTP and JSON metadata is not encrypted by this protocol. Venice can observe:
- the API credential and E2EE headers;
- the selected model and message roles;
- token limits, streaming flags, request shape, and other unencrypted parameters;
- IP address, destination, timing, approximate sizes, rate limits, and billing metadata.
Checks performed by getbased
getbased usesvenice-e2ee@0.5.3 with both requireDcap and requireGpu enabled. For each new cached session, the client generates a 32-byte nonce and fetches Venice’s TDX and GPU evidence. Session creation requires all of the following:
- Quote binding: The client accepts the documented hex or base64 quote representation, checks that it is an Intel TDX quote, confirms the requested model and fresh nonce, binds the signing-key address to TDX
REPORTDATA, rejects the debug attribute, and rejects negative or inconsistent fields reported by Venice. - Intel DCAP verification: The browser runs the TDX quote through the vendored
@phala/dcap-qvlverifier. This validates the quote signature, PCK certificate chain, collateral and revocation information, and TCB status. Unknown or rejected TCB states fail the session. - NVIDIA GPU verification: The raw
nvidia_payloadis relayed to NVIDIA’s Remote Attestation Service (NRAS). getbased requires a positive overall result and at least one GPU, then checks every GPU’s shared session nonce, secure boot, disabled debug mode, successful NVIDIA measurement result, and report-nonce match. - NRAS token authentication: The browser verifies the ES384 signature on the overall and every per-GPU NRAS token against NVIDIA’s published key set. It also fixes the expected issuer and validates token expiration and not-before times. No claims from an unsigned or partially verified token are accepted.
verificationLevel to "dcap". GPU verification is a separate required policy recorded through gpuVerified and gpu.tokensVerified; it is not folded into that string value.
NVIDIA does not accept this POST directly from a browser. On an official getbased host, getProxyApiUrl() routes only the fixed NRAS operation to https://integrations.getbased.health/api/proxy; an independent deployment uses its own same-origin /api/proxy. The compatibility service relays the evidence body verbatim. Message content is not part of the GPU evidence. The browser authenticates the returned tokens independently, but the service sees the evidence exchange and NVIDIA learns that the evidence was checked.
Checks not currently performed
getbased does not currently:- compare TDX measurements with an authenticated allowlist of approved Venice releases;
- independently evaluate the NVIDIA GPU quote instead of relying on NVIDIA’s signed NRAS verdict;
- prove that the verified TDX environment and GPU run together—the shared nonce proves only that both pieces of evidence were produced for the same request;
- verify the separate response signature described by Venice;
- cryptographically bind each streaming response’s ephemeral key to the signing key found in the TDX evidence.
venice-e2ee client reports the parsed TDX measurements, but reporting a measurement is not the same as approving it. Venice does not currently publish a stable measurement allowlist that getbased can authenticate and enforce.
Failure behavior and interpretation
Venice encrypted mode never falls through to a regular Venice model. Missing or failed binding, DCAP, GPU, or NRAS-token checks stop session setup. Transient attestation gateway failures are retried twice before the request fails. After a successful HTTP response, non-whitespace plaintext model content is rejected rather than displayed. AES-GCM authentication failures also stop the response. The chat header shows 🔒 TEE + GPU only after the required DCAP and signed NRAS checks pass. Hovering, focusing, or tapping the badge lists the successful checks and remaining limits. A plain 🔒 means encrypted mode is selected but no session evidence is available yet; 🔒 failed means setup was stopped. These labels describe the evidence getbased checked, not a guarantee that approved code is running or that every response came from that code. Venice’s current protocol and production recommendations are documented in TEE & E2EE Models.Trust assumptions and non-guarantees
Remote attestation identifies the running enclave and binds its encryption key. It is not a general proof that the software is safe. The design still trusts:- The getbased JavaScript delivered to the browser and the locked Tinfoil/EHBP dependencies
- The browser, operating system, and extensions before encryption and after decryption
- AMD SEV-SNP hardware, firmware, root certificates, and the verifier’s policy
- Intel TDX hardware, DCAP certificates and collateral, and the vendored DCAP verifier used for Venice
- NVIDIA GPU hardware, NRAS’s evaluation of the evidence, and NVIDIA’s published token-signing keys
- The expected GitHub repository, its tagged Sigstore release workflow, and the audited behavior of the measured release
- The absence of exploitable TEE implementation bugs and relevant side channels
Implementation references
js/tinfoil-secure-fetch.js— shared verified EHBP transport and fail-closed response handlingjs/api-routstr.js— Routstr private model routing and metadata boundaryjs/api-ppq.js— PPQ private endpoint integrationjs/api-venice.js— Venice DCAP/NRAS policy, bounded NRAS relay, retries, and encrypted streaming pathjs/chat-attestation.js— user-visible verification statustests/tinfoil-secure-fetch.test.js— transport security regression coveragetests/test-venice-e2ee.js— Venice encryption, attestation, badge, and fail-closed regression coverage