commit 53bbf9b0025a5d7a258d80a7e250a748abf3069a
parent 67b85e49a8c5870422ae5cf90f2488385ced1bd1
Author: triesap <tyson@radroots.org>
Date: Tue, 28 Jul 2026 06:18:49 +0000
identity: document public api contract
- Make the package README the crate-level rustdoc authority.
- Add a compiling public-identity example and module guidance.
- Record the pinned, reviewable public API baseline.
- Verify features, no-std targets, docs, and the root contract.
Diffstat:
9 files changed, 567 insertions(+), 28 deletions(-)
diff --git a/crates/identity/README.md b/crates/identity/README.md
@@ -1,24 +1,146 @@
# radroots_identity
-This is the README for `radroots_identity`, which provides public identity,
-account, and profile value types for the Radroots package family.
+`radroots_identity` provides the portable public identity and account values
+shared by Radroots packages: canonical public keys, identity and account IDs,
+public profiles, normalized usernames, and transport-neutral account status.
-## Overview
+The crate supports `no_std` environments with `alloc`. It performs no I/O,
+starts no tasks or threads, reads no clocks or process state, and owns no key
+generation, secrets, signing, persistence, networking, or account selection.
- * 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;
- * no filesystem, runtime-path, protected-store, or secret-vault behavior.
+## Example
-See `docs/migration/identity.md` in the repository for the approved signing,
-Nostr-key, secrets, and storage ownership boundaries.
+Construct public identities from validated keys and derive account identifiers
+without exposing or inventing secret material:
-## Copyright
+```rust
+use radroots_identity::{AccountId, Profile, PublicIdentity, PublicKey, Username};
-Except as otherwise noted, all files in the `radroots_identity` distribution
-are
+let public_key = PublicKey::from_hex(
+ "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df",
+)?;
+let username = Username::parse(" Alice.Farm ")?;
+let identity = PublicIdentity::new(public_key)
+ .with_profile(Profile::new().with_username(username));
+let account_id = AccountId::from_public_identity(&identity);
- Copyright (c) 2025 Tyson Lupul
+assert_eq!(identity.id().as_bytes(), public_key.as_bytes());
+assert_eq!(account_id.as_bytes(), public_key.as_bytes());
+assert_eq!(
+ identity.profile().and_then(Profile::username).map(Username::as_str),
+ Some("alice.farm"),
+);
+# Ok::<(), radroots_identity::Error>(())
+```
-For information on usage and redistribution, and for a DISCLAIMER OF ALL
-WARRANTIES, see LICENSE included in the `radroots_identity` distribution.
+The same program is available as the
+[`public_identity`](examples/public_identity.rs) example.
+
+## Public API
+
+The crate root intentionally exports `AccountId`, `IdentityId`,
+`PublicIdentity`, `PublicKey`, `Profile`, `Username`, and the aggregate
+[`Error`](https://docs.rs/radroots_identity/latest/radroots_identity/enum.Error.html).
+Focused values remain in these modules:
+
+- `account` — derived account IDs, public account records, and observable
+ readiness status;
+- `key` — validated 32-byte x-only secp256k1 public keys and identity IDs;
+- `profile` — public identity metadata and the invariant-matched public
+ identity aggregate;
+- `username` — canonical username parsing, bounds, and normalization.
+
+The normative responsibility and dependency boundary are defined by the
+[`radroots_identity` package charter](../../docs/specs/radroots_crates_release_v1.md).
+The reviewed pre-release surface is recorded in
+[`docs/api`](../../docs/api/README.md). Signing, Nostr-key, secret-provider,
+and storage ownership is documented in the
+[`identity` migration boundary](../../docs/migration/identity.md).
+
+## Features
+
+| Feature | Default | Effect |
+| --- | --- | --- |
+| `std` | yes | Implements the standard error integration; value behavior remains portable. |
+| `serde` | yes | Implements checked canonical serialization and deserialization for public values. |
+
+Disabling default features leaves the `no_std` + `alloc` value model. Features
+are additive; enabling one does not select a runtime, backend, global account,
+secret source, or side effect.
+
+## Invariants and untrusted input
+
+`PublicKey`, `IdentityId`, and `AccountId` store exactly 32 validated bytes.
+Text parsing accepts exact-width hexadecimal input and emits canonical
+lowercase hexadecimal output. `PublicKey` validation confirms that the x-only
+bytes identify a secp256k1 curve point. The three Rust types remain distinct so
+callers cannot accidentally substitute an account identifier for a public key.
+
+`PublicIdentity` derives its `IdentityId` from its `PublicKey` and rejects
+separately decoded parts that do not match. `account::Record` derives its
+`AccountId` from its public identity and rejects mismatched IDs or an update
+timestamp before creation. `Record::touch_updated` rejects time reversal and
+leaves the timestamp unchanged on error. `Record::set_label` does not read a
+clock or update the timestamp; the composing host owns clock policy and must
+advance the timestamp explicitly when appropriate.
+
+`Username` trims surrounding whitespace, folds ASCII uppercase to lowercase,
+enforces its byte bounds, and rejects unsupported characters or leading,
+trailing, and consecutive dots. Account labels are opaque host-facing strings:
+callers must apply their own content and resource limits before accepting them
+from untrusted sources.
+
+## Serialization
+
+With `serde`, identifiers are canonical lowercase 64-character hexadecimal
+strings and usernames are canonical strings. Profiles, public identities, and
+account records use named fields and reject unknown fields. Deserializing a
+public identity or account record re-applies identifier and timestamp
+invariants; invalid native state is not admitted through the supported serde
+surface. Account status variant names use `snake_case`.
+
+Serialization contains public metadata only and does not add confidentiality,
+authenticity, event signing, or transport framing. Versioned cross-process wire
+DTOs belong in `radroots_protocol` rather than this native value crate.
+
+## Execution and commit semantics
+
+Operations are synchronous and deterministic. Username parsing and account
+record construction may allocate in proportion to caller-supplied username or
+label text, so callers should cap untrusted input bytes before invoking them.
+There are no asynchronous cancellation points, deadlines, retries, callbacks,
+partial external effects, or durable commit points. A successful call returns
+or updates the complete in-memory value; an error returns before any durable
+commit because this crate owns no durable state.
+
+## Security boundary
+
+The crate forbids unsafe Rust. Public keys and identifiers are public data and
+must not be treated as proof of control; authentication requires a verified
+signature in the appropriate event or signing layer. This crate deliberately
+has no raw secret keys, key generation, nsec/NIP-49 helpers, keyrings, vaults,
+files, SQLite, runtime paths, signer sessions, or upstream Nostr events.
+
+```compile_fail
+use radroots_identity::{SecretKey, generate_keypair};
+```
+
+Do not recreate those responsibilities through compatibility wrappers around
+this crate. Compose the signing, Nostr, secrets, and storage packages named by
+the migration boundary instead.
+
+## Intended consumers
+
+`radroots_identity` is intended for lower-level Radroots event, signing,
+transport, storage, and domain packages and for integrators that need the
+portable public value model directly. Applications should normally begin with
+the curated `radroots` crate or the advanced `radroots_sdk` composition
+surface.
+
+The package is pre-1.0. Its durable responsibility and package identity are
+fixed, while API-breaking changes follow the workspace's pre-1.0 versioning
+policy.
+
+## License
+
+Licensed under either Apache-2.0 or MIT, at your option.
diff --git a/crates/identity/examples/public_identity.rs b/crates/identity/examples/public_identity.rs
@@ -0,0 +1,21 @@
+use radroots_identity::{AccountId, Profile, PublicIdentity, PublicKey, Username};
+
+fn main() -> Result<(), radroots_identity::Error> {
+ let public_key =
+ PublicKey::from_hex("585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df")?;
+ let username = Username::parse(" Alice.Farm ")?;
+ let identity =
+ PublicIdentity::new(public_key).with_profile(Profile::new().with_username(username));
+ let account_id = AccountId::from_public_identity(&identity);
+
+ assert_eq!(identity.id().as_bytes(), public_key.as_bytes());
+ assert_eq!(account_id.as_bytes(), public_key.as_bytes());
+ assert_eq!(
+ identity
+ .profile()
+ .and_then(Profile::username)
+ .map(Username::as_str),
+ Some("alice.farm"),
+ );
+ Ok(())
+}
diff --git a/crates/identity/src/account.rs b/crates/identity/src/account.rs
@@ -1,4 +1,14 @@
//! Public, transport-neutral account value types.
+//!
+//! [`AccountId`] is derived from the same canonical public bytes as an
+//! identity. [`Record`] combines that identifier with a [`PublicIdentity`], an
+//! optional host-facing label, and caller-supplied timestamps while preserving
+//! their cross-field invariants. [`Status`] reports only observable readiness;
+//! it contains no secret, signer, persistence, or host-selection state.
+//!
+//! This module does not read a clock or persist mutations. In particular,
+//! [`Record::set_label`] and [`Record::touch_updated`] are separate operations
+//! so the composing host retains explicit timestamp and commit policy.
use alloc::string::String;
diff --git a/crates/identity/src/key.rs b/crates/identity/src/key.rs
@@ -1,4 +1,10 @@
//! Canonical public-key and identity identifier value types.
+//!
+//! [`PublicKey`] validates a 32-byte x-only secp256k1 public key. [`IdentityId`]
+//! is a distinct Rust type derived from the same canonical bytes. Both values
+//! parse exact-width hexadecimal text, preserve fixed-width binary form, and
+//! emit lowercase hexadecimal text without exposing secret-key or Nostr
+//! bech32 behavior.
use crate::Error;
diff --git a/crates/identity/src/lib.rs b/crates/identity/src/lib.rs
@@ -1,19 +1,7 @@
+#![doc = include_str!("../README.md")]
#![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};
-//! ```
-//!
-//! Secret-bearing and upstream Nostr event APIs are intentionally absent:
-//!
-//! ```compile_fail
-//! use radroots_identity::{NostrEvent, SecretKey};
-//! ```
extern crate alloc;
diff --git a/crates/identity/src/profile.rs b/crates/identity/src/profile.rs
@@ -1,4 +1,12 @@
//! Public, transport-neutral identity profiles.
+//!
+//! [`Profile`] contains optional public metadata only. [`PublicIdentity`]
+//! couples a validated [`PublicKey`] to its derived [`IdentityId`] and rejects
+//! mismatched separately decoded parts. Empty profiles are omitted so one
+//! public identity has one canonical native representation.
+//!
+//! Profiles do not perform discovery, authorization, event verification,
+//! persistence, or secret handling.
use crate::{Error, IdentityId, PublicKey, Username};
diff --git a/crates/identity/src/username.rs b/crates/identity/src/username.rs
@@ -1,4 +1,9 @@
//! Canonical public usernames.
+//!
+//! [`Username`] trims surrounding whitespace, lowercases ASCII input, enforces
+//! [`MIN_LENGTH`] and [`MAX_LENGTH`], and accepts only the documented public
+//! character set and dot placement. Parsing and serde decoding use the same
+//! validator and always produce canonical text.
use alloc::string::String;
use core::{fmt, str::FromStr};
diff --git a/docs/api/README.md b/docs/api/README.md
@@ -31,3 +31,4 @@ expand a package beyond its charter.
| Package | Baseline | Charter |
| --- | --- | --- |
| `radroots_core` | [`radroots_core.txt`](radroots_core.txt) | [release V1 specification](../specs/radroots_crates_release_v1.md) |
+| `radroots_identity` | [`radroots_identity.txt`](radroots_identity.txt) | [release V1 specification](../specs/radroots_crates_release_v1.md) |
diff --git a/docs/api/radroots_identity.txt b/docs/api/radroots_identity.txt
@@ -0,0 +1,378 @@
+pub mod radroots_identity
+pub mod radroots_identity::account
+#[non_exhaustive] pub enum radroots_identity::account::Status
+pub radroots_identity::account::Status::NotConfigured
+pub radroots_identity::account::Status::PublicOnly
+pub radroots_identity::account::Status::PublicOnly::account: radroots_identity::account::Record
+pub radroots_identity::account::Status::Ready
+pub radroots_identity::account::Status::Ready::account: radroots_identity::account::Record
+impl radroots_identity::account::Status
+pub const fn radroots_identity::account::Status::account(&self) -> core::option::Option<&radroots_identity::account::Record>
+pub const fn radroots_identity::account::Status::is_ready(&self) -> bool
+#[repr(transparent)] pub struct radroots_identity::account::AccountId(_)
+impl radroots_identity::account::AccountId
+pub const radroots_identity::account::AccountId::BYTE_LENGTH: usize
+pub const radroots_identity::account::AccountId::HEX_LENGTH: usize
+pub const fn radroots_identity::account::AccountId::as_bytes(&self) -> &[u8; 32]
+pub fn radroots_identity::account::AccountId::from_bytes([u8; 32]) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::account::AccountId::from_hex(&str) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::account::AccountId::from_slice(&[u8]) -> core::result::Result<Self, radroots_identity::Error>
+pub const fn radroots_identity::account::AccountId::into_bytes(self) -> [u8; 32]
+pub fn radroots_identity::account::AccountId::to_hex(self) -> alloc::string::String
+impl radroots_identity::account::AccountId
+pub const fn radroots_identity::account::AccountId::from_identity_id(radroots_identity::key::IdentityId) -> Self
+pub const fn radroots_identity::account::AccountId::from_public_identity(&radroots_identity::profile::PublicIdentity) -> Self
+impl core::convert::AsRef<[u8]> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::as_ref(&self) -> &[u8]
+impl core::convert::From<&radroots_identity::profile::PublicIdentity> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(&radroots_identity::profile::PublicIdentity) -> Self
+impl core::convert::From<radroots_identity::key::IdentityId> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(radroots_identity::key::IdentityId) -> Self
+impl core::convert::TryFrom<&[u8]> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from(&[u8]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<&str> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<[u8; 32]> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from([u8; 32]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Err = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+pub struct radroots_identity::account::Record
+impl radroots_identity::account::Record
+pub const fn radroots_identity::account::Record::created_at_unix(&self) -> u64
+pub const fn radroots_identity::account::Record::id(&self) -> radroots_identity::account::AccountId
+pub fn radroots_identity::account::Record::label(&self) -> core::option::Option<&str>
+pub fn radroots_identity::account::Record::new(radroots_identity::profile::PublicIdentity, core::option::Option<alloc::string::String>, u64) -> Self
+pub const fn radroots_identity::account::Record::public_identity(&self) -> &radroots_identity::profile::PublicIdentity
+pub fn radroots_identity::account::Record::set_label(&mut self, core::option::Option<alloc::string::String>)
+pub fn radroots_identity::account::Record::touch_updated(&mut self, u64) -> core::result::Result<(), radroots_identity::Error>
+pub fn radroots_identity::account::Record::try_from_parts(radroots_identity::account::AccountId, radroots_identity::profile::PublicIdentity, core::option::Option<alloc::string::String>, u64, u64) -> core::result::Result<Self, radroots_identity::Error>
+pub const fn radroots_identity::account::Record::updated_at_unix(&self) -> u64
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::account::Record
+pub fn radroots_identity::account::Record::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+pub mod radroots_identity::key
+#[repr(transparent)] pub struct radroots_identity::key::IdentityId(_)
+impl radroots_identity::key::IdentityId
+pub const radroots_identity::key::IdentityId::BYTE_LENGTH: usize
+pub const radroots_identity::key::IdentityId::HEX_LENGTH: usize
+pub const fn radroots_identity::key::IdentityId::as_bytes(&self) -> &[u8; 32]
+pub fn radroots_identity::key::IdentityId::from_bytes([u8; 32]) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::IdentityId::from_hex(&str) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::IdentityId::from_slice(&[u8]) -> core::result::Result<Self, radroots_identity::Error>
+pub const fn radroots_identity::key::IdentityId::into_bytes(self) -> [u8; 32]
+pub fn radroots_identity::key::IdentityId::to_hex(self) -> alloc::string::String
+impl radroots_identity::key::IdentityId
+pub const fn radroots_identity::key::IdentityId::from_public_key(radroots_identity::key::PublicKey) -> Self
+impl core::convert::AsRef<[u8]> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::as_ref(&self) -> &[u8]
+impl core::convert::From<radroots_identity::key::IdentityId> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(radroots_identity::key::IdentityId) -> Self
+impl core::convert::From<radroots_identity::key::PublicKey> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::from(radroots_identity::key::PublicKey) -> Self
+impl core::convert::TryFrom<&[u8]> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from(&[u8]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<&str> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<[u8; 32]> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from([u8; 32]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Err = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+#[repr(transparent)] pub struct radroots_identity::key::PublicKey(_)
+impl radroots_identity::key::PublicKey
+pub const radroots_identity::key::PublicKey::BYTE_LENGTH: usize
+pub const radroots_identity::key::PublicKey::HEX_LENGTH: usize
+pub const fn radroots_identity::key::PublicKey::as_bytes(&self) -> &[u8; 32]
+pub fn radroots_identity::key::PublicKey::from_bytes([u8; 32]) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::PublicKey::from_hex(&str) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::PublicKey::from_slice(&[u8]) -> core::result::Result<Self, radroots_identity::Error>
+pub const fn radroots_identity::key::PublicKey::into_bytes(self) -> [u8; 32]
+pub fn radroots_identity::key::PublicKey::to_hex(self) -> alloc::string::String
+impl core::convert::AsRef<[u8]> for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::as_ref(&self) -> &[u8]
+impl core::convert::From<radroots_identity::key::PublicKey> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::from(radroots_identity::key::PublicKey) -> Self
+impl core::convert::TryFrom<&[u8]> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from(&[u8]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<&str> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<[u8; 32]> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from([u8; 32]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Err = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+pub mod radroots_identity::profile
+pub struct radroots_identity::profile::Profile
+impl radroots_identity::profile::Profile
+pub const fn radroots_identity::profile::Profile::is_empty(&self) -> bool
+pub const fn radroots_identity::profile::Profile::new() -> Self
+pub fn radroots_identity::profile::Profile::username(&self) -> core::option::Option<&radroots_identity::username::Username>
+pub fn radroots_identity::profile::Profile::with_username(self, radroots_identity::username::Username) -> Self
+pub struct radroots_identity::profile::PublicIdentity
+impl radroots_identity::profile::PublicIdentity
+pub const fn radroots_identity::profile::PublicIdentity::id(&self) -> radroots_identity::key::IdentityId
+pub const fn radroots_identity::profile::PublicIdentity::new(radroots_identity::key::PublicKey) -> Self
+pub fn radroots_identity::profile::PublicIdentity::profile(&self) -> core::option::Option<&radroots_identity::profile::Profile>
+pub const fn radroots_identity::profile::PublicIdentity::public_key(&self) -> radroots_identity::key::PublicKey
+pub fn radroots_identity::profile::PublicIdentity::try_from_parts(radroots_identity::key::IdentityId, radroots_identity::key::PublicKey, core::option::Option<radroots_identity::profile::Profile>) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::profile::PublicIdentity::with_profile(self, radroots_identity::profile::Profile) -> Self
+impl core::convert::From<&radroots_identity::profile::PublicIdentity> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(&radroots_identity::profile::PublicIdentity) -> Self
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::profile::PublicIdentity
+pub fn radroots_identity::profile::PublicIdentity::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+pub mod radroots_identity::username
+#[repr(transparent)] pub struct radroots_identity::username::Username(_)
+impl radroots_identity::username::Username
+pub fn radroots_identity::username::Username::as_str(&self) -> &str
+pub fn radroots_identity::username::Username::into_string(self) -> alloc::string::String
+pub fn radroots_identity::username::Username::parse(&str) -> core::result::Result<Self, radroots_identity::Error>
+impl core::convert::AsRef<str> for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::as_ref(&self) -> &str
+impl core::convert::TryFrom<&str> for radroots_identity::username::Username
+pub type radroots_identity::username::Username::Error = radroots_identity::Error
+pub fn radroots_identity::username::Username::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::username::Username
+pub type radroots_identity::username::Username::Error = radroots_identity::Error
+pub fn radroots_identity::username::Username::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::username::Username
+pub type radroots_identity::username::Username::Err = radroots_identity::Error
+pub fn radroots_identity::username::Username::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+pub const radroots_identity::username::MAX_LENGTH: usize
+pub const radroots_identity::username::MIN_LENGTH: usize
+#[non_exhaustive] pub enum radroots_identity::Error
+pub radroots_identity::Error::AccountIdMismatch
+pub radroots_identity::Error::AccountUpdateRegressed
+pub radroots_identity::Error::AccountUpdateRegressed::current_updated_at_unix: u64
+pub radroots_identity::Error::AccountUpdateRegressed::proposed_updated_at_unix: u64
+pub radroots_identity::Error::AccountUpdatedBeforeCreated
+pub radroots_identity::Error::AccountUpdatedBeforeCreated::created_at_unix: u64
+pub radroots_identity::Error::AccountUpdatedBeforeCreated::updated_at_unix: u64
+pub radroots_identity::Error::IdentityIdMismatch
+pub radroots_identity::Error::InvalidByteLength
+pub radroots_identity::Error::InvalidByteLength::actual: usize
+pub radroots_identity::Error::InvalidByteLength::expected: usize
+pub radroots_identity::Error::InvalidHexCharacter
+pub radroots_identity::Error::InvalidHexCharacter::index: usize
+pub radroots_identity::Error::InvalidHexLength
+pub radroots_identity::Error::InvalidHexLength::actual: usize
+pub radroots_identity::Error::InvalidHexLength::expected: usize
+pub radroots_identity::Error::InvalidPublicKeyBytes
+pub radroots_identity::Error::InvalidUsernameCharacter
+pub radroots_identity::Error::InvalidUsernameCharacter::index: usize
+pub radroots_identity::Error::InvalidUsernameDotPlacement
+pub radroots_identity::Error::InvalidUsernameLength
+pub radroots_identity::Error::InvalidUsernameLength::actual: usize
+pub radroots_identity::Error::InvalidUsernameLength::max: usize
+pub radroots_identity::Error::InvalidUsernameLength::min: usize
+#[repr(transparent)] pub struct radroots_identity::AccountId(_)
+impl radroots_identity::account::AccountId
+pub const radroots_identity::account::AccountId::BYTE_LENGTH: usize
+pub const radroots_identity::account::AccountId::HEX_LENGTH: usize
+pub const fn radroots_identity::account::AccountId::as_bytes(&self) -> &[u8; 32]
+pub fn radroots_identity::account::AccountId::from_bytes([u8; 32]) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::account::AccountId::from_hex(&str) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::account::AccountId::from_slice(&[u8]) -> core::result::Result<Self, radroots_identity::Error>
+pub const fn radroots_identity::account::AccountId::into_bytes(self) -> [u8; 32]
+pub fn radroots_identity::account::AccountId::to_hex(self) -> alloc::string::String
+impl radroots_identity::account::AccountId
+pub const fn radroots_identity::account::AccountId::from_identity_id(radroots_identity::key::IdentityId) -> Self
+pub const fn radroots_identity::account::AccountId::from_public_identity(&radroots_identity::profile::PublicIdentity) -> Self
+impl core::convert::AsRef<[u8]> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::as_ref(&self) -> &[u8]
+impl core::convert::From<&radroots_identity::profile::PublicIdentity> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(&radroots_identity::profile::PublicIdentity) -> Self
+impl core::convert::From<radroots_identity::key::IdentityId> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(radroots_identity::key::IdentityId) -> Self
+impl core::convert::TryFrom<&[u8]> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from(&[u8]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<&str> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<[u8; 32]> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from([u8; 32]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Error = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::account::AccountId
+pub type radroots_identity::account::AccountId::Err = radroots_identity::Error
+pub fn radroots_identity::account::AccountId::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+#[repr(transparent)] pub struct radroots_identity::IdentityId(_)
+impl radroots_identity::key::IdentityId
+pub const radroots_identity::key::IdentityId::BYTE_LENGTH: usize
+pub const radroots_identity::key::IdentityId::HEX_LENGTH: usize
+pub const fn radroots_identity::key::IdentityId::as_bytes(&self) -> &[u8; 32]
+pub fn radroots_identity::key::IdentityId::from_bytes([u8; 32]) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::IdentityId::from_hex(&str) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::IdentityId::from_slice(&[u8]) -> core::result::Result<Self, radroots_identity::Error>
+pub const fn radroots_identity::key::IdentityId::into_bytes(self) -> [u8; 32]
+pub fn radroots_identity::key::IdentityId::to_hex(self) -> alloc::string::String
+impl radroots_identity::key::IdentityId
+pub const fn radroots_identity::key::IdentityId::from_public_key(radroots_identity::key::PublicKey) -> Self
+impl core::convert::AsRef<[u8]> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::as_ref(&self) -> &[u8]
+impl core::convert::From<radroots_identity::key::IdentityId> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(radroots_identity::key::IdentityId) -> Self
+impl core::convert::From<radroots_identity::key::PublicKey> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::from(radroots_identity::key::PublicKey) -> Self
+impl core::convert::TryFrom<&[u8]> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from(&[u8]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<&str> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<[u8; 32]> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from([u8; 32]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Error = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::key::IdentityId
+pub type radroots_identity::key::IdentityId::Err = radroots_identity::Error
+pub fn radroots_identity::key::IdentityId::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+pub struct radroots_identity::Profile
+impl radroots_identity::profile::Profile
+pub const fn radroots_identity::profile::Profile::is_empty(&self) -> bool
+pub const fn radroots_identity::profile::Profile::new() -> Self
+pub fn radroots_identity::profile::Profile::username(&self) -> core::option::Option<&radroots_identity::username::Username>
+pub fn radroots_identity::profile::Profile::with_username(self, radroots_identity::username::Username) -> Self
+pub struct radroots_identity::PublicIdentity
+impl radroots_identity::profile::PublicIdentity
+pub const fn radroots_identity::profile::PublicIdentity::id(&self) -> radroots_identity::key::IdentityId
+pub const fn radroots_identity::profile::PublicIdentity::new(radroots_identity::key::PublicKey) -> Self
+pub fn radroots_identity::profile::PublicIdentity::profile(&self) -> core::option::Option<&radroots_identity::profile::Profile>
+pub const fn radroots_identity::profile::PublicIdentity::public_key(&self) -> radroots_identity::key::PublicKey
+pub fn radroots_identity::profile::PublicIdentity::try_from_parts(radroots_identity::key::IdentityId, radroots_identity::key::PublicKey, core::option::Option<radroots_identity::profile::Profile>) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::profile::PublicIdentity::with_profile(self, radroots_identity::profile::Profile) -> Self
+impl core::convert::From<&radroots_identity::profile::PublicIdentity> for radroots_identity::account::AccountId
+pub fn radroots_identity::account::AccountId::from(&radroots_identity::profile::PublicIdentity) -> Self
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::profile::PublicIdentity
+pub fn radroots_identity::profile::PublicIdentity::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+#[repr(transparent)] pub struct radroots_identity::PublicKey(_)
+impl radroots_identity::key::PublicKey
+pub const radroots_identity::key::PublicKey::BYTE_LENGTH: usize
+pub const radroots_identity::key::PublicKey::HEX_LENGTH: usize
+pub const fn radroots_identity::key::PublicKey::as_bytes(&self) -> &[u8; 32]
+pub fn radroots_identity::key::PublicKey::from_bytes([u8; 32]) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::PublicKey::from_hex(&str) -> core::result::Result<Self, radroots_identity::Error>
+pub fn radroots_identity::key::PublicKey::from_slice(&[u8]) -> core::result::Result<Self, radroots_identity::Error>
+pub const fn radroots_identity::key::PublicKey::into_bytes(self) -> [u8; 32]
+pub fn radroots_identity::key::PublicKey::to_hex(self) -> alloc::string::String
+impl core::convert::AsRef<[u8]> for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::as_ref(&self) -> &[u8]
+impl core::convert::From<radroots_identity::key::PublicKey> for radroots_identity::key::IdentityId
+pub fn radroots_identity::key::IdentityId::from(radroots_identity::key::PublicKey) -> Self
+impl core::convert::TryFrom<&[u8]> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from(&[u8]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<&str> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<[u8; 32]> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from([u8; 32]) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Error = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::key::PublicKey
+pub type radroots_identity::key::PublicKey::Err = radroots_identity::Error
+pub fn radroots_identity::key::PublicKey::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::key::PublicKey
+pub fn radroots_identity::key::PublicKey::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>
+#[repr(transparent)] pub struct radroots_identity::Username(_)
+impl radroots_identity::username::Username
+pub fn radroots_identity::username::Username::as_str(&self) -> &str
+pub fn radroots_identity::username::Username::into_string(self) -> alloc::string::String
+pub fn radroots_identity::username::Username::parse(&str) -> core::result::Result<Self, radroots_identity::Error>
+impl core::convert::AsRef<str> for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::as_ref(&self) -> &str
+impl core::convert::TryFrom<&str> for radroots_identity::username::Username
+pub type radroots_identity::username::Username::Error = radroots_identity::Error
+pub fn radroots_identity::username::Username::try_from(&str) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<alloc::string::String> for radroots_identity::username::Username
+pub type radroots_identity::username::Username::Error = radroots_identity::Error
+pub fn radroots_identity::username::Username::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::fmt::Display for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::str::traits::FromStr for radroots_identity::username::Username
+pub type radroots_identity::username::Username::Err = radroots_identity::Error
+pub fn radroots_identity::username::Username::from_str(&str) -> core::result::Result<Self, Self::Err>
+impl serde_core::ser::Serialize for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer
+impl<'de> serde_core::de::Deserialize<'de> for radroots_identity::username::Username
+pub fn radroots_identity::username::Username::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de>