commit 44ce8f2acb9d46a5d8b0eb912d9e7047a542aad0
parent 2c5927503fd34e3110906b3bbff46c42eabcffa7
Author: triesap <tyson@radroots.org>
Date: Sat, 1 Aug 2026 07:24:33 +0000
secrets: align package manifest and module root
- Freeze the final radroots_secrets identity at version 0.1.0-alpha.
- Declare the approved feature vocabulary without Radroots dependencies.
- Add the no_std crate root and eight governed module boundaries.
- Verify package, clippy, no-default, wasm, and architecture gates.
Diffstat:
11 files changed, 104 insertions(+), 0 deletions(-)
diff --git a/crates/secrets/Cargo.toml b/crates/secrets/Cargo.toml
@@ -9,10 +9,19 @@ repository.workspace = true
homepage.workspace = true
authors.workspace = true
readme = "README.md"
+documentation = "https://docs.rs/radroots_secrets"
publish = false
[lib]
name = "radroots_secrets"
+[features]
+default = ["std", "serde"]
+std = []
+serde = []
+memory = ["std"]
+file = ["std"]
+keyring = ["std"]
+
[lints]
workspace = true
diff --git a/crates/secrets/src/envelope.rs b/crates/secrets/src/envelope.rs
@@ -0,0 +1 @@
+//! Versioned encrypted-envelope contracts.
diff --git a/crates/secrets/src/error.rs b/crates/secrets/src/error.rs
@@ -0,0 +1 @@
+//! Normalized secret-operation errors.
diff --git a/crates/secrets/src/file.rs b/crates/secrets/src/file.rs
@@ -0,0 +1 @@
+//! Explicit file-backed secret adapters.
diff --git a/crates/secrets/src/id.rs b/crates/secrets/src/id.rs
@@ -0,0 +1 @@
+//! Typed secret identifiers and references.
diff --git a/crates/secrets/src/keyring.rs b/crates/secrets/src/keyring.rs
@@ -0,0 +1 @@
+//! Explicit operating-system keyring adapters.
diff --git a/crates/secrets/src/lib.rs b/crates/secrets/src/lib.rs
@@ -1 +1,15 @@
//! Secret material and protected-storage abstractions for Radroots.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub mod envelope;
+pub mod error;
+#[cfg(feature = "file")]
+pub mod file;
+pub mod id;
+#[cfg(feature = "keyring")]
+pub mod keyring;
+#[cfg(feature = "memory")]
+pub mod memory;
+pub mod provider;
+pub mod wrapping;
diff --git a/crates/secrets/src/memory.rs b/crates/secrets/src/memory.rs
@@ -0,0 +1 @@
+//! Deterministic in-process secret adapters.
diff --git a/crates/secrets/src/provider.rs b/crates/secrets/src/provider.rs
@@ -0,0 +1 @@
+//! Secret-provider contracts and capability selection.
diff --git a/crates/secrets/src/wrapping.rs b/crates/secrets/src/wrapping.rs
@@ -0,0 +1 @@
+//! Data-key wrapping contracts.
diff --git a/crates/secrets/tests/package_boundary.rs b/crates/secrets/tests/package_boundary.rs
@@ -0,0 +1,73 @@
+use std::collections::BTreeSet;
+
+const MANIFEST: &str = include_str!("../Cargo.toml");
+const ROOT: &str = include_str!("../src/lib.rs");
+
+#[test]
+fn manifest_has_final_identity_features_and_no_radroots_dependencies() {
+ for required in [
+ "name = \"radroots_secrets\"",
+ "version = \"0.1.0-alpha\"",
+ "publish = false",
+ "documentation = \"https://docs.rs/radroots_secrets\"",
+ "[lib]\nname = \"radroots_secrets\"",
+ "default = [\"std\", \"serde\"]",
+ "memory = [\"std\"]",
+ "file = [\"std\"]",
+ "keyring = [\"std\"]",
+ ] {
+ assert!(
+ MANIFEST.contains(required),
+ "manifest is missing `{required}`"
+ );
+ }
+
+ assert_eq!(
+ table_keys(MANIFEST, "[features]"),
+ BTreeSet::from(["default", "file", "keyring", "memory", "serde", "std"])
+ );
+ assert!(
+ table_keys(MANIFEST, "[dependencies]")
+ .into_iter()
+ .all(|dependency| !dependency.starts_with("radroots_")),
+ "security SPI must not depend on another Radroots package"
+ );
+}
+
+#[test]
+fn crate_root_contains_only_the_approved_module_skeleton() {
+ assert!(ROOT.contains("#![cfg_attr(not(feature = \"std\"), no_std)]"));
+ assert_eq!(
+ declarations(ROOT, "pub mod "),
+ BTreeSet::from([
+ "envelope", "error", "file", "id", "keyring", "memory", "provider", "wrapping",
+ ])
+ );
+ assert!(
+ ROOT.lines()
+ .map(str::trim)
+ .all(|line| !line.starts_with("pub use ")),
+ "behavioral root exports belong to later checkpoints"
+ );
+}
+
+fn table_keys<'a>(source: &'a str, table: &str) -> BTreeSet<&'a str> {
+ let Some((_, body)) = source.split_once(table) else {
+ return BTreeSet::new();
+ };
+ body.lines()
+ .skip(1)
+ .take_while(|line| !line.starts_with('['))
+ .filter_map(|line| line.split_once('=').map(|(key, _)| key.trim()))
+ .filter(|key| !key.is_empty())
+ .collect()
+}
+
+fn declarations<'a>(source: &'a str, prefix: &str) -> BTreeSet<&'a str> {
+ source
+ .lines()
+ .map(str::trim)
+ .filter_map(|line| line.strip_prefix(prefix))
+ .filter_map(|line| line.strip_suffix(';'))
+ .collect()
+}