commit 16ded0a690655fedd6da0e8a427785c6e76098d3
parent 2dd2a8dc1e18f6f1b112be63f8519f529229669f
Author: triesap <tyson@radroots.org>
Date: Sat, 1 Aug 2026 11:57:44 +0000
secrets: migrate workspace consumers
- activate the final SQLite secrets dependency edge
- quarantine exact publish-frozen predecessor consumers
- enforce package and consumer source boundaries
- record ordered migration and removal gates
Diffstat:
6 files changed, 144 insertions(+), 0 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -5126,6 +5126,9 @@ version = "0.1.0-alpha"
[[package]]
name = "radroots_storage_sqlite"
version = "0.1.0-alpha"
+dependencies = [
+ "radroots_secrets",
+]
[[package]]
name = "radroots_sync"
diff --git a/crates/secrets/tests/consumer_migration.rs b/crates/secrets/tests/consumer_migration.rs
@@ -0,0 +1,78 @@
+use std::collections::BTreeSet;
+use std::fs;
+use std::path::{Path, PathBuf};
+
+const DEVIATIONS: &str = include_str!("../../../docs/implementation/deviations.toml");
+
+#[test]
+fn legacy_secret_dependencies_are_confined_to_publish_frozen_quarantines() {
+ let workspace = Path::new(env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .and_then(Path::parent)
+ .expect("workspace root");
+ let crates = workspace.join("crates");
+ let mut manifests = Vec::new();
+ collect_manifests(&crates, &mut manifests);
+ let mut legacy_consumers = BTreeSet::new();
+
+ for path in manifests {
+ let manifest = fs::read_to_string(&path).expect("read package manifest");
+ if manifest.contains("radroots_secret_vault")
+ || manifest.contains("radroots_protected_store")
+ {
+ assert!(
+ manifest.contains("publish = false"),
+ "legacy secret consumer must remain publish-frozen: {}",
+ path.display()
+ );
+ let package = package_name(&manifest).expect("package name");
+ legacy_consumers.insert(package.to_owned());
+ }
+ }
+
+ assert_eq!(
+ legacy_consumers,
+ BTreeSet::from([
+ "radroots_nostr_accounts".to_owned(),
+ "radroots_protected_store".to_owned(),
+ "radroots_runtime".to_owned(),
+ "radroots_secret_vault".to_owned(),
+ "radroots_simplex_agent_store".to_owned(),
+ ])
+ );
+}
+
+#[test]
+fn quarantine_has_exact_future_removal_gates() {
+ for required in [
+ "id = \"RCRV1-DEV-008\"",
+ "affected_steps = [\"153\", \"155\", \"171\", \"179\", \"226\", \"288\", \"293\", \"313\"]",
+ "Step 179 transfers canonical private storage",
+ "Step 313 removes every remaining compatibility package and legacy name",
+ ] {
+ assert!(
+ DEVIATIONS.contains(required),
+ "secret consumer quarantine is missing `{required}`"
+ );
+ }
+}
+
+fn collect_manifests(root: &Path, manifests: &mut Vec<PathBuf>) {
+ for entry in fs::read_dir(root).expect("read crates directory") {
+ let path = entry.expect("crate entry").path();
+ if path.is_dir() {
+ let manifest = path.join("Cargo.toml");
+ if manifest.is_file() {
+ manifests.push(manifest);
+ }
+ }
+ }
+}
+
+fn package_name(manifest: &str) -> Option<&str> {
+ let package = manifest.split_once("[package]")?.1;
+ package
+ .lines()
+ .skip(1)
+ .find_map(|line| line.trim().strip_prefix("name = \"")?.strip_suffix('"'))
+}
diff --git a/crates/storage_sqlite/Cargo.toml b/crates/storage_sqlite/Cargo.toml
@@ -14,5 +14,8 @@ publish = false
[lib]
name = "radroots_storage_sqlite"
+[dependencies]
+radroots_secrets = { workspace = true, default-features = false }
+
[lints]
workspace = true
diff --git a/crates/storage_sqlite/tests/package_boundary.rs b/crates/storage_sqlite/tests/package_boundary.rs
@@ -0,0 +1,33 @@
+use std::collections::BTreeSet;
+
+const MANIFEST: &str = include_str!("../Cargo.toml");
+const ROOT: &str = include_str!("../src/lib.rs");
+
+#[test]
+fn sqlite_storage_declares_the_final_secret_boundary() {
+ assert_eq!(
+ dependency_keys(MANIFEST),
+ BTreeSet::from(["radroots_secrets"])
+ );
+ for forbidden in [
+ "radroots_protected_store",
+ "radroots_secret_vault",
+ "radroots_nostr_accounts",
+ ] {
+ assert!(!MANIFEST.contains(forbidden));
+ assert!(!ROOT.contains(forbidden));
+ }
+}
+
+fn dependency_keys(manifest: &str) -> BTreeSet<&str> {
+ manifest
+ .split_once("[dependencies]")
+ .map(|(_, dependencies)| dependencies)
+ .unwrap_or_default()
+ .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()
+}
diff --git a/docs/implementation/DEVIATIONS.md b/docs/implementation/DEVIATIONS.md
@@ -14,6 +14,7 @@ silently change `radroots.crates.release.v1`.
| `RCRV1-DEV-004` | 098, 155, 225, 260, 268, 294, 298-299, 301-304, 314 | Enforce a temporary 90% four-dimension coverage baseline during heavy development; restore 100% only through a future explicit contract update. |
| `RCRV1-DEV-005` | 013, 019-026, 027-315 | Pin every Rust crate and internal Radroots dependency in `radrootslabs/lib` to exactly `0.1.0-alpha` until further explicit authority. |
| `RCRV1-DEV-007` | 122, 170, 235, 305 | Remove the predecessor monolithic transport SPI now; quarantine publish-frozen runtime, SDK, CLI, and daemon consumer shims until their explicit removal gates. |
+| `RCRV1-DEV-008` | 153, 155, 171, 179, 226, 288, 293, 313 | Activate final secrets dependency edges now; quarantine legacy vault/store consumers until their ordered storage, SDK, downstream, and final-removal gates. |
## Record template
diff --git a/docs/implementation/deviations.toml b/docs/implementation/deviations.toml
@@ -37,6 +37,32 @@ normative_architecture_change = false
adr_required = false
[[deviation]]
+id = "RCRV1-DEV-008"
+date = "2026-08-01"
+status = "active"
+approval = "All code-review recommendations and the full multi-RCLD implementation sequence were explicitly approved by the user."
+affected_steps = ["153", "155", "171", "179", "226", "288", "293", "313"]
+spec_anchors = [
+ "docs/specs/radroots_crates_release_v1.md#12-radroots_secrets",
+ "docs/specs/radroots_crates_release_v1.md#20-current-to-target-migration-map",
+]
+source_evidence = [
+ "The final radroots_storage_sqlite scaffold can consume radroots_secrets immediately and has no predecessor secret dependency.",
+ "Step 179, not Step 153, owns transfer of the current SDK private database and its encrypted records into canonical SQLite storage.",
+ "Mixed publish-frozen runtime, Nostr-account, SimpleX preview, SDK private-store, Myc, and other external hosts still require predecessor vault/store behavior until their ordered migration steps.",
+]
+replacement_action = "Activate the final radroots_storage_sqlite and SDK dependency edges in Step 153; confine predecessor vault/store imports to exact publish-frozen quarantine packages and the SDK private-store module; Step 179 transfers canonical private storage, Steps 226/288/293 migrate the remaining SDK and downstream consumers, and Step 313 removes every remaining compatibility package and legacy name."
+verification = [
+ "Consumer-migration tests enumerate every lib package manifest that still names radroots_secret_vault or radroots_protected_store and reject any unapproved or publishable consumer.",
+ "Storage SQLite package-boundary tests require radroots_secrets and reject all predecessor secret package names.",
+ "SDK source-boundary tests confine predecessor imports to private_store.rs and require the final optional radroots_secrets dependency edge.",
+ "Step 155 release-policy validation keeps every quarantine package non-publishable until its exact removal gate.",
+]
+unresolved_risk = "Publish-frozen compatibility code remains reachable inside legacy runtime/private-preview paths until Steps 179, 226, 288, 293, and 313; no package-realistic publication may proceed while it remains."
+normative_architecture_change = false
+adr_required = false
+
+[[deviation]]
id = "RCRV1-DEV-007"
date = "2026-07-30"
status = "active"