commit 7c0177cd5b2261e2e5bea054907aa6147d52aae7
parent 2452accade2358dc65a38fb22f654d2eab60fd2e
Author: triesap <tyson@radroots.org>
Date: Mon, 27 Jul 2026 08:05:57 +0000
facade: align package manifest and module root
- Create the non-publishable radroots package at its final sdk-owned identity.
- Reserve the approved feature and eleven-module public vocabularies.
- Avoid temporary edges from the public facade to private migration packages.
- Guard metadata and surface boundaries with package and repository checks.
Diffstat:
17 files changed, 232 insertions(+), 0 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -9,6 +9,7 @@ members = [
"crates/replica_schema_bindings",
"crates/replica_sync_wasm",
"crates/runtime_contract_v1",
+ "crates/radroots",
"crates/sdk",
"crates/sql_wasm_runtime",
"crates/trade_bindings",
diff --git a/crates/radroots/Cargo.toml b/crates/radroots/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "radroots"
+description = "Curated ordinary-user Rust facade for Radroots"
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+readme = "README.md"
+publish = false
+
+[lib]
+name = "radroots"
+path = "src/lib.rs"
+
+[features]
+default = ["client"]
+client = []
+native = ["client"]
+nostr = ["client"]
+nip46 = ["nostr"]
+radrootsd = ["client"]
+geonames = ["client"]
+knowledge = ["client"]
+full = ["native", "nostr", "nip46", "radrootsd", "geonames", "knowledge"]
diff --git a/crates/radroots/README.md b/crates/radroots/README.md
@@ -0,0 +1,11 @@
+# radroots
+
+`radroots` is the curated ordinary-user Rust facade for Radroots. This initial
+non-publishable scaffold reserves the final package identity, public module
+vocabulary, and feature vocabulary while the lower release-v1 packages are
+being established.
+
+The facade intentionally contains no temporary dependency on private
+migration crates. Its final builders, safe defaults, curated re-exports, and
+dependency forwarding are implemented and qualified in the owned facade
+checkpoints before publication is enabled.
diff --git a/crates/radroots/src/client.rs b/crates/radroots/src/client.rs
@@ -0,0 +1 @@
+//! Curated client construction and operation entry points.
diff --git a/crates/radroots/src/event.rs b/crates/radroots/src/event.rs
@@ -0,0 +1 @@
+//! Curated event authoring and inspection entry points.
diff --git a/crates/radroots/src/farm.rs b/crates/radroots/src/farm.rs
@@ -0,0 +1 @@
+//! Curated farm-domain entry points.
diff --git a/crates/radroots/src/identity.rs b/crates/radroots/src/identity.rs
@@ -0,0 +1 @@
+//! Curated public identity entry points.
diff --git a/crates/radroots/src/knowledge.rs b/crates/radroots/src/knowledge.rs
@@ -0,0 +1 @@
+//! Curated knowledge-domain entry points.
diff --git a/crates/radroots/src/lib.rs b/crates/radroots/src/lib.rs
@@ -0,0 +1,19 @@
+#![forbid(unsafe_code)]
+#![warn(missing_docs)]
+
+//! Curated ordinary-user entry point for Radroots.
+//!
+//! This non-publishable scaffold reserves the final module and feature
+//! vocabulary without exposing private migration packages.
+
+pub mod client;
+pub mod event;
+pub mod farm;
+pub mod identity;
+pub mod knowledge;
+pub mod listing;
+pub mod signing;
+pub mod storage;
+pub mod sync;
+pub mod trade;
+pub mod transport;
diff --git a/crates/radroots/src/listing.rs b/crates/radroots/src/listing.rs
@@ -0,0 +1 @@
+//! Curated listing-domain entry points.
diff --git a/crates/radroots/src/signing.rs b/crates/radroots/src/signing.rs
@@ -0,0 +1 @@
+//! Curated signing entry points.
diff --git a/crates/radroots/src/storage.rs b/crates/radroots/src/storage.rs
@@ -0,0 +1 @@
+//! Curated storage entry points.
diff --git a/crates/radroots/src/sync.rs b/crates/radroots/src/sync.rs
@@ -0,0 +1 @@
+//! Curated synchronization entry points.
diff --git a/crates/radroots/src/trade.rs b/crates/radroots/src/trade.rs
@@ -0,0 +1 @@
+//! Curated trade-domain entry points.
diff --git a/crates/radroots/src/transport.rs b/crates/radroots/src/transport.rs
@@ -0,0 +1 @@
+//! Curated transport entry points.
diff --git a/crates/radroots/tests/public_surface.rs b/crates/radroots/tests/public_surface.rs
@@ -0,0 +1,7 @@
+#[test]
+fn approved_module_skeleton_is_public() {
+ #[allow(unused_imports)]
+ use radroots::{
+ client, event, farm, identity, knowledge, listing, signing, storage, sync, trade, transport,
+ };
+}
diff --git a/tools/xtask/src/check.rs b/tools/xtask/src/check.rs
@@ -30,6 +30,30 @@ const PACKAGE_HOMEPAGE: &str = "https://radroots.org";
const PACKAGE_REPOSITORY_URL: &str = "git+https://github.com/radrootslabs/sdk.git";
const PUBLISH_ACCESS: &str = "public";
const ALLOWED_RETICULUM_PREVIEW_PACKAGE: &str = "radroots-transport-reticulum";
+const RADROOTS_FACADE_MODULES: [&str; 11] = [
+ "client",
+ "event",
+ "farm",
+ "identity",
+ "knowledge",
+ "listing",
+ "signing",
+ "storage",
+ "sync",
+ "trade",
+ "transport",
+];
+const RADROOTS_FACADE_FEATURES: [&str; 9] = [
+ "client",
+ "default",
+ "full",
+ "geonames",
+ "knowledge",
+ "native",
+ "nip46",
+ "nostr",
+ "radrootsd",
+];
#[derive(Debug, Deserialize)]
struct PnpmPackEntry {
@@ -119,6 +143,7 @@ pub fn check() -> Result<(), String> {
let root = workspace_root()?;
crate::architecture::validate(&root)?;
check_publication_policy(&root)?;
+ check_radroots_facade_scaffold(&root)?;
validate_sdk_contracts(&root)?;
check_sdk_feature_matrix(&root)?;
check_forbidden_packages(&root)?;
@@ -131,6 +156,138 @@ pub fn check() -> Result<(), String> {
Ok(())
}
+fn check_radroots_facade_scaffold(root: &Path) -> Result<(), String> {
+ let manifest_path = root.join("crates/radroots/Cargo.toml");
+ let manifest_raw = fs::read_to_string(&manifest_path)
+ .map_err(|error| format!("failed to read {}: {error}", manifest_path.display()))?;
+ let manifest = manifest_raw
+ .parse::<toml::Value>()
+ .map_err(|error| format!("failed to parse {}: {error}", manifest_path.display()))?;
+ let package = manifest
+ .get("package")
+ .and_then(toml::Value::as_table)
+ .ok_or_else(|| "crates/radroots/Cargo.toml must define [package]".to_owned())?;
+ for (field, expected) in [
+ ("name", "radroots"),
+ (
+ "description",
+ "Curated ordinary-user Rust facade for Radroots",
+ ),
+ ("readme", "README.md"),
+ ] {
+ if package.get(field).and_then(toml::Value::as_str) != Some(expected) {
+ return Err(format!(
+ "crates/radroots/Cargo.toml package.{field} must be {expected}"
+ ));
+ }
+ }
+ if package.get("publish").and_then(toml::Value::as_bool) != Some(false) {
+ return Err("radroots facade must remain publish = false during the scaffold".to_owned());
+ }
+ for field in [
+ "version",
+ "edition",
+ "rust-version",
+ "license",
+ "repository",
+ "homepage",
+ ] {
+ let inherited = package
+ .get(field)
+ .and_then(toml::Value::as_table)
+ .and_then(|value| value.get("workspace"))
+ .and_then(toml::Value::as_bool);
+ if inherited != Some(true) {
+ return Err(format!(
+ "crates/radroots/Cargo.toml package.{field} must inherit workspace metadata"
+ ));
+ }
+ }
+ let library = manifest
+ .get("lib")
+ .and_then(toml::Value::as_table)
+ .ok_or_else(|| "crates/radroots/Cargo.toml must define [lib]".to_owned())?;
+ if library.get("name").and_then(toml::Value::as_str) != Some("radroots")
+ || library.get("path").and_then(toml::Value::as_str) != Some("src/lib.rs")
+ {
+ return Err("radroots facade [lib] must use name radroots and path src/lib.rs".to_owned());
+ }
+ for dependency_table in ["dependencies", "dev-dependencies", "build-dependencies"] {
+ if manifest
+ .get(dependency_table)
+ .and_then(toml::Value::as_table)
+ .is_some_and(|dependencies| !dependencies.is_empty())
+ {
+ return Err(format!(
+ "radroots scaffold must not depend on private migration packages through [{dependency_table}]"
+ ));
+ }
+ }
+
+ let features = manifest
+ .get("features")
+ .and_then(toml::Value::as_table)
+ .ok_or_else(|| "crates/radroots/Cargo.toml must define [features]".to_owned())?;
+ let actual_features = features.keys().map(String::as_str).collect::<BTreeSet<_>>();
+ let expected_features = RADROOTS_FACADE_FEATURES
+ .into_iter()
+ .collect::<BTreeSet<_>>();
+ if actual_features != expected_features {
+ return Err(format!(
+ "radroots facade features must be exactly {}; found {}",
+ expected_features.into_iter().collect::<Vec<_>>().join(", "),
+ actual_features.into_iter().collect::<Vec<_>>().join(", ")
+ ));
+ }
+ let defaults = features
+ .get("default")
+ .and_then(toml::Value::as_array)
+ .ok_or_else(|| "radroots facade default feature must be an array".to_owned())?;
+ if defaults.len() != 1 || defaults[0].as_str() != Some("client") {
+ return Err("radroots facade default feature must be exactly client".to_owned());
+ }
+
+ let source_path = root.join("crates/radroots/src/lib.rs");
+ let source = fs::read_to_string(&source_path)
+ .map_err(|error| format!("failed to read {}: {error}", source_path.display()))?;
+ let actual_modules = source
+ .lines()
+ .filter_map(|line| {
+ line.trim()
+ .strip_prefix("pub mod ")
+ .and_then(|module| module.strip_suffix(';'))
+ })
+ .collect::<BTreeSet<_>>();
+ let expected_modules = RADROOTS_FACADE_MODULES.into_iter().collect::<BTreeSet<_>>();
+ if actual_modules != expected_modules {
+ return Err(format!(
+ "radroots facade modules must be exactly {}; found {}",
+ expected_modules.into_iter().collect::<Vec<_>>().join(", "),
+ actual_modules.into_iter().collect::<Vec<_>>().join(", ")
+ ));
+ }
+ if source.contains("pub mod sdk") || source.contains("pub use radroots_sdk") {
+ return Err(
+ "radroots facade must not expose an sdk namespace or broad SDK re-export".to_owned(),
+ );
+ }
+ for module in RADROOTS_FACADE_MODULES {
+ if !root
+ .join("crates/radroots/src")
+ .join(format!("{module}.rs"))
+ .is_file()
+ {
+ return Err(format!(
+ "radroots facade module file {module}.rs is missing"
+ ));
+ }
+ }
+ if !root.join("crates/radroots/README.md").is_file() {
+ return Err("radroots facade README.md is missing".to_owned());
+ }
+ Ok(())
+}
+
fn check_publication_policy(root: &Path) -> Result<(), String> {
let policy_path = root.join("contracts/releases/publication.toml");
let raw = fs::read_to_string(&policy_path)