Skip to content

Commit c8a7579

Browse files
committed
Implement remote execution in scout (e.g. for firmware upgrades)
1 parent 91f7bde commit c8a7579

File tree

7 files changed

+407
-4
lines changed

7 files changed

+407
-4
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/rpc/proto/forge.proto

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6403,6 +6403,7 @@ message ScoutStreamApiBoundMessage {
64036403
mlx_device.MlxDeviceConfigSyncResponse mlx_device_config_sync_response = 12;
64046404
mlx_device.MlxDeviceConfigCompareResponse mlx_device_config_compare_response = 13;
64056405
ScoutStreamAgentPingResponse scout_stream_agent_ping_response = 14;
6406+
ScoutRemoteExecResponse scout_remote_exec_response = 15;
64066407
}
64076408
}
64086409

@@ -6432,6 +6433,7 @@ message ScoutStreamScoutBoundMessage {
64326433
mlx_device.MlxDeviceConfigSyncRequest mlx_device_config_sync_request = 13;
64336434
mlx_device.MlxDeviceConfigCompareRequest mlx_device_config_compare_request = 14;
64346435
ScoutStreamAgentPingRequest scout_stream_agent_ping_request = 15;
6436+
ScoutRemoteExecRequest scout_remote_exec_request = 16;
64356437
}
64366438
}
64376439

@@ -6493,6 +6495,30 @@ message ScoutStreamAgentPingResponse {
64936495
}
64946496
}
64956497

6498+
// ScoutRemoteExecRequest is sent from carbide-api to the scout agent
6499+
// to download files and execute a script on the host.
6500+
message ScoutRemoteExecRequest {
6501+
string component_type = 1;
6502+
string target_version = 2;
6503+
string script_url = 3;
6504+
uint32 timeout_seconds = 4;
6505+
// Files to download before running the script.
6506+
// Keys are download URLs, values are expected SHA-256 hex checksums.
6507+
// Scout will verify each file after download and reject execution
6508+
// if any checksum does not match.
6509+
map<string, string> download_files = 5;
6510+
}
6511+
6512+
// ScoutRemoteExecResponse is the result of a scout remote execution,
6513+
// sent from scout back to carbide-api.
6514+
message ScoutRemoteExecResponse {
6515+
bool success = 1;
6516+
int32 exit_code = 2;
6517+
string stdout = 3;
6518+
string stderr = 4;
6519+
string error = 5;
6520+
}
6521+
64966522
// ScoutStreamConnectionInfo contains information about an
64976523
// active scout agent connection.
64986524
message ScoutStreamConnectionInfo {
@@ -6696,4 +6722,4 @@ message DPFStateResponse {
66966722

66976723
message GetDPFStateRequest {
66986724
repeated common.MachineId machine_ids = 1;
6699-
}
6725+
}

crates/scout/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,15 @@ reqwest = { default-features = false, features = [
6666
"rustls-tls",
6767
"stream",
6868
], workspace = true }
69+
sha2 = { workspace = true }
70+
tempfile = { workspace = true }
6971
futures-util = { workspace = true }
7072
prost-types = { workspace = true }
7173
x509-parser = { workspace = true }
7274

75+
[dev-dependencies]
76+
axum = { workspace = true }
77+
7378
[build-dependencies]
7479
carbide-version = { path = "../version" }
7580

crates/scout/src/client.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,22 @@ pub(crate) async fn create_forge_client(
3838
.map_err(|err| CarbideClientError::TransportError(err.to_string()))?;
3939
Ok(client)
4040
}
41+
42+
// create_http_client builds a reqwest HTTP client configured with the same
43+
// mTLS certificates used for gRPC communication with carbide-api.
44+
pub(crate) fn create_http_client(config: &Options) -> CarbideClientResult<reqwest::Client> {
45+
let root_ca = std::fs::read(&config.root_ca)?;
46+
let root_cert = reqwest::Certificate::from_pem(&root_ca)
47+
.map_err(|e| CarbideClientError::TransportError(e.to_string()))?;
48+
49+
let client_cert = std::fs::read(&config.client_cert)?;
50+
let client_key = std::fs::read(&config.client_key)?;
51+
let identity = reqwest::Identity::from_pem(&[client_cert, client_key].concat())
52+
.map_err(|e| CarbideClientError::TransportError(e.to_string()))?;
53+
54+
reqwest::Client::builder()
55+
.add_root_certificate(root_cert)
56+
.identity(identity)
57+
.build()
58+
.map_err(|e| CarbideClientError::TransportError(e.to_string()))
59+
}

crates/scout/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ mod discovery;
5353
mod machine_validation;
5454
mod mlx_device;
5555
mod register;
56+
mod remote_exec;
5657
mod stream;
5758

5859
struct DevEnv {

0 commit comments

Comments
 (0)