rhi

Coordinated trade for connected markets
git clone https://radroots.dev/git/rhi.git
Log | Files | Refs | README | LICENSE

commit 2a8e39f82b9f67561d0d917304440ab79a2ea194
parent 05408359e5135a8b7672a4fb7d67f78df9959107
Author: triesap <tyson@radroots.org>
Date:   Thu,  4 Jun 2026 15:14:34 -0700

config: harden rhi runtime config

- move RHI TOML to canonical logging, relays, nostr, subscriber, and receipt groups

- reject old config roots and non-RHI bearer token env references

- route startup logging through explicit config instead of ambient LOG/RUST env

- serialize the RHI test app so global hook tests complete deterministically

- verify nix run .#fmt, nix run .#check, nix run .#test, focused subscriber tests, and git diff --check

Diffstat:
MCargo.lock | 269++++++++++++++++++++++++++++++++++++++++---------------------------------------
MCargo.toml | 2++
Mconfig.toml | 50++++++++++++++++----------------------------------
Mflake.nix | 2+-
Msrc/config.rs | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/features/trade_listing/subscriber.rs | 37+++++++++++++++++--------------------
Msrc/features/trade_validation_receipt.rs | 40++++++++++++++++++++++++++++++++--------
Msrc/lib.rs | 5+++++
Msrc/main.rs | 34++++++++++++++++++++++++++--------
Msrc/paths.rs | 3+--
10 files changed, 465 insertions(+), 248 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2566,9 +2566,9 @@ dependencies = [ [[package]] name = "p3-air" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a16a8d78c6a37d0eb66b008a18a9e8caa38c3a6a9ca9036416d509faf3dbc86" +checksum = "d3a5de20a2301bf2530de1ceb13768ec3a80f729fbf8b72f813e30bc54c5bce2" dependencies = [ "p3-field", "p3-matrix", @@ -2577,9 +2577,9 @@ dependencies = [ [[package]] name = "p3-baby-bear" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80b9c0a27092644dc22fd8fd6768dab62d325c6f7d121cf896e6bb3789779cf" +checksum = "d69e6e9af4eaaaa60f7bb9f0e0f73ebcbaefe7e00974d97ad0fa542d6a4f0890" dependencies = [ "cfg-if", "num-bigint 0.4.6", @@ -2594,9 +2594,9 @@ dependencies = [ [[package]] name = "p3-bn254-fr" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577200e3fa7e49e2b21e940a6dc7399dc63acb8581da088558cdf7c455adafc0" +checksum = "2077757c7cb514202ccb5368f521f23f5709c720599e6545c683c66e0a52d2d8" dependencies = [ "ff", "num-bigint 0.4.6", @@ -2609,9 +2609,9 @@ dependencies = [ [[package]] name = "p3-challenger" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75358edd6e2562752c01f5064a66d88144a3e75ace0407166dbdf8a727597f52" +checksum = "b6a908924d43e4cfb93fb41c8346cac211b70314385a9037e9241f5b7f3eaf77" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -2623,9 +2623,9 @@ dependencies = [ [[package]] name = "p3-commit" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0991de9c2f2f8c6a6667eaebe2a5495a2132f9709ffa93357dc18865d154f16" +checksum = "50acacc7219fce6c01db938f82c1b21b5e7133990b7fff861f91534aeb569419" dependencies = [ "itertools 0.12.1", "p3-challenger", @@ -2637,9 +2637,9 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761f1e1b014f2b1b69bd0309124e233d64aa3590e6a41ee786000dd849506d51" +checksum = "be6408b10a2c27eb13a7d5580c546c2179a8dc7dbc10a990657311891f9b41c0" dependencies = [ "p3-field", "p3-matrix", @@ -2650,9 +2650,9 @@ dependencies = [ [[package]] name = "p3-field" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df7cebaa4079b24e0dd7e3aad59eebcbb99a67c1271f79ad884a7c032f5f183" +checksum = "3dc75969ca3ac847f43e632ab979d59ff7a68f9eac8dbf8edcbba47fc2e1d3aa" dependencies = [ "itertools 0.12.1", "num-bigint 0.4.6", @@ -2664,9 +2664,9 @@ dependencies = [ [[package]] name = "p3-fri" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ef10c7f829294e16a6248200e9571908177c0b5f35bdd70748ac3239a02d29" +checksum = "5cbc4965ee488f3247867b7ec4bb005b8afa72cb0d461a4dcb1387ecab6426d5" dependencies = [ "itertools 0.12.1", "p3-challenger", @@ -2683,9 +2683,9 @@ dependencies = [ [[package]] name = "p3-interpolation" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413812d3ada8aa10ece23fc68d47d0c23eed1decbc3844a56f9647c7199796d7" +checksum = "08ad7e9f08c336d7ea39d12e11951188473542565323bac2a6535e536b58487d" dependencies = [ "p3-field", "p3-matrix", @@ -2694,9 +2694,9 @@ dependencies = [ [[package]] name = "p3-keccak-air" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a087526deb74bf12cc4efc1e50d5c387120624b15ea1de1f3efb440efbcd4d" +checksum = "1f5bf177d56740078b5a5a842fa2393427796283dc8174b4a1a325c2d0b042de" dependencies = [ "p3-air", "p3-field", @@ -2708,9 +2708,9 @@ dependencies = [ [[package]] name = "p3-koala-bear" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cea0ba3389b034b6088d566aea8b57aa29dd2e180966e0c8056f61331c92b4e" +checksum = "3a9683cd0ef68100df7c62490533047bcf19c04c4a0fa1efc9d7c1e03e31f6b3" dependencies = [ "cfg-if", "num-bigint 0.4.6", @@ -2725,9 +2725,9 @@ dependencies = [ [[package]] name = "p3-matrix" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae5cc6ce726cc265cc687c1214e3f1ac1f5c6e973442286ba00d1e75da1c3cb" +checksum = "75c3f150ceb90e09539413bf481e618d05ee19210b4e467d2902eb82d2e15281" dependencies = [ "itertools 0.12.1", "p3-field", @@ -2740,18 +2740,18 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac1d2f102cf8c71dba1b449575c99697781fcc028831e83d2245787bd7a650" +checksum = "e0641952b42da45e1dfa2d4a2a3163e330f944ad9740942f35026c0a71a605f1" dependencies = [ "rayon", ] [[package]] name = "p3-mds" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f072643e385d65fb9eb089ee6824b320417f78671a0db748566e057e28b250e" +checksum = "aa4a5f250e174dcfca5cbeac6ad75713924e7e7320e0a335e3c50b8b1f4fe8ec" dependencies = [ "itertools 0.12.1", "p3-dft", @@ -2764,9 +2764,9 @@ dependencies = [ [[package]] name = "p3-merkle-tree" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "946fcfa239847824c9216db8ac731611c7e82171ef51869bc89d985ad46000d0" +checksum = "d5703d9229d52a8c09970e4d722c3a8b4d37e688c306c3a1c03b872efcd204e6" dependencies = [ "itertools 0.12.1", "p3-commit", @@ -2781,9 +2781,9 @@ dependencies = [ [[package]] name = "p3-poseidon2" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00cc4b6e8a439f79541b0910a016da9e6e12a05a24309bbb713e1db0db396952" +checksum = "522986377b2164c5f94f2dae88e0e0a3d169cc6239202ef4aeb4322d60feffd0" dependencies = [ "gcd", "p3-field", @@ -2795,9 +2795,9 @@ dependencies = [ [[package]] name = "p3-symmetric" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eebff7fea7deb08a57ccf731a0ed39df25cc66a0e0c2d92c4472c4dee02ee21" +checksum = "9047ce85c086a9b3f118e10078f10636f7bfeed5da871a04da0b61400af8793a" dependencies = [ "itertools 0.12.1", "p3-field", @@ -2806,9 +2806,9 @@ dependencies = [ [[package]] name = "p3-uni-stark" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e352e1c9765674f618dbd56e33f673a688d1f85332929fcbefa0fc5e5f4373b5" +checksum = "fc3dfdeba14d8db621c4e52dd63973384ff35f353fd750154ff88397f4ea5adf" dependencies = [ "itertools 0.12.1", "p3-air", @@ -2825,9 +2825,9 @@ dependencies = [ [[package]] name = "p3-util" -version = "0.3.3-succinct" +version = "0.4.3-succinct" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8164df89bbc92e29938f916cc5f1ccbfe6a36fb5040f21ba93c1f21985b9868" +checksum = "cff962f8eaa5f36e0447cee7c241f6b4b475fadf3ee61f154327a26bb4e009ba" dependencies = [ "serde", ] @@ -3641,6 +3641,7 @@ dependencies = [ "radroots_events", "radroots_events_codec", "radroots_identity", + "radroots_log", "radroots_nostr", "radroots_runtime", "radroots_runtime_paths", @@ -4129,18 +4130,18 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "slop-air" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0f533af798f4f9095bbb2a04a91f2026acfc5c5d7578581193bcec71e6a8db" +checksum = "15597dcaa23dbf30bdbf9709938f34182736792300bb4623fe3459ebe33e775a" dependencies = [ "p3-air", ] [[package]] name = "slop-algebra" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a473c3a06b466dd0708829415a8a9fab451740da066e07862c8c098904aaad6" +checksum = "6d7e25239ac2fe1cbc2ed5114844209d70361c8d55ef812cb935c90d6f771b5b" dependencies = [ "itertools 0.14.0", "p3-field", @@ -4149,9 +4150,9 @@ dependencies = [ [[package]] name = "slop-alloc" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69234b7c30707f1ca518d469a014bbc10d38b97e17fef5dbfd158a8269255595" +checksum = "aadbdfa7badeb1c7576621d1eeb04f305ed6c93c1accd5da5012ad1ddaea2aa1" dependencies = [ "serde", "slop-algebra", @@ -4160,9 +4161,9 @@ dependencies = [ [[package]] name = "slop-baby-bear" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c830173902ff1d5fcb2fa8f40ef34c7d68685059a99a6b9ef91be4bb252abd" +checksum = "9da8ad7e63b774a2f20704bfc963ebeb4f891e23754a3cb6df47a4a465a85492" dependencies = [ "lazy_static", "p3-baby-bear", @@ -4175,9 +4176,9 @@ dependencies = [ [[package]] name = "slop-basefold" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2dfc41465ee2a8f65afc09da3570997f3c0bf58ae57d559dd7bb05ad5b3f2a0" +checksum = "012eb95fec1133adbf989e6e819943ce3cc2f12b817d063c13885a4b338581ae" dependencies = [ "derive-where", "itertools 0.14.0", @@ -4198,9 +4199,9 @@ dependencies = [ [[package]] name = "slop-basefold-prover" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fe45ae8840223fb6a1bc9cf1d97c91d0017246b168280242d75c6aa4dfb785" +checksum = "c16dfb91c773ba557a735efe3ffbd29bff54b314440d0623ad9756ffc2dfe3fd" dependencies = [ "derive-where", "itertools 0.14.0", @@ -4225,9 +4226,9 @@ dependencies = [ [[package]] name = "slop-bn254" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7fbae5dd16a3d1e87c9e99cfd557338171710be01458bd5b12dded3878d3fd8" +checksum = "1243ca619a3f4c07f536a060d1fd2c6f779d5b0e4cfeee3acee4d1f76ae35cb4" dependencies = [ "ff", "p3-bn254-fr", @@ -4240,9 +4241,9 @@ dependencies = [ [[package]] name = "slop-challenger" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e80df718cef7d3100658dc8b46fafcc994b814421ec9a7d0763a6ee1e5070c" +checksum = "594a3251b7fbbabf7e0ba1e5c2f563c2797fc2d51982c0208ce70b2f52b8fbe4" dependencies = [ "futures", "p3-challenger", @@ -4253,9 +4254,9 @@ dependencies = [ [[package]] name = "slop-commit" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e3b8f111af56f28eb847662fb87fa8caaee53930a13e8ecea9724163259664" +checksum = "ee8fd26ee268213b9c36d208a322f6d43ca46347d82cf07a5b83a6b95701890b" dependencies = [ "p3-commit", "serde", @@ -4264,9 +4265,9 @@ dependencies = [ [[package]] name = "slop-dft" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29b3439e560ad36f22860c1754e2d6b8715a26dd94fd0acd46a8b07be61add7f" +checksum = "f79c2c28e4155679cc2b2ff17a1e258cbffea580182158a8e4d71dc7cd5dc9fe" dependencies = [ "p3-dft", "serde", @@ -4278,18 +4279,18 @@ dependencies = [ [[package]] name = "slop-fri" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361123ccbbd5faa10edb44c6d76b46053e2f539538a159cd952dd4d5b4606c4b" +checksum = "3645dbec4f2ddfc598d0250e880d422ecae2e70fbb2d7f434f0d5c21df011c1e" dependencies = [ "p3-fri", ] [[package]] name = "slop-futures" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdae12f26b251c25144bae668d44da582ce12deb86d22ff6ebc10a84b2fc2abf" +checksum = "058c54147a291a867e4253356adf491a29e9d999f45b26103bfc02452d8daa8e" dependencies = [ "crossbeam", "futures", @@ -4302,9 +4303,9 @@ dependencies = [ [[package]] name = "slop-jagged" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07d9667c28a67f83e42e40c74711f23c072e731e06e9c9997d2e4924d544ce6" +checksum = "60222309dec4d4bffae090b160847267a23bc5b9ecd4412ba4494b486d21c776" dependencies = [ "derive-where", "futures", @@ -4336,18 +4337,18 @@ dependencies = [ [[package]] name = "slop-keccak-air" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13601bdd494e77e2d431ba4f555788caf1dc5e5812df49061fedbc957e1e19e3" +checksum = "c7d7511944cafbc44ce208dc768b591fa6fc55a87d3f7238f8888e2536db607c" dependencies = [ "p3-keccak-air", ] [[package]] name = "slop-koala-bear" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6586b1c0e66c503e4026a8cb007349fa99c2466957c5b09d18fe658d1391ed8" +checksum = "1ccc675284794435dd053d5e7415089bbd08fccf92cd91e044ea3213821dfb5c" dependencies = [ "lazy_static", "p3-koala-bear", @@ -4360,27 +4361,27 @@ dependencies = [ [[package]] name = "slop-matrix" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44c7beb600f1e47c43c2745711cf412872999b1ce6a44b8fb5683cd0b1a64a2" +checksum = "af3d316b50bf50f8e75d388c829a499ec2f2db77c87c4183d3884d75b99df244" dependencies = [ "p3-matrix", ] [[package]] name = "slop-maybe-rayon" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a2e15a4db7cbc703c203c1ea00d5a889bf3ff9646e8cfd7076ef584ebca441" +checksum = "cabdac3bd6efd75739924e4162340caa59b32b24e02d3f84ab8f56d3f3386ee2" dependencies = [ "p3-maybe-rayon", ] [[package]] name = "slop-merkle-tree" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3d8df667dc00a44093c22564cfc0140b0ca16e41e6b0be7368822832d71d45" +checksum = "c3b49522dc0a3b87966ba354d8cfcb762baadf9d2641f565aebca39e3032fe2a" dependencies = [ "derive-where", "itertools 0.14.0", @@ -4404,9 +4405,9 @@ dependencies = [ [[package]] name = "slop-multilinear" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33c77ba8c2c516592bc23669b47c38babdd3aed64389e368cc1f2f499f8b75e" +checksum = "e04d8ec813376aaa38b94c3e12a07dd6dd76d3e7240708f51a8daef48b6c2f8a" dependencies = [ "derive-where", "futures", @@ -4425,27 +4426,27 @@ dependencies = [ [[package]] name = "slop-poseidon2" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c956b11fff1b8a071fa4ba982dc35e458cff1620dc7b33d9cf22d8df30895f79" +checksum = "7f77602b918f667f970d017293189f9e60ba056d1933c9f717634877838297bc" dependencies = [ "p3-poseidon2", ] [[package]] name = "slop-primitives" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de169e0ca381847f9efa0db5a54533371c10558d7aaed4cb3b2a9bae24a0fe83" +checksum = "592ded42eb74fd87d2ce13592906cf86f201128dd18e2aa5a35d298bc3534dcb" dependencies = [ "slop-algebra", ] [[package]] name = "slop-stacked" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9103802fef961c064a96457b60da838b2d4aa336b00a89fe3af948d684b8226" +checksum = "fd59d332620d6da35a7b4f8837906ec7365761bc0bfda344bb232f8c0b395db7" dependencies = [ "derive-where", "futures", @@ -4466,9 +4467,9 @@ dependencies = [ [[package]] name = "slop-sumcheck" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b3d5051be430c5b47e95f8258221cb40d276fa3461d0239ca3cd96d95f4ccc" +checksum = "4d1176c38a586911076061f419101bdc6491b7b9e2039553f2d3a8237158cfb3" dependencies = [ "futures", "itertools 0.14.0", @@ -4484,18 +4485,18 @@ dependencies = [ [[package]] name = "slop-symmetric" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955145ad6e3a1d083a428f9274071cfbb44c3b29013aae9d6c4c29fb7328cfc0" +checksum = "108bf1769132b782218e3606b8e2f310741fb89d1745234b59ff17d9e7ab8700" dependencies = [ "p3-symmetric", ] [[package]] name = "slop-tensor" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84835a3d915fb0402eb7b821ba1637399e7f3d330ba8f9b6faca0317d6df7277" +checksum = "2bb040f7f2dc9be449a25184d2aee0321a748058d81d646a4a96ffcf32e53963" dependencies = [ "arrayvec", "derive-where", @@ -4513,18 +4514,18 @@ dependencies = [ [[package]] name = "slop-uni-stark" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd531cc607df2b64e68ea80cc1c05584205b06e70dc5b89563f6b74ab1723f74" +checksum = "5d8a279d09e78b0ea5c5e97daa1b6440d47db8118bf3f5014667661c169c4590" dependencies = [ "p3-uni-stark", ] [[package]] name = "slop-utils" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce2c30637af6348960554f9aea4cebce7eb172f173f2187892fcac5cceb3729" +checksum = "1e9910567ffcfdd03aec3e5d220853f01bdbdfe1c1752f7e34cd0a4e20b226bd" dependencies = [ "p3-util", "tracing-forest", @@ -4533,9 +4534,9 @@ dependencies = [ [[package]] name = "slop-whir" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf15dc092785295fe2fd22f1057941a3d5f4d0f6f9ffdce43bb1c9fee8e5578" +checksum = "99519ca11d444567b54d786f71687f63612a75c58404abde353185f53f38dd5e" dependencies = [ "derive-where", "futures", @@ -4602,9 +4603,9 @@ dependencies = [ [[package]] name = "sp1-build" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082381d1779d12762a5fb4efa150c2ebdede79a3500eb0a93bf875f7cd64efa0" +checksum = "1bb06355982a703c0839b6ca1dc69a50d56a3c85e55d0badc8c3b18ebe8c9fa6" dependencies = [ "anyhow", "cargo_metadata", @@ -4616,9 +4617,9 @@ dependencies = [ [[package]] name = "sp1-core-executor" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61464c74d36b4ab16d44011be6ec1896ee463d9369238ec9edcc1c6ce61f11fd" +checksum = "58d3af539d746d5312346d33899d297232351ca5aed1668f16d5a5a0ee0a3ab3" dependencies = [ "bincode", "bytemuck", @@ -4657,9 +4658,9 @@ dependencies = [ [[package]] name = "sp1-core-executor-runner" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52434a8037fd9f19a259f6a432df02fba3ccd2e23ce6a61a50477aba59e04c6e" +checksum = "19b3b61a7c5c891a8c18ac31deccca3914d59132f8656d6d7db09c453ee5ed6f" dependencies = [ "base64 0.22.1", "bincode", @@ -4679,9 +4680,9 @@ dependencies = [ [[package]] name = "sp1-core-executor-runner-binary" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d79d7911837cf8ef8fcd1fb791f9133914e7067f8bff3e7d6ec6f0ff46cf929b" +checksum = "6b0c810b99e4fca0b0d0e791a2a7d6b080aad6ffb6cf950e633409eb2723cba3" dependencies = [ "bincode", "crash-handler", @@ -4694,9 +4695,9 @@ dependencies = [ [[package]] name = "sp1-core-machine" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bab240796901b64aa65402d14351b37f8e955438e6ee1861e3c9219bba02691" +checksum = "29b3851384225aae5588b1fca3c9c8f67001187d53abff21709a7cf8f6098f08" dependencies = [ "bincode", "cfg-if", @@ -4743,9 +4744,9 @@ dependencies = [ [[package]] name = "sp1-cuda" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f787fa4b5cbd29e9baddee1e590c5e689333f2b01e5f704293b7f6f17570c" +checksum = "468c0e82c980535eacdc57d2969f087e56002cd764809596e2f8e513c74abff9" dependencies = [ "bincode", "bytes", @@ -4765,9 +4766,9 @@ dependencies = [ [[package]] name = "sp1-curves" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac661914a8708368643c805fbc6aeadba004a0619c2f5f1fbfc1866fd37b5c10" +checksum = "0bd90718c62dc06a42b9a47308ca914bb6d3f04f9265c77cb76bc135929cd59d" dependencies = [ "cfg-if", "dashu", @@ -4786,9 +4787,9 @@ dependencies = [ [[package]] name = "sp1-derive" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a4f810860abfdc645c4d0589d6efb9302b0d2b3beab8cc60804cb772d5acbe" +checksum = "ecac7ec9b0d2a12f486486a62b2805257e7ffcced51dd39d25fcefaa133adaf6" dependencies = [ "proc-macro2", "quote", @@ -4797,9 +4798,9 @@ dependencies = [ [[package]] name = "sp1-hypercube" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c2575307ebcd93b4320a06fb48a818669551a64a0fecf3b5666628e15f90e2" +checksum = "8449d118a6fe1532899da955c30247c413c90e8c78b06a452f3a40ff6432299e" dependencies = [ "arrayref", "deepsize2", @@ -4846,9 +4847,9 @@ dependencies = [ [[package]] name = "sp1-jit" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf63168fc46696206b9a8e664283ea630bda7911eb255e1d96391787858c581" +checksum = "9355cbdc9d7183d8ab244b695ae7630afb559c6b2bd466bc89a2f3e334b2047a" dependencies = [ "dynasmrt", "hashbrown 0.14.5", @@ -4863,9 +4864,9 @@ dependencies = [ [[package]] name = "sp1-primitives" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df14efe799ebd675cf530c853153a4787327a2385067716dfad4ede79ff31ad" +checksum = "f20c3dd3131b1285420ca25a37f507915ab609887d6dda73973396a0faa719c1" dependencies = [ "bincode", "blake3", @@ -4887,9 +4888,9 @@ dependencies = [ [[package]] name = "sp1-prover" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf089b2fc3cacd5040e6eaeec7d96dc97bfd428421492a0be939f72d48a49851" +checksum = "72c3cb0201a41e9da5372b2b437af07ee3ccd6dca99a69ece4213af40d9436b7" dependencies = [ "anyhow", "bincode", @@ -4951,9 +4952,9 @@ dependencies = [ [[package]] name = "sp1-prover-types" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29236cc1217ab04fdc548dbc83c817ecbf78c348879f5dbe65649680cd2ce89" +checksum = "8b289303f43e7ae1d07b528f34e78ca088c61dea13a3458f63a6570da11e32e5" dependencies = [ "anyhow", "async-scoped", @@ -4975,9 +4976,9 @@ dependencies = [ [[package]] name = "sp1-recursion-circuit" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c809d2ff42f22ebeafac078f7fae717e50f9658bcd1a5e847c0c7d7d7bf94019" +checksum = "19d9ce031605bc111474c11e7841536f3469311253791ef326ee16427e10178e" dependencies = [ "bincode", "itertools 0.14.0", @@ -5015,9 +5016,9 @@ dependencies = [ [[package]] name = "sp1-recursion-compiler" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9162e3ad6f369307142a81d627c4883316f7d65b1e5a0ece3dd45780e29ea2" +checksum = "993b5dcde57622009857883637177946254f3cf00290cc4b998b54a76ffab7c0" dependencies = [ "backtrace", "cfg-if", @@ -5036,9 +5037,9 @@ dependencies = [ [[package]] name = "sp1-recursion-executor" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec793f4c6c032d141476c97fb83dd86abfe3c68f8aace603d57a3d20859a10c5" +checksum = "983d18b6e0e641c8173b250c234fe355c3a9ba5b7485d3af5a54ddb7ab51e814" dependencies = [ "backtrace", "cfg-if", @@ -5060,9 +5061,9 @@ dependencies = [ [[package]] name = "sp1-recursion-gnark-ffi" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac3939b80a23bc369c2ffc1fdb136de3fd83323fe53b03b17fbb11ea383f330" +checksum = "43cb9411bdf57706fa6b342c60534c47c6a066aeb5129e89d8d39f6fb37d12e5" dependencies = [ "anyhow", "bincode", @@ -5084,9 +5085,9 @@ dependencies = [ [[package]] name = "sp1-recursion-machine" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e60fd9a5f5b9bc39e3ddb39c5ac49da32b6aa7ab63c4fef7b6bb2565c2e98b9e" +checksum = "01ab820757fa3783a0230efd48a509fb64b8e7fbaf1d257211dae0c66df79002" dependencies = [ "itertools 0.14.0", "rand 0.8.5", @@ -5106,9 +5107,9 @@ dependencies = [ [[package]] name = "sp1-sdk" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c15071380f43c33b3dbe5650cd5acf54a8e0af4b80a833075a56309256a383" +checksum = "321a52e7b41aef0eb04e880bc2625f9d150553a04be7607756ac5d494850d037" dependencies = [ "anyhow", "async-trait", @@ -5144,9 +5145,9 @@ dependencies = [ [[package]] name = "sp1-verifier" -version = "6.2.1" +version = "6.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91895b72db38423e477635cf22d65a3dc9dc333a872fc2fe0cd6e8daf9661891" +checksum = "44e7a8627fcbfd89ec21932ae33e3a5b9adc4ef844c0e295ef36ee43f0fbf442" dependencies = [ "bincode", "blake3", diff --git a/Cargo.toml b/Cargo.toml @@ -15,6 +15,7 @@ radroots_core = { path = "../lib/crates/core" } radroots_events = { path = "../lib/crates/events" } radroots_events_codec = { path = "../lib/crates/events_codec" } radroots_identity = { path = "../lib/crates/identity" } +radroots_log = { path = "../lib/crates/log" } radroots_nostr = { path = "../lib/crates/nostr" } radroots_runtime = { path = "../lib/crates/runtime" } radroots_runtime_paths = { path = "../lib/crates/runtime_paths" } @@ -36,6 +37,7 @@ radroots_core = { workspace = true, features = ["std", "serde", "typeshare"] } radroots_events = { workspace = true, features = ["serde"] } radroots_events_codec = { workspace = true, features = ["nostr"] } radroots_identity = { workspace = true } +radroots_log = { workspace = true } radroots_nostr = { workspace = true, features = ["client", "codec", "events", "http"] } radroots_runtime = { workspace = true, features = ["cli"] } radroots_runtime_paths = { workspace = true } diff --git a/config.toml b/config.toml @@ -1,47 +1,29 @@ [metadata] name = "rhi" -# display_name = "" -# about = "" -# picture = "" -# banner = "" -# banner = "" -# nip05 = "" -# lud06 = "" -# lud16 = "" -[config] -# Service-host sample location: -# /etc/radroots/workers/rhi/config.toml -# Launch with: -# RHI_PATHS_PROFILE=service_host -# -# Manual operator runs may instead place this file at: -# ~/.radroots/config/workers/rhi/config.toml -# Repo-owned local runs should prefer the root .env.local control plane, which derives -# RHI_PATHS_PROFILE=repo_local and RHI_PATHS_REPO_LOCAL_ROOT automatically -# when path-like values are omitted, the active profile derives: -# interactive_user: -# logs_dir = ~/.radroots/logs/workers/rhi -# worker identity = ~/.radroots/secrets/workers/rhi/identity.secret.json -# subscriber state = ~/.radroots/data/workers/rhi/trade-listing/state.json -# service_host: -# logs_dir = /var/log/radroots/workers/rhi -# worker identity = /etc/radroots/secrets/workers/rhi/identity.secret.json -# subscriber state = /var/lib/radroots/workers/rhi/trade-listing/state.json -# the canonical live worker identity is always an encrypted local envelope -# only override logs_dir or config.subscriber.state.path intentionally -relays = [ +[logging] +filter = "info" +stdout = true + +[relays] +urls = [ "ws://127.0.0.1:8080" ] -nip89_identifier = "rhi" -nip89_extra_tags = [] -[config.subscriber.backoff] +[nostr.nip89] +identifier = "rhi" +extra_tags = [] + +[subscriber.backoff] base_ms = 500 max_ms = 30000 factor = 2 jitter_ms = 0 -[config.subscriber.state] +[subscriber.state] replay_window_secs = 86400 replay_overlap_secs = 300 + +[trade_validation_receipt] +backend = "disabled" +proof_mode = "none" diff --git a/flake.nix b/flake.nix @@ -142,7 +142,7 @@ }; test = mkApp "test" { text = '' - cargo test + cargo test -- --test-threads=1 ''; }; } diff --git a/src/config.rs b/src/config.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use radroots_nostr::prelude::RadrootsNostrMetadata; use radroots_runtime::{BackoffConfig, RadrootsNostrServiceConfig}; use serde::{Deserialize, Serialize}; @@ -17,27 +17,78 @@ fn default_replay_overlap_secs() -> u64 { 5 * 60 } +fn default_logging_filter() -> String { + "info".to_owned() +} + +fn default_logging_stdout() -> bool { + true +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LoggingConfig { + pub output_dir: PathBuf, + pub filter: String, + pub stdout: bool, +} + +#[derive(Debug, Deserialize, Clone, Default)] +#[serde(default, deny_unknown_fields)] +struct RawLoggingConfig { + pub output_dir: Option<PathBuf>, + pub filter: Option<String>, + pub stdout: Option<bool>, +} + +impl RawLoggingConfig { + fn into_logging_config(self, paths: &RhiRuntimePaths) -> Result<LoggingConfig> { + let filter = self.filter.unwrap_or_else(default_logging_filter); + let filter = filter.trim(); + if filter.is_empty() { + bail!("logging.filter must not be empty"); + } + + Ok(LoggingConfig { + output_dir: self.output_dir.unwrap_or_else(|| paths.logs_dir.clone()), + filter: filter.to_owned(), + stdout: self.stdout.unwrap_or_else(default_logging_stdout), + }) + } +} + #[derive(Debug, Deserialize, Clone, Default)] +#[serde(default, deny_unknown_fields)] +struct RawRelaysConfig { + pub urls: Vec<String>, +} + +#[derive(Debug, Deserialize, Clone, Default)] +#[serde(default, deny_unknown_fields)] +struct RawNostrConfig { + pub nip89: RawNip89Config, +} + +#[derive(Debug, Deserialize, Clone, Default)] +#[serde(default, deny_unknown_fields)] +struct RawNip89Config { + pub identifier: Option<String>, + pub extra_tags: Vec<Vec<String>>, +} + +#[derive(Debug, Clone)] struct RawServiceConfig { - #[serde(default)] - pub logs_dir: Option<String>, - #[serde(default)] - pub relays: Vec<String>, - #[serde(default)] - pub nip89_identifier: Option<String>, - #[serde(default)] - pub nip89_extra_tags: Vec<Vec<String>>, + pub logging: LoggingConfig, + pub relays: RawRelaysConfig, + pub nostr: RawNostrConfig, } impl RawServiceConfig { - fn into_service_config(self, paths: &RhiRuntimePaths) -> RadrootsNostrServiceConfig { + fn into_service_config(self) -> RadrootsNostrServiceConfig { RadrootsNostrServiceConfig { - logs_dir: self - .logs_dir - .unwrap_or_else(|| paths.logs_dir.display().to_string()), - relays: self.relays, - nip89_identifier: self.nip89_identifier, - nip89_extra_tags: self.nip89_extra_tags, + logs_dir: self.logging.output_dir.display().to_string(), + relays: self.relays.urls, + nip89_identifier: self.nostr.nip89.identifier, + nip89_extra_tags: self.nostr.nip89.extra_tags, } } } @@ -46,6 +97,7 @@ impl RawServiceConfig { pub struct Configuration { #[serde(flatten)] pub service: RadrootsNostrServiceConfig, + pub logging: LoggingConfig, #[serde(default)] pub subscriber: SubscriberConfig, #[serde(default)] @@ -61,6 +113,7 @@ pub struct SubscriberConfig { } #[derive(Debug, Deserialize, Clone, Default)] +#[serde(default, deny_unknown_fields)] struct RawSubscriberConfig { #[serde(default)] pub backoff: BackoffConfig, @@ -85,6 +138,7 @@ pub struct SubscriberStateConfig { } #[derive(Debug, Deserialize, Clone)] +#[serde(deny_unknown_fields)] struct RawSubscriberStateConfig { #[serde(default)] pub path: Option<PathBuf>, @@ -128,31 +182,40 @@ impl Default for SubscriberStateConfig { } #[derive(Debug, Deserialize, Clone)] -struct RawConfiguration { - #[serde(flatten)] - pub service: RawServiceConfig, +#[serde(deny_unknown_fields)] +struct RawSettings { + pub metadata: RadrootsNostrMetadata, + #[serde(default)] + pub logging: RawLoggingConfig, + #[serde(default)] + pub relays: RawRelaysConfig, + #[serde(default)] + pub nostr: RawNostrConfig, #[serde(default)] pub subscriber: RawSubscriberConfig, #[serde(default)] pub trade_validation_receipt: TradeValidationReceiptProverPolicy, } -#[derive(Debug, Deserialize, Clone)] -struct RawSettings { - pub metadata: RadrootsNostrMetadata, - pub config: RawConfiguration, -} - impl RawSettings { - fn into_settings(self, paths: &RhiRuntimePaths) -> Settings { - Settings { + fn into_settings(self, paths: &RhiRuntimePaths) -> Result<Settings> { + let logging = self.logging.into_logging_config(paths)?; + let service = RawServiceConfig { + logging: logging.clone(), + relays: self.relays, + nostr: self.nostr, + } + .into_service_config(); + + Ok(Settings { metadata: self.metadata, config: Configuration { - service: self.config.service.into_service_config(paths), - subscriber: self.config.subscriber.into_subscriber_config(paths), - trade_validation_receipt: self.config.trade_validation_receipt, + service, + logging, + subscriber: self.subscriber.into_subscriber_config(paths), + trade_validation_receipt: self.trade_validation_receipt, }, - } + }) } } @@ -173,7 +236,7 @@ fn load_settings_from_path_with_resolver( .with_context(|| format!("read configuration from {}", path.display()))?; let settings: RawSettings = toml::from_str(&raw).with_context(|| format!("parse configuration {}", path.display()))?; - Ok(settings.into_settings(&paths)) + settings.into_settings(&paths) } pub fn load_settings_from_path(path: &Path) -> Result<Settings> { @@ -328,11 +391,13 @@ mod tests { [metadata] name = "rhi-test" -[config] -relays = ["wss://relay.example.com"] -nip89_identifier = "rhi" +[relays] +urls = ["wss://relay.example.com"] -[config.subscriber.state] +[nostr.nip89] +identifier = "rhi" + +[subscriber.state] replay_window_secs = 123 replay_overlap_secs = 45 "#, @@ -352,6 +417,20 @@ replay_overlap_secs = 45 "/home/treesap/.radroots/logs/workers/rhi" ); assert_eq!( + settings.config.logging.output_dir, + PathBuf::from("/home/treesap/.radroots/logs/workers/rhi") + ); + assert_eq!(settings.config.logging.filter, "info"); + assert!(settings.config.logging.stdout); + assert_eq!( + settings.config.service.relays, + vec!["wss://relay.example.com"] + ); + assert_eq!( + settings.config.service.nip89_identifier.as_deref(), + Some("rhi") + ); + assert_eq!( settings.config.subscriber.state.path, PathBuf::from("/home/treesap/.radroots/data/workers/rhi/trade-listing/state.json") ); @@ -377,10 +456,28 @@ replay_overlap_secs = 45 [metadata] name = "rhi-test" -[config] -relays = ["wss://relay.example.com"] +[logging] +output_dir = "logs/rhi" +filter = "warn" +stdout = false -[config.trade_validation_receipt] +[relays] +urls = ["wss://relay.example.com"] + +[nostr.nip89] +identifier = "rhi" +extra_tags = [["t", "radroots"]] + +[subscriber.backoff] +base_ms = 10 +max_ms = 100 +factor = 3 +jitter_ms = 5 + +[subscriber.state] +path = "state/trade-listing.json" + +[trade_validation_receipt] backend = "deterministic_none" proof_mode = "none" "#, @@ -395,6 +492,33 @@ proof_mode = "none" ) .expect("load settings"); + assert_eq!(settings.config.service.logs_dir, "logs/rhi"); + assert_eq!( + settings.config.logging.output_dir, + PathBuf::from("logs/rhi") + ); + assert_eq!(settings.config.logging.filter, "warn"); + assert!(!settings.config.logging.stdout); + assert_eq!( + settings.config.service.relays, + vec!["wss://relay.example.com"] + ); + assert_eq!( + settings.config.service.nip89_identifier.as_deref(), + Some("rhi") + ); + assert_eq!( + settings.config.service.nip89_extra_tags, + vec![vec!["t".to_owned(), "radroots".to_owned()]] + ); + assert_eq!(settings.config.subscriber.backoff.base_ms, 10); + assert_eq!(settings.config.subscriber.backoff.max_ms, 100); + assert_eq!(settings.config.subscriber.backoff.factor, 3); + assert_eq!(settings.config.subscriber.backoff.jitter_ms, 5); + assert_eq!( + settings.config.subscriber.state.path, + PathBuf::from("state/trade-listing.json") + ); assert_eq!( settings.config.trade_validation_receipt.backend, TradeValidationReceiptProverBackend::DeterministicNone @@ -406,6 +530,71 @@ proof_mode = "none" } #[test] + fn old_config_roots_are_rejected() { + let temp = tempfile::tempdir().expect("tempdir"); + for (name, body, needle) in [ + ( + "config-root", + r#" +[metadata] +name = "rhi-test" + +[config] +relays = ["wss://relay.example.com"] +"#, + "unknown field `config`", + ), + ( + "config-subscriber-backoff", + r#" +[metadata] +name = "rhi-test" + +[config.subscriber.backoff] +base_ms = 10 +"#, + "unknown field `config`", + ), + ( + "config-subscriber-state", + r#" +[metadata] +name = "rhi-test" + +[config.subscriber.state] +replay_window_secs = 10 +"#, + "unknown field `config`", + ), + ( + "config-trade-validation-receipt", + r#" +[metadata] +name = "rhi-test" + +[config.trade_validation_receipt] +backend = "deterministic_none" +proof_mode = "none" +"#, + "unknown field `config`", + ), + ] { + let config_path = temp.path().join(format!("{name}.toml")); + std::fs::write(&config_path, body).expect("write config"); + + let error = load_settings_from_path_with_resolver( + &config_path, + &linux_resolver(), + RadrootsPathProfile::InteractiveUser, + None, + ) + .expect_err("old config root must fail"); + let message = format!("{error:#}"); + assert!(message.contains(needle), "{message}"); + } + } + + #[test] fn default_subscriber_state_path_is_canonical_for_current_process() { let path = default_subscriber_state_path_for_process().expect("resolve current process defaults"); @@ -433,8 +622,8 @@ proof_mode = "none" assert_eq!( contract.path_overrides.subordinate_path_override_keys, vec![ - "config.service.logs_dir".to_owned(), - "config.subscriber.state.path".to_owned(), + "logging.output_dir".to_owned(), + "subscriber.state.path".to_owned(), ] ); assert_eq!( diff --git a/src/features/trade_listing/subscriber.rs b/src/features/trade_listing/subscriber.rs @@ -344,15 +344,12 @@ mod tests { RadrootsNostrRelayPoolNotification, RadrootsNostrRelayUrl, RadrootsNostrSubscriptionId, RadrootsNostrTag, }; - use std::sync::{Mutex, MutexGuard}; - use tokio::sync::watch; + use tokio::sync::{Mutex, MutexGuard, watch}; - static TEST_LOCK: Mutex<()> = Mutex::new(()); + static TEST_LOCK: Mutex<()> = Mutex::const_new(()); - fn test_guard() -> MutexGuard<'static, ()> { - let guard = TEST_LOCK - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + async fn test_guard() -> MutexGuard<'static, ()> { + let guard = TEST_LOCK.lock().await; *subscriber_test_hooks() .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = SubscriberTestHooks::default(); @@ -394,7 +391,7 @@ mod tests { #[tokio::test] async fn subscriber_io_wrappers_cover_fallback_and_hook_paths() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let event = RadrootsNostrEventBuilder::new(RadrootsNostrKind::TextNote, "test") @@ -456,7 +453,7 @@ mod tests { #[tokio::test] async fn subscriber_returns_ok_when_stop_is_pre_requested() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let (_tx, rx) = watch::channel(true); @@ -469,7 +466,7 @@ mod tests { #[tokio::test] async fn subscriber_reuses_runtime_owned_state_across_runs() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let runtime = shared_runtime(); @@ -504,7 +501,7 @@ mod tests { #[tokio::test] async fn subscriber_returns_err_when_no_relays_are_configured() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let (_tx, rx) = watch::channel(false); @@ -517,7 +514,7 @@ mod tests { #[tokio::test] async fn subscriber_can_stop_after_start_when_relay_is_present() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let _ = client.add_relay("wss://relay.example.com").await; @@ -536,7 +533,7 @@ mod tests { #[tokio::test] async fn subscriber_covers_notification_closed_path() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let _ = client.add_relay("wss://relay.example.com").await; @@ -555,7 +552,7 @@ mod tests { #[tokio::test] async fn subscriber_covers_non_event_notification_and_stop_ok_path() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let _ = client.add_relay("wss://relay.example.com").await; @@ -582,7 +579,7 @@ mod tests { #[tokio::test] async fn subscriber_covers_event_processing_paths() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let _ = client.add_relay("wss://relay.example.com").await; @@ -613,7 +610,7 @@ mod tests { #[tokio::test] async fn subscriber_covers_handle_event_and_error_paths() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let _ = client.add_relay("wss://relay.example.com").await; @@ -655,7 +652,7 @@ mod tests { #[tokio::test] async fn subscriber_covers_delay_and_error_feedback_warn_path() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let _ = client.add_relay("wss://relay.example.com").await; @@ -687,7 +684,7 @@ mod tests { #[tokio::test] async fn handled_domain_errors_advance_replay_anchor() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let runtime = shared_runtime(); @@ -718,7 +715,7 @@ mod tests { #[tokio::test] async fn subscriber_process_event_feedback_error_branches_are_covered() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let event = RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(6000), "event") @@ -745,7 +742,7 @@ mod tests { #[tokio::test] async fn subscriber_process_event_feedback_non_error_branches_are_covered() { - let _guard = test_guard(); + let _guard = test_guard().await; let keys = RadrootsNostrKeys::generate(); let client = RadrootsNostrClient::new(keys.clone()); let event_ok = RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(6000), "ok") diff --git a/src/features/trade_validation_receipt.rs b/src/features/trade_validation_receipt.rs @@ -314,30 +314,37 @@ impl TradeValidationReceiptRemoteHttpAuth { match self { Self::NoAuth => Ok(()), Self::BearerTokenEnv { env_var } => { - if env_var.trim().is_empty() { - return Err(TradeValidationReceiptJobError::RemoteHttpInvalidConfig( - "auth.env_var", - )); - } + validate_rhi_secret_env_var_name(env_var)?; Ok(()) } } } } +fn validate_rhi_secret_env_var_name(env_var: &str) -> Result<&str, TradeValidationReceiptJobError> { + let env_var = env_var.trim(); + if env_var.is_empty() || !env_var.starts_with("RHI_") { + return Err(TradeValidationReceiptJobError::RemoteHttpInvalidConfig( + "auth.env_var", + )); + } + Ok(env_var) +} + fn remote_http_auth_token( config: &TradeValidationReceiptRemoteHttpProverConfig, ) -> Result<Option<String>, TradeValidationReceiptJobError> { match &config.auth { TradeValidationReceiptRemoteHttpAuth::NoAuth => Ok(None), TradeValidationReceiptRemoteHttpAuth::BearerTokenEnv { env_var } => { + let env_var = validate_rhi_secret_env_var_name(env_var)?; let value = std::env::var(env_var).map_err(|_| { - TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing(env_var.clone()) + TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing(env_var.to_owned()) })?; let token = value.trim(); if token.is_empty() { return Err(TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing( - env_var.clone(), + env_var.to_owned(), )); } Ok(Some(token.to_owned())) @@ -1993,7 +2000,7 @@ mod tests { .as_mut() .expect("remote config") .auth = TradeValidationReceiptRemoteHttpAuth::BearerTokenEnv { - env_var: "RADROOTS_TEST_REMOTE_HTTP_TOKEN".to_string(), + env_var: "RHI_TEST_REMOTE_HTTP_TOKEN".to_string(), }; assert!(matches!( bearer_over_http.validate(), @@ -2003,6 +2010,23 @@ mod tests { )); } + #[test] + fn remote_http_auth_env_var_must_use_rhi_prefix_before_process_env_read() { + let mut policy = remote_http_policy(); + let remote_http = policy.remote_http.as_mut().expect("remote config"); + remote_http.endpoint_url = "https://example.test/prove".to_string(); + remote_http.auth = TradeValidationReceiptRemoteHttpAuth::BearerTokenEnv { + env_var: "RADROOTS_TEST_REMOTE_HTTP_TOKEN".to_string(), + }; + + assert!(matches!( + policy.validate(), + Err(TradeValidationReceiptJobError::RemoteHttpInvalidConfig( + "auth.env_var" + )) + )); + } + #[cfg(feature = "sp1_verify")] #[test] fn remote_http_policy_accepts_core_mode_when_configured() { diff --git a/src/lib.rs b/src/lib.rs @@ -235,6 +235,11 @@ mod tests { nip89_identifier: Some("rhi".to_string()), nip89_extra_tags: Vec::new(), }, + logging: config::LoggingConfig { + output_dir: std::env::temp_dir().join("rhi-test-logs"), + filter: "info".to_string(), + stdout: true, + }, subscriber: config::SubscriberConfig { backoff: radroots_runtime::BackoffConfig { base_ms: 1, diff --git a/src/main.rs b/src/main.rs @@ -6,6 +6,8 @@ use anyhow::Result; #[cfg(not(test))] use clap::Parser; #[cfg(not(test))] +use radroots_log::{LogFileLayout, LoggingOptions}; +#[cfg(not(test))] use rhi::cli::Command; use rhi::{cli_args, config, paths, run_rhi}; #[cfg(not(test))] @@ -91,14 +93,23 @@ fn load_args_and_settings() -> Result<(cli_args, config::Settings)> { .unwrap_or_else(paths::default_config_path_for_process)?; let settings = config::load_settings_from_path(&config_path).context("load configuration")?; - radroots_runtime::init_with_logs_dir( - std::path::Path::new(settings.config.service.logs_dir.as_str()), - None, - )?; + init_rhi_logging(&settings)?; Ok((args, settings)) } } +#[cfg(not(test))] +fn init_rhi_logging(settings: &config::Settings) -> Result<()> { + radroots_log::init_logging(LoggingOptions { + dir: Some(settings.config.logging.output_dir.clone()), + file_name: "rhi.log".to_owned(), + stdout: settings.config.logging.stdout, + default_level: Some(settings.config.logging.filter.clone()), + file_layout: LogFileLayout::PrefixedDate, + }) + .context("initialize logging") +} + fn runtime_startup_report( args: &cli_args, settings: &config::Settings, @@ -122,9 +133,9 @@ fn runtime_startup_report( &contract.canonical_config_path, ), canonical_config_path: contract.canonical_config_path.clone(), - logs_dir: PathBuf::from(settings.config.service.logs_dir.as_str()), + logs_dir: settings.config.logging.output_dir.clone(), logs_dir_source: config_or_profile_path_source( - &PathBuf::from(settings.config.service.logs_dir.as_str()), + &settings.config.logging.output_dir, &contract.canonical_logs_dir, ), canonical_logs_dir: contract.canonical_logs_dir.clone(), @@ -268,6 +279,11 @@ mod tests { nip89_identifier: Some("rhi".to_string()), nip89_extra_tags: Vec::new(), }, + logging: config::LoggingConfig { + output_dir: std::env::temp_dir().join("rhi-test-logs"), + filter: "info".to_string(), + stdout: true, + }, subscriber: config::SubscriberConfig::default(), trade_validation_receipt: rhi::features::trade_validation_receipt::TradeValidationReceiptProverPolicy::default(), @@ -290,8 +306,8 @@ mod tests { repo_local_root_source: None, subordinate_path_override_source: "config_artifact".to_string(), subordinate_path_override_keys: vec![ - "config.service.logs_dir".to_string(), - "config.subscriber.state.path".to_string(), + "logging.output_dir".to_string(), + "subscriber.state.path".to_string(), ], }, default_shared_secret_backend: "encrypted_file".to_string(), @@ -418,6 +434,7 @@ mod tests { }; let mut settings = minimal_settings(); settings.config.service.logs_dir = "/tmp/rhi/logs".to_string(); + settings.config.logging.output_dir = PathBuf::from("/tmp/rhi/logs"); settings.config.subscriber.state.path = PathBuf::from("/tmp/rhi/state.json"); let contract = sample_runtime_contract(); @@ -467,6 +484,7 @@ mod tests { let contract = sample_runtime_contract(); let mut settings = minimal_settings(); settings.config.service.logs_dir = contract.canonical_logs_dir.display().to_string(); + settings.config.logging.output_dir = contract.canonical_logs_dir.clone(); settings.config.subscriber.state.path = contract.canonical_subscriber_state_path.clone(); let report = diff --git a/src/paths.rs b/src/paths.rs @@ -17,8 +17,7 @@ const RHI_DEFAULT_SHARED_SECRET_BACKEND: &str = "encrypted_file"; const RHI_ALLOWED_PROFILES: [&str; 3] = ["interactive_user", "service_host", "repo_local"]; const RHI_ALLOWED_SHARED_SECRET_BACKENDS: [&str; 1] = ["encrypted_file"]; const SUBORDINATE_PATH_OVERRIDE_SOURCE: &str = "config_artifact"; -const SUBORDINATE_PATH_OVERRIDE_KEYS: [&str; 2] = - ["config.service.logs_dir", "config.subscriber.state.path"]; +const SUBORDINATE_PATH_OVERRIDE_KEYS: [&str; 2] = ["logging.output_dir", "subscriber.state.path"]; const MIGRATION_IMPORT_HINT: &str = "stop the worker, inspect this legacy path, then perform an explicit import or manual copy into the canonical destination; rhi will not move it on startup"; #[derive(Debug, Clone, PartialEq, Eq)]