All posts

Deployment Architecture — From Developer Laptop to SGX Enclave

13 min read · ComputeFlux Team
Observability & Scale

Stacked deployment layers laptop to K8s to SGX to server rack

In one sentence. ComputeFlux runs the same binary across five very different environments, and each step up the ladder removes another layer of software you're otherwise forced to trust.

Picture it like this. The same conversation held in a coffee shop, then an office, then a locked meeting room, then a secure facility. The words never change. What changes is the list of people who could be listening — and how much inconvenience you'll accept to shorten that list.

Why it matters. Most deployment write-ups are about scale. This one is about trust: the interesting axis isn't how many machines you run but how few parties you have to believe. It's also the most practical article in the series — the one that tells you how to run this thing on your own laptop this afternoon.


The Deployment Spectrum

ComputeFlux operates across a trust spectrum that maps directly to deployment topology. At one end, a developer running go run main.go on a MacBook trusts the operating system, the Go runtime, and the hardware. At the other end, a production validator running inside an Intel SGX enclave trusts only the CPU manufacturer's root of trust, the audited enclave code, and the cryptographic guarantees of remote attestation. Between these extremes lie a locally-run multi-node network (shared kernel trust), Kubernetes (orchestrator trust), and bare-metal systemd (OS trust). Each step up the spectrum removes a layer of trusted computing base (TCB) — and introduces corresponding operational complexity.

Deployment Architecture Layers

This article builds directly on Article 20. Tracing gives you visibility into a running system — but that system has to be deployed somewhere first, and the somewhere has profound implications for the security guarantees ComputeFlux can actually deliver.

Deployment is the infrastructure capstone of the series. It's where every subsystem covered so far — TEE attestation, CometBFT consensus, DKG key shares, sealed PebbleDB storage, the WASM contract runtime — becomes running processes on real machines. After this, the series turns outward: from how ComputeFlux is built to where it's headed and how to join in.

Layer 1: Local Multi-Node Networks — Running Three Nodes Directly

A local development environment has to satisfy two requirements that pull against each other. It must faithfully reproduce the distributed behavior of a multi-node CometBFT network, and it must be trivial to start and tear down. ComputeFlux gets both without any container orchestration. Each node is the same Go binary — go run main.go, or the signed EGo enclave binary in simulation mode — started in its own terminal or its own systemd unit, with a different set of port environment variables.

Why Three Nodes?

CometBFT's Byzantine Fault Tolerance guarantees safety (no two correct nodes decide differently) with up to f faulty nodes out of 3f + 1 total. For f = 1, you need 4 nodes for BFT. A local network with only 3 nodes accepts the weaker failure model where the network tolerates 1 crash fault (CFT) but only 0 Byzantine faults. This is acceptable for local/staging use because the nodes typically run under a single operator's control — a Byzantine fault would require that operator to intentionally compromise their own deployment, at which point security is moot.

Each node gets its own SIDE_CHAIN_PORT for P2P and GQL_PORT for GraphQL/API, passed as environment variables. The codebase's deploy scripts — hack/node0-deploy.sh, node1-deploy.sh, node2-deploy.sh — assign each node a distinct block of ports rather than following one fixed offset formula, so every node has its own SIDE_CHAIN_PORT / SIDE_CHAIN_RPC_PORT / GQL_PORT triple. The single-node default from the project README is GQL_PORT=61000 and SIDE_CHAIN_PORT=61001. Whatever the values, distinct triples make it trivial to trace a log line or error back to its originating node without parsing hostnames.

The Per-Node State Trade-off

In production, each node has its own PebbleDB instance with independent state, and this holds locally too: each node process (or container) points at its own /token_chain_data directory. This is intentional: it prevents state corruption from concurrent writes and forces developers to treat state as per-node. Pointing two node processes at the same data directory would allow state corruption and create irreproducible bugs that only manifest in production when nodes are properly isolated.

The Dockerfile does pre-create /token_chain_data/ via RUN mkdir -p /token_chain_data. This ensures the PebbleDB directory exists before the application starts, avoiding a race between the directory creation and the database open call. In a production K8s environment, this would be handled by an init container or a mkdir in the startup script, but in the Docker image, doing it at build time is both simpler and faster (no runtime overhead).

SGX Simulation Mode

The OE_SIMULATION=1 environment variable tells the Open Enclave runtime (which EGo wraps) to run in simulation mode. In this mode, all SGX-specific instructions (enclave creation, memory encryption, attestation) are emulated in userspace. This allows development without SGX hardware, but it means that:

  1. Memory is not encrypted — a debugger can read enclave memory.
  2. Attestation quotes are self-signed — no Intel DCAP verification occurs.
  3. Sealed data uses a software key — any simulation enclave can unseal data sealed by any other simulation enclave.

These are acceptable compromises for development because the alternative — requiring every developer to own SGX-capable hardware — would severely limit contributor participation. The simulation mode is sufficient for testing business logic, contract execution, and consensus behavior; only security-critical paths (key generation, remote attestation) need hardware mode for final validation.

