commit 0b180bd81b69d5f6e39418808eed39f8937aa524
parent a8487b1d3f5ee1d1086ebd03a91b14e99e303ef2
Author: triesap <tyson@radroots.org>
Date: Mon, 27 Jul 2026 19:14:15 +0000
identity: remove filesystem and runtime-path behavior
- Delete identity-owned JSON files, default paths, and legacy storage errors.
- Remove runtime, protected-store, path, vault, tracing, and tempfile edges.
- Prove the package manifest and public API exclude host persistence behavior.
- Preserve portable std, serde, no-default, wasm, and documentation coverage.
Diffstat:
9 files changed, 37 insertions(+), 161 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -4614,15 +4614,9 @@ name = "radroots_identity"
version = "0.1.0"
dependencies = [
"k256",
- "radroots_protected_store",
- "radroots_runtime",
- "radroots_runtime_paths",
- "radroots_secret_vault",
"serde",
"serde_json",
- "tempfile",
"thiserror 2.0.18",
- "tracing",
]
[[package]]
diff --git a/crates/identity/Cargo.toml b/crates/identity/Cargo.toml
@@ -16,36 +16,17 @@ readme = "README.md"
name = "radroots_identity"
[features]
-default = ["std", "serde", "json-file"]
-std = [
- "dep:radroots_protected_store",
- "dep:radroots_runtime_paths",
- "dep:radroots_secret_vault",
- "dep:serde_json",
- "dep:tracing",
- "thiserror/std",
-]
+default = ["std", "serde"]
+std = ["thiserror/std"]
serde = ["dep:serde"]
-json-file = ["std", "serde", "dep:radroots_runtime"]
[dependencies]
k256 = { version = "0.13", default-features = false, features = ["arithmetic"] }
-radroots_runtime = { workspace = true, optional = true }
-radroots_protected_store = { workspace = true, optional = true, features = [
- "std",
-] }
-radroots_runtime_paths = { workspace = true, optional = true }
-radroots_secret_vault = { workspace = true, optional = true, features = [
- "std",
-] }
serde = { workspace = true, optional = true }
-serde_json = { workspace = true, optional = true }
thiserror = { version = "2", default-features = false }
-tracing = { workspace = true, optional = true }
[dev-dependencies]
serde_json = { workspace = true, features = ["std"] }
-tempfile = { workspace = true }
[lints]
workspace = true
diff --git a/crates/identity/README.md b/crates/identity/README.md
@@ -8,8 +8,7 @@ account, and profile value types for the Radroots package family.
* validated canonical public keys, identity IDs, and account IDs;
* public identity profiles and normalized usernames;
* no raw secret keys, key generation, nsec/NIP-49 helpers, or secret export;
- * transitional public-profile JSON file helpers pending their extraction to
- the host storage layer.
+ * no filesystem, runtime-path, protected-store, or secret-vault behavior.
See `docs/migration/identity.md` in the repository for the approved signing,
Nostr-key, secrets, and storage ownership boundaries.
diff --git a/crates/identity/src/error.rs b/crates/identity/src/error.rs
@@ -1,10 +1,5 @@
use thiserror::Error;
-#[cfg(all(feature = "std", feature = "json-file"))]
-use radroots_runtime::RuntimeJsonError;
-#[cfg(feature = "std")]
-use std::{io, path::PathBuf};
-
/// Errors produced while validating public identity values.
#[non_exhaustive]
#[derive(Debug, Error)]
@@ -39,30 +34,3 @@ pub enum Error {
#[error("username dots cannot be leading, trailing, or consecutive")]
InvalidUsernameDotPlacement,
}
-
-/// Transitional errors from the legacy filesystem identity API.
-#[cfg(feature = "std")]
-#[derive(Debug, Error)]
-pub enum IdentityError {
- #[error("identity file missing at {0}")]
- NotFound(PathBuf),
-
- #[error("failed to read identity file at {0}: {1}")]
- Read(PathBuf, #[source] io::Error),
-
- #[error("failed to create identity directory {0}: {1}")]
- CreateDir(PathBuf, #[source] io::Error),
-
- #[error("failed to write identity file at {0}: {1}")]
- Write(PathBuf, #[source] io::Error),
-
- #[error("invalid identity JSON: {0}")]
- InvalidJson(#[from] serde_json::Error),
-
- #[cfg(feature = "json-file")]
- #[error(transparent)]
- Store(#[from] RuntimeJsonError),
-
- #[error(transparent)]
- Paths(#[from] radroots_runtime_paths::RadrootsRuntimePathsError),
-}
diff --git a/crates/identity/src/lib.rs b/crates/identity/src/lib.rs
@@ -1,6 +1,13 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#![forbid(unsafe_code)]
+//! Portable public identity and account values.
+//!
+//! Host filesystem and runtime-path APIs are intentionally absent:
+//!
+//! ```compile_fail
+//! use radroots_identity::{storage, IdentityError};
+//! ```
extern crate alloc;
@@ -8,16 +15,10 @@ pub mod account;
pub mod error;
pub mod key;
pub mod profile;
-#[cfg(feature = "json-file")]
-pub mod storage;
pub mod username;
pub use account::AccountId;
pub use error::Error;
-#[cfg(feature = "std")]
-pub use error::IdentityError;
pub use key::{IdentityId, PublicKey};
pub use profile::{Profile, PublicIdentity};
-#[cfg(feature = "json-file")]
-pub use storage::{load_identity_profile, store_identity_profile};
pub use username::Username;
diff --git a/crates/identity/src/storage.rs b/crates/identity/src/storage.rs
@@ -1,87 +0,0 @@
-//! Transitional filesystem helpers for public profile snapshots.
-//!
-//! Filesystem ownership is removed from this package in the next ordered
-//! migration checkpoint.
-
-use std::{fs, path::Path};
-
-use crate::{IdentityError, PublicIdentity};
-
-/// Stores a validated public identity profile as JSON.
-pub fn store_identity_profile(
- path: impl AsRef<Path>,
- identity: &PublicIdentity,
-) -> Result<(), IdentityError> {
- store_identity_profile_path(path.as_ref(), identity)
-}
-
-fn store_identity_profile_path(
- path: &Path,
- identity: &PublicIdentity,
-) -> Result<(), IdentityError> {
- if let Some(parent) = path.parent().filter(|value| !value.as_os_str().is_empty()) {
- fs::create_dir_all(parent)
- .map_err(|source| IdentityError::CreateDir(parent.to_path_buf(), source))?;
- }
- let encoded = serde_json::to_vec_pretty(identity)?;
- fs::write(path, encoded).map_err(|source| IdentityError::Write(path.to_path_buf(), source))
-}
-
-/// Loads and revalidates a public identity profile from JSON.
-pub fn load_identity_profile(path: impl AsRef<Path>) -> Result<PublicIdentity, IdentityError> {
- load_identity_profile_path(path.as_ref())
-}
-
-fn load_identity_profile_path(path: &Path) -> Result<PublicIdentity, IdentityError> {
- let encoded = fs::read(path).map_err(|source| {
- if source.kind() == std::io::ErrorKind::NotFound {
- IdentityError::NotFound(path.to_path_buf())
- } else {
- IdentityError::Read(path.to_path_buf(), source)
- }
- })?;
- serde_json::from_slice(&encoded).map_err(IdentityError::from)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::{Profile, PublicKey, Username};
-
- const ALICE: &str = "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df";
-
- fn fixture_identity() -> PublicIdentity {
- PublicIdentity::new(PublicKey::from_hex(ALICE).unwrap())
- .with_profile(Profile::new().with_username(Username::parse("alice.farm").unwrap()))
- }
-
- #[test]
- fn public_profile_file_round_trip_revalidates_identity() {
- let directory = tempfile::tempdir().unwrap();
- let path = directory.path().join("profiles/alice.json");
- let identity = fixture_identity();
-
- store_identity_profile(&path, &identity).unwrap();
- assert_eq!(load_identity_profile(&path).unwrap(), identity);
- }
-
- #[test]
- fn public_profile_file_rejects_mismatched_or_missing_data() {
- let directory = tempfile::tempdir().unwrap();
- let path = directory.path().join("alice.json");
- let mut value = serde_json::to_value(fixture_identity()).unwrap();
- value["id"] = serde_json::Value::String(
- "e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af".into(),
- );
- fs::write(&path, serde_json::to_vec(&value).unwrap()).unwrap();
-
- assert!(matches!(
- load_identity_profile(&path),
- Err(IdentityError::InvalidJson(_))
- ));
- assert!(matches!(
- load_identity_profile(directory.path().join("missing.json")),
- Err(IdentityError::NotFound(_))
- ));
- }
-}
diff --git a/crates/identity/tests/package_boundary.rs b/crates/identity/tests/package_boundary.rs
@@ -0,0 +1,22 @@
+const MANIFEST: &str = include_str!("../Cargo.toml");
+
+#[test]
+fn manifest_has_no_host_persistence_feature_or_dependency() {
+ for forbidden in [
+ "json-file",
+ "radroots_protected_store",
+ "radroots_runtime",
+ "radroots_runtime_paths",
+ "radroots_secret_vault",
+ "tracing",
+ "tempfile",
+ ] {
+ assert!(
+ !MANIFEST.contains(forbidden),
+ "identity manifest must not contain host persistence edge {forbidden}"
+ );
+ }
+
+ assert!(MANIFEST.contains("default = [\"std\", \"serde\"]"));
+ assert!(MANIFEST.contains("std = [\"thiserror/std\"]"));
+}
diff --git a/crates/nostr_accounts/Cargo.toml b/crates/nostr_accounts/Cargo.toml
@@ -29,10 +29,7 @@ os-keyring = ["std", "radroots_secret_vault/os-keyring"]
nostrdb-bridge = ["std", "dep:radroots_nostrdb"]
[dependencies]
-radroots_identity = { workspace = true, optional = true, default-features = false, features = [
- "std",
- "json-file",
-] }
+radroots_identity = { workspace = true, optional = true, default-features = false, features = ["std"] }
radroots_nostr_signer = { workspace = true, optional = true }
radroots_nostrdb = { workspace = true, optional = true, default-features = false, features = [
"nostrdb",
diff --git a/docs/migration/identity.md b/docs/migration/identity.md
@@ -19,6 +19,7 @@ The approved destination boundaries are:
`radroots_storage_sqlite` for durable secret persistence.
Those destination APIs are introduced by their ordered release checkpoints.
-Until then, callers must not recreate secret ownership in `radroots_identity`
-or add a compatibility shim. Public identity profile file helpers remain only
-for the immediately following filesystem-extraction checkpoint.
+Until then, callers must not recreate secret ownership or persistence in
+`radroots_identity` or add a compatibility shim. The former identity/profile
+file helpers, default paths, runtime-path resolution, and encrypted storage
+APIs have been removed from this package.