commit 7442409e410b8709a6d0330bc0c329d73e4d2caf
parent 0d11a1a0733a7c6ff7b05bed28ebecb718a2fd1f
Author: triesap <tyson@radroots.org>
Date: Mon, 27 Apr 2026 21:06:30 +0000
cli: remove daemon runtime modules
- delete the daemon bridge and managed runtime modules
- remove direct radrootsd and runtime manager dependencies
- keep runtime errors scoped to active CLI paths
- declare direct events codec features used by farm encoding
Diffstat:
5 files changed, 12 insertions(+), 2877 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -160,49 +160,6 @@ dependencies = [
]
[[package]]
-name = "async-utility"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a34a3b57207a7a1007832416c3e4862378c8451b4e8e093e436f48c2d3d2c151"
-dependencies = [
- "futures-util",
- "gloo-timers",
- "tokio",
- "wasm-bindgen-futures",
-]
-
-[[package]]
-name = "async-wsocket"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069"
-dependencies = [
- "async-utility",
- "futures",
- "futures-util",
- "js-sys",
- "tokio",
- "tokio-rustls",
- "tokio-socks",
- "tokio-tungstenite",
- "url",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "atomic-destructor"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4"
-
-[[package]]
-name = "atomic-waker"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
-
-[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -311,12 +268,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
-name = "bytes"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
-
-[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -342,12 +293,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
-name = "cfg_aliases"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
-
-[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -561,17 +506,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
- "rand_core 0.6.4",
+ "rand_core",
"typenum",
]
[[package]]
-name = "data-encoding"
-version = "2.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
-
-[[package]]
name = "dbus"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -640,12 +579,6 @@ dependencies = [
]
[[package]]
-name = "either"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
-
-[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -752,70 +685,6 @@ dependencies = [
]
[[package]]
-name = "futures"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
-
-[[package]]
-name = "futures-io"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
-
-[[package]]
-name = "futures-sink"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
-
-[[package]]
-name = "futures-task"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
-
-[[package]]
-name = "futures-util"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "slab",
-]
-
-[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -840,44 +709,18 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
-dependencies = [
- "cfg-if",
- "js-sys",
- "libc",
- "r-efi 5.3.0",
- "wasip2",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "getrandom"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
- "r-efi 6.0.0",
+ "r-efi",
"wasip2",
"wasip3",
]
[[package]]
-name = "gloo-timers"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
-dependencies = [
- "futures-channel",
- "futures-core",
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -945,105 +788,6 @@ dependencies = [
]
[[package]]
-name = "http"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
-dependencies = [
- "bytes",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
-dependencies = [
- "bytes",
- "http",
-]
-
-[[package]]
-name = "http-body-util"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
-dependencies = [
- "bytes",
- "futures-core",
- "http",
- "http-body",
- "pin-project-lite",
-]
-
-[[package]]
-name = "httparse"
-version = "1.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
-
-[[package]]
-name = "hyper"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
-dependencies = [
- "atomic-waker",
- "bytes",
- "futures-channel",
- "futures-core",
- "http",
- "http-body",
- "httparse",
- "itoa",
- "pin-project-lite",
- "smallvec",
- "tokio",
- "want",
-]
-
-[[package]]
-name = "hyper-rustls"
-version = "0.27.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
-dependencies = [
- "http",
- "hyper",
- "hyper-util",
- "rustls",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls",
- "tower-service",
- "webpki-roots 1.0.6",
-]
-
-[[package]]
-name = "hyper-util"
-version = "0.1.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
-dependencies = [
- "base64 0.22.1",
- "bytes",
- "futures-channel",
- "futures-util",
- "http",
- "http-body",
- "hyper",
- "ipnet",
- "libc",
- "percent-encoding",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
-]
-
-[[package]]
name = "iana-time-zone"
version = "0.1.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1211,22 +955,6 @@ dependencies = [
]
[[package]]
-name = "ipnet"
-version = "2.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
-
-[[package]]
-name = "iri-string"
-version = "0.7.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20"
-dependencies = [
- "memchr",
- "serde",
-]
-
-[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1244,8 +972,6 @@ version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
dependencies = [
- "cfg-if",
- "futures-util",
"once_cell",
"wasm-bindgen",
]
@@ -1315,7 +1041,7 @@ dependencies = [
"bitflags",
"libc",
"plain",
- "redox_syscall 0.7.4",
+ "redox_syscall",
]
[[package]]
@@ -1352,33 +1078,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
-name = "lock_api"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
-dependencies = [
- "scopeguard",
-]
-
-[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
-name = "lru"
-version = "0.16.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
-
-[[package]]
-name = "lru-slab"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
-
-[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1421,12 +1126,6 @@ dependencies = [
]
[[package]]
-name = "negentropy"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d"
-
-[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1462,59 +1161,6 @@ dependencies = [
]
[[package]]
-name = "nostr-database"
-version = "0.44.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1"
-dependencies = [
- "lru",
- "nostr",
- "tokio",
-]
-
-[[package]]
-name = "nostr-gossip"
-version = "0.44.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6"
-dependencies = [
- "nostr",
-]
-
-[[package]]
-name = "nostr-relay-pool"
-version = "0.44.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b1073ccfbaea5549fb914a9d52c68dab2aecda61535e5143dd73e95445a804b"
-dependencies = [
- "async-utility",
- "async-wsocket",
- "atomic-destructor",
- "hex",
- "lru",
- "negentropy",
- "nostr",
- "nostr-database",
- "tokio",
- "tracing",
-]
-
-[[package]]
-name = "nostr-sdk"
-version = "0.44.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393"
-dependencies = [
- "async-utility",
- "nostr",
- "nostr-database",
- "nostr-gossip",
- "nostr-relay-pool",
- "tokio",
- "tracing",
-]
-
-[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1615,36 +1261,13 @@ dependencies = [
]
[[package]]
-name = "parking_lot"
-version = "0.12.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall 0.5.18",
- "smallvec",
- "windows-link",
-]
-
-[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
- "rand_core 0.6.4",
+ "rand_core",
"subtle",
]
@@ -1813,61 +1436,6 @@ dependencies = [
]
[[package]]
-name = "quinn"
-version = "0.11.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
-dependencies = [
- "bytes",
- "cfg_aliases",
- "pin-project-lite",
- "quinn-proto",
- "quinn-udp",
- "rustc-hash",
- "rustls",
- "socket2",
- "thiserror 2.0.18",
- "tokio",
- "tracing",
- "web-time",
-]
-
-[[package]]
-name = "quinn-proto"
-version = "0.11.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
-dependencies = [
- "bytes",
- "getrandom 0.3.4",
- "lru-slab",
- "rand 0.9.2",
- "ring",
- "rustc-hash",
- "rustls",
- "rustls-pki-types",
- "slab",
- "thiserror 2.0.18",
- "tinyvec",
- "tracing",
- "web-time",
-]
-
-[[package]]
-name = "quinn-udp"
-version = "0.5.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
-dependencies = [
- "cfg_aliases",
- "libc",
- "once_cell",
- "socket2",
- "tracing",
- "windows-sys 0.60.2",
-]
-
-[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1878,12 +1446,6 @@ dependencies = [
[[package]]
name = "r-efi"
-version = "5.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
-
-[[package]]
-name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
@@ -1909,22 +1471,15 @@ dependencies = [
"radroots_protected_store",
"radroots_replica_db",
"radroots_replica_sync",
- "radroots_runtime",
- "radroots_runtime_distribution",
- "radroots_runtime_manager",
"radroots_runtime_paths",
- "radroots_sdk",
"radroots_secret_vault",
"radroots_sql_core",
"radroots_trade",
- "reqwest",
- "rhi",
"serde",
"serde_json",
"tar",
"tempfile",
"thiserror 2.0.18",
- "tokio",
"toml",
"url",
"zeroize",
@@ -1993,11 +1548,6 @@ name = "radroots_nostr"
version = "0.1.0-alpha.2"
dependencies = [
"nostr",
- "nostr-sdk",
- "radroots_events",
- "radroots_events_codec",
- "radroots_identity",
- "reqwest",
"serde",
"serde_json",
"thiserror 1.0.69",
@@ -2105,7 +1655,6 @@ version = "0.1.0-alpha.2"
dependencies = [
"anyhow",
"chacha20poly1305",
- "clap",
"config",
"getrandom 0.2.17",
"radroots_log",
@@ -2123,48 +1672,15 @@ dependencies = [
]
[[package]]
-name = "radroots_runtime_distribution"
+name = "radroots_runtime_paths"
version = "0.1.0-alpha.2"
dependencies = [
"serde",
"thiserror 1.0.69",
- "toml",
]
[[package]]
-name = "radroots_runtime_manager"
-version = "0.1.0-alpha.2"
-dependencies = [
- "flate2",
- "radroots_runtime_paths",
- "serde",
- "tar",
- "thiserror 1.0.69",
- "toml",
-]
-
-[[package]]
-name = "radroots_runtime_paths"
-version = "0.1.0-alpha.2"
-dependencies = [
- "serde",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "radroots_sdk"
-version = "0.1.0-alpha.2"
-dependencies = [
- "radroots_events",
- "radroots_events_codec",
- "radroots_trade",
- "reqwest",
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "radroots_secret_vault"
+name = "radroots_secret_vault"
version = "0.1.0-alpha.2"
dependencies = [
"keyring",
@@ -2210,18 +1726,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha 0.3.1",
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
-dependencies = [
- "rand_chacha 0.9.0",
- "rand_core 0.9.5",
+ "rand_chacha",
+ "rand_core",
]
[[package]]
@@ -2231,17 +1737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.9.5",
+ "rand_core",
]
[[package]]
@@ -2254,24 +1750,6 @@ dependencies = [
]
[[package]]
-name = "rand_core"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
-dependencies = [
- "getrandom 0.3.4",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
name = "redox_syscall"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2298,82 +1776,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
-name = "reqwest"
-version = "0.12.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
-dependencies = [
- "base64 0.22.1",
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "http",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-rustls",
- "hyper-util",
- "js-sys",
- "log",
- "percent-encoding",
- "pin-project-lite",
- "quinn",
- "rustls",
- "rustls-pki-types",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper",
- "tokio",
- "tokio-rustls",
- "tower",
- "tower-http",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
- "webpki-roots 1.0.6",
-]
-
-[[package]]
-name = "rhi"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "clap",
- "radroots_core",
- "radroots_events",
- "radroots_events_codec",
- "radroots_identity",
- "radroots_nostr",
- "radroots_runtime",
- "radroots_runtime_paths",
- "radroots_trade",
- "serde",
- "serde_json",
- "thiserror 2.0.18",
- "tokio",
- "toml",
- "tracing",
-]
-
-[[package]]
-name = "ring"
-version = "0.17.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
-dependencies = [
- "cc",
- "cfg-if",
- "getrandom 0.2.17",
- "libc",
- "untrusted",
- "windows-sys 0.52.0",
-]
-
-[[package]]
name = "ron"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2442,12 +1844,6 @@ dependencies = [
]
[[package]]
-name = "rustc-hash"
-version = "2.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
-
-[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2461,53 +1857,12 @@ dependencies = [
]
[[package]]
-name = "rustls"
-version = "0.23.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
-dependencies = [
- "once_cell",
- "ring",
- "rustls-pki-types",
- "rustls-webpki",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "rustls-pki-types"
-version = "1.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
-dependencies = [
- "web-time",
- "zeroize",
-]
-
-[[package]]
-name = "rustls-webpki"
-version = "0.103.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
-dependencies = [
- "ring",
- "rustls-pki-types",
- "untrusted",
-]
-
-[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
-name = "ryu"
-version = "1.0.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
-
-[[package]]
name = "salsa20"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2517,12 +1872,6 @@ dependencies = [
]
[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
-[[package]]
name = "scrypt"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2540,7 +1889,7 @@ version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
dependencies = [
- "rand 0.8.5",
+ "rand",
"secp256k1-sys",
"serde",
]
@@ -2649,29 +1998,6 @@ dependencies = [
]
[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "sha1"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2714,28 +2040,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
-name = "slab"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
-
-[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
-name = "socket2"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
-dependencies = [
- "libc",
- "windows-sys 0.61.2",
-]
-
-[[package]]
name = "sqlite-wasm-rs"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2777,15 +2087,6 @@ dependencies = [
]
[[package]]
-name = "sync_wrapper"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
-dependencies = [
- "futures-core",
-]
-
-[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2955,13 +2256,10 @@ version = "1.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd"
dependencies = [
- "bytes",
"libc",
"mio",
- "parking_lot",
"pin-project-lite",
"signal-hook-registry",
- "socket2",
"tokio-macros",
"windows-sys 0.61.2",
]
@@ -2978,44 +2276,6 @@ dependencies = [
]
[[package]]
-name = "tokio-rustls"
-version = "0.26.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
-dependencies = [
- "rustls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-socks"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
-dependencies = [
- "either",
- "futures-util",
- "thiserror 1.0.69",
- "tokio",
-]
-
-[[package]]
-name = "tokio-tungstenite"
-version = "0.26.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
-dependencies = [
- "futures-util",
- "log",
- "rustls",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls",
- "tungstenite",
- "webpki-roots 0.26.11",
-]
-
-[[package]]
name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3057,58 +2317,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
-name = "tower"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project-lite",
- "sync_wrapper",
- "tokio",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "tower-http"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
-dependencies = [
- "bitflags",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "iri-string",
- "pin-project-lite",
- "tower",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
-
-[[package]]
-name = "tower-service"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
-
-[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
- "tracing-attributes",
"tracing-core",
]
@@ -3125,17 +2339,6 @@ dependencies = [
]
[[package]]
-name = "tracing-attributes"
-version = "0.1.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3175,12 +2378,6 @@ dependencies = [
]
[[package]]
-name = "try-lock"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
-
-[[package]]
name = "ts-rs"
version = "11.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3203,25 +2400,6 @@ dependencies = [
]
[[package]]
-name = "tungstenite"
-version = "0.26.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
-dependencies = [
- "bytes",
- "data-encoding",
- "http",
- "httparse",
- "log",
- "rand 0.9.2",
- "rustls",
- "rustls-pki-types",
- "sha1",
- "thiserror 2.0.18",
- "utf-8",
-]
-
-[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3293,12 +2471,6 @@ dependencies = [
]
[[package]]
-name = "untrusted"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
-
-[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3312,12 +2484,6 @@ dependencies = [
]
[[package]]
-name = "utf-8"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
-
-[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3368,15 +2534,6 @@ dependencies = [
]
[[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
-[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3415,16 +2572,6 @@ dependencies = [
]
[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.67"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
name = "wasm-bindgen-macro"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3501,34 +2648,6 @@ dependencies = [
]
[[package]]
-name = "web-time"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.26.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
-dependencies = [
- "webpki-roots 1.0.6",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed"
-dependencies = [
- "rustls-pki-types",
-]
-
-[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3598,15 +2717,6 @@ dependencies = [
[[package]]
name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
diff --git a/Cargo.toml b/Cargo.toml
@@ -26,7 +26,7 @@ clap = { version = "4.5", features = ["derive"] }
getrandom = "0.2"
radroots_core = { path = "../lib/crates/core", features = ["std", "serde"] }
radroots_events = { path = "../lib/crates/events" }
-radroots_events_codec = { path = "../lib/crates/events_codec", features = ["serde_json"] }
+radroots_events_codec = { path = "../lib/crates/events_codec", features = ["nostr", "serde_json"] }
radroots_identity = { path = "../lib/crates/identity" }
radroots_log = { path = "../lib/crates/log" }
radroots_nostr_accounts = { path = "../lib/crates/nostr_accounts", features = ["os-keyring"] }
@@ -35,20 +35,13 @@ radroots_nostr_signer = { path = "../lib/crates/nostr_signer" }
radroots_protected_store = { path = "../lib/crates/protected_store", features = ["std"] }
radroots_replica_db = { path = "../lib/crates/replica_db" }
radroots_replica_sync = { path = "../lib/crates/replica_sync" }
-radroots_runtime_distribution = { path = "../lib/crates/runtime_distribution" }
-radroots_runtime_manager = { path = "../lib/crates/runtime_manager" }
radroots_runtime_paths = { path = "../lib/crates/runtime_paths" }
-radroots_runtime = { path = "../lib/crates/runtime" }
radroots_secret_vault = { path = "../lib/crates/secret_vault", features = ["std", "os-keyring"] }
-radroots_sdk = { path = "../lib/crates/sdk", default-features = false, features = ["std", "serde", "serde_json", "radrootsd-client"] }
radroots_sql_core = { path = "../lib/crates/sql_core", features = ["native"] }
radroots_trade = { path = "../lib/crates/trade" }
-rhi = { path = "../rhi" }
-reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "2.0"
-tokio = { version = "1", features = ["net", "rt-multi-thread", "time"] }
toml = "0.8"
url = "2.5"
zeroize = "1.8"
diff --git a/src/runtime/daemon.rs b/src/runtime/daemon.rs
@@ -1,832 +0,0 @@
-use std::time::Duration;
-
-use radroots_events::farm::RadrootsFarm;
-use radroots_events::listing::RadrootsListing;
-use radroots_events::profile::{RadrootsProfile, RadrootsProfileType};
-use radroots_events::trade::RadrootsTradeOrder;
-use radroots_sdk::{
- RadrootsSdkConfig, RadrootsdAuth, SdkPublishError, SdkRadrootsdFarmPublishOptions,
- SdkRadrootsdListingPublishOptions, SdkRadrootsdProfilePublishOptions,
- SdkRadrootsdSignerAuthority, SdkRadrootsdSignerSessionRef, SdkTransportMode, SignerConfig,
-};
-use reqwest::blocking::Client;
-use serde::de::DeserializeOwned;
-use serde::{Deserialize, Serialize};
-use serde_json::Value;
-
-use crate::domain::runtime::{JobDetailView, JobSummaryView};
-use crate::runtime::config::RuntimeConfig;
-use crate::runtime::provider;
-use crate::runtime::signer::ActorWriteSignerAuthority;
-
-const BRIDGE_SOURCE: &str = "daemon bridge · durable write plane";
-const RPC_TIMEOUT_SECS: u64 = 2;
-
-#[derive(Debug)]
-pub enum DaemonRpcError {
- Unconfigured(String),
- Unauthorized(String),
- MethodUnavailable(String),
- UnknownJob(String),
- External(String),
- InvalidResponse(String),
- Remote(String),
-}
-
-#[derive(Debug, Clone, Copy)]
-enum RpcAuthMode {
- None,
- BridgeBearer,
-}
-
-#[derive(Debug, Clone)]
-struct RpcTarget {
- url: String,
- bridge_bearer_token: Option<String>,
-}
-
-#[derive(Debug, Serialize)]
-struct JsonRpcRequest<'a> {
- jsonrpc: &'static str,
- id: u64,
- method: &'a str,
- #[serde(skip_serializing_if = "Option::is_none")]
- params: Option<Value>,
-}
-
-#[derive(Debug, Deserialize)]
-struct JsonRpcResponse<T> {
- result: Option<T>,
- error: Option<JsonRpcResponseError>,
-}
-
-#[derive(Debug, Deserialize)]
-struct JsonRpcResponseError {
- code: i64,
- message: String,
-}
-
-#[derive(Debug, Clone, Deserialize)]
-struct BridgeJobRemote {
- job_id: String,
- command: String,
- status: String,
- terminal: bool,
- recovered_after_restart: bool,
- requested_at_unix: u64,
- completed_at_unix: Option<u64>,
- signer_mode: String,
- #[serde(default)]
- signer_session_id: Option<String>,
- event_id: Option<String>,
- event_addr: Option<String>,
- delivery_policy: String,
- delivery_quorum: Option<usize>,
- relay_count: usize,
- acknowledged_relay_count: usize,
- required_acknowledged_relay_count: usize,
- attempt_count: usize,
- relay_outcome_summary: String,
- #[serde(default)]
- attempt_summaries: Vec<String>,
-}
-
-#[derive(Debug, Clone, Deserialize)]
-struct Nip46SessionRemote {
- session_id: String,
- user_pubkey: Option<String>,
- #[serde(default)]
- permissions: Vec<String>,
- authorized: bool,
- #[serde(default)]
- signer_authority: Option<ActorWriteSignerAuthority>,
-}
-
-#[derive(Debug, Clone)]
-pub struct BridgeListingPublishResult {
- pub deduplicated: bool,
- pub job_id: String,
- pub idempotency_key: Option<String>,
- pub status: String,
- pub signer_mode: String,
- pub signer_session_id: Option<String>,
- pub event_kind: Option<u32>,
- pub event_id: Option<String>,
- pub event_addr: Option<String>,
-}
-
-#[derive(Debug, Clone)]
-pub struct BridgeEventPublishResult {
- pub deduplicated: bool,
- pub job_id: String,
- pub idempotency_key: Option<String>,
- pub status: String,
- pub signer_mode: String,
- pub signer_session_id: Option<String>,
- pub event_kind: Option<u32>,
- pub event_id: Option<String>,
- pub event_addr: Option<String>,
-}
-
-#[derive(Debug, Clone)]
-pub struct BridgeOrderRequestResult {
- pub deduplicated: bool,
- pub job_id: String,
- pub idempotency_key: Option<String>,
- pub status: String,
- pub signer_mode: String,
- pub signer_session_id: Option<String>,
- pub event_id: Option<String>,
- pub event_addr: Option<String>,
-}
-
-pub fn bridge_job_list(config: &RuntimeConfig) -> Result<Vec<JobSummaryView>, DaemonRpcError> {
- bridge_jobs(config).map(|jobs| jobs.into_iter().map(map_job_summary_view).collect())
-}
-
-pub fn bridge_job(
- config: &RuntimeConfig,
- job_id: &str,
-) -> Result<Option<JobDetailView>, DaemonRpcError> {
- match bridge_job_status(config, job_id) {
- Ok(job) => Ok(Some(map_job_detail_view(job))),
- Err(DaemonRpcError::UnknownJob(_)) => Ok(None),
- Err(error) => Err(error),
- }
-}
-
-pub fn bridge_listing_publish(
- config: &RuntimeConfig,
- listing: &RadrootsListing,
- kind: u32,
- idempotency_key: Option<&str>,
- signer_session_id: Option<&str>,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> Result<BridgeListingPublishResult, DaemonRpcError> {
- if kind != 30402 {
- return Err(DaemonRpcError::External(format!(
- "sdk listing publish only supports kind 30402, got {kind}"
- )));
- }
-
- let Some(signer_session_id) = signer_session_id else {
- return Err(DaemonRpcError::Unconfigured(
- "listing publish requires a signer session id".to_owned(),
- ));
- };
-
- let sdk = actor_write_sdk_client(config)?;
- let session = SdkRadrootsdSignerSessionRef::from_session_id(signer_session_id.to_owned());
- let mut options = SdkRadrootsdListingPublishOptions::from_signer_session_ref(&session);
- if let Some(idempotency_key) = idempotency_key {
- options = options.with_idempotency_key(idempotency_key.to_owned());
- }
- if let Some(signer_authority) = signer_authority {
- options = options.with_signer_authority(sdk_signer_authority(signer_authority));
- }
-
- let receipt = block_on_sdk(
- sdk.listing()
- .publish_listing_via_radrootsd_with_options(listing, &options),
- )?
- .map_err(map_sdk_publish_error)?;
-
- map_listing_publish_receipt(receipt, idempotency_key)
-}
-
-pub fn bridge_profile_publish(
- config: &RuntimeConfig,
- profile: &RadrootsProfile,
- profile_type: Option<RadrootsProfileType>,
- idempotency_key: Option<&str>,
- signer_session_id: Option<&str>,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> Result<BridgeEventPublishResult, DaemonRpcError> {
- let Some(signer_session_id) = signer_session_id else {
- return Err(DaemonRpcError::Unconfigured(
- "profile publish requires a signer session id".to_owned(),
- ));
- };
-
- let sdk = actor_write_sdk_client(config)?;
- let session = SdkRadrootsdSignerSessionRef::from_session_id(signer_session_id.to_owned());
- let mut options = SdkRadrootsdProfilePublishOptions::from_signer_session_ref(&session);
- if let Some(idempotency_key) = idempotency_key {
- options = options.with_idempotency_key(idempotency_key.to_owned());
- }
- if let Some(signer_authority) = signer_authority {
- options = options.with_signer_authority(sdk_signer_authority(signer_authority));
- }
-
- let receipt = block_on_sdk(sdk.profile().publish_profile_via_radrootsd_with_options(
- profile,
- profile_type,
- &options,
- ))?
- .map_err(map_sdk_publish_error)?;
-
- map_event_publish_receipt(receipt, idempotency_key, "profile")
-}
-
-pub fn bridge_farm_publish(
- config: &RuntimeConfig,
- farm: &RadrootsFarm,
- idempotency_key: Option<&str>,
- signer_session_id: Option<&str>,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> Result<BridgeEventPublishResult, DaemonRpcError> {
- let Some(signer_session_id) = signer_session_id else {
- return Err(DaemonRpcError::Unconfigured(
- "farm publish requires a signer session id".to_owned(),
- ));
- };
-
- let sdk = actor_write_sdk_client(config)?;
- let session = SdkRadrootsdSignerSessionRef::from_session_id(signer_session_id.to_owned());
- let mut options = SdkRadrootsdFarmPublishOptions::from_signer_session_ref(&session);
- if let Some(idempotency_key) = idempotency_key {
- options = options.with_idempotency_key(idempotency_key.to_owned());
- }
- if let Some(signer_authority) = signer_authority {
- options = options.with_signer_authority(sdk_signer_authority(signer_authority));
- }
-
- let receipt = block_on_sdk(
- sdk.farm()
- .publish_farm_via_radrootsd_with_options(farm, &options),
- )?
- .map_err(map_sdk_publish_error)?;
-
- map_event_publish_receipt(receipt, idempotency_key, "farm")
-}
-
-pub fn bridge_order_request(
- config: &RuntimeConfig,
- order: &RadrootsTradeOrder,
- idempotency_key: Option<&str>,
- signer_session_id: Option<&str>,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> Result<BridgeOrderRequestResult, DaemonRpcError> {
- let Some(signer_session_id) = signer_session_id else {
- return Err(DaemonRpcError::Unconfigured(
- "order publish requires a signer session id".to_owned(),
- ));
- };
-
- let sdk = actor_write_sdk_client(config)?;
- let session = SdkRadrootsdSignerSessionRef::from_session_id(signer_session_id.to_owned());
- let mut options =
- radroots_sdk::SdkRadrootsdOrderRequestPublishOptions::from_signer_session_ref(&session);
- if let Some(idempotency_key) = idempotency_key {
- options = options.with_idempotency_key(idempotency_key.to_owned());
- }
- if let Some(signer_authority) = signer_authority {
- options = options.with_signer_authority(sdk_signer_authority(signer_authority));
- }
-
- let receipt = block_on_sdk(
- sdk.trade()
- .publish_order_request_via_radrootsd_with_options(order, &options),
- )?
- .map_err(map_sdk_publish_error)?;
-
- map_order_request_receipt(receipt, idempotency_key)
-}
-
-fn bridge_jobs(config: &RuntimeConfig) -> Result<Vec<BridgeJobRemote>, DaemonRpcError> {
- call(
- &default_target(config),
- "bridge.job.list",
- None,
- RpcAuthMode::BridgeBearer,
- )
-}
-
-fn bridge_job_status(
- config: &RuntimeConfig,
- job_id: &str,
-) -> Result<BridgeJobRemote, DaemonRpcError> {
- call(
- &default_target(config),
- "bridge.job.status",
- Some(serde_json::json!({ "job_id": job_id })),
- RpcAuthMode::BridgeBearer,
- )
-}
-
-fn nip46_sessions_with_target(
- target: &RpcTarget,
-) -> Result<Vec<Nip46SessionRemote>, DaemonRpcError> {
- call(target, "nip46.session.list", None, RpcAuthMode::None)
-}
-
-fn hydrate_nip46_session_user_pubkeys(
- target: &RpcTarget,
- sessions: Vec<Nip46SessionRemote>,
-) -> Result<Vec<Nip46SessionRemote>, DaemonRpcError> {
- if sessions
- .iter()
- .all(|session| session_user_pubkey(session).is_some())
- {
- return Ok(sessions);
- }
-
- let sdk = radrootsd_sdk_client(target)?;
- let signer_sessions = sdk.radrootsd().signer_sessions();
- sessions
- .into_iter()
- .map(|mut session| {
- if session_user_pubkey(&session).is_none() {
- let session_ref =
- SdkRadrootsdSignerSessionRef::from_session_id(session.session_id.clone());
- let public_key = block_on_sdk(signer_sessions.get_public_key(&session_ref))?
- .map_err(|error| {
- DaemonRpcError::Remote(format!(
- "failed to hydrate signer session `{}` user pubkey: {error}",
- session.session_id
- ))
- })?;
- let pubkey = public_key.pubkey.trim().to_owned();
- if pubkey.is_empty() {
- return Err(DaemonRpcError::InvalidResponse(format!(
- "hydrated signer session `{}` reported an empty user pubkey",
- session.session_id
- )));
- }
- session.user_pubkey = Some(pubkey);
- }
- Ok(session)
- })
- .collect()
-}
-
-fn actor_write_target(config: &RuntimeConfig) -> Result<RpcTarget, DaemonRpcError> {
- let resolved =
- provider::resolve_actor_write_plane_target(config).map_err(DaemonRpcError::Unconfigured)?;
- Ok(RpcTarget {
- url: resolved.url,
- bridge_bearer_token: Some(resolved.bridge_bearer_token),
- })
-}
-
-fn default_target(config: &RuntimeConfig) -> RpcTarget {
- RpcTarget {
- url: config.rpc.url.clone(),
- bridge_bearer_token: config.rpc.bridge_bearer_token.clone(),
- }
-}
-
-fn actor_write_sdk_client(
- config: &RuntimeConfig,
-) -> Result<radroots_sdk::RadrootsSdkClient, DaemonRpcError> {
- let target = actor_write_target(config)?;
- radrootsd_sdk_client(&target)
-}
-
-fn radrootsd_sdk_client(
- target: &RpcTarget,
-) -> Result<radroots_sdk::RadrootsSdkClient, DaemonRpcError> {
- let mut sdk_config = RadrootsSdkConfig::custom();
- sdk_config.transport = SdkTransportMode::Radrootsd;
- sdk_config.signer = SignerConfig::Nip46;
- sdk_config.radrootsd.endpoint = Some(target.url.clone());
- let Some(bridge_bearer_token) = target.bridge_bearer_token.clone() else {
- return Err(DaemonRpcError::Unconfigured(
- "actor write plane target is missing a bridge bearer token".to_owned(),
- ));
- };
- sdk_config.radrootsd.auth = RadrootsdAuth::BearerToken(bridge_bearer_token);
- radroots_sdk::RadrootsSdkClient::from_config(sdk_config)
- .map_err(|err| DaemonRpcError::Unconfigured(err.to_string()))
-}
-
-fn sdk_signer_authority(value: &ActorWriteSignerAuthority) -> SdkRadrootsdSignerAuthority {
- SdkRadrootsdSignerAuthority {
- provider_runtime_id: value.provider_runtime_id.clone(),
- account_identity_id: value.account_identity_id.clone(),
- provider_signer_session_id: value.provider_signer_session_id.clone(),
- }
-}
-
-fn map_sdk_publish_error(error: SdkPublishError) -> DaemonRpcError {
- match error {
- SdkPublishError::Config(err) => DaemonRpcError::Unconfigured(err.to_string()),
- SdkPublishError::Radrootsd(message) => DaemonRpcError::Remote(message),
- other => DaemonRpcError::External(other.to_string()),
- }
-}
-
-fn map_listing_publish_receipt(
- receipt: radroots_sdk::SdkPublishReceipt,
- idempotency_key: Option<&str>,
-) -> Result<BridgeListingPublishResult, DaemonRpcError> {
- let radroots_sdk::SdkTransportReceipt::Radrootsd(transport_receipt) = receipt.transport_receipt
- else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk listing publish returned a non-radrootsd transport receipt".to_owned(),
- ));
- };
- let Some(job_id) = transport_receipt.job_id else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk listing publish did not return a job id".to_owned(),
- ));
- };
- let Some(status) = transport_receipt.status else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk listing publish did not return a job status".to_owned(),
- ));
- };
- let Some(signer_mode) = transport_receipt.signer_mode else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk listing publish did not return a signer mode".to_owned(),
- ));
- };
- Ok(BridgeListingPublishResult {
- deduplicated: transport_receipt.deduplicated,
- job_id,
- idempotency_key: idempotency_key.map(str::to_owned),
- status,
- signer_mode,
- signer_session_id: transport_receipt.signer_session_id,
- event_kind: receipt.event_kind,
- event_id: receipt.event_id,
- event_addr: transport_receipt.event_addr,
- })
-}
-
-fn map_event_publish_receipt(
- receipt: radroots_sdk::SdkPublishReceipt,
- idempotency_key: Option<&str>,
- label: &str,
-) -> Result<BridgeEventPublishResult, DaemonRpcError> {
- let radroots_sdk::SdkTransportReceipt::Radrootsd(transport_receipt) = receipt.transport_receipt
- else {
- return Err(DaemonRpcError::InvalidResponse(format!(
- "sdk {label} publish returned a non-radrootsd transport receipt"
- )));
- };
- let Some(job_id) = transport_receipt.job_id else {
- return Err(DaemonRpcError::InvalidResponse(format!(
- "sdk {label} publish did not return a job id"
- )));
- };
- let Some(status) = transport_receipt.status else {
- return Err(DaemonRpcError::InvalidResponse(format!(
- "sdk {label} publish did not return a job status"
- )));
- };
- let Some(signer_mode) = transport_receipt.signer_mode else {
- return Err(DaemonRpcError::InvalidResponse(format!(
- "sdk {label} publish did not return a signer mode"
- )));
- };
- Ok(BridgeEventPublishResult {
- deduplicated: transport_receipt.deduplicated,
- job_id,
- idempotency_key: idempotency_key.map(str::to_owned),
- status,
- signer_mode,
- signer_session_id: transport_receipt.signer_session_id,
- event_kind: receipt.event_kind,
- event_id: receipt.event_id,
- event_addr: transport_receipt.event_addr,
- })
-}
-
-fn map_order_request_receipt(
- receipt: radroots_sdk::SdkPublishReceipt,
- idempotency_key: Option<&str>,
-) -> Result<BridgeOrderRequestResult, DaemonRpcError> {
- let radroots_sdk::SdkTransportReceipt::Radrootsd(transport_receipt) = receipt.transport_receipt
- else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk order publish returned a non-radrootsd transport receipt".to_owned(),
- ));
- };
- let Some(job_id) = transport_receipt.job_id else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk order publish did not return a job id".to_owned(),
- ));
- };
- let Some(status) = transport_receipt.status else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk order publish did not return a job status".to_owned(),
- ));
- };
- let Some(signer_mode) = transport_receipt.signer_mode else {
- return Err(DaemonRpcError::InvalidResponse(
- "sdk order publish did not return a signer mode".to_owned(),
- ));
- };
- Ok(BridgeOrderRequestResult {
- deduplicated: transport_receipt.deduplicated,
- job_id,
- idempotency_key: idempotency_key.map(str::to_owned),
- status,
- signer_mode,
- signer_session_id: transport_receipt.signer_session_id,
- event_id: receipt.event_id,
- event_addr: transport_receipt.event_addr,
- })
-}
-
-fn block_on_sdk<F, T>(future: F) -> Result<T, DaemonRpcError>
-where
- F: std::future::Future<Output = T>,
-{
- let runtime = tokio::runtime::Builder::new_current_thread()
- .enable_all()
- .build()
- .map_err(|err| DaemonRpcError::External(format!("build sdk runtime: {err}")))?;
- Ok(runtime.block_on(future))
-}
-
-pub fn resolve_signer_session_id(
- config: &RuntimeConfig,
- actor_role: &str,
- actor_pubkey: &str,
- event_kind: u32,
- requested_session_id: Option<&str>,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> Result<String, DaemonRpcError> {
- let target = actor_write_target(config)?;
- let sessions =
- hydrate_nip46_session_user_pubkeys(&target, nip46_sessions_with_target(&target)?)?;
-
- if let Some(session_id) = requested_session_id {
- let Some(session) = sessions
- .into_iter()
- .find(|session| session.session_id == session_id)
- else {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{session_id}` was not found"
- )));
- };
- validate_signer_session(
- &session,
- actor_role,
- actor_pubkey,
- event_kind,
- signer_authority,
- )?;
- return Ok(session.session_id);
- }
-
- let mut matches = sessions
- .into_iter()
- .filter(|session| {
- session_matches_actor(session, actor_pubkey, event_kind, signer_authority)
- })
- .map(|session| session.session_id)
- .collect::<Vec<_>>();
-
- match matches.len() {
- 1 => Ok(matches.pop().expect("exactly one signer session")),
- 0 => Err(DaemonRpcError::Unconfigured(format!(
- "no authorized signer session matched {actor_role} pubkey `{actor_pubkey}` for sign_event:{event_kind}; configure exactly one signer session"
- ))),
- _ => Err(DaemonRpcError::Unconfigured(format!(
- "multiple authorized signer sessions matched {actor_role} pubkey `{actor_pubkey}` for sign_event:{event_kind}; configure exactly one signer session"
- ))),
- }
-}
-
-fn validate_signer_session(
- session: &Nip46SessionRemote,
- actor_role: &str,
- actor_pubkey: &str,
- event_kind: u32,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> Result<(), DaemonRpcError> {
- if !session.authorized {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` is not authorized",
- session.session_id
- )));
- }
- let Some(user_pubkey) = session_user_pubkey(session) else {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` did not report a user pubkey",
- session.session_id
- )));
- };
- if !user_pubkey.eq_ignore_ascii_case(actor_pubkey) {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` user pubkey `{}` does not match {actor_role} pubkey `{actor_pubkey}`",
- session.session_id, user_pubkey
- )));
- }
- if !sign_event_allowed(&session.permissions, event_kind) {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` is not approved for sign_event:{event_kind}",
- session.session_id
- )));
- }
- validate_signer_authority(session, signer_authority)?;
- Ok(())
-}
-
-fn session_matches_actor(
- session: &Nip46SessionRemote,
- actor_pubkey: &str,
- event_kind: u32,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> bool {
- session.authorized
- && session_user_pubkey(session)
- .is_some_and(|user_pubkey| user_pubkey.eq_ignore_ascii_case(actor_pubkey))
- && sign_event_allowed(&session.permissions, event_kind)
- && signer_authority_matches(session, signer_authority)
-}
-
-fn session_user_pubkey(session: &Nip46SessionRemote) -> Option<&str> {
- session
- .user_pubkey
- .as_deref()
- .map(str::trim)
- .filter(|value| !value.is_empty())
-}
-
-fn validate_signer_authority(
- session: &Nip46SessionRemote,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> Result<(), DaemonRpcError> {
- let Some(expected) = signer_authority else {
- return Ok(());
- };
- let Some(actual) = session.signer_authority.as_ref() else {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` is missing signer authority continuity metadata",
- session.session_id
- )));
- };
- if actual.provider_runtime_id != expected.provider_runtime_id {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` provider `{}` does not match required provider `{}`",
- session.session_id, actual.provider_runtime_id, expected.provider_runtime_id
- )));
- }
- if actual.account_identity_id != expected.account_identity_id {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` account identity `{}` does not match required account `{}`",
- session.session_id, actual.account_identity_id, expected.account_identity_id
- )));
- }
- if actual.provider_signer_session_id != expected.provider_signer_session_id {
- return Err(DaemonRpcError::Unconfigured(format!(
- "requested signer session `{}` provider signer session `{}` does not match required provider session `{}`",
- session.session_id,
- actual
- .provider_signer_session_id
- .as_deref()
- .unwrap_or("<none>"),
- expected
- .provider_signer_session_id
- .as_deref()
- .unwrap_or("<none>")
- )));
- }
- Ok(())
-}
-
-fn signer_authority_matches(
- session: &Nip46SessionRemote,
- signer_authority: Option<&ActorWriteSignerAuthority>,
-) -> bool {
- validate_signer_authority(session, signer_authority).is_ok()
-}
-
-fn sign_event_allowed(perms: &[String], kind: u32) -> bool {
- perms.iter().any(|entry| entry == "sign_event")
- || perms
- .iter()
- .any(|entry| entry == &format!("sign_event:{kind}"))
-}
-
-fn call<T: DeserializeOwned>(
- target: &RpcTarget,
- method: &str,
- params: Option<Value>,
- auth_mode: RpcAuthMode,
-) -> Result<T, DaemonRpcError> {
- let client = Client::builder()
- .timeout(Duration::from_secs(RPC_TIMEOUT_SECS))
- .build()
- .map_err(|error| DaemonRpcError::InvalidResponse(format!("build rpc client: {error}")))?;
-
- let mut request = client.post(target.url.as_str()).json(&JsonRpcRequest {
- jsonrpc: "2.0",
- id: 1,
- method,
- params,
- });
-
- if matches!(auth_mode, RpcAuthMode::BridgeBearer) {
- let Some(token) = target.bridge_bearer_token.as_deref() else {
- return Err(DaemonRpcError::Unconfigured(
- "bridge bearer token is not configured".to_owned(),
- ));
- };
- request = request.bearer_auth(token);
- }
-
- let response = request.send().map_err(|error| {
- DaemonRpcError::External(format!(
- "failed to reach daemon rpc at {}: {error}",
- target.url
- ))
- })?;
- let status = response.status();
- let body = response.text().map_err(|error| {
- DaemonRpcError::InvalidResponse(format!("read daemon rpc response: {error}"))
- })?;
- if !status.is_success() {
- return Err(DaemonRpcError::External(format!(
- "daemon rpc returned http {}",
- status.as_u16()
- )));
- }
-
- let envelope: JsonRpcResponse<T> = serde_json::from_str(body.as_str()).map_err(|error| {
- DaemonRpcError::InvalidResponse(format!("parse daemon rpc response: {error}"))
- })?;
- if let Some(result) = envelope.result {
- return Ok(result);
- }
- let Some(error) = envelope.error else {
- return Err(DaemonRpcError::InvalidResponse(
- "daemon rpc response did not include a result".to_owned(),
- ));
- };
- Err(map_rpc_error(method, error))
-}
-
-fn map_rpc_error(method: &str, error: JsonRpcResponseError) -> DaemonRpcError {
- match error.code {
- -32601 => DaemonRpcError::MethodUnavailable(error.message),
- -32001 => DaemonRpcError::Unauthorized(error.message),
- -32000
- if method == "bridge.job.status"
- && error.message.starts_with("unknown bridge job:") =>
- {
- DaemonRpcError::UnknownJob(error.message)
- }
- -32000 => DaemonRpcError::Remote(error.message),
- _ => DaemonRpcError::InvalidResponse(format!(
- "daemon rpc returned unexpected error {}: {}",
- error.code, error.message
- )),
- }
-}
-
-fn map_job_command(command: String) -> String {
- match command.as_str() {
- "bridge.profile.publish" => "profile.publish".to_owned(),
- "bridge.farm.publish" => "farm.publish".to_owned(),
- "bridge.listing.publish" => "listing.publish".to_owned(),
- "bridge.order.request" => "order.submit".to_owned(),
- other => other.to_owned(),
- }
-}
-
-fn map_job_summary_view(job: BridgeJobRemote) -> JobSummaryView {
- JobSummaryView {
- id: job.job_id,
- command: map_job_command(job.command),
- state: job.status,
- terminal: job.terminal,
- signer: job.signer_mode,
- signer_session_id: job.signer_session_id,
- requested_at_unix: job.requested_at_unix,
- completed_at_unix: job.completed_at_unix,
- recovered_after_restart: job.recovered_after_restart,
- }
-}
-
-fn map_job_detail_view(job: BridgeJobRemote) -> JobDetailView {
- JobDetailView {
- id: job.job_id,
- command: map_job_command(job.command),
- state: job.status,
- terminal: job.terminal,
- signer: job.signer_mode,
- signer_session_id: job.signer_session_id,
- requested_at_unix: job.requested_at_unix,
- completed_at_unix: job.completed_at_unix,
- recovered_after_restart: job.recovered_after_restart,
- event_id: job.event_id,
- event_addr: job.event_addr,
- delivery_policy: job.delivery_policy,
- delivery_quorum: job.delivery_quorum,
- relay_count: job.relay_count,
- acknowledged_relay_count: job.acknowledged_relay_count,
- required_acknowledged_relay_count: job.required_acknowledged_relay_count,
- attempt_count: job.attempt_count,
- relay_outcome_summary: job.relay_outcome_summary,
- attempt_summaries: job.attempt_summaries,
- }
-}
-
-pub fn bridge_source() -> &'static str {
- BRIDGE_SOURCE
-}
diff --git a/src/runtime/management.rs b/src/runtime/management.rs
@@ -1,1128 +0,0 @@
-use std::fs;
-use std::path::{Path, PathBuf};
-
-use chrono::Utc;
-use getrandom::getrandom;
-use radroots_runtime_distribution::{RadrootsRuntimeDistributionResolver, RuntimeArtifactRequest};
-use radroots_runtime_manager::{
- ManagedRuntimeActionInspection, ManagedRuntimeConfigInspection,
- ManagedRuntimeContext as RuntimeManagementContext, ManagedRuntimeGroup as RuntimeGroup,
- ManagedRuntimeInspection, ManagedRuntimeInspectionAvailability, ManagedRuntimeInstallState,
- ManagedRuntimeInstancePaths, ManagedRuntimeInstanceRecord, ManagedRuntimeLifecycleAction,
- ManagedRuntimeLogsInspection, ManagedRuntimeStatusInspection,
- ManagedRuntimeTarget as RuntimeTarget, extract_binary_archive,
- inspect_runtime_action as inspect_target_action,
- inspect_runtime_config as inspect_target_config, inspect_runtime_logs as inspect_target_logs,
- inspect_runtime_status as inspect_target_status, load_management_context_with_selection,
- parse_contract_str, process_running as managed_process_running, remove_instance,
- remove_instance_artifacts, resolve_runtime_target, save_registry, start_process, stop_process,
- upsert_instance, write_instance_metadata, write_managed_file, write_secret_file,
-};
-use radroots_runtime_paths::{RadrootsPathResolver, RadrootsRuntimePathSelection};
-use serde::{Deserialize, Serialize};
-
-use crate::domain::runtime::{
- RuntimeActionView, RuntimeInstancePathsView, RuntimeInstanceRecordView, RuntimeLogsView,
- RuntimeManagedConfigView, RuntimeStatusView,
-};
-use crate::runtime::{RuntimeError, config::RuntimeConfig};
-
-const MANAGEMENT_CONTRACT_RAW: &str = include_str!(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/../../../../foundation/contracts/runtime/management.toml"
-));
-const DISTRIBUTION_CONTRACT_RAW: &str = include_str!(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/../../../../foundation/contracts/runtime/distribution.toml"
-));
-const RADROOTSD_RUNTIME_ID: &str = "radrootsd";
-const RADROOTSD_BINARY_NAME: &str = "radrootsd";
-const RADROOTSD_ARTIFACT_CHANNEL: &str = "stable";
-const RADROOTSD_DEFAULT_RPC_ADDR: &str = "127.0.0.1:7070";
-const RADROOTSD_DEFAULT_METADATA_NAME: &str = "radrootsd";
-const RADROOTSD_BRIDGE_TOKEN_FILE: &str = "bridge-bearer-token.txt";
-const RADROOTSD_IDENTITY_FILE: &str = "identity.secret.json";
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct ManagedRadrootsdSettingsFile {
- metadata: ManagedRadrootsdMetadata,
- config: ManagedRadrootsdConfig,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct ManagedRadrootsdMetadata {
- name: String,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct ManagedRadrootsdConfig {
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- relays: Vec<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- logs_dir: Option<String>,
- rpc: ManagedRadrootsdRpc,
- bridge: ManagedRadrootsdBridge,
- #[serde(default)]
- nip46: ManagedRadrootsdNip46,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct ManagedRadrootsdRpc {
- addr: String,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct ManagedRadrootsdBridge {
- enabled: bool,
- #[serde(skip_serializing_if = "Option::is_none")]
- bearer_token: Option<String>,
- delivery_policy: String,
- publish_max_attempts: usize,
- #[serde(skip_serializing_if = "Option::is_none")]
- state_path: Option<String>,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct ManagedRadrootsdNip46 {
- public_jsonrpc_enabled: bool,
- session_ttl_secs: u64,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- perms: Vec<String>,
-}
-
-impl Default for ManagedRadrootsdNip46 {
- fn default() -> Self {
- Self {
- public_jsonrpc_enabled: false,
- session_ttl_secs: 900,
- perms: Vec::new(),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct RuntimeConfigMutationRequest {
- pub runtime_id: String,
- pub instance_id: Option<String>,
- pub key: String,
- pub value: String,
-}
-
-pub type RuntimeCommandAvailability = ManagedRuntimeInspectionAvailability;
-pub type RuntimeLifecycleAction = ManagedRuntimeLifecycleAction;
-
-type RuntimeInspection<T> = ManagedRuntimeInspection<T>;
-
-pub fn inspect_status(
- config: &RuntimeConfig,
- runtime_id: &str,
- instance_id: Option<&str>,
-) -> Result<RuntimeInspection<RuntimeStatusView>, RuntimeError> {
- let context = load_management_context(config)?;
- let target = resolve_runtime_target(&context, runtime_id, instance_id);
- let inspection = inspect_target_status(&target, &context.contract.lifecycle.actions);
- Ok(RuntimeInspection {
- availability: inspection.availability,
- view: runtime_status_view(inspection.view),
- })
-}
-
-pub fn inspect_logs(
- config: &RuntimeConfig,
- runtime_id: &str,
- instance_id: Option<&str>,
-) -> Result<RuntimeInspection<RuntimeLogsView>, RuntimeError> {
- let context = load_management_context(config)?;
- let target = resolve_runtime_target(&context, runtime_id, instance_id);
- let inspection = inspect_target_logs(&target);
- Ok(RuntimeInspection {
- availability: inspection.availability,
- view: runtime_logs_view(inspection.view),
- })
-}
-
-pub fn inspect_config_show(
- config: &RuntimeConfig,
- runtime_id: &str,
- instance_id: Option<&str>,
-) -> Result<RuntimeInspection<RuntimeManagedConfigView>, RuntimeError> {
- let context = load_management_context(config)?;
- let target = resolve_runtime_target(&context, runtime_id, instance_id);
- let inspection = inspect_target_config(&target);
- Ok(RuntimeInspection {
- availability: inspection.availability,
- view: runtime_config_view(inspection.view),
- })
-}
-
-pub fn inspect_action(
- config: &RuntimeConfig,
- runtime_id: &str,
- instance_id: Option<&str>,
- action: RuntimeLifecycleAction,
- dry_run: bool,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- let mut context = load_management_context(config)?;
- let target = resolve_runtime_target(&context, runtime_id, instance_id);
- if target.runtime_group == RuntimeGroup::ActiveManagedTarget && !dry_run {
- return execute_action(config, &mut context, target, action);
- }
- let inspection = inspect_target_action(&target, action, None);
- Ok(RuntimeInspection {
- availability: inspection.availability,
- view: runtime_action_view(inspection.view),
- })
-}
-
-pub fn inspect_config_set(
- config: &RuntimeConfig,
- request: &RuntimeConfigMutationRequest,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- let mut context = load_management_context(config)?;
- let target = resolve_runtime_target(
- &context,
- request.runtime_id.as_str(),
- request.instance_id.as_deref(),
- );
- if target.runtime_group == RuntimeGroup::ActiveManagedTarget {
- return execute_config_set(config, &mut context, target, request);
- }
- let detail = Some(format!(
- "requested managed config mutation {}={} for runtime `{}` instance `{}`; runtime `{}` is not an active managed target in this wave",
- request.key, request.value, target.runtime_id, target.instance_id, target.runtime_id
- ));
- let inspection = inspect_target_action(&target, RuntimeLifecycleAction::ConfigSet, detail);
- Ok(RuntimeInspection {
- availability: inspection.availability,
- view: runtime_action_view(inspection.view),
- })
-}
-
-fn load_management_context(
- config: &RuntimeConfig,
-) -> Result<RuntimeManagementContext, RuntimeError> {
- let contract = parse_contract_str(MANAGEMENT_CONTRACT_RAW)?;
- let selection = RadrootsRuntimePathSelection::from_profile_value(
- config.paths.profile.as_str(),
- config.paths.repo_local_root.clone(),
- )
- .map_err(|error| RuntimeError::Config(error.to_string()))?;
- let resolver = RadrootsPathResolver::current();
- load_management_context_with_selection(contract, &resolver, &selection)
- .map_err(RuntimeError::from)
-}
-
-fn runtime_status_view(view: ManagedRuntimeStatusInspection) -> RuntimeStatusView {
- RuntimeStatusView {
- runtime_id: view.runtime_id,
- instance_id: view.instance_id,
- instance_source: view.instance_source,
- runtime_group: view.runtime_group,
- management_posture: view.management_posture,
- state: view.state,
- source: view.source,
- detail: view.detail,
- management_mode: view.management_mode,
- service_manager_integration: view.service_manager_integration,
- uses_absolute_binary_paths: view.uses_absolute_binary_paths,
- preferred_cli_binding: view.preferred_cli_binding,
- install_state: view.install_state,
- health_state: view.health_state,
- health_source: view.health_source,
- registry_path: view.registry_path.display().to_string(),
- lifecycle_actions: view.lifecycle_actions,
- instance_paths: view.instance_paths.map(runtime_instance_paths_view),
- instance_record: view.instance_record.map(runtime_instance_record_view),
- }
-}
-
-fn runtime_logs_view(view: ManagedRuntimeLogsInspection) -> RuntimeLogsView {
- RuntimeLogsView {
- runtime_id: view.runtime_id,
- instance_id: view.instance_id,
- instance_source: view.instance_source,
- runtime_group: view.runtime_group,
- state: view.state,
- source: view.source,
- detail: view.detail,
- stdout_log_path: view.stdout_log_path.map(|path| path.display().to_string()),
- stderr_log_path: view.stderr_log_path.map(|path| path.display().to_string()),
- stdout_log_present: view.stdout_log_present,
- stderr_log_present: view.stderr_log_present,
- }
-}
-
-fn runtime_config_view(view: ManagedRuntimeConfigInspection) -> RuntimeManagedConfigView {
- RuntimeManagedConfigView {
- runtime_id: view.runtime_id,
- instance_id: view.instance_id,
- instance_source: view.instance_source,
- runtime_group: view.runtime_group,
- state: view.state,
- source: view.source,
- detail: view.detail,
- config_format: view.config_format,
- config_path: view.config_path.map(|path| path.display().to_string()),
- config_present: view.config_present,
- requires_bootstrap_secret: view.requires_bootstrap_secret,
- requires_config_bootstrap: view.requires_config_bootstrap,
- requires_signer_provider: view.requires_signer_provider,
- }
-}
-
-fn runtime_action_view(view: ManagedRuntimeActionInspection) -> RuntimeActionView {
- RuntimeActionView {
- action: view.action,
- runtime_id: view.runtime_id,
- instance_id: view.instance_id,
- instance_source: view.instance_source,
- runtime_group: view.runtime_group,
- state: view.state,
- source: view.source,
- detail: view.detail,
- mutates_bindings: view.mutates_bindings,
- next_step: view.next_step,
- }
-}
-
-fn execute_action(
- config: &RuntimeConfig,
- context: &mut RuntimeManagementContext,
- target: RuntimeTarget,
- action: RuntimeLifecycleAction,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- if target.runtime_id != RADROOTSD_RUNTIME_ID {
- let inspection = inspect_target_action(
- &target,
- action,
- Some(format!(
- "runtime `{}` is not admitted as an active managed implementation in this wave",
- target.runtime_id
- )),
- );
- return Ok(RuntimeInspection {
- availability: inspection.availability,
- view: runtime_action_view(inspection.view),
- });
- }
-
- match action {
- RuntimeLifecycleAction::Install => install_managed_radrootsd(config, context, target),
- RuntimeLifecycleAction::Start => start_managed_radrootsd(config, context, target),
- RuntimeLifecycleAction::Stop => stop_managed_radrootsd(context, target),
- RuntimeLifecycleAction::Restart => restart_managed_radrootsd(config, context, target),
- RuntimeLifecycleAction::Uninstall => uninstall_managed_radrootsd(context, target),
- RuntimeLifecycleAction::ConfigSet => unreachable!("config set is handled separately"),
- }
-}
-
-fn execute_config_set(
- _config: &RuntimeConfig,
- context: &mut RuntimeManagementContext,
- target: RuntimeTarget,
- request: &RuntimeConfigMutationRequest,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- if target.runtime_id != RADROOTSD_RUNTIME_ID {
- let inspection = inspect_target_action(
- &target,
- RuntimeLifecycleAction::ConfigSet,
- Some(format!(
- "runtime `{}` is not admitted as an active managed implementation in this wave",
- target.runtime_id
- )),
- );
- return Ok(RuntimeInspection {
- availability: inspection.availability,
- view: runtime_action_view(inspection.view),
- });
- }
-
- let Some(predicted_paths) = target.predicted_paths.as_ref() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::ConfigSet,
- "active managed runtime is missing predicted instance paths".to_owned(),
- ));
- };
- let Some(mut record) = target.instance_record.clone() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::ConfigSet,
- format!(
- "managed runtime `{}` instance `{}` is not installed in local runtime state; run `radroots runtime status get` for current status",
- target.runtime_id, target.instance_id
- ),
- ));
- };
-
- let mut settings = load_managed_radrootsd_settings(&record.config_path)?;
- let token_path = managed_radrootsd_token_path(predicted_paths);
- let identity_path = managed_radrootsd_identity_path(predicted_paths);
- apply_managed_radrootsd_config_mutation(
- &mut settings,
- &mut record,
- predicted_paths,
- request.key.as_str(),
- request.value.as_str(),
- token_path.as_path(),
- )?;
- write_secret_material_state(&settings, &mut record, token_path.as_path())?;
- save_managed_radrootsd_settings(record.config_path.as_path(), &settings)?;
- write_instance_metadata(predicted_paths, &record)?;
- upsert_instance(&mut context.registry, record.clone());
- save_registry(
- &context.shared_paths.instance_registry_path,
- &context.registry,
- )?;
-
- Ok(RuntimeInspection {
- availability: RuntimeCommandAvailability::Success,
- view: RuntimeActionView {
- action: RuntimeLifecycleAction::ConfigSet.as_str().to_owned(),
- runtime_id: target.runtime_id,
- instance_id: target.instance_id,
- instance_source: target.instance_source,
- runtime_group: target.runtime_group.as_str().to_owned(),
- state: "configured".to_owned(),
- source: "generic runtime-management command family".to_owned(),
- detail: format!(
- "updated managed {} instance `{}` config key `{}`; config path = {}, identity path = {}",
- RADROOTSD_RUNTIME_ID,
- record.instance_id,
- request.key,
- record.config_path.display(),
- identity_path.display()
- ),
- mutates_bindings: false,
- next_step: None,
- },
- })
-}
-
-fn install_managed_radrootsd(
- _config: &RuntimeConfig,
- context: &mut RuntimeManagementContext,
- target: RuntimeTarget,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- let Some(predicted_paths) = target.predicted_paths.as_ref() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::Install,
- "active managed runtime is missing predicted instance paths".to_owned(),
- ));
- };
-
- let artifact = resolve_radrootsd_artifact(&context.shared_paths)?;
- let binary_path = extract_binary_archive(
- artifact.archive_path.as_path(),
- artifact.archive_format.as_str(),
- predicted_paths,
- artifact.binary_name.as_str(),
- )?;
-
- let rpc_addr = RADROOTSD_DEFAULT_RPC_ADDR.to_owned();
- let health_endpoint = rpc_addr_to_http_url(rpc_addr.as_str())?;
- let token_path = managed_radrootsd_token_path(predicted_paths);
- let bridge_token = generate_bridge_token()?;
- let config_path = predicted_paths.state_dir.join("config.toml");
- let settings = bootstrap_managed_radrootsd_settings(
- predicted_paths,
- rpc_addr.as_str(),
- bridge_token.as_str(),
- );
- write_secret_file(token_path.as_path(), bridge_token.as_str())?;
- save_managed_radrootsd_settings(config_path.as_path(), &settings)?;
-
- let record = ManagedRuntimeInstanceRecord {
- runtime_id: target.runtime_id.clone(),
- instance_id: target.instance_id.clone(),
- management_mode: target
- .management_mode
- .clone()
- .unwrap_or_else(|| "interactive_user_managed".to_owned()),
- install_state: ManagedRuntimeInstallState::Configured,
- binary_path: binary_path.clone(),
- config_path: config_path.clone(),
- logs_path: predicted_paths.logs_dir.clone(),
- run_path: predicted_paths.run_dir.clone(),
- installed_version: artifact.version.clone(),
- health_endpoint: Some(health_endpoint.clone()),
- secret_material_ref: Some(token_path.display().to_string()),
- last_started_at: None,
- last_stopped_at: None,
- notes: Some(format!(
- "installed from artifact cache {}",
- artifact.archive_path.display()
- )),
- };
- write_instance_metadata(predicted_paths, &record)?;
- upsert_instance(&mut context.registry, record.clone());
- save_registry(
- &context.shared_paths.instance_registry_path,
- &context.registry,
- )?;
-
- let identity_path = managed_radrootsd_identity_path(predicted_paths);
- Ok(RuntimeInspection {
- availability: RuntimeCommandAvailability::Success,
- view: RuntimeActionView {
- action: RuntimeLifecycleAction::Install.as_str().to_owned(),
- runtime_id: target.runtime_id,
- instance_id: target.instance_id,
- instance_source: target.instance_source,
- runtime_group: target.runtime_group.as_str().to_owned(),
- state: "configured".to_owned(),
- source: "generic runtime-management command family".to_owned(),
- detail: format!(
- "installed managed {RADROOTSD_RUNTIME_ID} instance `{}` from artifact {} to {}; config = {}; identity bootstrap path = {}; health endpoint = {}",
- record.instance_id,
- artifact.archive_path.display(),
- binary_path.display(),
- config_path.display(),
- identity_path.display(),
- health_endpoint
- ),
- mutates_bindings: false,
- next_step: None,
- },
- })
-}
-
-fn start_managed_radrootsd(
- config: &RuntimeConfig,
- context: &mut RuntimeManagementContext,
- target: RuntimeTarget,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- let Some(predicted_paths) = target.predicted_paths.as_ref() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::Start,
- "active managed runtime is missing predicted instance paths".to_owned(),
- ));
- };
- let Some(mut record) = target.instance_record.clone() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::Start,
- format!(
- "managed runtime `{}` instance `{}` is not installed in local runtime state; run `radroots runtime status get` for current status",
- target.runtime_id, target.instance_id
- ),
- ));
- };
-
- if managed_process_running(predicted_paths)? {
- return Ok(RuntimeInspection {
- availability: RuntimeCommandAvailability::Success,
- view: RuntimeActionView {
- action: RuntimeLifecycleAction::Start.as_str().to_owned(),
- runtime_id: target.runtime_id,
- instance_id: target.instance_id,
- instance_source: target.instance_source,
- runtime_group: target.runtime_group.as_str().to_owned(),
- state: "running".to_owned(),
- source: "generic runtime-management command family".to_owned(),
- detail: format!(
- "managed {} instance `{}` is already running from {}",
- RADROOTSD_RUNTIME_ID,
- record.instance_id,
- record.binary_path.display()
- ),
- mutates_bindings: false,
- next_step: None,
- },
- });
- }
-
- let args = vec![
- "--config".to_owned(),
- record.config_path.display().to_string(),
- "--identity".to_owned(),
- managed_radrootsd_identity_path(predicted_paths)
- .display()
- .to_string(),
- "--allow-generate-identity".to_owned(),
- ];
- let envs = managed_radrootsd_start_envs(config);
- let pid = start_process(record.binary_path.as_path(), &args, &envs, predicted_paths)?;
- record.last_started_at = Some(Utc::now().to_rfc3339());
- write_instance_metadata(predicted_paths, &record)?;
- upsert_instance(&mut context.registry, record.clone());
- save_registry(
- &context.shared_paths.instance_registry_path,
- &context.registry,
- )?;
-
- Ok(RuntimeInspection {
- availability: RuntimeCommandAvailability::Success,
- view: RuntimeActionView {
- action: RuntimeLifecycleAction::Start.as_str().to_owned(),
- runtime_id: target.runtime_id,
- instance_id: target.instance_id,
- instance_source: target.instance_source,
- runtime_group: target.runtime_group.as_str().to_owned(),
- state: "running".to_owned(),
- source: "generic runtime-management command family".to_owned(),
- detail: format!(
- "started managed {} instance `{}` with pid {} using config {}",
- RADROOTSD_RUNTIME_ID,
- record.instance_id,
- pid,
- record.config_path.display()
- ),
- mutates_bindings: false,
- next_step: None,
- },
- })
-}
-
-fn stop_managed_radrootsd(
- context: &mut RuntimeManagementContext,
- target: RuntimeTarget,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- let Some(predicted_paths) = target.predicted_paths.as_ref() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::Stop,
- "active managed runtime is missing predicted instance paths".to_owned(),
- ));
- };
- let Some(mut record) = target.instance_record.clone() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::Stop,
- format!(
- "managed runtime `{}` instance `{}` is not installed",
- target.runtime_id, target.instance_id
- ),
- ));
- };
-
- let stopped = stop_process(predicted_paths)?;
- record.last_stopped_at = Some(Utc::now().to_rfc3339());
- write_instance_metadata(predicted_paths, &record)?;
- upsert_instance(&mut context.registry, record.clone());
- save_registry(
- &context.shared_paths.instance_registry_path,
- &context.registry,
- )?;
-
- Ok(RuntimeInspection {
- availability: RuntimeCommandAvailability::Success,
- view: RuntimeActionView {
- action: RuntimeLifecycleAction::Stop.as_str().to_owned(),
- runtime_id: target.runtime_id,
- instance_id: target.instance_id,
- instance_source: target.instance_source,
- runtime_group: target.runtime_group.as_str().to_owned(),
- state: "stopped".to_owned(),
- source: "generic runtime-management command family".to_owned(),
- detail: if stopped {
- format!(
- "stopped managed {} instance `{}`",
- RADROOTSD_RUNTIME_ID, record.instance_id
- )
- } else {
- format!(
- "managed {} instance `{}` was already stopped",
- RADROOTSD_RUNTIME_ID, record.instance_id
- )
- },
- mutates_bindings: false,
- next_step: None,
- },
- })
-}
-
-fn restart_managed_radrootsd(
- config: &RuntimeConfig,
- context: &mut RuntimeManagementContext,
- target: RuntimeTarget,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- let stop_result = stop_managed_radrootsd(context, target.clone())?;
- if stop_result.availability != RuntimeCommandAvailability::Success {
- return Ok(stop_result);
- }
- let refreshed_target = resolve_runtime_target(
- context,
- RADROOTSD_RUNTIME_ID,
- Some(target.instance_id.as_str()),
- );
- let start_result = start_managed_radrootsd(config, context, refreshed_target)?;
- Ok(RuntimeInspection {
- availability: start_result.availability,
- view: RuntimeActionView {
- action: RuntimeLifecycleAction::Restart.as_str().to_owned(),
- runtime_id: start_result.view.runtime_id,
- instance_id: start_result.view.instance_id,
- instance_source: start_result.view.instance_source,
- runtime_group: start_result.view.runtime_group,
- state: start_result.view.state,
- source: start_result.view.source,
- detail: format!(
- "restarted managed {} instance `{}`",
- RADROOTSD_RUNTIME_ID, target.instance_id
- ),
- mutates_bindings: false,
- next_step: None,
- },
- })
-}
-
-fn uninstall_managed_radrootsd(
- context: &mut RuntimeManagementContext,
- target: RuntimeTarget,
-) -> Result<RuntimeInspection<RuntimeActionView>, RuntimeError> {
- let Some(predicted_paths) = target.predicted_paths.as_ref() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::Uninstall,
- "active managed runtime is missing predicted instance paths".to_owned(),
- ));
- };
- let Some(record) = target.instance_record.clone() else {
- return Ok(runtime_action_unconfigured(
- &target,
- RuntimeLifecycleAction::Uninstall,
- format!(
- "managed runtime `{}` instance `{}` is not installed",
- target.runtime_id, target.instance_id
- ),
- ));
- };
-
- let _ = stop_process(predicted_paths);
- remove_instance_artifacts(predicted_paths)?;
- remove_instance(
- &mut context.registry,
- record.runtime_id.as_str(),
- record.instance_id.as_str(),
- );
- save_registry(
- &context.shared_paths.instance_registry_path,
- &context.registry,
- )?;
-
- Ok(RuntimeInspection {
- availability: RuntimeCommandAvailability::Success,
- view: RuntimeActionView {
- action: RuntimeLifecycleAction::Uninstall.as_str().to_owned(),
- runtime_id: target.runtime_id,
- instance_id: target.instance_id,
- instance_source: target.instance_source,
- runtime_group: target.runtime_group.as_str().to_owned(),
- state: "uninstalled".to_owned(),
- source: "generic runtime-management command family".to_owned(),
- detail: format!(
- "uninstalled managed {} instance `{}` and removed {}",
- RADROOTSD_RUNTIME_ID,
- record.instance_id,
- predicted_paths
- .install_dir
- .parent()
- .unwrap_or(predicted_paths.install_dir.as_path())
- .display()
- ),
- mutates_bindings: false,
- next_step: None,
- },
- })
-}
-
-fn runtime_action_unconfigured(
- target: &RuntimeTarget,
- action: RuntimeLifecycleAction,
- detail: String,
-) -> RuntimeInspection<RuntimeActionView> {
- RuntimeInspection {
- availability: RuntimeCommandAvailability::Unconfigured,
- view: RuntimeActionView {
- action: action.as_str().to_owned(),
- runtime_id: target.runtime_id.clone(),
- instance_id: target.instance_id.clone(),
- instance_source: target.instance_source.clone(),
- runtime_group: target.runtime_group.as_str().to_owned(),
- state: "not_installed".to_owned(),
- source: "generic runtime-management command family".to_owned(),
- detail,
- mutates_bindings: false,
- next_step: None,
- },
- }
-}
-
-#[derive(Debug, Clone)]
-struct ResolvedManagedArtifact {
- archive_path: PathBuf,
- archive_format: String,
- binary_name: String,
- version: String,
-}
-
-fn resolve_radrootsd_artifact(
- shared_paths: &radroots_runtime_manager::ManagedRuntimeSharedPaths,
-) -> Result<ResolvedManagedArtifact, RuntimeError> {
- let resolver = RadrootsRuntimeDistributionResolver::parse_str(DISTRIBUTION_CONTRACT_RAW)?;
- let request = RuntimeArtifactRequest {
- runtime_id: RADROOTSD_RUNTIME_ID,
- os: current_distribution_os(),
- arch: current_distribution_arch(),
- version: "0.0.0",
- channel: Some(RADROOTSD_ARTIFACT_CHANNEL),
- };
- let artifact = resolver.resolve_artifact(&request)?;
- let search_root = shared_paths.artifact_cache_dir.join(RADROOTSD_RUNTIME_ID);
- let matches = find_cached_artifacts(
- search_root.as_path(),
- RADROOTSD_RUNTIME_ID,
- artifact.target_id.as_str(),
- artifact.archive_extension.as_str(),
- )?;
- match matches.as_slice() {
- [] => Err(RuntimeError::Config(format!(
- "no cached {RADROOTSD_RUNTIME_ID} artifact found under {} for target {}{}",
- search_root.display(),
- artifact.target_id,
- artifact.archive_extension
- ))),
- [found] => Ok(found.clone()),
- _ => Err(RuntimeError::Config(format!(
- "multiple cached {RADROOTSD_RUNTIME_ID} artifacts found under {}; keep exactly one matching target {}{}",
- search_root.display(),
- artifact.target_id,
- artifact.archive_extension
- ))),
- }
-}
-
-fn find_cached_artifacts(
- root: &Path,
- runtime_id: &str,
- target_id: &str,
- extension: &str,
-) -> Result<Vec<ResolvedManagedArtifact>, RuntimeError> {
- let mut matches = Vec::new();
- if !root.exists() {
- return Ok(matches);
- }
- collect_cached_artifacts(root, runtime_id, target_id, extension, &mut matches)?;
- Ok(matches)
-}
-
-fn collect_cached_artifacts(
- root: &Path,
- runtime_id: &str,
- target_id: &str,
- extension: &str,
- matches: &mut Vec<ResolvedManagedArtifact>,
-) -> Result<(), RuntimeError> {
- for entry in fs::read_dir(root)? {
- let entry = entry?;
- let path = entry.path();
- if path.is_dir() {
- collect_cached_artifacts(path.as_path(), runtime_id, target_id, extension, matches)?;
- continue;
- }
- let Some(file_name) = path.file_name().and_then(|value| value.to_str()) else {
- continue;
- };
- let prefix = format!("{runtime_id}-");
- let suffix = format!("-{target_id}{extension}");
- if !file_name.starts_with(prefix.as_str()) || !file_name.ends_with(suffix.as_str()) {
- continue;
- }
- let version = file_name
- .strip_prefix(prefix.as_str())
- .and_then(|value| value.strip_suffix(suffix.as_str()))
- .ok_or_else(|| {
- RuntimeError::Config(format!(
- "invalid cached artifact name `{file_name}` under {}",
- root.display()
- ))
- })?;
- matches.push(ResolvedManagedArtifact {
- archive_path: path.clone(),
- archive_format: archive_format_from_extension(extension).to_owned(),
- binary_name: RADROOTSD_BINARY_NAME.to_owned(),
- version: version.to_owned(),
- });
- }
- Ok(())
-}
-
-fn archive_format_from_extension(extension: &str) -> &str {
- match extension {
- ".tar.gz" => "tar.gz",
- other => other.trim_start_matches('.'),
- }
-}
-
-fn bootstrap_managed_radrootsd_settings(
- predicted_paths: &radroots_runtime_manager::ManagedRuntimeInstancePaths,
- rpc_addr: &str,
- bridge_token: &str,
-) -> ManagedRadrootsdSettingsFile {
- ManagedRadrootsdSettingsFile {
- metadata: ManagedRadrootsdMetadata {
- name: RADROOTSD_DEFAULT_METADATA_NAME.to_owned(),
- },
- config: ManagedRadrootsdConfig {
- relays: Vec::new(),
- logs_dir: Some(predicted_paths.logs_dir.display().to_string()),
- rpc: ManagedRadrootsdRpc {
- addr: rpc_addr.to_owned(),
- },
- bridge: ManagedRadrootsdBridge {
- enabled: true,
- bearer_token: Some(bridge_token.to_owned()),
- delivery_policy: "any".to_owned(),
- publish_max_attempts: 2,
- state_path: Some(
- predicted_paths
- .state_dir
- .join("bridge/bridge-jobs.json")
- .display()
- .to_string(),
- ),
- },
- nip46: ManagedRadrootsdNip46::default(),
- },
- }
-}
-
-fn load_managed_radrootsd_settings(
- path: &Path,
-) -> Result<ManagedRadrootsdSettingsFile, RuntimeError> {
- let raw = fs::read_to_string(path)?;
- toml::from_str(raw.as_str()).map_err(|err| {
- RuntimeError::Config(format!(
- "parse managed {RADROOTSD_RUNTIME_ID} config {}: {err}",
- path.display()
- ))
- })
-}
-
-fn save_managed_radrootsd_settings(
- path: &Path,
- settings: &ManagedRadrootsdSettingsFile,
-) -> Result<(), RuntimeError> {
- let raw = toml::to_string_pretty(settings).map_err(|err| {
- RuntimeError::Config(format!(
- "serialize managed {RADROOTSD_RUNTIME_ID} config {}: {err}",
- path.display()
- ))
- })?;
- write_managed_file(path, raw.as_str())?;
- Ok(())
-}
-
-fn apply_managed_radrootsd_config_mutation(
- settings: &mut ManagedRadrootsdSettingsFile,
- record: &mut ManagedRuntimeInstanceRecord,
- predicted_paths: &radroots_runtime_manager::ManagedRuntimeInstancePaths,
- key: &str,
- value: &str,
- token_path: &Path,
-) -> Result<(), RuntimeError> {
- match key {
- "metadata.name" => {
- settings.metadata.name = non_empty_value(key, value)?;
- }
- "config.logs_dir" => {
- settings.config.logs_dir = Some(non_empty_value(key, value)?);
- }
- "config.rpc.addr" => {
- let rpc_addr = non_empty_value(key, value)?;
- settings.config.rpc.addr = rpc_addr.clone();
- record.health_endpoint = Some(rpc_addr_to_http_url(rpc_addr.as_str())?);
- }
- "config.bridge.enabled" => {
- let enabled = parse_bool(value, key)?;
- settings.config.bridge.enabled = enabled;
- if !enabled {
- settings.config.bridge.bearer_token = None;
- if token_path.exists() {
- fs::remove_file(token_path)?;
- }
- record.secret_material_ref = None;
- }
- }
- "config.bridge.bearer_token" => {
- let token = non_empty_value(key, value)?;
- settings.config.bridge.enabled = true;
- settings.config.bridge.bearer_token = Some(token.clone());
- write_secret_file(token_path, token.as_str())?;
- record.secret_material_ref = Some(token_path.display().to_string());
- }
- "config.bridge.state_path" => {
- settings.config.bridge.state_path = Some(non_empty_value(key, value)?);
- }
- other => {
- return Err(RuntimeError::Config(format!(
- "unsupported managed {RADROOTSD_RUNTIME_ID} config key `{other}`; supported keys: metadata.name, config.logs_dir, config.rpc.addr, config.bridge.enabled, config.bridge.bearer_token, config.bridge.state_path"
- )));
- }
- }
-
- if settings.config.logs_dir.is_none() {
- settings.config.logs_dir = Some(predicted_paths.logs_dir.display().to_string());
- }
- if settings.config.bridge.state_path.is_none() {
- settings.config.bridge.state_path = Some(
- predicted_paths
- .state_dir
- .join("bridge/bridge-jobs.json")
- .display()
- .to_string(),
- );
- }
- Ok(())
-}
-
-fn write_secret_material_state(
- settings: &ManagedRadrootsdSettingsFile,
- record: &mut ManagedRuntimeInstanceRecord,
- token_path: &Path,
-) -> Result<(), RuntimeError> {
- if settings.config.bridge.enabled {
- let token = settings
- .config
- .bridge
- .bearer_token
- .as_deref()
- .ok_or_else(|| {
- RuntimeError::Config(format!(
- "managed {RADROOTSD_RUNTIME_ID} bridge is enabled but bearer_token is missing"
- ))
- })?;
- write_secret_file(token_path, token)?;
- record.secret_material_ref = Some(token_path.display().to_string());
- } else {
- record.secret_material_ref = None;
- }
- Ok(())
-}
-
-fn managed_radrootsd_start_envs(config: &RuntimeConfig) -> Vec<(String, String)> {
- let mut envs = Vec::new();
- envs.push((
- "RADROOTSD_PATHS_PROFILE".to_owned(),
- config.paths.profile.clone(),
- ));
- if config.paths.profile == "repo_local" {
- if let Some(root) = &config.paths.repo_local_root {
- envs.push((
- "RADROOTSD_PATHS_REPO_LOCAL_ROOT".to_owned(),
- root.display().to_string(),
- ));
- }
- }
- envs
-}
-
-fn managed_radrootsd_token_path(
- predicted_paths: &radroots_runtime_manager::ManagedRuntimeInstancePaths,
-) -> PathBuf {
- predicted_paths
- .secrets_dir
- .join(RADROOTSD_BRIDGE_TOKEN_FILE)
-}
-
-fn managed_radrootsd_identity_path(
- predicted_paths: &radroots_runtime_manager::ManagedRuntimeInstancePaths,
-) -> PathBuf {
- predicted_paths.secrets_dir.join(RADROOTSD_IDENTITY_FILE)
-}
-
-fn current_distribution_os() -> &'static str {
- match std::env::consts::OS {
- "macos" => "macos",
- "linux" => "linux",
- "windows" => "windows",
- other => other,
- }
-}
-
-fn current_distribution_arch() -> &'static str {
- match std::env::consts::ARCH {
- "x86_64" => "amd64",
- "aarch64" => "arm64",
- other => other,
- }
-}
-
-fn non_empty_value(key: &str, value: &str) -> Result<String, RuntimeError> {
- let trimmed = value.trim();
- if trimmed.is_empty() {
- return Err(RuntimeError::Config(format!(
- "managed config key `{key}` must not be empty"
- )));
- }
- Ok(trimmed.to_owned())
-}
-
-fn parse_bool(value: &str, key: &str) -> Result<bool, RuntimeError> {
- match value.trim() {
- "true" => Ok(true),
- "false" => Ok(false),
- other => Err(RuntimeError::Config(format!(
- "managed config key `{key}` must be `true` or `false`, got `{other}`"
- ))),
- }
-}
-
-fn rpc_addr_to_http_url(value: &str) -> Result<String, RuntimeError> {
- let trimmed = value.trim();
- if trimmed.is_empty() {
- return Err(RuntimeError::Config(
- "managed rpc addr must not be empty".to_owned(),
- ));
- }
- if trimmed.contains("://") {
- return Ok(trimmed.to_owned());
- }
- Ok(format!("http://{trimmed}"))
-}
-
-fn generate_bridge_token() -> Result<String, RuntimeError> {
- let mut bytes = [0_u8; 24];
- getrandom(&mut bytes)
- .map_err(|err| RuntimeError::Config(format!("generate bridge token: {err}")))?;
- Ok(bytes.iter().map(|byte| format!("{byte:02x}")).collect())
-}
-
-fn runtime_instance_paths_view(paths: ManagedRuntimeInstancePaths) -> RuntimeInstancePathsView {
- RuntimeInstancePathsView {
- install_dir: paths.install_dir.display().to_string(),
- state_dir: paths.state_dir.display().to_string(),
- logs_dir: paths.logs_dir.display().to_string(),
- run_dir: paths.run_dir.display().to_string(),
- secrets_dir: paths.secrets_dir.display().to_string(),
- pid_file_path: paths.pid_file_path.display().to_string(),
- stdout_log_path: paths.stdout_log_path.display().to_string(),
- stderr_log_path: paths.stderr_log_path.display().to_string(),
- metadata_path: paths.metadata_path.display().to_string(),
- }
-}
-
-fn runtime_instance_record_view(record: ManagedRuntimeInstanceRecord) -> RuntimeInstanceRecordView {
- RuntimeInstanceRecordView {
- management_mode: record.management_mode,
- install_state: match record.install_state {
- ManagedRuntimeInstallState::NotInstalled => "not_installed".to_owned(),
- ManagedRuntimeInstallState::Installed => "installed".to_owned(),
- ManagedRuntimeInstallState::Configured => "configured".to_owned(),
- ManagedRuntimeInstallState::Failed => "failed".to_owned(),
- },
- binary_path: record.binary_path.display().to_string(),
- config_path: record.config_path.display().to_string(),
- logs_path: record.logs_path.display().to_string(),
- run_path: record.run_path.display().to_string(),
- installed_version: record.installed_version,
- health_endpoint: record.health_endpoint,
- secret_material_ref: record.secret_material_ref,
- last_started_at: record.last_started_at,
- last_stopped_at: record.last_stopped_at,
- notes: record.notes,
- }
-}
diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs
@@ -1,6 +1,5 @@
pub mod accounts;
pub mod config;
-pub mod daemon;
pub mod farm;
pub mod farm_config;
pub mod find;
@@ -8,7 +7,6 @@ pub mod hyf;
pub mod listing;
pub mod local;
pub mod logging;
-pub mod management;
pub mod network;
pub mod order;
pub mod paths;
@@ -34,10 +32,6 @@ pub enum RuntimeError {
Json(#[from] serde_json::Error),
#[error("failed to write output: {0}")]
Io(#[from] std::io::Error),
- #[error("runtime manager error: {0}")]
- RuntimeManager(#[from] radroots_runtime_manager::RadrootsRuntimeManagerError),
- #[error("runtime distribution error: {0}")]
- RuntimeDistribution(#[from] radroots_runtime_distribution::RadrootsRuntimeDistributionError),
}
impl RuntimeError {
@@ -49,9 +43,7 @@ impl RuntimeError {
| Self::Sql(_)
| Self::ReplicaSync(_)
| Self::Json(_)
- | Self::Io(_)
- | Self::RuntimeManager(_)
- | Self::RuntimeDistribution(_) => ExitCode::from(1),
+ | Self::Io(_) => ExitCode::from(1),
}
}
}