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)
- Node.js and npm installed
- Alchemy API key (for Sepolia) - get one at https://www.alchemy.com/
cd dstack/kms/auth-eth
npm install
npx hardhat compile
PRIVATE_KEY=<your-key> ALCHEMY_API_KEY=<your-key> npx hardhat kms:deploy --with-app-impl --network sepoliaThe command will prompt for confirmation. Sample output:
✅ DstackApp implementation deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
DstackKms Proxy deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Implementation deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
Note the proxy address (e.g., 0x9fE4...).
Set environment variables for subsequent commands:
export KMS_CONTRACT_ADDRESS="<DstackKms-proxy-address>"
export PRIVATE_KEY="<your-private-key>"
export ALCHEMY_API_KEY="<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>Note: The auth-api uses KMS_CONTRACT_ADDR, while Hardhat tasks use KMS_CONTRACT_ADDRESS.
The auth-api validates boot requests against the smart contract. See Deployment Guide for complete setup instructions.
npx hardhat kms:add-image --network sepolia 0x<os-image-hash>Output: Image added successfully
The os_image_hash is in the digest.txt file from the guest OS image build (see Building Guest Images).
npx hardhat kms:create-app --network sepolia --allow-any-deviceSample output:
✅ App deployed and registered successfully!
Proxy Address (App Id): 0x75537828f2ce51be7289709686A69CbFDbB714F1
Note the App ID (Proxy Address) from the output.
Set it as the gateway app:
npx hardhat kms:set-gateway --network sepolia <app-id>Output: Gateway App ID set successfully
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:
npx hardhat app:add-hash --network sepolia --app-id <app-id> <compose-hash>Output: Compose hash added successfully
For each app you want to deploy:
npx hardhat kms:create-app --network sepolia --allow-any-deviceNote 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:
npx hardhat app:add-hash --network sepolia --app-id <app-id> <compose-hash>Use 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: kms/auth-eth/contracts/
- Deployment Guide - Setting up dstack infrastructure
- Security Best Practices