This guide is for self-hosted deployments on your own TDX hardware. For cloud deployments, see Quickstart.
This guide covers setting up on-chain governance for dstack using smart contracts on Ethereum.
On-chain governance adds:
- Smart contract-based authorization: App registration and whitelisting managed by smart contracts
- Decentralized trust: No single operator controls keys
- Transparent policies: Anyone can verify authorization rules on-chain
- Production dstack deployment with KMS and Gateway as CVMs (see Deployment Guide)
- Ethereum wallet with funds on Sepolia testnet (or your target network)
- Foundry installed
- Node.js and npm installed (for the bootAuth server)
cd dstack/kms/auth-eth
npm install # Install Node.js dependencies
forge install # Install Foundry dependencies
# Deploy contracts (deploys both DstackApp implementation and DstackKms proxy)
PRIVATE_KEY=<your-key> forge script script/Deploy.s.sol:DeployScript \
--broadcast --rpc-url https://eth-sepolia.g.alchemy.com/v2/<your-alchemy-key>Sample output:
Deploying with account: 0x...
DstackApp implementation deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
DstackKms implementation deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
DstackKms proxy deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Note the proxy address (e.g., 0x9fE4...).
Set environment variables for subsequent commands:
export KMS_CONTRACT_ADDR="<DstackKms-proxy-address>"
export PRIVATE_KEY="<your-private-key>"
export RPC_URL="https://eth-sepolia.g.alchemy.com/v2/<your-alchemy-key>"The KMS CVM includes an auth-api service that connects to your DstackKms contract. Configure it via environment variables in the KMS CVM:
KMS_CONTRACT_ADDR=<your-dstack-kms-contract-address>
ETH_RPC_URL=<ethereum-rpc-endpoint>The auth-api validates boot requests against the smart contract. See Deployment Guide for complete setup instructions.
OS_IMAGE_HASH=0x<os-image-hash> \
forge script script/Manage.s.sol:AddOsImage --broadcast --rpc-url $RPC_URLOutput: Added OS image hash: 0x...
The os_image_hash is in the digest.txt file from the guest OS image build (see Building Guest Images).
# Create a new app with allowAnyDevice=true
ALLOW_ANY_DEVICE=true \
forge script script/Manage.s.sol:DeployApp --broadcast --rpc-url $RPC_URLSample output:
Deployed new app at: 0x75537828f2ce51be7289709686A69CbFDbB714F1
Owner: 0x...
Allow any device: true
Note the App ID (deployed app address) from the output.
Set it as the gateway app:
GATEWAY_APP_ID=<app-id> \
forge script script/Manage.s.sol:SetGatewayAppId --broadcast --rpc-url $RPC_URLOutput: Set gateway app ID: <app-id>
Add the gateway's compose hash to the whitelist. To compute the compose hash:
sha256sum /path/to/gateway-compose.json | awk '{print "0x"$1}'Then add it:
APP_CONTRACT_ADDR=<app-id> COMPOSE_HASH=<compose-hash> \
forge script script/Manage.s.sol:AddComposeHash --broadcast --rpc-url $RPC_URLOutput: Added compose hash: 0x...
For each app you want to deploy:
ALLOW_ANY_DEVICE=true \
forge script script/Manage.s.sol:DeployApp --broadcast --rpc-url $RPC_URLNote the App ID from the output.
Compute your app's compose hash:
sha256sum /path/to/your-app-compose.json | awk '{print "0x"$1}'Then add it:
APP_CONTRACT_ADDR=<app-id> COMPOSE_HASH=<compose-hash> \
forge script script/Manage.s.sol:AddComposeHash --broadcast --rpc-url $RPC_URLUse the App ID when deploying through the VMM dashboard or VMM CLI.
The central governance contract that manages OS image whitelisting, app registration, and KMS authorization.
| Function | Description |
|---|---|
addOsImageHash(bytes32) |
Whitelist an OS image hash |
removeOsImageHash(bytes32) |
Remove an OS image from whitelist |
setGatewayAppId(string) |
Set the trusted Gateway app ID |
registerApp(address) |
Register an app contract |
deployAndRegisterApp(...) |
Deploy and register app in one transaction |
isAppAllowed(AppBootInfo) |
Check if an app is allowed to boot |
isKmsAllowed(AppBootInfo) |
Check if KMS is allowed to boot |
Each app has its own contract controlling which compose hashes and devices are allowed.
| Function | Description |
|---|---|
addComposeHash(bytes32) |
Whitelist a compose hash |
removeComposeHash(bytes32) |
Remove a compose hash from whitelist |
addDevice(bytes32) |
Whitelist a device ID |
removeDevice(bytes32) |
Remove a device from whitelist |
setAllowAnyDevice(bool) |
Allow any device to run this app |
isAppAllowed(AppBootInfo) |
Check if app can boot with given config |
disableUpgrades() |
Permanently disable contract upgrades |
Both isAppAllowed and isKmsAllowed take an AppBootInfo struct:
struct AppBootInfo {
address appId; // Unique app identifier (contract address)
bytes32 composeHash; // Hash of docker-compose configuration
address instanceId; // Unique instance identifier
bytes32 deviceId; // Hardware device identifier
bytes32 mrAggregated; // Aggregated measurement register
bytes32 mrSystem; // System measurement register
bytes32 osImageHash; // OS image hash
string tcbStatus; // TCB status (e.g., "UpToDate")
string[] advisoryIds; // Security advisory IDs
}Source: dstack/kms/auth-eth/contracts/
- Deployment Guide - Setting up dstack infrastructure
- Security Best Practices