Layer 2: Kubernetes Deployment — One Pod, One Volume, No Ordinal Identity

ComputeFlux's actual Kubernetes manifest (hack/k8s-temp.yaml, templated per node by the node0/1/2-deploy.sh scripts) is a plain Deployment, not a StatefulSet. Each node gets its own Deployment (named e.g. tee-provider-0, tee-provider-1, tee-provider-2), its own NodePort Service, and its own hard-coded hostPath volume mount at /token_chain_data pointing at a node-specific directory on the underlying host (e.g. /srv/token_node0/token_chain_data). SGX access is requested through a resource limit/request pair (in the current manifest, an Alibaba Cloud SGX EPC resource) and a nodeSelector: {TEE: "SGX"} that pins the pod to SGX-capable hardware.

The PebbleDB Constraint

PebbleDB is an embedded LSM-tree storage engine that writes to local disk. It is not a distributed database — each instance owns its data directory exclusively, and concurrent access from multiple processes will corrupt the database. This means that if a node's pod is rescheduled onto different hardware without carrying its data directory along, it loses all consensus state: the blockchain history, the contract storage, the DKG key shares, the validator identity. The node cannot rejoin the network without a full state sync, and if all nodes restart simultaneously with fresh state, the network loses all history.

ComputeFlux's manifest satisfies this with a hostPath volume mount, tying the pod's storage directory to a specific physical node rather than to a portable, cluster-managed volume. That's a real constraint. A hostPath volume doesn't follow the pod, so each validator is implicitly pinned to whichever host holds its data directory.

The update strategy is Recreate, not a rolling update. The old pod stops completely before the replacement starts, which prevents two containers racing for the same SGX EPC allocation and the same host ports during an upgrade. The price is a short availability gap on every deploy.

Network Identity and Exposure

CometBFT nodes identify each other by node_id@host:port tuples, where node_id is derived from the node's Ed25519 private key. ComputeFlux's manifest exposes each node's P2P and GraphQL ports through a NodePort Service, giving every node a stable, externally reachable port on the cluster. That's simpler and less elastic than the headless-service-plus-PVC-template pattern a StatefulSet offers. It also matches the current one-Deployment-per-node model, where each validator is deployed, scaled, and upgraded as its own unit rather than as part of a replicated set.

Validator Set Changes

Because each node is its own Deployment rather than a member of a StatefulSet, Kubernetes provides no ordered-scaling guarantee. Rollout ordering and DKG ceremony coordination — discussed further in Article 22 — are the application's responsibility, not something the orchestrator enforces. Moving to a StatefulSet topology, with ordinal pod identities and per-ordinal PersistentVolumeClaims, would be a reasonable evolution once the validator set outgrows a small, individually managed number of nodes. It isn't what's deployed today.

Layer 3: SGX EGo Configuration — Enclave Engineering

EGo (Edgeless Go) compiles Go programs into SGX enclaves using an Open Enclave SDK-based runtime. The enclave configuration determines the security properties and resource constraints of the running application.

Enclave Size: The EPC Constraint

Intel SGX reserves a portion of physical RAM as the Enclave Page Cache (EPC), which is the only memory the CPU allows to hold encrypted enclave pages. On most server-class CPUs, the EPC is 64-128 MB (Ice Lake) or up to 512 MB (Sapphire Rapids with EPC expansion). That's the hard limit on an enclave's working set. Allocate more than fits and the CPU swaps pages out to unprotected RAM — still encrypted, but the paging overhead typically costs a 10–100x slowdown.

For ComputeFlux, the enclave must hold the Go runtime and all application code, the PebbleDB block cache, the CometBFT mempool and consensus state, and the DKG state and key material.

ComputeFlux's enclave configuration (hack/build/enclave.json) sets heapSize: 2048 — a 2GB heap for the main dsecret binary, comfortably above the 64–512MB EPC range on current SGX hardware from Ice Lake through Sapphire Rapids. That's deliberate. Rather than squeezing entirely inside the EPC, the enclave is sized for its real working set and leans on SGX2's dynamic paging to move pages between the EPC and encrypted host RAM.

The trade-off is the one paging always carries. Pages evicted and later reaccessed pay a real penalty, so the practical question isn't whether the whole heap fits in the EPC. It's whether the hot working set — mempool, in-flight consensus state, the PebbleDB block cache — stays resident often enough to keep block processing fast. Get that wrong and a validator takes too long to process a block, misses its voting window, and accumulates downtime penalties. This is operational tuning, not theory.

The WASM sub-enclave used for contract execution (tee-chain/runtime/wasm/ego/enclave.json) is configured much smaller, at a 512MB heap, matching its narrower and more bounded workload.

Thread Count: The TCS Constraint

