Home / Blog / Architecture

Why a Confidential VM Isn't Enough - What it really takes to put your stack in a TEE

A confidential VM protects one machine, and your app is ten. What sealing a SaaS stack in TEEs involves, and why Calimero uses one attested node instead.

Why a Confidential VM Isn't Enough - What it really takes to put your stack in a TEEWhy a Confidential VM Isn't Enough - What it really takes to put your stack in a TEE

The tempting shortcut

Confidential computing has become easy to buy. Every major cloud now offers confidential VMs: Intel TDX or AMD SEV-SNP machines whose memory the host cannot read. So when a customer asks for stronger guarantees about their data, the obvious move is to take the stack you already run and put it inside them.

It sounds like a weekend project. In practice it is the start of a long one, because a confidential VM protects one machine, and your app is ten.

This post walks through what "just put it in a TEE" turns into for a typical SaaS stack, and why Calimero takes a different shape: one attested node with every layer built in.


What a typical stack looks like

Take an ordinary multi-tenant product:

  • a TLS-terminating load balancer or proxy;
  • an API service and a handful of internal services;
  • a database cluster, with replicas and failover;
  • a queue and a cache;
  • disks, snapshots and backups;
  • a secrets manager holding the keys that tie it together.

Each of these is a place where customer data exists in plaintext at some point. A confidential VM around one of them protects that one. The data is still readable everywhere else it travels.


Moving it into TEEs, piece by piece

Here is what the exercise actually involves.

Every service needs its own attested image. Each one has to be built reproducibly, measured, and published with its measurements so that something can check it. Five services means five images to measure, attest and keep track of.

Every hop needs attested encryption. Plain TLS between services is not enough: a service must check that the peer it is talking to is the attested image it expects, not merely a host with a valid certificate. That means building attestation into every connection.

TLS still ends at a proxy. The load balancer in front usually runs outside any enclave. Unless you move termination inside, every request is readable at the edge.

The database is the hardest part. It holds every customer's data in one place. Sealing it means a confidential database with sealed replicas, encrypted failover, and encrypted backups whose keys only attested replicas can get.

Disks, snapshots and backups sit outside. They need their own encryption, with keys released only to attested machines.

You build the key service. Something has to decide which machine gets which key, based on attestation. That service must itself run in a TEE and be attested, or it becomes the weakest link.

Note: None of this is impossible. It is simply a platform team's worth of work, and it is work your customers never see.


The release problem

The cost does not stop once it is built. In a TEE, an image's measurements are its identity. Change the code and the measurements change. So every release of every service becomes a security change:

  1. Rebuild each service's sealed image.
  2. Publish new measurements for every image.
  3. Update attestation and key-release policies.
  4. Re-provision secrets to the new enclaves.
  5. Migrate the database inside its enclave.
  6. Repeat at every customer who runs their own copy.

A team that used to ship several times a day now has a security process wrapped around each deploy.


A different shape: one attested node

Calimero starts from the opposite end. Instead of sealing each tier, it puts every tier inside one node image, and runs that same node everywhere.

Inside every node:

LayerWho provides it
FrontendYou: your web UI, shipped inside the signed bundle
Application logicYou: your code, in Rust or JavaScript, compiled to WebAssembly
RuntimeBuilt in: a WebAssembly sandbox
Replicated stateBuilt in: each app instance replicated across attested nodes
StorageBuilt in: encrypted at rest
NetworkingBuilt in: encrypted, attested node-to-node connections
Identity and accessBuilt in: passkeys, organizations, teams, API keys
Sealed hardwareBuilt in: Intel TDX attestation

There is one image to measure and attest, and one kind of node to add when you need capacity. Your part is the logic and the frontend, signed together into one bundle.


No central database to seal

The database problem goes away by not having a central database at all. A Calimero app is a sharded monolith: every node runs the whole app, and the app's data is split into app instances, one per customer, team or document, whatever unit fits your product.

Each app instance is replicated by its own Raft log across three or more attested nodes. Calls are applied in one order, reads can be strongly consistent, and the instance keeps serving when a node is lost. Instances are independent, so you scale by adding instances and nodes rather than growing one database.

That also changes the blast radius. In a shared database, one breach or outage reaches every tenant. Here, an app instance lives only on the attested nodes that replicate it.


Releases that don't touch attestation

A node's measurements cover the node image, not the apps on it. Your app ships as a signed bundle, and the node checks the publisher's signature before running it. So releasing a new version of your app:

  • does not change node measurements or keys;
  • needs no new attestation policies and no secrets to move;
  • is one upgrade operation, applied on every node;
  • migrates each app instance's data when that instance is upgraded, with the same result on every replica.

The app's owner approves the upgrade with a passkey, unless the deployment gives that decision to its operator. Shipping goes back to being a deploy, not a security review.


Sealed transport, end to end

The proxy problem is handled by the clients. The browser, Rust, Python and CLI clients verify a node's attestation before they send anything, then seal each request to a transport key that the node's hardware quote vouches for. That key is created inside the VM at start-up and never leaves it. There is no TLS proxy outside the enclave that can read traffic, because the session ends inside the node.


When DIY still makes sense

To be fair to the shortcut: if your sensitive workload is a single stateless service, with no database and few releases, wrapping it in a confidential VM may be all you need. The same goes for workloads that can't be expressed as WebAssembly apps, or teams that already run a mature confidential-computing platform of their own.

The DIY path gets expensive when the data is stateful, shared across tiers, released often and deployed at more than one customer. That is most SaaS products, and it is the case Calimero is built for.


Summary

QuestionStack of TEEsCalimero
Images to attestOne per service, database and key serviceOne node image
Where data livesOne central database for every tenantEach app instance on its own attested replicas
Where TLS endsUsually at a proxy outside the enclaveInside the node, sealed to an attested key
Key serviceBuilt and attested by youBuilt in, itself attested
A releaseNew measurements, policies and secrets per serviceOne signed bundle, nothing to re-attest

If you want to see how the pieces fit, read how it works, see how a node proves what it runs, or start on Calimero Cloud.

Your customers want control. You want to ship.

Start on Calimero Cloud, or build on the open-source runtime.