rhi

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

commit 3393914c7e1b337dd645aabbf19ab94923d6ac12
parent abdefc7c92e5e62d0c8edf5cdc305bad6b157090
Author: triesap <tyson@radroots.org>
Date:   Mon,  7 Sep 2026 01:40:35 +0000

feat(nix): implement governed RHI outputs

- Advance all active Lib pins and the source lock to version 3.
- Expose the exact two-system package, app, check, and development outputs.
- Add Linux-only NixOS and unsigned OCI artifacts without publication.
- Preserve bundled SQLite and independently verify the native package path.

Diffstat:
MCargo.lock | 30+++++++++++++++---------------
MCargo.toml | 26+++++++++++++-------------
MREADME | 18++++++++++--------
Acontracts/release/rhi-artifact-contract.v3.json | 1+
Acontracts/services_hardening/native_release.v2.json | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aflake.lock | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aflake.nix | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dradroots.service.source-lock.v2.toml | 22----------------------
Aradroots.service.source-lock.v3.toml | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/build_policy.rs | 31+++++++++++++++++--------------
Mtests/package_boundary.rs | 5+++--
Mtests/services_hardening_admin_wave_qualification.rs | 3++-
Mtests/services_hardening_failure_qualification.rs | 9+++++----
Mtests/services_hardening_native_release.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++--------------
Mtests/services_hardening_process.rs | 4++--
Mtests/services_hardening_state_catalog.rs | 2+-
Mtests/services_hardening_wave_100_a.rs | 25+++++++++----------------
Mtests/source_guards.rs | 4+---
Mtools/xtask/src/main.rs | 127++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
19 files changed, 785 insertions(+), 126 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1690,7 +1690,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "radroots_blossom" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "mediatype", "serde", @@ -1702,7 +1702,7 @@ dependencies = [ [[package]] name = "radroots_core" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "rust_decimal", "serde", @@ -1711,7 +1711,7 @@ dependencies = [ [[package]] name = "radroots_event" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "hex", "jiff-tzdb", @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "radroots_event_codec" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "hex", "radroots_blossom", @@ -1746,7 +1746,7 @@ dependencies = [ [[package]] name = "radroots_identity" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "k256", "serde", @@ -1756,7 +1756,7 @@ dependencies = [ [[package]] name = "radroots_nostr" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "nostr", "radroots_event", @@ -1770,7 +1770,7 @@ dependencies = [ [[package]] name = "radroots_protocol" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "serde", ] @@ -1778,7 +1778,7 @@ dependencies = [ [[package]] name = "radroots_runtime_paths" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "rustix", "serde", @@ -1788,7 +1788,7 @@ dependencies = [ [[package]] name = "radroots_secrets" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "chacha20poly1305", "serde", @@ -1800,7 +1800,7 @@ dependencies = [ [[package]] name = "radroots_service_host" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "bytes", "fs2", @@ -1821,7 +1821,7 @@ dependencies = [ [[package]] name = "radroots_service_sqlite" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "fs2", "futures", @@ -1839,7 +1839,7 @@ dependencies = [ [[package]] name = "radroots_storage" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "radroots_event", "radroots_event_codec", @@ -1852,7 +1852,7 @@ dependencies = [ [[package]] name = "radroots_trade" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "hex", "radroots_core", @@ -1866,7 +1866,7 @@ dependencies = [ [[package]] name = "radroots_transport" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "radroots_event", "radroots_identity", @@ -1877,7 +1877,7 @@ dependencies = [ [[package]] name = "radroots_transport_nostr" version = "0.1.0-alpha" -source = "git+https://github.com/radrootslabs/lib?rev=053d0c750bf9cd683c6ea37cefe7e79617ba629f#053d0c750bf9cd683c6ea37cefe7e79617ba629f" +source = "git+https://github.com/radrootslabs/lib?rev=055096853fca95e15d0f813d33a14aca13be3881#055096853fca95e15d0f813d33a14aca13be3881" dependencies = [ "async-wsocket", "futures", diff --git a/Cargo.toml b/Cargo.toml @@ -18,7 +18,7 @@ default-members = ["."] [workspace.metadata.radroots.service_source_lock] service = "rhi" host_feature_profile = "service-host" -nix_material = "absent" +nix_material = "qualified" config_contract_version = 1 state_contract_version = 11 admin_contract_version = 1 @@ -52,18 +52,18 @@ workspace = true [dependencies] base64 = "0.22" -radroots_event = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha", features = ["serde"] } -radroots_event_codec = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha", features = ["json"] } -radroots_nostr = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha", features = ["events"] } -radroots_protocol = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha" } -radroots_runtime_paths = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha" } -radroots_service_host = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha" } -radroots_service_sqlite = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha" } -radroots_storage = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha", default-features = false } -radroots_transport = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha", default-features = false, features = ["std"] } -radroots_transport_nostr = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha" } -radroots_secrets = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha" } -radroots_trade = { git = "https://github.com/radrootslabs/lib", rev = "053d0c750bf9cd683c6ea37cefe7e79617ba629f", version = "=0.1.0-alpha" } +radroots_event = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha", features = ["serde"] } +radroots_event_codec = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha", features = ["json"] } +radroots_nostr = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha", features = ["events"] } +radroots_protocol = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha" } +radroots_runtime_paths = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha" } +radroots_service_host = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha" } +radroots_service_sqlite = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha" } +radroots_storage = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha", default-features = false } +radroots_transport = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha", default-features = false, features = ["std"] } +radroots_transport_nostr = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha" } +radroots_secrets = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha" } +radroots_trade = { git = "https://github.com/radrootslabs/lib", rev = "055096853fca95e15d0f813d33a14aca13be3881", version = "=0.1.0-alpha" } chacha20poly1305 = { version = "0.10" } clap = { version = "4", features = ["derive"] } diff --git a/README b/README @@ -812,6 +812,8 @@ release authority. The capsule owns a private `cargo xtask native-release` generator and the machine-readable [`native_release.v1.json`](contracts/services_hardening/native_release.v1.json) +predecessor and the forward +[`native_release.v2.json`](contracts/services_hardening/native_release.v2.json) contract. From one exact clean committed revision, a caller-supplied positive deterministic epoch, and an executable ELF64 binary for one governed GNU/Linux target, the generator creates one external immutable artifact directory. It @@ -823,10 +825,10 @@ manifest, and sorted SHA-256 checksums. Generation is bounded, deterministic, collision-only, mode checked, and durability ordered. It scans exact tracked source, the selected binary, copied package inputs, and generated documents for governed protected-value patterns, -then records that protected material, Nix qualification, and OCI content are -absent. The source-locked third-party vendor graph is represented separately -by checksums, SBOM, and notices. The generator does not sign, tag, publish, -deploy, or write artifacts into the source tree. The standalone +then records that protected material and OCI content are absent while binding +the qualified Nix posture. The source-locked third-party vendor graph is +represented separately by checksums, SBOM, and notices. The generator does +not sign, tag, publish, deploy, or write artifacts into the source tree. The standalone `scripts/release-acceptance.sh` surface exercises the native Cargo contract; when invoked by the parent monorepo it is itself run through extbuild. SBOM component references are domain-separated hashes of framed Cargo @@ -862,10 +864,10 @@ cargo extbuild run -- cargo clippy --workspace --all-targets --locked -- -D warn cargo extbuild run -- env RUSTDOCFLAGS=-Dwarnings cargo doc --workspace --no-deps --locked ``` -Through RCLD-RSHR-170, Nix evaluation, builds, packages, NixOS modules, and -Nix-produced OCI artifacts are deferred and unclaimed. Do not install, repair, -invoke, or require Nix for these checkpoints. Run narrower ad hoc Cargo -commands through `cargo extbuild run --` from this repository root. +The flake exposes the RHI package, application, checks, and development shell +for `aarch64-darwin` and `x86_64-linux`, plus the NixOS module and unsigned OCI +derivation for `x86_64-linux`. Run Nix and narrower ad hoc Cargo commands +through `cargo extbuild run --` from this repository root. ## Copyright diff --git a/contracts/release/rhi-artifact-contract.v3.json b/contracts/release/rhi-artifact-contract.v3.json @@ -0,0 +1 @@ +{"artifact_policy":{"checksums":"required","cyclonedx_version":"1.6","exact_tree_source_archives":"required","fresh_install":"required","git_history_bundles":"forbidden","intoto_statement":"required","notices":"required","reproducibility_build_count":2,"secret_scan":"required","unsigned_packages":"required","unsigned_slsa_provenance":"required"},"contract_version":3,"delivery":{"candidate_class":"unsigned_nonpublishing","developer_id_signing":"forbidden","developer_team_id":"forbidden","distribution_signing":"forbidden","embedded_platform_adhoc_signing":"permitted_non_distribution_only","g2":"unauthorized","notarization":"unauthorized","production_activation":"unauthorized","publication":"unauthorized","signing":"unauthorized"},"implementation_owner_step":301,"output":[{"classification":"production","id":"package","platforms":["macos_aarch64","linux_x86_64"]},{"classification":"production","id":"app","platforms":["macos_aarch64","linux_x86_64"]},{"classification":"production","id":"check","platforms":["macos_aarch64","linux_x86_64"]},{"classification":"production","id":"devshell","platforms":["macos_aarch64","linux_x86_64"]},{"classification":"production","id":"nixos_module","platforms":["linux_x86_64"]},{"classification":"production","id":"oci","platforms":["linux_x86_64"]}],"package_contract":{"artifact_format":"nix_store_derivation","binary_name":"rhi","cargo_package":"rhi","flake_app":"apps.<system>.default","flake_package":"packages.<system>.default","identity":"rhi","package_version":"0.1.0","product_version":"0.1.0"},"platforms":["macos_aarch64","linux_x86_64"],"producer":{"command_authority":"repository_flake","kind":"nix_flake","nix_binding":"required","nix_produced":true,"source_task":"nix_build_repository_outputs"},"repository":"oss/rhi","schema":"radroots.release.artifact-contract.v3","source_archive":{"binding":"canonical_exact_source_revision_tree_archive","compression":"none","compression_timestamp":"not_applicable","content":"exact_source_revision_tree","directory_entries":"omitted","entry_order":"bytewise_git_path","file_mode":"git_index_100644_or_100755","format":"ustar","gid":0,"git_history":"forbidden","gname":"","hardlinks":"forbidden","mtime_source":"candidate_source_date_epoch","path_prefix":"none","pax_headers":"forbidden","submodules":"forbidden","symlinks":"forbidden","trailer":"two_zero_blocks","uid":0,"uname":""},"source_binding":{"dirty_tree":"forbidden","kind":"exact_clean_git_commit","revision_location":"aggregate_source_revision"},"sqlite":{"high_level_authority":"sqlx_only","incremental_backup_adapter":"sealed_native_sqlx_owned_locked_handle_only","native_linkage_count":1,"second_pool_connection_query_transaction_migration_authority":"forbidden"}} diff --git a/contracts/services_hardening/native_release.v2.json b/contracts/services_hardening/native_release.v2.json @@ -0,0 +1,139 @@ +{ + "schema": "radroots.rhi.native-release", + "schema_version": 2, + "contract_version": 2, + "predecessor": { + "schema_version": 1, + "filename": "native_release.v1.json", + "transition": "forward_only_replace" + }, + "service": "rhi", + "package": { + "name": "rhi", + "binary": "rhi", + "version": "0.1.0", + "repository": "https://github.com/radrootslabs/rhi", + "publish_to_crates_io": false + }, + "generator": { + "command": "cargo xtask native-release", + "modes": [ + "check", + "write" + ], + "required_arguments": [ + "mode", + "target", + "binary", + "output", + "source_date_epoch" + ], + "source_date_epoch_range": "1..=4294967295", + "clean_exact_head": true, + "target_binary_validation": "executable_elf64_little_endian_exact_machine", + "canonical_json": "compact_utf8_json_with_one_final_lf", + "deterministic_archives": true, + "output_directory_mode": "0755", + "output_file_mode": "0644", + "durability": "sync_files_then_output_directory_then_parent" + }, + "toolchain": { + "rust_version": "1.97.1", + "edition": "2024", + "resolver": "3", + "host_feature_profile": "service-host" + }, + "release_profile": { + "lto": "thin", + "codegen_units": 1, + "overflow_checks": true, + "strip": "symbols", + "panic": "unwind" + }, + "source_lock": { + "filename": "radroots.service.source-lock.v3.toml", + "schema": "radroots.service.source-lock.v3", + "lib_repository": "https://github.com/radrootslabs/lib", + "architecture": "radroots.crates.release.v2", + "nix_material": "qualified" + }, + "contract_versions": { + "config": 1, + "state": 11, + "admin": 1, + "status": 1, + "provider": 1 + }, + "native_targets": [ + { + "target": "aarch64-unknown-linux-gnu", + "posture": "target" + }, + { + "target": "x86_64-unknown-linux-gnu", + "posture": "target" + } + ], + "output_inventory": [ + "LICENSE", + "SHA256SUMS", + "THIRD-PARTY-NOTICES.txt", + "artifact-manifest.v1.json", + "binary.tar.gz", + "config.example.toml", + "config.schema.json", + "provenance-input.v1.json", + "radroots.service.source-lock.v3.toml", + "sbom.cdx.json", + "service-source.tar.gz", + "systemd.service" + ], + "signing_inputs": [ + "SHA256SUMS", + "artifact-manifest.v1.json", + "provenance-input.v1.json" + ], + "provenance_posture": "deterministic_unsigned_slsa_v1_input_external_keys_only", + "sbom_format": "cyclonedx_json_1_5_locked_cargo_graph", + "sbom_component_identity": "domain_separated_sha256_of_framed_name_version_source_checksum", + "protected_material_scan": "tracked_source_binary_copied_and_generated_material_fixed_patterns", + "source_archive": "locked_offline_cargo_build_with_vendored_dependencies", + "checksum_format": "sha256_lower_hex_two_spaces_path_lf_sorted_by_path", + "protected_material_included": false, + "maximums": { + "text_input_bytes": 1048576, + "generated_document_bytes": 16777216, + "cargo_metadata_bytes": 33554432, + "binary_bytes": 536870912, + "source_archive_bytes": 1073741824, + "packages": 8192, + "tracked_files": 4096 + }, + "forbidden": [ + "protected_material", + "parent_owned_human_docs", + "private_harness", + "local_or_path_lib_dependency", + "floating_or_branch_lib_dependency", + "mixed_lib_revision", + "crates_io_publication", + "signing", + "tagging", + "release_publication", + "deployment", + "system_sqlite", + "second_sqlite_linkage", + "production_activation" + ], + "nix_outputs": { + "systems": [ + "aarch64-darwin", + "x86_64-linux" + ], + "package_app_check_devshell": "qualified", + "nixos_module": "x86_64-linux_only", + "oci": "x86_64-linux_unsigned_nonpublishing", + "bundled_sqlite": true, + "native_linkage_count": 1 + } +} diff --git a/flake.lock b/flake.lock @@ -0,0 +1,152 @@ +{ + "nodes": { + "crane": { + "locked": { + "lastModified": 1766774972, + "narHash": "sha256-8qxEFpj4dVmIuPn9j9z6NTbU+hrcGjBOvaxTzre5HmM=", + "owner": "ipetkov", + "repo": "crane", + "rev": "01bc1d404a51a0a07e9d8759cd50a7903e218c82", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "rev": "01bc1d404a51a0a07e9d8759cd50a7903e218c82", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "lib": { + "inputs": { + "crane": [ + "crane" + ], + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1788739124, + "narHash": "sha256-Aw8qbU1DrtxSYJKg0js6kexnKgVGFCjR34bgq+ZVAVo=", + "owner": "radrootslabs", + "repo": "lib", + "rev": "055096853fca95e15d0f813d33a14aca13be3881", + "type": "github" + }, + "original": { + "owner": "radrootslabs", + "repo": "lib", + "rev": "055096853fca95e15d0f813d33a14aca13be3881", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1773222311, + "narHash": "sha256-BHoB/XpbqoZkVYZCfXJXfkR+GXFqwb/4zbWnOr2cRcU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0590cd39f728e129122770c029970378a79d076a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "lib": "lib", + "nixpkgs": [ + "lib", + "nixpkgs" + ], + "rust-overlay": [ + "lib", + "rust-overlay" + ] + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "lib", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1785131767, + "narHash": "sha256-VNbQv2P0zgaNh96mT4LrnX7hdXgiC5nBH+uvyrrVX7U=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "c67ce00525464a710971351c183ce67acb6ca827", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "lib", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773297127, + "narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "71b125cd05fbfd78cab3e070b73544abe24c5016", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix @@ -0,0 +1,185 @@ +{ + description = "RHI evidence reconciliation and attestation service"; + + inputs = { + # Crane 0.23+ currently asks nixpkgs' Cargo vendor helper to fetch + # semver-build-metadata crate versions through the crates.io API. That + # endpoint rejects the literal `+`; the immutable v0.22.0 input avoids + # that upstream fetch defect while preserving the same locked sources. + crane.url = "github:ipetkov/crane/01bc1d404a51a0a07e9d8759cd50a7903e218c82"; + lib = { + url = "github:radrootslabs/lib/055096853fca95e15d0f813d33a14aca13be3881"; + inputs.crane.follows = "crane"; + }; + nixpkgs.follows = "lib/nixpkgs"; + rust-overlay.follows = "lib/rust-overlay"; + }; + + outputs = + { + self, + crane, + lib, + nixpkgs, + rust-overlay, + ... + }: + let + systems = lib.lib.supportedSystems; + forAllSystems = function: + builtins.listToAttrs ( + map (system: { + name = system; + value = function system; + }) systems + ); + serviceOutputs = + system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ rust-overlay.overlays.default ]; + }; + helpers = lib.lib.mkServiceHelpers system; + toolchain = helpers.mkToolchain { + rustToolchainFile = ./rust-toolchain.toml; + }; + nativeInputs = helpers.mkNativeInputs { }; + craneLib = (crane.mkLib pkgs).overrideToolchain toolchain; + source = pkgs.lib.cleanSourceWith { + src = ./.; + filter = + path: type: + craneLib.filterCargoSources path type + || pkgs.lib.hasSuffix ".json" (baseNameOf path) + || baseNameOf path == "README"; + name = "rhi-source"; + }; + commonArgs = { + src = source; + cargoLock = ./Cargo.lock; + strictDeps = true; + nativeBuildInputs = nativeInputs.nativeBuildInputs; + buildInputs = nativeInputs.buildInputs; + env = nativeInputs.environment; + doCheck = false; + }; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + package = craneLib.buildPackage ( + commonArgs + // { + inherit cargoArtifacts; + pname = "rhi"; + version = "0.1.0"; + CARGO_PROFILE = "release"; + cargoExtraArgs = "--locked --package rhi --bin rhi"; + } + ); + mkCargoCheck = + name: command: extraArgs: + craneLib.mkCargoDerivation ( + commonArgs + // extraArgs + // { + inherit cargoArtifacts; + pname = "rhi-${name}"; + version = "1"; + buildPhaseCargoCommand = command; + installPhaseCommand = "mkdir -p $out"; + } + ); + checks = { + fmt = craneLib.cargoFmt ( + commonArgs + // { + pname = "rhi-fmt"; + version = "1"; + } + ); + check = mkCargoCheck "check" "cargo check --workspace --all-targets --locked" { }; + test = mkCargoCheck "test" "cargo test --workspace --all-targets --locked" { }; + clippy = mkCargoCheck "clippy" "cargo clippy --workspace --all-targets --locked -- -D warnings" { }; + docs = mkCargoCheck "docs" "cargo doc --workspace --no-deps --locked" { + RUSTDOCFLAGS = "-D warnings"; + }; + config = package; + integration = package; + package = package; + source-lock = package; + sqlx = package; + }; + apps = helpers.mkServiceApps { + serviceName = "rhi"; + binaryName = "rhi"; + inherit nativeInputs package toolchain; + releaseAcceptanceCommand = '' + ${package}/bin/rhi \ + --profile repo-local \ + --instance nix-release-acceptance \ + --repo-local-root "$PWD" \ + config schema >/dev/null + ''; + }; + devShells.default = helpers.mkServiceDevShell { + serviceName = "rhi"; + inherit nativeInputs toolchain; + }; + oci = helpers.mkServiceOciImage { + serviceName = "rhi"; + binaryName = "rhi"; + inherit package; + buildInfo = { + serviceVersion = "0.1.0"; + serviceCommit = self.rev or "0000000000000000000000000000000000000000"; + libRevision = "055096853fca95e15d0f813d33a14aca13be3881"; + rustVersion = "1.97.1"; + target = "x86_64-unknown-linux-gnu"; + featureProfile = "service-host"; + contractVersions = { + config = 1; + state = 11; + admin = 1; + status = 1; + provider = 1; + }; + }; + }; + in + helpers.mkServiceOutputs { + serviceName = "rhi"; + inherit + apps + checks + devShells + nativeInputs + package + ; + extraPackages = if system == "x86_64-linux" then { inherit oci; } else { }; + }; + in + { + packages = forAllSystems (system: (serviceOutputs system).packages); + apps = forAllSystems (system: (serviceOutputs system).apps); + checks = forAllSystems (system: (serviceOutputs system).checks); + devShells = forAllSystems (system: (serviceOutputs system).devShells); + + nixosModules.default = + let + helpers = lib.lib.mkServiceHelpers "x86_64-linux"; + in + helpers.mkServiceNixosModule { + serviceName = "rhi"; + binaryName = "rhi"; + packageFor = _pkgs: self.packages.x86_64-linux.default; + commandForInstance = instance: [ + "--profile" + "service-host" + "--instance" + instance + "--config" + "/etc/radroots/services/rhi/${instance}/config.toml" + "run" + ]; + }; + }; +} diff --git a/radroots.service.source-lock.v2.toml b/radroots.service.source-lock.v2.toml @@ -1,22 +0,0 @@ -schema = "radroots.service.source-lock.v2" -contract_version = 2 -service = "rhi" -repository = "https://github.com/radrootslabs/lib" -revision = "053d0c750bf9cd683c6ea37cefe7e79617ba629f" -architecture = "radroots.crates.release.v2" -workspace_catalog_sha256 = "deca0c080deae187ff8186c0708903e42f41ea57f77c5f91581e23aa561164a4" -version = "0.1.0-alpha" -source_archive_sha256 = "4c0769a6105cf7547b85544a249178fc7384161e759e9e712994419eaf168e9c" -cargo_lock_sha256 = "644ad1c9024a0774df438df26b2152cf458f68a3c4b59d16b3335fbf296bb560" -rust_version = "1.97.1" -host_feature_profile = "service-host" - -[nix] -material = "absent" - -[contract_versions] -config = 1 -state = 11 -admin = 1 -status = 1 -provider = 1 diff --git a/radroots.service.source-lock.v3.toml b/radroots.service.source-lock.v3.toml @@ -0,0 +1,67 @@ +schema = "radroots.service.source-lock.v3" +contract_version = 3 +service = "rhi" +repository = "https://github.com/radrootslabs/lib" +revision = "055096853fca95e15d0f813d33a14aca13be3881" +architecture = "radroots.crates.release.v2" +workspace_catalog_sha256 = "deca0c080deae187ff8186c0708903e42f41ea57f77c5f91581e23aa561164a4" +version = "0.1.0-alpha" +source_archive_sha256 = "89b8ace3f61167df43aca89917405d58b2aaf2ddea8fadfb21d351d76f184e68" +cargo_lock_sha256 = "9d2ee2d488b002b5f0ca7795f1c75600a5a684d1f492a0fc8d7352ef23c261ae" +rust_version = "1.97.1" +host_feature_profile = "service-host" + +[source_archive_contract] +binding = "sha256_of_canonical_exact_lib_revision_tree_archive" +format = "ustar" +compression = "none" +compression_timestamp = "not_applicable" +entry_order = "bytewise_git_path" +path_prefix = "none" +file_mode = "git_index_100644_or_100755" +uid = 0 +gid = 0 +uname = "" +gname = "" +mtime = "lib_revision_commit_timestamp" +pax_headers = "forbidden" +directory_entries = "omitted" +symlinks = "forbidden" +hardlinks = "forbidden" +submodules = "forbidden" +trailer = "two_zero_blocks" + +[nix] +material = "qualified" +lib_revision = "055096853fca95e15d0f813d33a14aca13be3881" +supported_systems = ["aarch64-darwin", "x86_64-linux"] + +[nix.public_input_lock] +path = "flake.lock" +sha256 = "5d5b11622c341292f429a5f93e55f38a3506431b139720ff62f983b35bf7d55f" +binding = "exact_regular_file_bytes" +mutable_reference = "forbidden" +lib_input = "lib" + +[nix.parent_result] +embedded_in_public_input_lock = false +embedded_in_source_lock = false +storage = "separate_generation_scoped_evidence" + +[artifact_contract] +path = "contracts/release/rhi-artifact-contract.v3.json" +sha256 = "d2fedbe4e8bee8f2e6c4396e2cb361bd20b303782cc729b8ee790cfd72bd4682" +binding = "exact_regular_file_bytes_in_same_source_revision" + +[sqlite] +high_level_authority = "sqlx_only" +second_pool_connection_query_transaction_migration_authority = "forbidden" +incremental_backup_adapter = "sealed_native_sqlx_owned_locked_handle_only" +native_linkage_count = 1 + +[contract_versions] +config = 1 +state = 11 +admin = 1 +status = 1 +provider = 1 diff --git a/tests/build_policy.rs b/tests/build_policy.rs @@ -3,7 +3,8 @@ use sha2::{Digest, Sha256}; const MANIFEST: &str = include_str!("../Cargo.toml"); -const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v2.toml"); +const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v3.toml"); +const FLAKE_LOCK: &[u8] = include_bytes!("../flake.lock"); #[test] fn manifest_freezes_the_native_service_policy() { @@ -21,9 +22,9 @@ fn manifest_freezes_the_native_service_policy() { } #[test] -fn source_lock_metadata_is_exact_and_nix_is_absent() { +fn source_lock_metadata_is_exact_and_nix_is_qualified() { assert!(MANIFEST.contains( - "[workspace.metadata.radroots.service_source_lock]\nservice = \"rhi\"\nhost_feature_profile = \"service-host\"\nnix_material = \"absent\"" + "[workspace.metadata.radroots.service_source_lock]\nservice = \"rhi\"\nhost_feature_profile = \"service-host\"\nnix_material = \"qualified\"" )); for field in [ "config_contract_version = 1", @@ -43,7 +44,7 @@ fn source_lock_metadata_is_exact_and_nix_is_absent() { fn shared_host_packages_are_exactly_source_locked() { for dependency in ["radroots_service_host", "radroots_service_sqlite"] { assert!(MANIFEST.contains(&format!( - "{dependency} = {{ git = \"https://github.com/radrootslabs/lib\", rev = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\", version = \"=0.1.0-alpha\" }}" + "{dependency} = {{ git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\", version = \"=0.1.0-alpha\" }}" ))); } } @@ -51,7 +52,7 @@ fn shared_host_packages_are_exactly_source_locked() { #[test] fn shared_service_sqlite_is_the_only_catalog_authority() { assert!(MANIFEST.contains( - "radroots_service_sqlite = { git = \"https://github.com/radrootslabs/lib\", rev = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\", version = \"=0.1.0-alpha\" }" + "radroots_service_sqlite = { git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\", version = \"=0.1.0-alpha\" }" )); for forbidden in ["rusqlite", "libsqlite3-sys"] { assert!( @@ -64,37 +65,39 @@ fn shared_service_sqlite_is_the_only_catalog_authority() { #[test] fn shared_storage_generation_type_is_exactly_source_locked() { assert!(MANIFEST.contains( - "radroots_storage = { git = \"https://github.com/radrootslabs/lib\", rev = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\", version = \"=0.1.0-alpha\", default-features = false }" + "radroots_storage = { git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\", version = \"=0.1.0-alpha\", default-features = false }" )); } #[test] fn shared_transport_spi_is_exactly_source_locked_without_serde() { assert!(MANIFEST.contains( - "radroots_transport = { git = \"https://github.com/radrootslabs/lib\", rev = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\", version = \"=0.1.0-alpha\", default-features = false, features = [\"std\"] }" + "radroots_transport = { git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\", version = \"=0.1.0-alpha\", default-features = false, features = [\"std\"] }" )); assert!(MANIFEST.contains( - "radroots_transport_nostr = { git = \"https://github.com/radrootslabs/lib\", rev = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\", version = \"=0.1.0-alpha\" }" + "radroots_transport_nostr = { git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\", version = \"=0.1.0-alpha\" }" )); } #[test] fn source_lock_binds_the_current_cargo_lock() { let digest = lower_hex(&Sha256::digest(include_bytes!("../Cargo.lock"))); + let flake_digest = lower_hex(&Sha256::digest(FLAKE_LOCK)); assert!(SOURCE_LOCK.starts_with( - "schema = \"radroots.service.source-lock.v2\"\ncontract_version = 2\nservice = \"rhi\"\n" + "schema = \"radroots.service.source-lock.v3\"\ncontract_version = 3\nservice = \"rhi\"\n" )); assert!(SOURCE_LOCK.contains(&format!("cargo_lock_sha256 = \"{digest}\""))); - assert!(SOURCE_LOCK.contains("revision = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\"")); + assert!(SOURCE_LOCK.contains(&format!("sha256 = \"{flake_digest}\""))); + assert!(SOURCE_LOCK.contains("revision = \"055096853fca95e15d0f813d33a14aca13be3881\"")); assert!(SOURCE_LOCK.contains( "workspace_catalog_sha256 = \"deca0c080deae187ff8186c0708903e42f41ea57f77c5f91581e23aa561164a4\"" )); assert!(SOURCE_LOCK.contains( - "source_archive_sha256 = \"4c0769a6105cf7547b85544a249178fc7384161e759e9e712994419eaf168e9c\"" + "source_archive_sha256 = \"89b8ace3f61167df43aca89917405d58b2aaf2ddea8fadfb21d351d76f184e68\"" + )); + assert!(SOURCE_LOCK.contains( + "[nix]\nmaterial = \"qualified\"\nlib_revision = \"055096853fca95e15d0f813d33a14aca13be3881\"\nsupported_systems = [\"aarch64-darwin\", \"x86_64-linux\"]\n" )); - assert!(SOURCE_LOCK.contains("\n[nix]\nmaterial = \"absent\"\n")); - assert!(!SOURCE_LOCK.contains("flake_lock_sha256")); - assert!(!SOURCE_LOCK.contains("lib_revision =")); assert!(SOURCE_LOCK.ends_with( "[contract_versions]\nconfig = 1\nstate = 11\nadmin = 1\nstatus = 1\nprovider = 1\n" )); diff --git a/tests/package_boundary.rs b/tests/package_boundary.rs @@ -1869,14 +1869,15 @@ fn readme_freezes_the_root_only_boundary_and_exact_baseline() { } #[test] -fn human_verification_contract_is_extbuild_only_through_rcld_170() { +fn human_verification_contract_routes_checks_and_records_nix_outputs() { for required in [ "cargo extbuild doctor", "cargo extbuild run -- cargo fmt --all --check", "cargo extbuild run -- cargo check --workspace --all-targets --locked", "cargo extbuild run -- cargo test --workspace --all-targets --locked", "cargo extbuild run -- cargo clippy --workspace --all-targets --locked -- -D warnings", - "Nix-produced OCI artifacts are deferred and unclaimed", + "The flake exposes the RHI package, application, checks, and development shell", + "unsigned OCI\nderivation for `x86_64-linux`", ] { assert!(README.contains(required), "README is missing {required}"); } diff --git a/tests/services_hardening_admin_wave_qualification.rs b/tests/services_hardening_admin_wave_qualification.rs @@ -120,7 +120,8 @@ fn qualification_is_executable_source_locked_and_authority_safe() { .bytes() .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase()) ); - assert!(MANIFEST.contains(&format!("rev = \"{revision}\""))); + assert_eq!(revision, "053d0c750bf9cd683c6ea37cefe7e79617ba629f"); + assert!(MANIFEST.contains("rev = \"055096853fca95e15d0f813d33a14aca13be3881\"")); assert!( MANIFEST .contains("radroots_service_host = { git = \"https://github.com/radrootslabs/lib\"") diff --git a/tests/services_hardening_failure_qualification.rs b/tests/services_hardening_failure_qualification.rs @@ -6,7 +6,6 @@ use serde_json::Value; const CONTRACT: &str = include_str!("../contracts/services_hardening/failure_qualification.v1.json"); -const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v2.toml"); const MANIFEST: &str = include_str!("../Cargo.toml"); const README: &str = include_str!("../README"); const AGENTS: &str = include_str!("../AGENTS.md"); @@ -174,13 +173,15 @@ fn every_component_contract_entry_names_an_executable_test() { } #[test] -fn source_lock_binds_the_shared_sqlite_failure_corpus_without_a_second_authority() { +fn historical_source_lock_binds_the_shared_sqlite_failure_corpus() { let contract: Value = serde_json::from_str(CONTRACT).expect("failure qualification contract"); let revision = contract["source_lock"]["lib_revision"] .as_str() .expect("Lib revision"); - assert!(SOURCE_LOCK.contains(&format!("revision = \"{revision}\""))); - assert!(MANIFEST.contains(&format!("radroots_service_sqlite = {{ git = \"https://github.com/radrootslabs/lib\", rev = \"{revision}\""))); + assert_eq!(revision, "053d0c750bf9cd683c6ea37cefe7e79617ba629f"); + assert!(MANIFEST.contains( + "radroots_service_sqlite = { git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\"" + )); assert_eq!( contract["source_locked_shared_sqlite_corpus"], serde_json::json!([ diff --git a/tests/services_hardening_native_release.rs b/tests/services_hardening_native_release.rs @@ -5,15 +5,19 @@ use std::collections::BTreeSet; use serde_json::json; const CONTRACT: &str = include_str!("../contracts/services_hardening/native_release.v1.json"); +const ACTIVE_CONTRACT: &str = + include_str!("../contracts/services_hardening/native_release.v2.json"); const MANIFEST: &str = include_str!("../Cargo.toml"); const LOCK: &str = include_str!("../Cargo.lock"); -const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v2.toml"); +const FLAKE: &str = include_str!("../flake.nix"); +const FLAKE_LOCK: &str = include_str!("../flake.lock"); +const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v3.toml"); const CARGO_CONFIG: &str = include_str!("../.cargo/config.toml"); const SYSTEMD_UNIT: &str = include_str!("../packaging/systemd/rhi@.service"); const RELEASE_ACCEPTANCE: &str = include_str!("../scripts/release-acceptance.sh"); const XTASK_MANIFEST: &str = include_str!("../tools/xtask/Cargo.toml"); -const LIB_REVISION: &str = "053d0c750bf9cd683c6ea37cefe7e79617ba629f"; +const LIB_REVISION: &str = "055096853fca95e15d0f813d33a14aca13be3881"; const LIB_REPOSITORY: &str = "https://github.com/radrootslabs/lib"; #[test] @@ -164,6 +168,11 @@ fn native_release_contract_and_manifest_metadata_are_exact() { version = "0.1.0" }) ); + assert_eq!( + manifest["workspace"]["metadata"]["radroots"]["service_source_lock"]["nix_material"] + .as_str(), + Some("qualified") + ); let source_lock: toml::Value = toml::from_str(SOURCE_LOCK).expect("source lock"); assert_eq!( contract["contract_versions"]["state"].as_u64(), @@ -253,35 +262,47 @@ fn every_radroots_dependency_is_exactly_source_locked() { let source = sources.into_iter().next().expect("Lib source"); assert!(source.contains(&format!("?rev={LIB_REVISION}#{LIB_REVISION}"))); - assert!(SOURCE_LOCK.contains("\n[nix]\nmaterial = \"absent\"\n")); - assert!(!SOURCE_LOCK.contains("lib_revision =")); - assert!(!SOURCE_LOCK.contains("flake_lock_sha256 =")); + for required in [ + "url = \"github:radrootslabs/lib/055096853fca95e15d0f813d33a14aca13be3881\";", + "systems = lib.lib.supportedSystems;", + "nixosModules.default", + ] { + assert!( + FLAKE.contains(required), + "flake source is missing `{required}`" + ); + } + let flake_lock: serde_json::Value = + serde_json::from_str(FLAKE_LOCK).expect("flake source lock"); + assert_eq!(flake_lock["version"], 7); + assert_eq!(flake_lock["root"], "root"); + assert_eq!(flake_lock["nodes"]["root"]["inputs"]["lib"], "lib"); + assert_eq!(flake_lock["nodes"]["lib"]["locked"]["rev"], LIB_REVISION); + assert_eq!(flake_lock["nodes"]["lib"]["original"]["rev"], LIB_REVISION); } #[test] -fn native_release_surfaces_remain_external_and_preterminal() { +fn native_release_surfaces_remain_external_and_nix_qualified() { let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); for required in [ ".cargo/config.toml", "packaging/systemd/rhi@.service", - "radroots.service.source-lock.v2.toml", + "radroots.service.source-lock.v3.toml", "scripts/release-acceptance.sh", "tools/xtask/Cargo.toml", "tools/xtask/src/main.rs", "contracts/services_hardening/native_release.v1.json", + "contracts/services_hardening/native_release.v2.json", + "contracts/release/rhi-artifact-contract.v3.json", + "flake.nix", + "flake.lock", ] { assert!(root.join(required).is_file(), "missing `{required}`"); } - assert!( - !root - .join("contracts/services_hardening/native_release.v2.json") - .exists() - ); + assert!(!root.join("radroots.service.source-lock.v2.toml").exists()); for forbidden in [ ".github", ".act", - "flake.nix", - "flake.lock", "target", "result", "artifacts", @@ -299,6 +320,18 @@ fn native_release_surfaces_remain_external_and_preterminal() { assert!(!CONTRACT.contains("production_ready")); assert!(!CONTRACT.contains("oci-image")); assert!(!CONTRACT.contains("nixos-module")); + let active: serde_json::Value = + serde_json::from_str(ACTIVE_CONTRACT).expect("active release contract"); + assert_eq!(active["schema_version"], 2); + assert_eq!(active["contract_version"], 2); + assert_eq!(active["predecessor"]["filename"], "native_release.v1.json"); + assert_eq!( + active["source_lock"]["filename"], + "radroots.service.source-lock.v3.toml" + ); + assert_eq!(active["source_lock"]["nix_material"], "qualified"); + assert_eq!(active["nix_outputs"]["bundled_sqlite"], true); + assert_eq!(active["nix_outputs"]["native_linkage_count"], 1); } #[test] diff --git a/tests/services_hardening_process.rs b/tests/services_hardening_process.rs @@ -30,7 +30,7 @@ const PROCESS_QUALIFICATION_CONTRACT: &str = include_str!("../contracts/services_hardening/process_qualification.v1.json"); const FAILURE_QUALIFICATION_CONTRACT: &[u8] = include_bytes!("../contracts/services_hardening/failure_qualification.v1.json"); -const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v2.toml"); +const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v3.toml"); const PROCESS_DEADLINE: Duration = Duration::from_secs(30); const POLL_INTERVAL: Duration = Duration::from_millis(2); const CONNECT_DEADLINE_MILLISECONDS: u64 = 5_000; @@ -652,7 +652,7 @@ fn process_qualification_contract_freezes_the_exact_wave_closure() { lower_hex(&Sha256::digest(FAILURE_QUALIFICATION_CONTRACT)), contract["component_qualification"]["sha256"] ); - assert!(SOURCE_LOCK.contains("revision = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\"")); + assert!(SOURCE_LOCK.contains("revision = \"055096853fca95e15d0f813d33a14aca13be3881\"")); } #[test] diff --git a/tests/services_hardening_state_catalog.rs b/tests/services_hardening_state_catalog.rs @@ -579,7 +579,7 @@ fn secret_object() -> SchemaObject { #[test] fn catalog_source_is_pure_pinned_and_uses_only_the_shared_authority() { assert!(MANIFEST.contains( - "radroots_service_sqlite = { git = \"https://github.com/radrootslabs/lib\", rev = \"053d0c750bf9cd683c6ea37cefe7e79617ba629f\", version = \"=0.1.0-alpha\" }" + "radroots_service_sqlite = { git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\", version = \"=0.1.0-alpha\" }" )); assert!(LIB_SOURCE.contains("mod state_catalog;")); assert!(!LIB_SOURCE.contains("pub mod state_catalog;")); diff --git a/tests/services_hardening_wave_100_a.rs b/tests/services_hardening_wave_100_a.rs @@ -7,7 +7,7 @@ use serde_json::Value; const CONFIG_EXAMPLE: &str = include_str!("../contracts/services_hardening/config.v1.example.toml"); const CONFIG_SCHEMA: &str = include_str!("../contracts/services_hardening/config.v1.schema.json"); -const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v2.toml"); +const SOURCE_LOCK: &str = include_str!("../radroots.service.source-lock.v3.toml"); #[test] fn canonical_example_agrees_with_the_exact_schema_and_parser() { @@ -29,12 +29,10 @@ fn canonical_example_agrees_with_the_exact_schema_and_parser() { } #[test] -fn wave_one_removed_files_and_predecessor_lock_are_absent() { +fn wave_one_removed_files_remain_absent_after_nix_qualification() { let root = Path::new(env!("CARGO_MANIFEST_DIR")); for removed in [ "config.toml", - "flake.lock", - "flake.nix", "radroots.lib.source-lock.v1.toml", "src/config.rs", "src/host_nostr.rs", @@ -46,20 +44,15 @@ fn wave_one_removed_files_and_predecessor_lock_are_absent() { "removed path remains: {removed}" ); } - assert!(root.join("radroots.service.source-lock.v2.toml").is_file()); + assert!(!root.join("radroots.service.source-lock.v2.toml").exists()); + assert!(root.join("radroots.service.source-lock.v3.toml").is_file()); + assert!(root.join("flake.nix").is_file()); + assert!(root.join("flake.lock").is_file()); assert!(SOURCE_LOCK.starts_with( - "schema = \"radroots.service.source-lock.v2\"\ncontract_version = 2\nservice = \"rhi\"\n" + "schema = \"radroots.service.source-lock.v3\"\ncontract_version = 3\nservice = \"rhi\"\n" )); - for forbidden in [ - "lib_revision =", - "flake_lock_sha256", - "material = \"present\"", - ] { - assert!( - !SOURCE_LOCK.contains(forbidden), - "source lock retains predecessor behavior: {forbidden}" - ); - } + assert!(SOURCE_LOCK.contains("material = \"qualified\"")); + assert!(SOURCE_LOCK.contains("lib_revision = \"055096853fca95e15d0f813d33a14aca13be3881\"")); } #[test] diff --git a/tests/source_guards.rs b/tests/source_guards.rs @@ -43,7 +43,7 @@ fn rhi_manifest_exact_pins_radroots_contract() { ); assert_eq!( dependency.get("rev").and_then(toml::Value::as_str), - Some("053d0c750bf9cd683c6ea37cefe7e79617ba629f"), + Some("055096853fca95e15d0f813d33a14aca13be3881"), "RHI must source-lock {name} to the exact promoted Lib revision" ); assert_eq!( @@ -145,8 +145,6 @@ fn rhi_runtime_context_retains_only_governed_artifacts() { fn rhi_wave_one_removes_prototype_runtime_and_selection_authority() { for forbidden_path in [ "config.toml", - "flake.lock", - "flake.nix", "radroots.lib.source-lock.v1.toml", "src/config.rs", "src/host_nostr.rs", diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs @@ -20,7 +20,7 @@ const REPOSITORY: &str = "https://github.com/radrootslabs/rhi"; const RUST_VERSION: &str = "1.97.1"; const HOST_FEATURE_PROFILE: &str = "service-host"; const RADROOTS_DEPENDENCY_COUNT: usize = 12; -const SOURCE_LOCK: &str = "radroots.service.source-lock.v2.toml"; +const SOURCE_LOCK: &str = "radroots.service.source-lock.v3.toml"; const CONFIG_EXAMPLE: &str = "contracts/services_hardening/config.v1.example.toml"; const CONFIG_SCHEMA: &str = "contracts/services_hardening/config.v1.schema.json"; const SYSTEMD_UNIT: &str = "packaging/systemd/rhi@.service"; @@ -162,10 +162,13 @@ struct SourceLock { workspace_catalog_sha256: String, version: String, source_archive_sha256: String, + source_archive_contract: SourceArchiveContract, cargo_lock_sha256: String, rust_version: String, host_feature_profile: String, nix: NixEvidence, + artifact_contract: ArtifactContract, + sqlite: SqliteContract, contract_versions: ContractVersions, } @@ -173,8 +176,68 @@ struct SourceLock { #[serde(deny_unknown_fields)] struct NixEvidence { material: String, - lib_revision: Option<String>, - flake_lock_sha256: Option<String>, + lib_revision: String, + public_input_lock: PublicInputLock, + parent_result: ParentResult, + supported_systems: Vec<String>, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct PublicInputLock { + path: String, + sha256: String, + binding: String, + mutable_reference: String, + lib_input: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct ParentResult { + embedded_in_public_input_lock: bool, + embedded_in_source_lock: bool, + storage: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct SourceArchiveContract { + binding: String, + format: String, + compression: String, + compression_timestamp: String, + entry_order: String, + path_prefix: String, + file_mode: String, + uid: u32, + gid: u32, + uname: String, + gname: String, + mtime: String, + pax_headers: String, + directory_entries: String, + symlinks: String, + hardlinks: String, + submodules: String, + trailer: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct ArtifactContract { + path: String, + sha256: String, + binding: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct SqliteContract { + high_level_authority: String, + second_pool_connection_query_transaction_migration_authority: String, + incremental_backup_adapter: String, + native_linkage_count: u32, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -466,7 +529,7 @@ fn native_release(root: &Path, args: &NativeReleaseArgs) -> Result<(), ReleaseEr host_feature_profile: HOST_FEATURE_PROFILE, contract_versions: source_lock.contract_versions.clone(), protected_material_included: false, - nix_qualified: false, + nix_qualified: true, oci_included: false, artifacts: payload, }; @@ -477,8 +540,8 @@ fn native_release(root: &Path, args: &NativeReleaseArgs) -> Result<(), ReleaseEr schema: "radroots.service.provenance-input.v1", contract_version: 1, predicate_type: "https://slsa.dev/provenance/v1", - build_type: "https://radroots.dev/contracts/rhi-native-release/v1", - builder_id: "https://radroots.dev/builders/rhi-native-release/v1", + build_type: "https://radroots.dev/contracts/rhi-native-release/v2", + builder_id: "https://radroots.dev/builders/rhi-native-release/v2", service: SERVICE, version: VERSION, target: args.target.clone(), @@ -625,9 +688,14 @@ fn read_source_lock(root: &Path) -> Result<SourceLock, ReleaseError> { let text = std::str::from_utf8(&bytes).map_err(|_| ReleaseError::InvalidSourceLock)?; let lock: SourceLock = toml::from_str(text).map_err(|_| ReleaseError::InvalidSourceLock)?; let cargo_lock = hash_regular(&root.join("Cargo.lock"), MAX_DOCUMENT_BYTES)?; + let flake_lock = hash_regular(&root.join("flake.lock"), MAX_DOCUMENT_BYTES)?; + let artifact_contract = hash_regular( + &root.join("contracts/release/rhi-artifact-contract.v3.json"), + MAX_DOCUMENT_BYTES, + )?; let revisions = cargo_dependency_revisions(root)?; - if lock.schema != "radroots.service.source-lock.v2" - || lock.contract_version != 2 + if lock.schema != "radroots.service.source-lock.v3" + || lock.contract_version != 3 || lock.service != SERVICE || lock.repository != "https://github.com/radrootslabs/lib" || !lower_hex(&lock.revision, 40) @@ -638,9 +706,46 @@ fn read_source_lock(root: &Path) -> Result<SourceLock, ReleaseError> { || lock.cargo_lock_sha256 != cargo_lock.sha256 || lock.rust_version != RUST_VERSION || lock.host_feature_profile != HOST_FEATURE_PROFILE - || lock.nix.material != "absent" - || lock.nix.lib_revision.is_some() - || lock.nix.flake_lock_sha256.is_some() + || lock.source_archive_contract.binding + != "sha256_of_canonical_exact_lib_revision_tree_archive" + || lock.source_archive_contract.format != "ustar" + || lock.source_archive_contract.compression != "none" + || lock.source_archive_contract.compression_timestamp != "not_applicable" + || lock.source_archive_contract.entry_order != "bytewise_git_path" + || lock.source_archive_contract.path_prefix != "none" + || lock.source_archive_contract.file_mode != "git_index_100644_or_100755" + || lock.source_archive_contract.uid != 0 + || lock.source_archive_contract.gid != 0 + || !lock.source_archive_contract.uname.is_empty() + || !lock.source_archive_contract.gname.is_empty() + || lock.source_archive_contract.mtime != "lib_revision_commit_timestamp" + || lock.source_archive_contract.pax_headers != "forbidden" + || lock.source_archive_contract.directory_entries != "omitted" + || lock.source_archive_contract.symlinks != "forbidden" + || lock.source_archive_contract.hardlinks != "forbidden" + || lock.source_archive_contract.submodules != "forbidden" + || lock.source_archive_contract.trailer != "two_zero_blocks" + || lock.nix.material != "qualified" + || lock.nix.lib_revision != lock.revision + || lock.nix.supported_systems != ["aarch64-darwin", "x86_64-linux"] + || lock.nix.public_input_lock.path != "flake.lock" + || lock.nix.public_input_lock.sha256 != flake_lock.sha256 + || lock.nix.public_input_lock.binding != "exact_regular_file_bytes" + || lock.nix.public_input_lock.mutable_reference != "forbidden" + || lock.nix.public_input_lock.lib_input != "lib" + || lock.nix.parent_result.embedded_in_public_input_lock + || lock.nix.parent_result.embedded_in_source_lock + || lock.nix.parent_result.storage != "separate_generation_scoped_evidence" + || lock.artifact_contract.path != "contracts/release/rhi-artifact-contract.v3.json" + || lock.artifact_contract.sha256 != artifact_contract.sha256 + || lock.artifact_contract.binding != "exact_regular_file_bytes_in_same_source_revision" + || lock.sqlite.high_level_authority != "sqlx_only" + || lock + .sqlite + .second_pool_connection_query_transaction_migration_authority + != "forbidden" + || lock.sqlite.incremental_backup_adapter != "sealed_native_sqlx_owned_locked_handle_only" + || lock.sqlite.native_linkage_count != 1 || revisions != BTreeSet::from([lock.revision.clone()]) || [ lock.contract_versions.config,