commit a3781823586e4a61c49afa4fb0a8396dd309afe7
parent d43f72542d6296194944d95328bf172b887d3f89
Author: triesap <tyson@radroots.org>
Date: Mon, 3 Aug 2026 23:01:35 +0000
release: classify front doors as std-only
- record the governed bare-metal and WASM targets
- reject nominal portability for both public front doors
- preserve the empty SDK portable public-package set
- refresh locked resolution after the Nostr boundary fix
Diffstat:
4 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1931,7 +1931,7 @@ dependencies = [
"radroots_signing",
"serde",
"serde_json",
- "thiserror 1.0.69",
+ "thiserror 2.0.18",
]
[[package]]
diff --git a/contracts/releases/portable_matrix.toml b/contracts/releases/portable_matrix.toml
@@ -0,0 +1,7 @@
+schema_version = 1
+no_std_target = "thumbv7em-none-eabihf"
+no_std_cross_compiler = "zig cc"
+no_std_cross_cflags = "-target thumb-freestanding-eabihf -mcpu=cortex_m4"
+wasm_target = "wasm32-unknown-unknown"
+portable_public_packages = []
+std_only_public_packages = ["radroots", "radroots_sdk"]
diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs
@@ -13,6 +13,7 @@ mod manifest;
mod output;
mod package_matrix;
mod package_metadata;
+mod portable_qualification;
mod release_qualification;
mod smoke;
mod target_qualification;
@@ -32,6 +33,7 @@ enum CommandAction<'a> {
GeneratePackageMetadata,
Coverage(&'a [String]),
QualifyFeatures,
+ QualifyPortable,
QualifyTargets,
Check,
Smoke(&'a [String]),
@@ -68,6 +70,7 @@ fn run(args: impl IntoIterator<Item = String>) -> Result<(), String> {
CommandAction::QualifyFeatures => {
release_qualification::run_feature_matrix(&fs::workspace_root()?)
}
+ CommandAction::QualifyPortable => portable_qualification::run(&fs::workspace_root()?),
CommandAction::QualifyTargets => target_qualification::run(&fs::workspace_root()?),
CommandAction::Check => check::check(),
CommandAction::Smoke(rest) => smoke::run(rest),
@@ -99,6 +102,9 @@ fn command_action(args: &[String]) -> Result<CommandAction<'_>, String> {
[command, target] if command == "release" && target == "qualify-features" => {
Ok(CommandAction::QualifyFeatures)
}
+ [command, target] if command == "release" && target == "qualify-portable" => {
+ Ok(CommandAction::QualifyPortable)
+ }
[command, target] if command == "release" && target == "qualify-targets" => {
Ok(CommandAction::QualifyTargets)
}
@@ -110,7 +116,7 @@ fn command_action(args: &[String]) -> Result<CommandAction<'_>, String> {
}
fn usage() -> String {
- "usage: cargo xtask architecture | cargo xtask architecture-ci | cargo xtask check-api-boundaries | cargo xtask check-dependency-boundaries | cargo xtask generate | cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask generate bindings <swift|kotlin> | cargo xtask generate package-metadata | cargo xtask check | cargo xtask smoke facade-rust-local | sdk-rust-local | front-doors-rust-local | cargo xtask coverage run | cargo xtask release qualify-features | cargo xtask release qualify-targets"
+ "usage: cargo xtask architecture | cargo xtask architecture-ci | cargo xtask check-api-boundaries | cargo xtask check-dependency-boundaries | cargo xtask generate | cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask generate bindings <swift|kotlin> | cargo xtask generate package-metadata | cargo xtask check | cargo xtask smoke facade-rust-local | sdk-rust-local | front-doors-rust-local | cargo xtask coverage run | cargo xtask release qualify-features | cargo xtask release qualify-portable | cargo xtask release qualify-targets"
.to_owned()
}
diff --git a/tools/xtask/src/portable_qualification.rs b/tools/xtask/src/portable_qualification.rs
@@ -0,0 +1,58 @@
+use std::{fs, path::Path};
+
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+struct PortableMatrix {
+ schema_version: u32,
+ no_std_target: String,
+ no_std_cross_compiler: String,
+ no_std_cross_cflags: String,
+ wasm_target: String,
+ portable_public_packages: Vec<String>,
+ std_only_public_packages: Vec<String>,
+}
+
+pub fn run(workspace_root: &Path) -> Result<(), String> {
+ load(workspace_root).map(|_| ())
+}
+
+fn load(workspace_root: &Path) -> Result<PortableMatrix, String> {
+ let path = workspace_root.join("contracts/releases/portable_matrix.toml");
+ let raw = fs::read_to_string(&path)
+ .map_err(|error| format!("failed to read {}: {error}", path.display()))?;
+ let matrix = toml::from_str::<PortableMatrix>(&raw)
+ .map_err(|error| format!("failed to parse {}: {error}", path.display()))?;
+ if matrix.schema_version != 1
+ || matrix.no_std_target != "thumbv7em-none-eabihf"
+ || matrix.no_std_cross_compiler != "zig cc"
+ || matrix.no_std_cross_cflags != "-target thumb-freestanding-eabihf -mcpu=cortex_m4"
+ || matrix.wasm_target != "wasm32-unknown-unknown"
+ || !matrix.portable_public_packages.is_empty()
+ || matrix.std_only_public_packages != ["radroots", "radroots_sdk"]
+ {
+ return Err(
+ "SDK portability contract must classify both public front doors as std-only".to_owned(),
+ );
+ }
+ Ok(matrix)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::load;
+
+ #[test]
+ fn current_contract_rejects_nominal_front_door_portability() {
+ let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .and_then(std::path::Path::parent)
+ .expect("workspace root");
+ let matrix = load(root).expect("portable matrix");
+ assert!(matrix.portable_public_packages.is_empty());
+ assert_eq!(
+ matrix.std_only_public_packages,
+ ["radroots", "radroots_sdk"]
+ );
+ }
+}