myc

Self-custodial remote signer for Radroots apps
git clone https://radroots.dev/git/myc.git
Log | Files | Refs | README | LICENSE

commit 4904035864f5b45e831d352548a17104f537bcc9
parent fb088f860d648088b945aeaddf4c216d99106eab
Author: triesap <tyson@radroots.org>
Date:   Fri, 21 Aug 2026 22:52:53 +0000

release: define Myc native package contract

- freeze final service-source-lock and release metadata\n- bind dependency source trust and native target posture\n- defer artifacts, Nix, OCI, signing, publication, and deployment

Diffstat:
MAGENTS.md | 6++++++
MCargo.toml | 25+++++++++++++++++++++++++
MREADME | 7+++++++
Acontracts/services_hardening/native_release.v1.json | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/services_hardening_native_release.rs | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 326 insertions(+), 0 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -89,6 +89,12 @@ every intentional public-surface change. Shared runtime-path, SQLite, and storage identity types are deliberate governed contract dependencies; provider, SQLx, Serde, transport, and task implementation types are not. +- Step 139 owns the final service source-lock schema and the pre-promotion + native package metadata. Keep the exact Lib revision consistent across every + direct Radroots dependency, Cargo.lock, flake.lock source data, the verified + source archive, and the generated service lock. Native target metadata does + not qualify an artifact; Nix, OCI, signing, tags, publication, and deployment + remain deferred. - Treat checked-in source, tests, and prototype behavior as implementation evidence, not permission to preserve behavior that the active requirement removes. diff --git a/Cargo.toml b/Cargo.toml @@ -6,10 +6,28 @@ authors = ["Radroots Authors"] rust-version = "1.97.1" license = "AGPL-3.0-or-later" description = "Radroots NIP-46 remote signer for delegated Nostr accounts" +repository = "https://github.com/radrootslabs/myc" +readme = "README" +publish = false [workspace] resolver = "3" +[workspace.metadata.radroots.service_source_lock] +service = "myc" +host_feature_profile = "service-host" +config_contract_version = 1 +state_contract_version = 7 +admin_contract_version = 1 +status_contract_version = 1 +provider_contract_version = 1 + +[workspace.metadata.radroots.service_release] +service = "myc" +service_package = "myc" +binary_name = "myc" +version = "0.1.0" + [workspace.lints.rust] unsafe_code = "deny" unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } @@ -56,3 +74,10 @@ zeroize = "1.8" [dev-dependencies] tempfile = "3.17" + +[profile.release] +lto = "thin" +codegen-units = 1 +overflow-checks = true +strip = "symbols" +panic = "unwind" diff --git a/README b/README @@ -24,6 +24,13 @@ fn forge(_: MycRuntimeFoundation) {} The reviewed all-features surface is frozen in the [Myc API baseline](contracts/api_baselines/myc.txt). +The native package and dependency-trust metadata is frozen by +`contracts/services_hardening/native_release.v1.json`. Linux x86_64 and +aarch64 are declared release targets, not qualified artifacts. The canonical +service source lock binds the exact public Lib cohort, Cargo and flake lock +source data, toolchain, feature profile, and service contract versions. Nix, +OCI, signing, tags, publication, and deployment remain deferred and unclaimed. + ## Hardened v1 configuration contract The target service configuration is frozen by diff --git a/contracts/services_hardening/native_release.v1.json b/contracts/services_hardening/native_release.v1.json @@ -0,0 +1,82 @@ +{ + "schema": "radroots.myc.native-release", + "schema_version": 1, + "contract_version": 1, + "service": "myc", + "package": { + "name": "myc", + "binary": "myc", + "version": "0.1.0", + "repository": "https://github.com/radrootslabs/myc", + "publish_to_crates_io": false + }, + "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.v1.toml", + "schema": "radroots.service.source-lock.v1", + "generator": "cargo xtask service-source-lock", + "lib_repository": "https://github.com/radrootslabs/lib", + "architecture": "radroots.crates.release.v2" + }, + "contract_versions": { + "config": 1, + "state": 7, + "admin": 1, + "status": 1, + "provider": 1 + }, + "native_targets": [ + { + "target": "aarch64-unknown-linux-gnu", + "posture": "target" + }, + { + "target": "x86_64-unknown-linux-gnu", + "posture": "target" + } + ], + "step_139_outputs": [ + "cargo_package_metadata", + "release_profile", + "dependency_source_trust", + "service_source_lock" + ], + "deferred_to_step_160": [ + "native_binary_archive", + "service_source_archive", + "systemd_material", + "sbom", + "provenance", + "notices", + "checksums", + "signing_inputs" + ], + "deferred_through_rcld_rshr_170": [ + "nix_evaluation", + "nix_build", + "nixos_module_qualification", + "oci_artifact" + ], + "forbidden": [ + "local_or_path_lib_dependency", + "floating_or_branch_lib_dependency", + "mixed_lib_revision", + "crates_io_publication", + "signing", + "tagging", + "release_publication", + "deployment" + ] +} diff --git a/tests/services_hardening_native_release.rs b/tests/services_hardening_native_release.rs @@ -0,0 +1,206 @@ +#![forbid(unsafe_code)] + +use std::collections::BTreeSet; + +use serde_json::json; + +const CONTRACT: &str = include_str!("../contracts/services_hardening/native_release.v1.json"); +const MANIFEST: &str = include_str!("../Cargo.toml"); +const LOCK: &str = include_str!("../Cargo.lock"); + +const LIB_REVISION: &str = "b44119fbac5985be8127ad1bf56d2950e6399427"; +const LIB_REPOSITORY: &str = "https://github.com/radrootslabs/lib"; + +#[test] +fn native_release_contract_and_manifest_metadata_are_exact() { + let contract: serde_json::Value = serde_json::from_str(CONTRACT).expect("release contract"); + assert_eq!( + contract, + json!({ + "schema": "radroots.myc.native-release", + "schema_version": 1, + "contract_version": 1, + "service": "myc", + "package": { + "name": "myc", + "binary": "myc", + "version": "0.1.0", + "repository": "https://github.com/radrootslabs/myc", + "publish_to_crates_io": false + }, + "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.v1.toml", + "schema": "radroots.service.source-lock.v1", + "generator": "cargo xtask service-source-lock", + "lib_repository": LIB_REPOSITORY, + "architecture": "radroots.crates.release.v2" + }, + "contract_versions": { + "config": 1, + "state": 7, + "admin": 1, + "status": 1, + "provider": 1 + }, + "native_targets": [ + { "target": "aarch64-unknown-linux-gnu", "posture": "target" }, + { "target": "x86_64-unknown-linux-gnu", "posture": "target" } + ], + "step_139_outputs": [ + "cargo_package_metadata", + "release_profile", + "dependency_source_trust", + "service_source_lock" + ], + "deferred_to_step_160": [ + "native_binary_archive", + "service_source_archive", + "systemd_material", + "sbom", + "provenance", + "notices", + "checksums", + "signing_inputs" + ], + "deferred_through_rcld_rshr_170": [ + "nix_evaluation", + "nix_build", + "nixos_module_qualification", + "oci_artifact" + ], + "forbidden": [ + "local_or_path_lib_dependency", + "floating_or_branch_lib_dependency", + "mixed_lib_revision", + "crates_io_publication", + "signing", + "tagging", + "release_publication", + "deployment" + ] + }) + ); + + let manifest: toml::Value = toml::from_str(MANIFEST).expect("Cargo manifest"); + let package = manifest["package"].as_table().expect("package"); + assert_eq!( + package["repository"].as_str(), + Some("https://github.com/radrootslabs/myc") + ); + assert_eq!(package["readme"].as_str(), Some("README")); + assert_eq!(package["publish"].as_bool(), Some(false)); + + let metadata = &manifest["workspace"]["metadata"]["radroots"]; + assert_eq!( + metadata["service_source_lock"], + toml::Value::Table(toml::toml! { + service = "myc" + host_feature_profile = "service-host" + config_contract_version = 1 + state_contract_version = 7 + admin_contract_version = 1 + status_contract_version = 1 + provider_contract_version = 1 + }) + ); + assert_eq!( + metadata["service_release"], + toml::Value::Table(toml::toml! { + service = "myc" + service_package = "myc" + binary_name = "myc" + version = "0.1.0" + }) + ); + assert_eq!( + manifest["profile"]["release"], + toml::Value::Table(toml::toml! { + lto = "thin" + codegen-units = 1 + overflow-checks = true + strip = "symbols" + panic = "unwind" + }) + ); +} + +#[test] +fn every_radroots_dependency_is_exactly_source_locked() { + let manifest: toml::Value = toml::from_str(MANIFEST).expect("Cargo manifest"); + let dependencies = manifest["dependencies"].as_table().expect("dependencies"); + let radroots = dependencies + .iter() + .filter(|(name, _)| name.starts_with("radroots_")) + .collect::<Vec<_>>(); + assert_eq!(radroots.len(), 7); + for (name, dependency) in radroots { + let dependency = dependency.as_table().expect("detailed dependency"); + assert_eq!( + dependency.get("git").and_then(toml::Value::as_str), + Some(LIB_REPOSITORY), + "{name}" + ); + assert_eq!( + dependency.get("rev").and_then(toml::Value::as_str), + Some(LIB_REVISION), + "{name}" + ); + assert_eq!( + dependency.get("version").and_then(toml::Value::as_str), + Some("=0.1.0-alpha"), + "{name}" + ); + for forbidden in ["path", "branch", "tag"] { + assert!( + !dependency.contains_key(forbidden), + "{name} contains `{forbidden}`" + ); + } + } + assert!(!MANIFEST.contains("[patch.")); + + let sources = LOCK + .lines() + .filter_map(|line| line.strip_prefix("source = \"git+")) + .filter_map(|line| line.strip_suffix('"')) + .filter(|source| source.contains("radrootslabs/lib")) + .collect::<BTreeSet<_>>(); + assert_eq!(sources.len(), 1); + let source = sources.into_iter().next().expect("Lib source"); + assert!(source.contains(&format!("?rev={LIB_REVISION}#{LIB_REVISION}"))); +} + +#[test] +fn removed_and_deferred_release_surfaces_cannot_be_smuggled_into_step_139() { + let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); + for forbidden in [ + ".github", + "target", + "result", + "artifacts", + "dist", + "sbom.cdx.json", + "provenance-input.v1.json", + "oci-image.tar.gz", + ] { + assert!( + !root.join(forbidden).exists(), + "forbidden generated surface `{forbidden}` exists" + ); + } + assert!(!CONTRACT.contains("qualified")); + assert!(!CONTRACT.contains("production_ready")); +}