diff --git a/kms/auth-eth/src/server.ts b/kms/auth-eth/src/server.ts index 16f8cbc87..d2db629b9 100644 --- a/kms/auth-eth/src/server.ts +++ b/kms/auth-eth/src/server.ts @@ -59,6 +59,7 @@ export async function build(): Promise { return { status: 'ok', kmsContractAddr: kmsContractAddr, + ethRpcUrl: rpcUrl, gatewayAppId: batch[0], chainId: batch[1], appAuthImplementation: batch[2], // NOTE: for backward compatibility diff --git a/kms/auth-mock/index.ts b/kms/auth-mock/index.ts index cef8becb5..947fcb754 100644 --- a/kms/auth-mock/index.ts +++ b/kms/auth-mock/index.ts @@ -85,6 +85,7 @@ app.get('/', async (c) => { return c.json({ status: 'ok', kmsContractAddr: process.env.KMS_CONTRACT_ADDR || '0xmockcontract1234567890123456789012345678', + ethRpcUrl: process.env.ETH_RPC_URL || '', gatewayAppId: batch[0], chainId: batch[1], appAuthImplementation: batch[2], // NOTE: for backward compatibility diff --git a/kms/kms.toml b/kms/kms.toml index 70b2b7177..d64fa1bd2 100644 --- a/kms/kms.toml +++ b/kms/kms.toml @@ -45,5 +45,6 @@ gateway_app_id = "any" [core.onboard] enabled = true auto_bootstrap_domain = "" +site_name = "" address = "0.0.0.0" port = 8000 diff --git a/kms/rpc/proto/kms_rpc.proto b/kms/rpc/proto/kms_rpc.proto index adaaa5f52..eb56f7307 100644 --- a/kms/rpc/proto/kms_rpc.proto +++ b/kms/rpc/proto/kms_rpc.proto @@ -131,6 +131,8 @@ message OnboardRequest { } message OnboardResponse { + // k256 public key (secp256k1) inherited from source KMS + bytes k256_pubkey = 1; } // Attestation info needed for on-chain KMS authorization. @@ -143,6 +145,12 @@ message AttestationInfoResponse { bytes os_image_hash = 3; // Attestation mode (e.g. "dstack-tdx", "dstack-gcp-tdx") string attestation_mode = 4; + // Custom site name for display + string site_name = 5; + // Ethereum RPC URL from auth API + string eth_rpc_url = 6; + // KMS contract address from auth API + string kms_contract_address = 7; } // The Onboard RPC service. diff --git a/kms/src/config.rs b/kms/src/config.rs index 3eaa2d117..314542446 100644 --- a/kms/src/config.rs +++ b/kms/src/config.rs @@ -119,4 +119,6 @@ pub(crate) struct Dev { pub(crate) struct OnboardConfig { pub enabled: bool, pub auto_bootstrap_domain: String, + #[serde(default)] + pub site_name: String, } diff --git a/kms/src/main_service/upgrade_authority.rs b/kms/src/main_service/upgrade_authority.rs index d2b64016b..e41e17868 100644 --- a/kms/src/main_service/upgrade_authority.rs +++ b/kms/src/main_service/upgrade_authority.rs @@ -99,6 +99,8 @@ pub(crate) struct BootResponse { pub(crate) struct AuthApiInfoResponse { pub status: String, pub kms_contract_addr: String, + #[serde(default)] + pub eth_rpc_url: String, pub gateway_app_id: String, pub chain_id: u64, pub app_implementation: String, @@ -110,6 +112,7 @@ pub(crate) struct GetInfoResponse { pub is_dev: bool, pub gateway_app_id: Option, pub kms_contract_address: Option, + pub eth_rpc_url: Option, pub chain_id: Option, pub app_implementation: Option, } @@ -161,15 +164,22 @@ impl AuthApi { AuthApi::Dev { dev } => Ok(GetInfoResponse { is_dev: true, kms_contract_address: None, + eth_rpc_url: None, gateway_app_id: Some(dev.gateway_app_id.clone()), chain_id: None, app_implementation: None, }), AuthApi::Webhook { webhook } => { let info: AuthApiInfoResponse = http_get(&webhook.url).await?; + let eth_rpc_url = if info.eth_rpc_url.is_empty() { + None + } else { + Some(info.eth_rpc_url.clone()) + }; Ok(GetInfoResponse { is_dev: false, kms_contract_address: Some(info.kms_contract_addr.clone()), + eth_rpc_url, chain_id: Some(info.chain_id), gateway_app_id: Some(info.gateway_app_id.clone()), app_implementation: Some(info.app_implementation.clone()), diff --git a/kms/src/onboard_service.rs b/kms/src/onboard_service.rs index 93eeb5629..84f54a98c 100644 --- a/kms/src/onboard_service.rs +++ b/kms/src/onboard_service.rs @@ -98,9 +98,10 @@ impl OnboardRpc for OnboardHandler { ) .await .context("Failed to onboard")?; + let k256_pubkey = keys.k256_key.verifying_key().to_sec1_bytes().to_vec(); keys.store(&self.state.config) .context("Failed to store keys")?; - Ok(OnboardResponse {}) + Ok(OnboardResponse { k256_pubkey }) } async fn get_attestation_info(self) -> Result { @@ -136,11 +137,26 @@ impl OnboardRpc for OnboardHandler { .decode_app_info_ex(false, &info.vm_config) .context("Failed to decode app info")?; + let (eth_rpc_url, kms_contract_address) = + match self.state.config.auth_api.get_info().await { + Ok(info) => ( + info.eth_rpc_url.unwrap_or_default(), + info.kms_contract_address.unwrap_or_default(), + ), + Err(err) => { + tracing::warn!("failed to get auth api info: {err}"); + (String::new(), String::new()) + } + }; + Ok(AttestationInfoResponse { device_id: app_info.device_id, mr_aggregated: app_info.mr_aggregated.to_vec(), os_image_hash: app_info.os_image_hash, attestation_mode, + site_name: self.state.config.onboard.site_name.clone(), + eth_rpc_url, + kms_contract_address, }) } diff --git a/kms/src/www/onboard.html b/kms/src/www/onboard.html index bca53d2ac..6f7b6e010 100644 --- a/kms/src/www/onboard.html +++ b/kms/src/www/onboard.html @@ -145,6 +145,19 @@ color: #333; } + .chain-info { + background-color: #f8f4e8; + border: 1px solid #ddc; + border-radius: 4px; + padding: 15px; + margin-bottom: 20px; + } + + .chain-info h3 { + margin-top: 0; + color: #444; + } + .loading { color: #888; font-style: italic; @@ -154,7 +167,7 @@
-

dstack KMS Setup

+

{{ siteName || 'dstack KMS Setup' }}

Loading attestation info...
Attestation info: {{ attestationError }}
@@ -178,6 +191,18 @@

Attestation Info (for on-chain registration)

+
+

Chain Info

+
+ ETH RPC URL: + {{ attestationInfo.eth_rpc_url }} +
+
+ KMS Contract: + {{ attestationInfo.kms_contract_address }} +
+
+
@@ -261,7 +286,8 @@

Onboard from an Existing KMS Instance

setupFinished: false, attestationInfo: null, attestationLoading: true, - attestationError: '' + attestationError: '', + siteName: '' } }, async mounted() { @@ -271,6 +297,10 @@

Onboard from an Existing KMS Instance

this.attestationError = data.error; } else { this.attestationInfo = data; + if (data.site_name) { + this.siteName = data.site_name; + document.title = data.site_name; + } } } catch (err) { this.attestationError = err.message; @@ -310,6 +340,9 @@

Onboard from an Existing KMS Instance

if (data.error) throw new Error(data.error); this.success = 'Onboarding successful!'; + this.result = JSON.stringify({ + k256Pubkey: '0x' + data.k256_pubkey, + }, null, 2); this.error = ''; } catch (err) { this.error = err.message;