# Identity Module
> Certificate-based identity resolution and contact management for the BSV ecosystem
## Overview
The identity module provides certificate-based identity discovery and management for BSV applications. It enables users to:
- **Resolve identities** by public key or attribute values (email, phone, etc.)
- **Discover certificates** associated with an identity
- **Parse certificates** into user-friendly display formats
- **Manage contacts** with encrypted local storage
- **Reveal attributes** publicly on the overlay network
- **Revoke revelations** by spending identity tokens
This module integrates with the `auth` module for certificate handling and the `overlay` module for network communication.
## Files
| `mod.rs` | Module root with re-exports (111 lines) |
| `types.rs` | Core types: KnownCertificateType, DisplayableIdentity, Contact, configs (886 lines) |
| `client.rs` | IdentityClient for identity resolution, revelation, and management (2053 lines) |
| `contacts.rs` | ContactsManager for encrypted contact storage with caching (1276 lines) |
## Feature Flag
```toml
[features]
identity = ["auth", "overlay"]
```
The identity feature depends on both `auth` (for certificate types) and `overlay` (for network communication).
## Public Exports
```rust
// From mod.rs re-exports
pub use client::IdentityClient;
pub use contacts::ContactsManager;
pub use types::{
// Type aliases
Base64String,
CertificateFieldNameUnder50Bytes,
OriginatorDomainNameStringUnder250Bytes,
PubKeyHex,
// Broadcast types
BroadcastFailure,
BroadcastResult,
BroadcastSuccess,
// Core types
CertifierInfo,
Contact,
ContactsManagerConfig,
DefaultIdentityValues,
DisplayableIdentity,
IdentityCertificate,
IdentityClientConfig,
IdentityQuery,
IdentityResolutionResult,
KnownCertificateType,
StaticAvatarUrls,
// Constants
DEFAULT_SOCIALCERT_CERTIFIER,
};
```
## Key Types
### KnownCertificateType
Enum representing the 9 known certificate types in the BSV ecosystem:
| `IdentiCert` | `z40BOInXkI8m7f/wBrv4MJ09bZfzZbTj2fJqCtONqCY=` | firstName, lastName, profilePhoto |
| `DiscordCert` | `2TgqRC35B1zehGmB21xveZNc7i5iqHc0uxMb+1NMPW4=` | userName, profilePhoto |
| `PhoneCert` | `mffUklUzxbHr65xLohn0hRL0Tq2GjW1GYF/OPfzqJ6A=` | phoneNumber |
| `XCert` | `vdDWvftf1H+5+ZprUw123kjHlywH+v20aPQTuXgMpNc=` | userName, profilePhoto |
| `Registrant` | `YoPsbfR6YQczjzPdHCoGC7nJsOdPQR50+SYqcWpJ0y0=` | name, icon |
| `EmailCert` | `exOl3KM0dIJ04EW5pZgbZmPag6MdJXd3/a1enmUU/BA=` | email |
| `Anyone` | `mfkOMfLDQmrr3SBxBQ5WeE+6Hy3VJRFq6w4A5Ljtlis=` | (none) |
| `SelfCert` | `Hkge6X5JRxt1cWXtHLCrSTg6dCVTxjQJJ48iOYd7n3g=` | (none) |
| `CoolCert` | `AGfk/WrT1eBDXpz3mcw386Zww2HmqcIn3uY6x4Af1eo=` | cool (boolean) |
```rust
use bsv_rs::identity::KnownCertificateType;
// Get type ID for a certificate type
let type_id = KnownCertificateType::XCert.type_id();
// Get human-readable name
let name = KnownCertificateType::XCert.name(); // "XCert"
// Parse type ID back to enum
let cert_type = KnownCertificateType::from_type_id(type_id);
// Get all known types
let all_types = KnownCertificateType::all();
```
### DisplayableIdentity
User-friendly representation of an identity for UI display:
```rust
use bsv_rs::identity::DisplayableIdentity;
let identity = DisplayableIdentity {
name: "Alice".to_string(),
avatar_url: "https://example.com/avatar.png".to_string(),
identity_key: "02abc123...".to_string(),
abbreviated_key: "02abc1...23".to_string(),
badge_icon_url: "https://socialcert.net/icon.png".to_string(),
badge_label: "X account certified by SocialCert".to_string(),
badge_click_url: "https://socialcert.net".to_string(),
};
// Create from just an identity key (uses default values)
let identity = DisplayableIdentity::from_key("02abc123...");
// Create unknown/default identity
let unknown = DisplayableIdentity::unknown();
```
### Contact
Stored contact with identity information and metadata:
```rust
use bsv_rs::identity::{Contact, DisplayableIdentity};
let contact = Contact {
identity_key: "02abc123...".to_string(),
name: "Alice".to_string(),
avatar_url: Some("https://example.com/avatar.png".to_string()),
added_at: 1706400000000, // Unix timestamp in milliseconds
notes: Some("Met at conference".to_string()),
tags: vec!["friends".to_string(), "work".to_string()],
metadata: None,
};
// Convert between Contact and DisplayableIdentity
let display = contact.to_displayable_identity();
let contact = Contact::from_identity(display);
```
### IdentityQuery
Query parameters for identity resolution with builder pattern:
```rust
use bsv_rs::identity::IdentityQuery;
use std::collections::HashMap;
// Query by identity key
let query = IdentityQuery::by_identity_key("02abc123...");
// Query by single attribute
let query = IdentityQuery::by_attribute("email", "user@example.com");
// Query by multiple attributes
let mut attrs = HashMap::new();
attrs.insert("email".to_string(), "user@example.com".to_string());
let query = IdentityQuery::by_attributes(attrs);
// Builder pattern for complex queries
let query = IdentityQuery::by_attribute("email", "user@example.com")
.with_certifier("02cf6cdf...")
.with_limit(10)
.with_offset(5);
```
### IdentityCertificate
Enriched certificate with decrypted fields and certifier information:
```rust
pub struct IdentityCertificate {
pub certificate: VerifiableCertificate,
pub certifier_info: CertifierInfo,
pub publicly_revealed_keyring: HashMap<String, Vec<u8>>,
pub decrypted_fields: HashMap<String, String>,
}
// Access methods
let type_id = cert.type_base64();
let subject = cert.subject_hex();
let certifier = cert.certifier_hex();
let known_type = cert.known_type(); // Option<KnownCertificateType>
```
### CertifierInfo
Information about a certificate certifier:
```rust
pub struct CertifierInfo {
pub name: String,
pub icon_url: String,
pub description: String,
pub trust: u8, // Trust level 0-10
}
```
## IdentityClient
Main client for identity operations. Generic over wallet implementation:
```rust
use bsv_rs::identity::{IdentityClient, IdentityClientConfig};
use bsv_rs::wallet::ProtoWallet;
use bsv_rs::PrivateKey;
// Create a wallet
let private_key = PrivateKey::random();
let wallet = ProtoWallet::new(Some(private_key));
// Create client with default config
let client = IdentityClient::new(wallet.clone(), IdentityClientConfig::default());
// Create client with custom config
let config = IdentityClientConfig::with_originator("myapp.example.com")
.with_network(NetworkPreset::Mainnet)
.with_token_amount(5);
let client = IdentityClient::new(wallet, config);
// Create with custom resolver
let client = IdentityClient::with_resolver(wallet, config, custom_resolver);
```
### Public Revelation Methods
```rust
// Publicly reveal certificate attributes
let result = client.publicly_reveal_attributes(
certificate,
vec!["userName".to_string(), "profilePhoto".to_string()]
).await?;
match result {
BroadcastResult::Success(s) => println!("TXID: {}", s.txid),
BroadcastResult::Failure(f) => println!("Failed: {}", f.description),
}
// Simplified version returning just txid
let txid = client.publicly_reveal_attributes_simple(certificate, fields).await?;
// Revoke a previous revelation by spending the identity token
client.revoke_certificate_revelation("serial-number-123").await?;
```
### Resolution Methods
```rust
// Resolve by identity key (second param enables contact override)
let identity = client.resolve_by_identity_key("02abc123...", true).await?;
if let Some(id) = identity {
println!("Found: {} ({})", id.name, id.abbreviated_key);
}
// Resolve by attributes (second param enables contact override)
let mut attrs = HashMap::new();
attrs.insert("email".to_string(), "user@example.com".to_string());
let identities = client.resolve_by_attributes(attrs, true).await?;
// Discover all certificates for an identity
let certs = client.discover_certificates("02abc123...").await?;
// Discover specific certificate type
let x_certs = client.discover_certificates_by_type(
"02abc123...",
KnownCertificateType::XCert.type_id()
).await?;
// Complex query returning IdentityResolutionResult
let query = IdentityQuery::by_identity_key("02abc123...").with_limit(10);
let results = client.query(query).await?;
for result in results {
println!("{}: {} certs", result.identity.name, result.certificates.len());
}
```
### Contact Management via Client
```rust
// Get all contacts (pass true to force refresh from storage)
let contacts = client.get_contacts(false).await?;
// Get specific contact
if let Some(contact) = client.get_contact("02abc123...").await? {
println!("Contact: {}", contact.name);
}
// Save a contact (with optional metadata)
let identity = DisplayableIdentity::from_key("02abc123...");
client.save_contact(identity, Some(serde_json::json!({"source": "import"}))).await?;
// Remove a contact
client.remove_contact("02abc123...").await?;
```
### Utility Methods
```rust
// Get the wallet's identity key
let my_key = client.get_identity_key().await?;
// Parse an identity certificate into displayable format (static method)
let displayable = IdentityClient::<ProtoWallet>::parse_identity(&cert);
```
## ContactsManager
Direct contact management with encrypted storage, search, and caching:
```rust
use bsv_rs::identity::{ContactsManager, ContactsManagerConfig, Contact};
let config = ContactsManagerConfig::with_originator("myapp.example.com");
let manager = ContactsManager::new(wallet, config);
// CRUD operations
manager.add_contact(contact).await?;
let contact = manager.get_contact("02abc123...").await?;
manager.update_contact("02abc123...", updated_contact).await?;
manager.remove_contact("02abc123...").await?;
// List and search
let all = manager.list_contacts().await?;
let refreshed = manager.list_contacts_with_refresh(true).await?;
let results = manager.search_contacts("alice").await?; // Searches name, tags, notes
let tagged = manager.get_contacts_by_tag("friends").await?;
// Cache management
manager.clear_cache().await;
let is_init = manager.is_cache_initialized().await;
let count = manager.cached_count().await;
```
### Storage Model
Contacts are stored as encrypted PushDrop tokens in the wallet's basket system:
- Each contact is encrypted with a per-contact key
- Tagged with HMAC-hashed identity key for privacy-preserving lookup
- Stored in the "contacts" basket
- Custom instructions store the key ID for decryption
## Configuration Types
### IdentityClientConfig
```rust
pub struct IdentityClientConfig {
pub network_preset: NetworkPreset, // Mainnet or Testnet
pub protocol_id: (u8, String), // Default: (1, "identity")
pub key_id: String, // Default: "1"
pub token_amount: u64, // Satoshis for revelation outputs (default: 1)
pub output_index: u32, // Output index for identity token (default: 0)
pub originator: Option<String>, // Application originator for audit trails
}
// Builder methods
let config = IdentityClientConfig::with_originator("myapp.com")
.with_network(NetworkPreset::Testnet)
.with_token_amount(100);
```
### ContactsManagerConfig
```rust
pub struct ContactsManagerConfig {
pub protocol_id: (u8, String), // Default: (2, "contact")
pub basket: String, // Default: "contacts"
pub originator: Option<String>, // Application originator
}
let config = ContactsManagerConfig::with_originator("myapp.com");
```
## Broadcast Result Types
```rust
pub enum BroadcastResult {
Success(BroadcastSuccess),
Failure(BroadcastFailure),
}
pub struct BroadcastSuccess {
pub txid: String,
pub message: Option<String>,
}
pub struct BroadcastFailure {
pub code: String,
pub description: String,
}
// Helper methods
result.is_success();
result.txid(); // Option<&str>
result.into_result(); // Result<BroadcastSuccess, BroadcastFailure>
```
## Constants
| `DEFAULT_SOCIALCERT_CERTIFIER` | `02cf6cdf466951d8dfc9e7c9367511d0007ed6fba35ed42d425cc412fd6cfd4a17` |
### StaticAvatarUrls
UHRP hashes for certificate type avatars:
```rust
use bsv_rs::identity::StaticAvatarUrls;
StaticAvatarUrls::EMAIL; // Envelope icon
StaticAvatarUrls::PHONE; // Phone icon
StaticAvatarUrls::ANYONE; // Anyone certificate avatar
StaticAvatarUrls::SELF; // Self certificate avatar
```
### DefaultIdentityValues
Constants for unverified/unknown identities:
```rust
use bsv_rs::identity::DefaultIdentityValues;
DefaultIdentityValues::NAME; // "Unknown Identity"
DefaultIdentityValues::AVATAR_URL; // UHRP hash for default avatar
DefaultIdentityValues::BADGE_ICON_URL; // UHRP hash for default badge icon
DefaultIdentityValues::BADGE_LABEL; // "Not verified by anyone you trust."
DefaultIdentityValues::BADGE_CLICK_URL; // Documentation link
```
## Error Handling
The module uses error variants from `crate::Error`:
```rust
// General identity error
Error::IdentityError(String)
// Contact not found
Error::ContactNotFound(String)
```
## Overlay Network Integration
The identity module uses the overlay network for discovery and broadcast:
| `ls_identity` | Lookup service for identity queries |
| `tm_identity` | Topic for broadcasting identity revelations |
Queries are performed via `LookupResolver`, broadcasts via `TopicBroadcaster`.
## Wire Format Compatibility
JSON serialization uses camelCase field names for cross-SDK compatibility:
```rust
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DisplayableIdentity {
pub name: String,
pub avatar_url: String, // serializes as "avatarUrl"
pub identity_key: String, // serializes as "identityKey"
// ...
}
```
## Cross-SDK Compatibility
This module maintains API compatibility with:
- [TypeScript SDK identity](https://github.com/bitcoin-sv/ts-sdk) - `IdentityClient`, `ContactsManager`
- [Go SDK identity](https://github.com/bitcoin-sv/go-sdk) - `identity.Client`
Key compatibility points:
- Same certificate type IDs (base64-encoded SHA-256 hashes)
- Same default certifier public key
- Same overlay service/topic names
- Compatible JSON serialization format with camelCase
## Related Documentation
- `../auth/CLAUDE.md` - Authentication module with certificate types
- `../overlay/CLAUDE.md` - Overlay network module for resolution queries
- `../wallet/CLAUDE.md` - Wallet module for key operations
- `../../CLAUDE.md` - Project root documentation