SGX enclaves have a fixed number of Thread Control Structures (TCS), each representing a hardware thread that can enter the enclave. The TCS count is specified at enclave creation time and cannot be changed without re-signing the enclave (which changes its MRENCLAVE identity, invalidating all sealed data). This is a genuine tuning knob for any EGo deployment. More TCS means more concurrent request processing, which matters for the GraphQL and REST layers. But each one consumes EPC memory for its stack and thread-local storage. A validator doing mostly sequential consensus work needs far fewer than a gateway juggling hundreds of concurrent API requests. The counts ComputeFlux's build actually configures are worth checking in the build tooling rather than assuming a default here.

Entrypoint and Bootstrapping

EGo's entrypoint is the standard Go main(), but a lot happens before main() runs. The runtime creates the enclave, establishes the OCALL/ECALL interface between enclave and untrusted host, initializes the cryptographic RNG — seeded from the SGX hardware RNG, not the OS entropy pool — and sets up sealed storage access. On hardware this takes 1–3 seconds, negligible next to the application's own startup: opening the database, connecting P2P, syncing blocks.

The ego run dsecret command in the Dockerfile tells EGo to sign and launch the dsecret binary. "dsecret" (distributed secret) is the application binary name, reflecting the DKG-centric nature of the system.

Layer 4: systemd for Bare-Metal

For operators who run ComputeFlux on bare-metal servers with SGX hardware but without Kubernetes, systemd provides process supervision. A typical unit file would include:

[Unit]
Description=ComputeFlux TEE Validator
After=network.target

[Service]
Type=simple
Environment=CHAIN_ENV=production
Environment=OE_MODE=HW
Environment=GQL_PORT=61000
Environment=SIDE_CHAIN_PORT=61001
ExecStart=/usr/local/bin/ego run /opt/computeflux/dsecret
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

The Restart=always directive brings the validator back after a crash. ComputeFlux's bare-metal deploy scripts (hack/node0-run.sh) configure it through a systemctl edit-style drop-in unit rather than a hand-edited file. They pair RestartSec=5 with StartLimitIntervalSec=0 and StartLimitBurst=0, which disables systemd's restart-rate limiting entirely. The preference is explicit: always come back up, as fast as possible, rather than stop retrying after N failures in a window. It's the bare-metal equivalent of Kubernetes' restartPolicy: Always.

CI/CD Pipeline Overview

CI/CD Pipeline: Git Push to Enclave Deployment

A complete CI/CD pipeline for ComputeFlux must handle three distinct build artifacts:

  1. The EGo enclave binary (dsecret): compiled with ego-go build, signed with a developer key (for development) or a production signing key (for mainnet). The enclave signature is embedded in the binary and verified by Intel's attestation infrastructure.

  2. The Docker image: built with docker build, tagged with the git commit SHA and the EGo version. The image is pushed to a private registry (for production) or Docker Hub (for public releases). Image scanning for known vulnerabilities is performed on every push.

  3. The DApp frontend: built with vite build, producing static assets that are deployed to a CDN (for production) or served by the Vite dev server (for development). The Prerenderer step runs as part of the build pipeline, generating static HTML for SEO-critical routes.

Each artifact is gated on three sets of tests. Unit tests via go test ./.... Integration tests against a local 3-node network, each node its own process, driven by simulated user traffic. And enclave attestation verification, which in hardware mode confirms the signed enclave produces valid DCAP quotes.

The tracing from Article 20 is present at every rung of this ladder. The same OTel instrumentation runs whether the enclave sits in OE_SIMULATION=1 on a laptop or OE_MODE=HW on a Sapphire Rapids validator, so the operational picture a developer debugs against locally is the one they'll meet in production.

Key Takeaways

  • The deployment spectrum is a trust ladder, not a size ladder. Each rung removes something from the set of things you must believe in, and charges you operational complexity for the privilege.
  • Three nodes on a laptop, no container orchestration required. Simulation mode drops the special-hardware requirement while preserving genuine multi-node consensus behavior — which is precisely what makes contributing realistic for someone without an enclave-capable CPU.
  • Enclave deployment inverts ordinary operations instincts. Memory budget becomes a security parameter rather than a capacity one, and comfortable conveniences — dynamic linking, just-in-time compilation, C libraries — are simply off the table.
  • The same instrumentation runs at every rung. The operational picture a developer debugs against locally is the same picture they'll see in production, which is rarer than it should be.
  • In hardware mode, the pipeline gates on attestation itself. The build doesn't pass unless the enclave can cryptographically prove what it is.

What Comes Next

With the current architecture — trust model, consensus, cryptography, contracts, scheduling, billing, protocol layer, governance, audits, observability, and now deployment topology — fully covered, the series turns outward. Part 6 looks at where ComputeFlux is headed, how it stacks up against the rest of the AI gateway landscape, how it reaches a global audience, and how to become part of it yourself.

Next — Article 22: Roadmap 2026-2027: what's honestly missing, and what gets built next.