Give Codex Connector its own Idunn health identity
This commit is contained in:
parent
366225eeae
commit
155fac6d22
8 changed files with 872 additions and 13 deletions
322
Cargo.lock
generated
322
Cargo.lock
generated
|
|
@ -58,12 +58,24 @@ version = "1.5.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
|
|
@ -130,18 +142,30 @@ name = "codex-connector"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64",
|
||||
"cultcache-rs",
|
||||
"rand_core",
|
||||
"anyhow",
|
||||
"base64 0.23.1",
|
||||
"chrono",
|
||||
"cultcache-rs 0.1.0 (git+https://github.com/GameCult/cultcache-rs.git?rev=fdbf3bf0a4b3f6b617c224d7439feb8e69d4474c)",
|
||||
"cultnet-rs",
|
||||
"ed25519-dalek",
|
||||
"rand_core 0.6.4",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"ureq",
|
||||
"uuid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
|
|
@ -164,7 +188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
|
|
@ -177,6 +201,23 @@ dependencies = [
|
|||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cultcache-rs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/GameCult/CultLib.git?rev=75c180782aeba7cfd22d6412877397708a4ed28f#75c180782aeba7cfd22d6412877397708a4ed28f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"cultcache-rs-derive 0.1.0 (git+https://github.com/GameCult/CultLib.git?rev=75c180782aeba7cfd22d6412877397708a4ed28f)",
|
||||
"fs2",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cultcache-rs"
|
||||
version = "0.1.0"
|
||||
|
|
@ -184,7 +225,7 @@ source = "git+https://github.com/GameCult/cultcache-rs.git?rev=fdbf3bf0a4b3f6b61
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"cultcache-rs-derive",
|
||||
"cultcache-rs-derive 0.1.0 (git+https://github.com/GameCult/cultcache-rs.git?rev=fdbf3bf0a4b3f6b617c224d7439feb8e69d4474c)",
|
||||
"fs2",
|
||||
"redb",
|
||||
"rmp-serde",
|
||||
|
|
@ -195,6 +236,16 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cultcache-rs-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/GameCult/CultLib.git?rev=75c180782aeba7cfd22d6412877397708a4ed28f#75c180782aeba7cfd22d6412877397708a4ed28f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cultcache-rs-derive"
|
||||
version = "0.1.0"
|
||||
|
|
@ -205,6 +256,65 @@ dependencies = [
|
|||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cultnet-rs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/GameCult/CultLib.git?rev=75c180782aeba7cfd22d6412877397708a4ed28f#75c180782aeba7cfd22d6412877397708a4ed28f"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"cultcache-rs 0.1.0 (git+https://github.com/GameCult/CultLib.git?rev=75c180782aeba7cfd22d6412877397708a4ed28f)",
|
||||
"hmac",
|
||||
"rand",
|
||||
"rmp-serde",
|
||||
"rmpv",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"socket2",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"digest",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
|
@ -213,8 +323,39 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||
dependencies = [
|
||||
"pkcs8",
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"serde",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.11"
|
||||
|
|
@ -276,6 +417,18 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.3"
|
||||
|
|
@ -284,7 +437,7 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"r-efi 6.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -297,6 +450,15 @@ dependencies = [
|
|||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.5.0"
|
||||
|
|
@ -414,6 +576,16 @@ version = "0.2.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
|
|
@ -426,6 +598,15 @@ dependencies = [
|
|||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
|
|
@ -444,12 +625,38 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
|
|
@ -459,6 +666,15 @@ dependencies = [
|
|||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[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 = "redb"
|
||||
version = "4.2.0"
|
||||
|
|
@ -501,6 +717,26 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmpv"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a4e1d4b9b938a26d2996af33229f0ca0956c652c1375067f0b45291c1df8417"
|
||||
dependencies = [
|
||||
"rmp",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.43"
|
||||
|
|
@ -542,6 +778,12 @@ version = "1.0.23"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
|
|
@ -612,12 +854,41 @@ version = "2.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
|
@ -700,7 +971,7 @@ version = "3.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.23.1",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
|
|
@ -716,7 +987,7 @@ version = "0.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.23.1",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
|
|
@ -751,6 +1022,15 @@ version = "0.11.1+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.4+wasi-0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.127"
|
||||
|
|
@ -968,6 +1248,32 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.9.0"
|
||||
|
|
|
|||
17
Cargo.toml
17
Cargo.toml
|
|
@ -18,17 +18,32 @@ required-features = ["daemon"]
|
|||
|
||||
[features]
|
||||
default = []
|
||||
daemon = ["dep:cultcache-rs", "dep:ureq"]
|
||||
daemon = [
|
||||
"dep:anyhow",
|
||||
"dep:chrono",
|
||||
"dep:cultcache-rs",
|
||||
"dep:cultnet-rs",
|
||||
"dep:ed25519-dalek",
|
||||
"dep:serde_bytes",
|
||||
"dep:ureq",
|
||||
"dep:uuid",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
aes-gcm = "0.10"
|
||||
anyhow = { version = "1", optional = true }
|
||||
base64 = "0.23"
|
||||
chrono = { version = "0.4", features = ["serde"], optional = true }
|
||||
cultcache-rs = { git = "https://github.com/GameCult/cultcache-rs.git", rev = "fdbf3bf0a4b3f6b617c224d7439feb8e69d4474c", optional = true }
|
||||
cultnet-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "75c180782aeba7cfd22d6412877397708a4ed28f", optional = true }
|
||||
ed25519-dalek = { version = "2.2", optional = true }
|
||||
rand_core = { version = "0.6", features = ["getrandom"] }
|
||||
rmp-serde = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_bytes = { version = "0.11", optional = true }
|
||||
serde_json = "1"
|
||||
sha2 = "0.10"
|
||||
thiserror = "2"
|
||||
ureq = { version = "3.4", default-features = false, features = ["rustls"], optional = true }
|
||||
uuid = { version = "1", features = ["v4"], optional = true }
|
||||
zeroize = "1"
|
||||
|
|
|
|||
|
|
@ -55,9 +55,10 @@ cargo check --bin codex-connector --features daemon
|
|||
|
||||
The same production binary can initialize a typed single-caller `.cc`
|
||||
configuration for first deployment, then serve only from that admitted state.
|
||||
Redacted CultMesh/Odin publication and the independent Idunn target remain
|
||||
open. The old daemon remains the sole live credential writer until that
|
||||
deployment transaction lands.
|
||||
The daemon publishes private-free signed health to Idunn from its own runtime
|
||||
identity. Its source revision also owns the exact Codex Linux package and
|
||||
binary hashes in `deployment/codex-linux-x64.manifest`, so changing the provider
|
||||
toolchain necessarily changes the connector release selected by Idunn.
|
||||
|
||||
## Contract invariant
|
||||
|
||||
|
|
|
|||
4
deployment/codex-linux-x64.manifest
Normal file
4
deployment/codex-linux-x64.manifest
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
schema_version=gamecult.codex_connector.codex_toolchain.v1
|
||||
package_url=https://registry.npmjs.org/@openai/codex/-/codex-0.150.0-alpha.7-linux-x64.tgz
|
||||
package_sha256=8aca3112aef60127d4ffaf4c6f08e524fa7315279a1d08e7aa9c5e7f1a6943e0
|
||||
binary_sha256=aca268dc02dcef8b1ea9f528a10173d5071ae76d659a03cce6458bbebe228bee
|
||||
|
|
@ -190,6 +190,8 @@ pub fn serve(config_path: &Path) -> Result<(), CodexDaemonError> {
|
|||
config.callers.len(),
|
||||
readiness.auth_mode
|
||||
);
|
||||
#[cfg(target_os = "linux")]
|
||||
start_idunn_health_publisher()?;
|
||||
|
||||
for accepted in listener.incoming() {
|
||||
let stream = match accepted {
|
||||
|
|
@ -224,6 +226,52 @@ pub fn serve(config_path: &Path) -> Result<(), CodexDaemonError> {
|
|||
Err(CodexDaemonError::ListenerClosed)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn start_idunn_health_publisher() -> Result<(), CodexDaemonError> {
|
||||
let Some(endpoint) = std::env::var_os("CODEX_CONNECTOR_IDUNN_RUDP") else {
|
||||
return Ok(());
|
||||
};
|
||||
let endpoint = endpoint
|
||||
.to_string_lossy()
|
||||
.parse::<SocketAddr>()
|
||||
.map_err(|_| CodexDaemonError::InvalidConfig("Idunn health endpoint"))?;
|
||||
let daemon_id = required_environment("CODEX_CONNECTOR_IDUNN_DAEMON")?;
|
||||
let runtime_id = required_environment("CODEX_CONNECTOR_RUNTIME_ID")?;
|
||||
let contract = std::env::var("CODEX_CONNECTOR_IDUNN_HEALTH_CONTRACT")
|
||||
.unwrap_or_else(|_| crate::CODEX_CONNECTOR_IDUNN_HEALTH_CONTRACT.to_string());
|
||||
let identity = PathBuf::from(required_environment(
|
||||
"CODEX_CONNECTOR_IDUNN_HEALTH_IDENTITY",
|
||||
)?);
|
||||
let mut publisher =
|
||||
crate::ProviderHealthPublisher::open(endpoint, daemon_id, runtime_id, contract, &identity)
|
||||
.map_err(CodexDaemonError::Health)?;
|
||||
publisher
|
||||
.publish("active", "credential-isolated-transport-ready")
|
||||
.map_err(CodexDaemonError::Health)?;
|
||||
thread::Builder::new()
|
||||
.name("codex-connector-health".to_string())
|
||||
.spawn(move || {
|
||||
loop {
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
if let Err(error) =
|
||||
publisher.publish("active", "credential-isolated-transport-ready")
|
||||
{
|
||||
eprintln!("codex-connector health publication failed: {error}");
|
||||
}
|
||||
}
|
||||
})
|
||||
.map_err(CodexDaemonError::HealthThread)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn required_environment(name: &'static str) -> Result<String, CodexDaemonError> {
|
||||
std::env::var(name)
|
||||
.ok()
|
||||
.filter(|value| !value.trim().is_empty() && value == value.trim())
|
||||
.ok_or(CodexDaemonError::InvalidConfig(name))
|
||||
}
|
||||
|
||||
fn load_admissions(
|
||||
config: &CodexDaemonConfig,
|
||||
) -> Result<Vec<CodexCallerAdmission>, CodexDaemonError> {
|
||||
|
|
@ -349,6 +397,12 @@ pub enum CodexDaemonError {
|
|||
Listen(#[source] std::io::Error),
|
||||
#[error("connector request thread failed")]
|
||||
Thread(#[source] std::io::Error),
|
||||
#[cfg(target_os = "linux")]
|
||||
#[error("connector health publication could not start")]
|
||||
Health(#[source] anyhow::Error),
|
||||
#[cfg(target_os = "linux")]
|
||||
#[error("connector health thread could not start")]
|
||||
HealthThread(#[source] std::io::Error),
|
||||
#[error("connector connection failed")]
|
||||
Connection(#[source] std::io::Error),
|
||||
#[error("connector frame exceeded its bound")]
|
||||
|
|
|
|||
449
src/idunn_health.rs
Normal file
449
src/idunn_health.rs
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use cultcache_rs::{CacheBackingStore, CultCacheEnvelope, SingleFileMessagePackBackingStore};
|
||||
use cultnet_rs::{
|
||||
CultNetMessage, CultNetRawDocumentRecord, CultNetRawPayloadEncoding,
|
||||
CultNetRudpSocketTransportConnection, CultNetRudpSocketTransportOptions, CultNetWireContract,
|
||||
encode_cultnet_message_to_vec,
|
||||
};
|
||||
use ed25519_dalek::{Signer, SigningKey};
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub const CODEX_CONNECTOR_IDUNN_HEALTH_CONTRACT: &str =
|
||||
"codex-connector.cultnet-rudp-service-health";
|
||||
const HEALTH_SCHEMA: &str = "idunn.signed_daemon_health.v1";
|
||||
const IDENTITY_SCHEMA: &str = "gamecult.provider_health_identity.private.v1";
|
||||
const IDENTITY_TYPE: &str = "gamecult.provider_health_identity.private.v1";
|
||||
const IDENTITY_KEY: &str = "gamecult-provider-health-identity";
|
||||
const ID_DOMAIN: &[u8] = b"gamecult.provider-health.identity.v1\0";
|
||||
const SIGNATURE_DOMAIN: &[u8] = b"gamecult.provider-health.signature.v1\0";
|
||||
const SIGNATURE_PURPOSE: &[u8] = b"idunn.signed_daemon_health.v1";
|
||||
const PROTECTOR_CONTEXT: &str = "gamecult-provider-health-identity-v1";
|
||||
const RUDP_PROTOCOL_ID: &str = "cultnet.transport.rudp.v0";
|
||||
const RUDP_CONNECTION_ID: u32 = 0x1d0d_0004;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
struct ProviderHealthIdentity {
|
||||
schema_version: String,
|
||||
identity_id: String,
|
||||
#[serde(with = "serde_bytes")]
|
||||
public_key: Vec<u8>,
|
||||
#[serde(with = "serde_bytes")]
|
||||
protected_private_seed: Vec<u8>,
|
||||
protector_kind: String,
|
||||
protector_binding: String,
|
||||
protector_version: String,
|
||||
assurance: String,
|
||||
created_at: String,
|
||||
#[serde(with = "serde_bytes")]
|
||||
enrollment_nonce: Vec<u8>,
|
||||
}
|
||||
|
||||
struct ProviderHealthSigner {
|
||||
entry: ProviderHealthIdentity,
|
||||
key: SigningKey,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct SignedDaemonHealth {
|
||||
schema_version: String,
|
||||
daemon_id: String,
|
||||
health_contract: String,
|
||||
source_runtime_id: String,
|
||||
state: String,
|
||||
detail: String,
|
||||
signer_identity_id: String,
|
||||
publisher_incarnation_id: String,
|
||||
publisher_sequence: u64,
|
||||
observed_at_unix_millis: u64,
|
||||
release_id: Option<String>,
|
||||
release_witness_sha256: Option<String>,
|
||||
source_commit: Option<String>,
|
||||
deployment_id: Option<String>,
|
||||
signature_algorithm: String,
|
||||
#[serde(with = "serde_bytes")]
|
||||
signature: Vec<u8>,
|
||||
private_state_exposed: bool,
|
||||
}
|
||||
|
||||
impl SignedDaemonHealth {
|
||||
fn validate(&self) -> Result<()> {
|
||||
for (value, label) in [
|
||||
(&self.daemon_id, "daemon id"),
|
||||
(&self.health_contract, "health contract"),
|
||||
(&self.source_runtime_id, "runtime id"),
|
||||
(&self.signer_identity_id, "signer identity"),
|
||||
(&self.publisher_incarnation_id, "publisher incarnation"),
|
||||
] {
|
||||
require_id(value, label)?;
|
||||
}
|
||||
if self.schema_version != HEALTH_SCHEMA
|
||||
|| !matches!(
|
||||
self.state.as_str(),
|
||||
"active" | "warming" | "degraded" | "failed"
|
||||
)
|
||||
|| self.detail.len() > 512
|
||||
|| self.detail.chars().any(char::is_control)
|
||||
|| self.publisher_sequence == 0
|
||||
|| self.observed_at_unix_millis == 0
|
||||
|| self.release_id.is_some()
|
||||
|| self.release_witness_sha256.is_some()
|
||||
|| self.source_commit.is_some()
|
||||
|| self.deployment_id.is_some()
|
||||
|| self.signature_algorithm != "ed25519"
|
||||
|| self.signature.len() != 64
|
||||
|| self.private_state_exposed
|
||||
{
|
||||
bail!("signed daemon health shape is invalid");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProviderHealthPublisher {
|
||||
endpoint: SocketAddr,
|
||||
daemon_id: String,
|
||||
runtime_id: String,
|
||||
contract: String,
|
||||
signer: ProviderHealthSigner,
|
||||
incarnation: String,
|
||||
sequence: u64,
|
||||
}
|
||||
|
||||
impl ProviderHealthPublisher {
|
||||
pub fn open(
|
||||
endpoint: SocketAddr,
|
||||
daemon_id: impl Into<String>,
|
||||
runtime_id: impl Into<String>,
|
||||
contract: impl Into<String>,
|
||||
identity_store: &Path,
|
||||
) -> Result<Self> {
|
||||
let daemon_id = daemon_id.into();
|
||||
let runtime_id = runtime_id.into();
|
||||
let contract = contract.into();
|
||||
require_id(&daemon_id, "daemon id")?;
|
||||
require_id(&runtime_id, "runtime id")?;
|
||||
require_id(&contract, "health contract")?;
|
||||
Ok(Self {
|
||||
endpoint,
|
||||
daemon_id,
|
||||
runtime_id,
|
||||
contract,
|
||||
signer: open_identity(identity_store)?,
|
||||
incarnation: uuid::Uuid::new_v4().to_string(),
|
||||
sequence: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn publish(&mut self, state: &str, detail: &str) -> Result<()> {
|
||||
self.sequence = self
|
||||
.sequence
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| anyhow!("health publisher sequence overflow"))?;
|
||||
let mut record = SignedDaemonHealth {
|
||||
schema_version: HEALTH_SCHEMA.into(),
|
||||
daemon_id: self.daemon_id.clone(),
|
||||
health_contract: self.contract.clone(),
|
||||
source_runtime_id: self.runtime_id.clone(),
|
||||
state: state.into(),
|
||||
detail: detail.into(),
|
||||
signer_identity_id: self.signer.entry.identity_id.clone(),
|
||||
publisher_incarnation_id: self.incarnation.clone(),
|
||||
publisher_sequence: self.sequence,
|
||||
observed_at_unix_millis: chrono::Utc::now().timestamp_millis().try_into()?,
|
||||
release_id: None,
|
||||
release_witness_sha256: None,
|
||||
source_commit: None,
|
||||
deployment_id: None,
|
||||
signature_algorithm: "ed25519".into(),
|
||||
signature: Vec::new(),
|
||||
private_state_exposed: false,
|
||||
};
|
||||
let unsigned = unsigned_record(&record)?;
|
||||
record.signature = self
|
||||
.signer
|
||||
.key
|
||||
.sign(&signing_message(&unsigned))
|
||||
.to_bytes()
|
||||
.to_vec();
|
||||
record.validate()?;
|
||||
publish(self.endpoint, &self.runtime_id, &record)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enroll_provider_health_identity(path: &Path) -> Result<String> {
|
||||
if path.exists() {
|
||||
bail!("provider health identity already exists");
|
||||
}
|
||||
let parent = path
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("provider health identity path has no parent"))?;
|
||||
std::fs::create_dir_all(parent)?;
|
||||
std::fs::set_permissions(parent, std::fs::Permissions::from_mode(0o700))?;
|
||||
let mut seed = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut seed);
|
||||
let key = SigningKey::from_bytes(&seed);
|
||||
let public_key = key.verifying_key().to_bytes();
|
||||
let mut nonce = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
let binding = machine_binding()?;
|
||||
let entry = ProviderHealthIdentity {
|
||||
schema_version: IDENTITY_SCHEMA.into(),
|
||||
identity_id: identity_id(&public_key),
|
||||
public_key: public_key.to_vec(),
|
||||
protected_private_seed: mask_seed(&seed, &binding),
|
||||
protector_kind: "linux_file_mode_machine_id_binding".into(),
|
||||
protector_binding: binding,
|
||||
protector_version: "v1".into(),
|
||||
assurance: "os_installation_file_bound_cloneable_baseline".into(),
|
||||
created_at: chrono::Utc::now().to_rfc3339(),
|
||||
enrollment_nonce: nonce.to_vec(),
|
||||
};
|
||||
validate_identity(&entry)?;
|
||||
let envelope = CultCacheEnvelope {
|
||||
key: IDENTITY_KEY.into(),
|
||||
r#type: IDENTITY_TYPE.into(),
|
||||
payload: rmp_serde::to_vec(&entry)?,
|
||||
stored_at: entry.created_at.clone(),
|
||||
schema_id: Some(IDENTITY_SCHEMA.into()),
|
||||
};
|
||||
let bytes = rmp_serde::to_vec(&vec![envelope])?;
|
||||
let mut file = OpenOptions::new().create_new(true).write(true).open(path)?;
|
||||
if let Err(error) = file.write_all(&bytes).and_then(|()| file.sync_all()) {
|
||||
drop(file);
|
||||
let _ = std::fs::remove_file(path);
|
||||
return Err(error.into());
|
||||
}
|
||||
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
|
||||
provider_health_public_key_hex(path)
|
||||
}
|
||||
|
||||
pub fn provider_health_public_key_hex(path: &Path) -> Result<String> {
|
||||
Ok(hex(&open_identity(path)?.entry.public_key))
|
||||
}
|
||||
|
||||
fn open_identity(path: &Path) -> Result<ProviderHealthSigner> {
|
||||
let entries = SingleFileMessagePackBackingStore::new(path).pull_all()?;
|
||||
let [envelope] = entries.as_slice() else {
|
||||
bail!("provider health identity store must contain exactly one record");
|
||||
};
|
||||
if envelope.key != IDENTITY_KEY
|
||||
|| envelope.r#type != IDENTITY_TYPE
|
||||
|| envelope.schema_id.as_deref() != Some(IDENTITY_SCHEMA)
|
||||
{
|
||||
bail!("provider health identity store has the wrong type");
|
||||
}
|
||||
let entry: ProviderHealthIdentity = rmp_serde::from_slice(&envelope.payload)?;
|
||||
validate_identity(&entry)?;
|
||||
let binding = machine_binding()?;
|
||||
if entry.protector_binding != binding {
|
||||
bail!("provider health identity belongs to another machine");
|
||||
}
|
||||
let seed: [u8; 32] = mask_seed(&entry.protected_private_seed, &binding)
|
||||
.try_into()
|
||||
.map_err(|_| anyhow!("provider health identity seed has invalid length"))?;
|
||||
let key = SigningKey::from_bytes(&seed);
|
||||
if key.verifying_key().to_bytes().as_slice() != entry.public_key.as_slice() {
|
||||
bail!("provider health identity seed does not match its public key");
|
||||
}
|
||||
Ok(ProviderHealthSigner { entry, key })
|
||||
}
|
||||
|
||||
fn validate_identity(entry: &ProviderHealthIdentity) -> Result<()> {
|
||||
if entry.schema_version != IDENTITY_SCHEMA
|
||||
|| entry.public_key.len() != 32
|
||||
|| entry.protected_private_seed.len() != 32
|
||||
|| entry.enrollment_nonce.len() != 32
|
||||
|| entry.protector_kind != "linux_file_mode_machine_id_binding"
|
||||
|| entry.protector_version != "v1"
|
||||
|| entry.identity_id != identity_id(&entry.public_key)
|
||||
{
|
||||
bail!("provider health identity is invalid");
|
||||
}
|
||||
chrono::DateTime::parse_from_rfc3339(&entry.created_at)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn machine_binding() -> Result<String> {
|
||||
let raw = std::fs::read_to_string("/etc/machine-id")
|
||||
.or_else(|_| std::fs::read_to_string("/var/lib/dbus/machine-id"))?;
|
||||
let id = raw.trim();
|
||||
if id.is_empty() {
|
||||
bail!("Linux machine-id is empty");
|
||||
}
|
||||
Ok(format!(
|
||||
"{PROTECTOR_CONTEXT}:machine-id-sha256:{}",
|
||||
hex(&Sha256::digest(id.as_bytes()))
|
||||
))
|
||||
}
|
||||
|
||||
fn mask_seed(seed: &[u8], binding: &str) -> Vec<u8> {
|
||||
let mask = Sha256::digest(
|
||||
[
|
||||
b"gamecult-linux-service-seed-v1\0".as_slice(),
|
||||
PROTECTOR_CONTEXT.as_bytes(),
|
||||
binding.as_bytes(),
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
seed.iter()
|
||||
.zip(mask)
|
||||
.map(|(left, right)| left ^ right)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn identity_id(public_key: &[u8]) -> String {
|
||||
hex(&Sha256::digest([ID_DOMAIN, public_key].concat()))
|
||||
}
|
||||
|
||||
fn unsigned_record(record: &SignedDaemonHealth) -> Result<Vec<u8>> {
|
||||
let mut unsigned = record.clone();
|
||||
unsigned.signature.clear();
|
||||
uuid::Uuid::parse_str(&unsigned.publisher_incarnation_id)?;
|
||||
Ok(rmp_serde::to_vec(&unsigned)?)
|
||||
}
|
||||
|
||||
fn signing_message(payload: &[u8]) -> Vec<u8> {
|
||||
let mut out =
|
||||
Vec::with_capacity(SIGNATURE_DOMAIN.len() + SIGNATURE_PURPOSE.len() + payload.len() + 16);
|
||||
out.extend_from_slice(SIGNATURE_DOMAIN);
|
||||
out.extend_from_slice(&(SIGNATURE_PURPOSE.len() as u64).to_be_bytes());
|
||||
out.extend_from_slice(SIGNATURE_PURPOSE);
|
||||
out.extend_from_slice(&(payload.len() as u64).to_be_bytes());
|
||||
out.extend_from_slice(payload);
|
||||
out
|
||||
}
|
||||
|
||||
fn publish(endpoint: SocketAddr, runtime_id: &str, record: &SignedDaemonHealth) -> Result<()> {
|
||||
let payload = rmp_serde::to_vec(record)?;
|
||||
if rmp_serde::from_slice::<SignedDaemonHealth>(&payload)? != *record {
|
||||
bail!("signed health MessagePack did not round trip");
|
||||
}
|
||||
let message = CultNetMessage::DocumentPutRaw {
|
||||
message_id: format!(
|
||||
"codex-connector-health:{}:{}:{}",
|
||||
record.daemon_id, record.publisher_incarnation_id, record.publisher_sequence
|
||||
),
|
||||
document: CultNetRawDocumentRecord {
|
||||
schema_id: HEALTH_SCHEMA.into(),
|
||||
record_key: record.daemon_id.clone(),
|
||||
stored_at: chrono::DateTime::from_timestamp_millis(
|
||||
record.observed_at_unix_millis.try_into()?,
|
||||
)
|
||||
.context("health observation time is invalid")?
|
||||
.to_rfc3339(),
|
||||
payload_encoding: CultNetRawPayloadEncoding::Messagepack,
|
||||
payload,
|
||||
source_runtime_id: Some(runtime_id.into()),
|
||||
source_agent_id: Some(record.signer_identity_id.clone()),
|
||||
source_role: Some("daemon-health-publisher".into()),
|
||||
tags: Some(vec![RUDP_PROTOCOL_ID.into()]),
|
||||
},
|
||||
};
|
||||
let socket = UdpSocket::bind(if endpoint.is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
})?;
|
||||
socket.set_read_timeout(Some(Duration::from_millis(100)))?;
|
||||
let mut transport = CultNetRudpSocketTransportConnection::new(
|
||||
CultNetRudpSocketTransportOptions::client(runtime_id, socket, endpoint, RUDP_CONNECTION_ID),
|
||||
)?;
|
||||
transport.connect(Vec::new())?;
|
||||
let deadline = Instant::now() + Duration::from_millis(500);
|
||||
while !transport.connected() {
|
||||
let _ = transport.receive_once()?;
|
||||
transport.poll_resends()?;
|
||||
if Instant::now() >= deadline {
|
||||
bail!("timed out connecting provider health to {endpoint}");
|
||||
}
|
||||
}
|
||||
transport.send(
|
||||
"schema",
|
||||
encode_cultnet_message_to_vec(&message, CultNetWireContract::CultNetSchemaV0)?,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn require_id(value: &str, label: &str) -> Result<()> {
|
||||
if value.is_empty()
|
||||
|| value != value.trim()
|
||||
|| value.len() > 256
|
||||
|| value.chars().any(char::is_control)
|
||||
{
|
||||
bail!("{label} is invalid");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn hex(bytes: &[u8]) -> String {
|
||||
const DIGITS: &[u8; 16] = b"0123456789abcdef";
|
||||
let mut output = String::with_capacity(bytes.len() * 2);
|
||||
for byte in bytes {
|
||||
output.push(DIGITS[(byte >> 4) as usize] as char);
|
||||
output.push(DIGITS[(byte & 15) as usize] as char);
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ed25519_dalek::{Signature, Verifier, VerifyingKey};
|
||||
|
||||
#[test]
|
||||
fn identity_round_trips_and_signed_health_is_private_free() {
|
||||
let root = std::env::temp_dir().join(format!(
|
||||
"codex-connector-health-{}-{}",
|
||||
std::process::id(),
|
||||
uuid::Uuid::new_v4()
|
||||
));
|
||||
std::fs::create_dir_all(&root).unwrap();
|
||||
let path = root.join("identity.cc");
|
||||
let public_key = enroll_provider_health_identity(&path).unwrap();
|
||||
assert_eq!(provider_health_public_key_hex(&path).unwrap(), public_key);
|
||||
let signer = open_identity(&path).unwrap();
|
||||
let mut record = SignedDaemonHealth {
|
||||
schema_version: HEALTH_SCHEMA.into(),
|
||||
daemon_id: "yggdrasil-codex-connector".into(),
|
||||
health_contract: CODEX_CONNECTOR_IDUNN_HEALTH_CONTRACT.into(),
|
||||
source_runtime_id: "codex-connector-yggdrasil".into(),
|
||||
state: "active".into(),
|
||||
detail: "credential-isolated-transport-ready".into(),
|
||||
signer_identity_id: signer.entry.identity_id.clone(),
|
||||
publisher_incarnation_id: uuid::Uuid::new_v4().to_string(),
|
||||
publisher_sequence: 1,
|
||||
observed_at_unix_millis: 1_787_315_696_789,
|
||||
release_id: None,
|
||||
release_witness_sha256: None,
|
||||
source_commit: None,
|
||||
deployment_id: None,
|
||||
signature_algorithm: "ed25519".into(),
|
||||
signature: Vec::new(),
|
||||
private_state_exposed: false,
|
||||
};
|
||||
let unsigned = unsigned_record(&record).unwrap();
|
||||
record.signature = signer
|
||||
.key
|
||||
.sign(&signing_message(&unsigned))
|
||||
.to_bytes()
|
||||
.to_vec();
|
||||
record.validate().unwrap();
|
||||
let public: [u8; 32] = signer.entry.public_key.try_into().unwrap();
|
||||
let signature = Signature::from_slice(&record.signature).unwrap();
|
||||
VerifyingKey::from_bytes(&public)
|
||||
.unwrap()
|
||||
.verify(&signing_message(&unsigned), &signature)
|
||||
.unwrap();
|
||||
assert!(!record.private_state_exposed);
|
||||
std::fs::remove_dir_all(root).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,8 @@ use zeroize::{Zeroize, Zeroizing};
|
|||
|
||||
#[cfg(feature = "daemon")]
|
||||
mod daemon;
|
||||
#[cfg(all(feature = "daemon", target_os = "linux"))]
|
||||
mod idunn_health;
|
||||
#[cfg(feature = "daemon")]
|
||||
mod provider_backend;
|
||||
|
||||
|
|
@ -35,6 +37,11 @@ pub use daemon::load_daemon_config;
|
|||
pub use daemon::serve;
|
||||
#[cfg(feature = "daemon")]
|
||||
pub use daemon::write_daemon_config;
|
||||
#[cfg(all(feature = "daemon", target_os = "linux"))]
|
||||
pub use idunn_health::{
|
||||
CODEX_CONNECTOR_IDUNN_HEALTH_CONTRACT, ProviderHealthPublisher,
|
||||
enroll_provider_health_identity, provider_health_public_key_hex,
|
||||
};
|
||||
#[cfg(feature = "daemon")]
|
||||
pub use provider_backend::CodexAppServerConfig;
|
||||
#[cfg(feature = "daemon")]
|
||||
|
|
|
|||
25
src/main.rs
25
src/main.rs
|
|
@ -3,10 +3,33 @@ use std::path::PathBuf;
|
|||
|
||||
use codex_connector::{CodexCallerConfig, CodexDaemonConfig};
|
||||
|
||||
const USAGE: &str = "usage:\n codex-connector --config PATH.cc\n codex-connector --initialize-single-caller-config PATH.cc BIND CODEX_EXECUTABLE CODEX_SHA256 CODEX_HOME REPLAY_STORE CALLER_RUNTIME_ID CONNECTION_KEY_FILE CONNECTION_KEY_EPOCH ALLOWED_MODEL MAX_CONCURRENT_REQUESTS MAX_PAYLOAD_BYTES MAX_OUTPUT_TOKENS";
|
||||
const USAGE: &str = "usage:\n codex-connector --config PATH.cc\n codex-connector --initialize-single-caller-config PATH.cc BIND CODEX_EXECUTABLE CODEX_SHA256 CODEX_HOME REPLAY_STORE CALLER_RUNTIME_ID CONNECTION_KEY_FILE CONNECTION_KEY_EPOCH ALLOWED_MODEL MAX_CONCURRENT_REQUESTS MAX_PAYLOAD_BYTES MAX_OUTPUT_TOKENS\n codex-connector --enroll-provider-health-identity PATH.cc\n codex-connector --provider-health-public-key PATH.cc";
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args_os().skip(1).collect::<Vec<_>>();
|
||||
#[cfg(target_os = "linux")]
|
||||
if let [flag, path] = args.as_slice() {
|
||||
if flag == "--enroll-provider-health-identity" {
|
||||
match codex_connector::enroll_provider_health_identity(&PathBuf::from(path)) {
|
||||
Ok(public_key) => println!("{public_key}"),
|
||||
Err(error) => {
|
||||
eprintln!("codex-connector health identity enrollment failed: {error}");
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if flag == "--provider-health-public-key" {
|
||||
match codex_connector::provider_health_public_key_hex(&PathBuf::from(path)) {
|
||||
Ok(public_key) => println!("{public_key}"),
|
||||
Err(error) => {
|
||||
eprintln!("codex-connector health identity read failed: {error}");
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if args
|
||||
.first()
|
||||
.is_some_and(|value| value == "--initialize-single-caller-config")
|
||||
|
|
|
|||
Loading…
Reference in a new issue