Skip to content

Latest commit

 

History

History
227 lines (162 loc) · 8.75 KB

onchain-governance.md

File metadata and controls

227 lines (162 loc) · 8.75 KB

On-Chain Governance

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.

Overview

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

Prerequisites

  • 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

Deploy DstackKms Contract

cd dstack/kms/auth-eth
forge install      # Install Foundry dependencies (git submodules)

# 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>"

Configure KMS for On-Chain Auth

The KMS CVM includes an auth-api service, auth-eth, 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>
ETH_CHAIN_ID=<expected-chain-id>   # optional, rejects decisions from another chain
ETH_BLOCK_TAG=finalized            # latest, safe or finalized (default)

The auth-api validates boot requests against the smart contract. See Deployment Guide for complete setup instructions.

Whitelist OS Image

OS_IMAGE_HASH=0x<os-image-hash> ROOT_EPOCH=0 \
  forge script script/Manage.s.sol:AddOsImage --broadcast --rpc-url $RPC_URL

Output: 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).

Register Gateway App

# Create a new app with allowAnyDevice=true
ALLOW_ANY_DEVICE=true \
  forge script script/Manage.s.sol:DeployApp --broadcast --rpc-url $RPC_URL

Sample 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_URL

Output: Set gateway app ID: <app-id>

To trust more than one gateway (for example during a gateway migration), set the value to several app IDs joined by commas, e.g. <app-id-1>,<app-id-2>. A CVM accepts a gateway whose certificate carries any app ID contained in this string.

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> APP_EPOCH=0 \
  forge script script/Manage.s.sol:AddComposeHash --broadcast --rpc-url $RPC_URL

Output: Added compose hash: 0x...

Register Apps On-Chain

For each app you want to deploy:

Create App

ALLOW_ANY_DEVICE=true \
  forge script script/Manage.s.sol:DeployApp --broadcast --rpc-url $RPC_URL

Note the App ID from the output.

Add Compose Hash

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> APP_EPOCH=0 \
  forge script script/Manage.s.sol:AddComposeHash --broadcast --rpc-url $RPC_URL

Deploy via VMM

Use the App ID when deploying through the VMM dashboard or VMM CLI.

Smart Contract Reference

DstackKms (Main Contract)

The central governance contract that manages OS image whitelisting, app registration, and KMS authorization.

Function Description
addOsImageHashAtEpochs(uint256,uint256,bytes32) Whitelist an OS image hash in a range of root epochs
removeOsImageHashAtEpochs(uint256,uint256,bytes32) Remove an OS image from a range of root epochs; 0 through the latest revokes it
setGatewayAppId(string) Set the trusted Gateway app ID(s)
setAllowAnyTeeVariant(bool) / setAllowedTeeVariants(string[]) Allow KMS instances and apps on any TEE platform, or only the listed ones, at every root epoch
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

DstackApp (Per-App Contract)

Each app has its own contract controlling which compose hashes and devices are allowed, per app epoch. Every setter names the app epoch rows it writes: one epoch, or a range up to the next epoch. Removing a value from rows 0 through the current epoch revokes it.

Function Description
addComposeHashAtEpoch(uint256,bytes32) / addComposeHashAtEpochs(uint256,uint256,bytes32) Whitelist a compose hash in one app epoch or a range
removeComposeHashAtEpoch(uint256,bytes32) / removeComposeHashAtEpochs(uint256,uint256,bytes32) Remove a compose hash from one app epoch or a range
addDeviceAtEpoch(uint256,bytes32) / addDeviceAtEpochs(uint256,uint256,bytes32) Whitelist a device ID
removeDeviceAtEpoch(uint256,bytes32) / removeDeviceAtEpochs(uint256,uint256,bytes32) Remove a device
setAllowAnyDevice(bool) Allow any device to run this app, at every app epoch
setAllowAnyTeeVariant(bool) / setAllowedTeeVariants(string[]) Allow any TEE platform, or only the listed ones (e.g. dstack-tdx), at every app epoch. Set the list before restricting: a restricted empty list refuses every platform
currentAppEpoch() The app epoch new keys are derived for
isAppAllowed(AppBootInfo) Check if app can boot with given config (app epoch 0 only)
disableUpgrades() Permanently disable contract upgrades

Apps deployed before app epochs

App contracts deployed from dstack 0.6.x or earlier, and not upgraded since, have only app epoch 0 and the epoch-less setters addComposeHash(bytes32), removeComposeHash(bytes32), addDevice(bytes32), removeDevice(bytes32) and setAllowAnyDevice(bool). They keep working with an upgraded DstackKms and KMS, which authorize them for app epoch 0. Tell the two kinds apart by ERC-165 interface ID:

cast call <app-id> "supportsInterface(bytes4)(bool)" 0x8c0524c4 --rpc-url $RPC_URL  # IAppEpochManagement: epoch setters
cast call <app-id> "supportsInterface(bytes4)(bool)" 0x8fd37527 --rpc-url $RPC_URL  # IAppAuthBasicManagement: epoch-less setters

The Manage.s.sol app scripts check this themselves: on an older app they call the epoch-less setter and accept only APP_EPOCH=0 or no epoch; on a current app they require APP_EPOCH, or FROM_APP_EPOCH and TO_APP_EPOCH. SetAllowAnyDevice takes no epoch on either kind. Upgrading an older app to the current DstackApp keeps its compose hashes and devices as app epoch 0, and its allowAnyDevice for every epoch.

AppBootInfo Structure

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/

See Also