Skip to main content

softchat/
lib.rs

1//! Portable Softchat protocol values and cryptographic operations.
2//!
3//! The native API uses validated values and retained capabilities. Callers
4//! construct protocol data explicitly: signing does not read a hidden clock or
5//! normalize exact Nostr tag arrays.
6//!
7//! # Protocol workflow
8//!
9//! ```
10//! use softchat::{
11//!     LocalIdentity, NostrEventDraft, NostrEventKind, NostrTag,
12//!     SignedNostrEvent,
13//! };
14//!
15//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
16//! let alice = LocalIdentity::from_secret_hex(
17//!     "5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a",
18//! )?;
19//! let bob = LocalIdentity::from_secret_hex(
20//!     "4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d",
21//! )?;
22//!
23//! let encrypted = alice.encrypt_utf8(&bob.public_key(), "hello")?;
24//! assert_eq!(bob.decrypt_utf8(&encrypted)?, "hello");
25//!
26//! let recipient = NostrTag::new(vec![
27//!     "p".to_owned(),
28//!     bob.public_key().to_hex(),
29//!     String::new(),
30//!     String::new(),
31//! ])?;
32//! let draft = NostrEventDraft::new(
33//!     1_700_000_000,
34//!     NostrEventKind::SHORT_TEXT_NOTE,
35//!     vec![recipient],
36//!     "A signed protocol event",
37//! )?;
38//! let authored = alice.sign_event(draft)?;
39//! let received = SignedNostrEvent::from_json(&authored.to_json()?)?;
40//! assert_eq!(received, authored);
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! The keys above are fixtures. Never use them outside tests or examples.
46//! Network transport, attachment files, and application lifecycle remain
47//! platform-owned. With `sqlite-storage`, Rust also owns the Android account
48//! schema, transactions, authenticated truth, delivery recovery, and bounded
49//! use-case queries; Android supplies only an app-private path and lifecycle.
50
51mod account;
52#[cfg(feature = "sqlite-storage")]
53#[cfg_attr(not(any(feature = "native-bindings", test)), allow(dead_code))]
54mod account_diagnostics;
55#[cfg(feature = "sqlite-storage")]
56mod account_product;
57#[cfg(feature = "sqlite-storage")]
58mod account_transport;
59mod chat;
60mod core;
61mod diagnostics;
62mod envelope;
63mod error;
64mod event;
65#[cfg(any(
66    feature = "native-bindings",
67    all(feature = "javascript-bindings", target_arch = "wasm32"),
68    test
69))]
70mod facade;
71#[cfg(all(feature = "android-jni", target_os = "android"))]
72mod jni;
73mod json;
74#[cfg(feature = "managed-transport")]
75mod managed_transport;
76mod media;
77#[cfg(any(
78    feature = "sqlite-storage",
79    feature = "native-bindings",
80    all(feature = "javascript-bindings", target_arch = "wasm32"),
81    all(feature = "android-jni", target_os = "android"),
82    test
83))]
84#[cfg_attr(
85    not(any(
86        feature = "native-bindings",
87        all(feature = "javascript-bindings", target_arch = "wasm32"),
88        test
89    )),
90    allow(dead_code)
91)]
92mod message;
93mod negentropy;
94mod nip17;
95mod nip19;
96mod nip44_profile;
97mod noise;
98#[cfg(feature = "performance-probes")]
99mod performance_probes;
100mod relay;
101mod relay_catalog;
102mod replacement;
103mod runtime;
104#[cfg(feature = "sqlite-storage")]
105#[cfg_attr(not(any(feature = "native-bindings", test)), allow(dead_code))]
106mod runtime_facade;
107mod session;
108#[cfg(feature = "sqlite-storage")]
109mod storage;
110mod sync;
111
112#[cfg(all(feature = "javascript-bindings", target_arch = "wasm32"))]
113mod wasm;
114
115pub use account::{
116    AppDataContext, AppDataSyncView, ApplicationDataView, Contact, FollowListView,
117    MAX_APP_DATA_JSON_BYTES, MAX_CONTACTS, UserMetadataView, classify_application_data,
118    classify_follow_list, classify_user_metadata, latest_application_data, latest_user_metadata,
119};
120#[cfg(feature = "sqlite-storage")]
121pub use account_product::{
122    ACCOUNT_READ_STATE_SCHEMA_VERSION, ACCOUNT_SETTINGS_SCHEMA_VERSION, AccountDraft,
123    AccountMediaItem, AccountMediaLease, AccountMediaOperation, AccountMediaOperationPage,
124    AccountMediaOperationState, AccountMediaProtection, AccountMessageView, AccountProfile,
125    AccountReadState, AccountSettings, AccountSettingsMutation, AssetReplacementTarget,
126    ConversationCursor, ConversationPage, ConversationRecord, CustomEmojiReference, DraftSpan,
127    DraftSpanKind, MAX_ACCOUNT_READ_STATE_BYTES, MAX_ACCOUNT_READ_STATE_ENTRIES,
128    MAX_ACCOUNT_SETTINGS_BYTES, MAX_PRODUCT_ATTACHMENT_TAG_BYTES, MAX_PRODUCT_DRAFT_ATTACHMENTS,
129    MAX_PRODUCT_DRAFT_SPANS, MAX_PRODUCT_MEDIA_OPERATION_LOOKUPS, MAX_PRODUCT_MESSAGE_BYTES,
130    MAX_PRODUCT_MESSAGE_LOOKUPS, MAX_PRODUCT_PROFILE_LOOKUPS, MAX_PRODUCT_QUERY_PAGE,
131    MAX_PRODUCT_SEARCH_BYTES, MAX_PRODUCT_TRANSCRIPT_BYTES, MAX_PRODUCT_WAVEFORM_SAMPLES,
132    MediaCompletionResult, MediaPreparationInput, MessageContentInput, MessageCursor,
133    MessageLocalExtras, MessagePage, MessageReactionInput, MessageReactionView,
134    PendingAssetReplacement, PendingAssetReplacementState, PendingMediaMessage,
135    PendingMediaMessageState, ProductOperationResult, PushPlatform,
136};
137#[cfg(feature = "sqlite-storage")]
138pub use account_transport::{
139    AccountTransportAction, AccountTransportActionKind, AccountTransportBatch,
140    AccountTransportResult, AccountTransportResultKind, AccountTypingIndicator,
141};
142pub use chat::{
143    ChatMessageDraft, ChatMessageView, ChatRelation, ChatRelationInput, ChatRelationKind,
144    DeletionView, EditView, GenericRepostView, MAX_CHAT_ATTACHMENTS, MAX_CHAT_EMOJI_TAGS,
145    MAX_CHAT_EXTENSION_TAGS, MAX_CHAT_PARTICIPANTS, MessageProjection, ReactionDraft, ReactionView,
146    SubjectDraft, SubjectView, TypingView, classify_chat_message, classify_deletion, classify_edit,
147    classify_generic_repost, classify_reaction, classify_subject, classify_typing,
148    project_chat_message,
149};
150pub use core::{
151    LocalIdentity, MAX_CIPHERTEXT_BYTES, MAX_NIP44_BINARY_WRITER_PLAINTEXT_BYTES,
152    MAX_NIP44_ENCODED_PAYLOAD_BYTES, MAX_NIP44_READER_PLAINTEXT_BYTES,
153    MAX_NIP44_WRITER_PLAINTEXT_BYTES, MAX_PLAINTEXT_BYTES, NIP44_V2_ALGORITHM,
154    Nip44EncryptedMessage, Nip44Payload, NostrPublicKey,
155};
156pub use diagnostics::{
157    BuildFingerprint, DiagnosticsSnapshot, build_fingerprint, diagnostics_snapshot,
158};
159pub use envelope::{
160    MAX_NIP59_TIMESTAMP_TWEAK_SECONDS, Nip59EnvelopeKind, Nip59EnvelopeRoute,
161    UnwrappedNip59Envelope, route_nip59_envelope,
162};
163pub use error::SoftchatError;
164pub use event::{
165    MAX_NOSTR_EVENT_JSON_BYTES, MAX_PORTABLE_TIMESTAMP_SECONDS, NostrEventDraft, NostrEventId,
166    NostrEventKind, NostrEventSignature, NostrRumor, NostrTag, SignedNostrEvent,
167};
168#[cfg(feature = "managed-transport")]
169pub use managed_transport::{
170    ManagedAccountTransport, ManagedTransportFailure, ManagedTransportSnapshot,
171};
172pub use media::{
173    AttachmentDecryptionStream, AttachmentEncryptionStream, AttachmentMetadata,
174    AttachmentStreamFinal, HttpAuthorizationPlan, MAX_ATTACHMENT_COMPATIBILITY_BYTES,
175    MAX_ATTACHMENT_FALLBACKS, MAX_ATTACHMENT_METADATA_FIELDS, MAX_ATTACHMENT_STREAM_BYTES,
176    MAX_ATTACHMENT_STREAM_CHUNK_BYTES, MAX_HTTP_AUTHORIZATION_PAYLOAD_BYTES,
177    MAX_HTTP_AUTHORIZATION_URL_BYTES, create_nip98_authorization,
178    create_nip98_authorization_for_payload_hash, decrypt_attachment_bytes,
179    encrypt_attachment_bytes, parse_attachment_metadata,
180};
181pub use negentropy::{
182    MAX_NEGENTROPY_FRAME_BYTES, MAX_NEGENTROPY_ITEMS, MIN_NEGENTROPY_FRAME_BYTES, NegentropyClient,
183    NegentropyItem, NegentropyStep,
184};
185pub use nip17::Nip17TextMessage;
186pub use nip19::{
187    MAX_NIP19_IDENTIFIER_CHARS, MAX_NIP19_RELAYS, Nip19Identifier, Nip19IdentifierKind, Nip19Tlv,
188};
189pub use noise::{
190    MAX_NOISE_FRAME_CIPHERTEXT, MAX_NOISE_FRAME_PLAINTEXT, MAX_NOISE_MESSAGE_SIZE,
191    NOISE_PROTOCOL_NAME, NoiseClientHandshake, NoiseTransport,
192};
193#[cfg(feature = "performance-probes")]
194#[doc(hidden)]
195pub use performance_probes::{
196    Nip44PerformanceProbe, performance_consume_string, performance_echo_string,
197    performance_produce_string,
198};
199pub use relay::{
200    BatchAcknowledgement, BatchEventState, BatchProgress, ClientRelayFrame, MAX_RELAY_BATCH_EVENTS,
201    MAX_RELAY_FILTER_VALUES, MAX_RELAY_FILTERS, MAX_RELAY_FRAME_BYTES, MAX_RELAY_MESSAGE_CHARS,
202    MAX_SUBSCRIPTION_ID_CHARS, NIP42_AUTH_WINDOW_SECONDS, RelayFilter, RelayResponseFrame,
203    encode_events_frame, parse_client_relay_frame, parse_relay_response_frame,
204    plan_nip42_authentication, track_batch_acknowledgements, validate_nip42_authentication,
205};
206pub use relay_catalog::{
207    DEFAULT_FALLBACK_RELAY_TTL_SECONDS, DnsRelayRecord, MAX_DNS_RELAY_RECORDS,
208    MAX_RELAY_CATALOG_ENTRIES, MAX_RELAY_RETRY_DELAY_MS, MAX_RELAY_RETRY_JITTER_MS,
209    MAX_RELAY_TTL_SECONDS, RelayCatalogEntry, RelayCatalogPlan, RelayCatalogReducer,
210    RelayCatalogSource, RelayEndpointPlan, RelayFailureKind, RelayRetryAction, RelayRetryPlan,
211    parse_relay_endpoint, plan_relay_retry,
212};
213pub use runtime::{
214    AccountEngine, DeliveryClaim, DeliveryIntentSnapshot, DeliveryIntentState, DeliveryReducer,
215    DeliveryStateMutation, IngestionReceipt, MAX_ACCOUNT_INGESTION_BYTES,
216    MAX_ACCOUNT_INGESTION_EVENTS, MAX_DELIVERY_CLAIM_BYTES, MAX_DELIVERY_CLAIM_INTENTS,
217    MAX_OPERATION_DELIVERY_INTENTS, MAX_OPERATION_EVENT_COPIES, MAX_OPERATION_RELAYS,
218    OperationSnapshot, OperationState, PreparedEventCopy, PreparedIncomingBatch,
219    PreparedIncomingEvent, PreparedOutgoingOperation, PreparedRelayIntent, ProjectionKind,
220    ProjectionMutation, RelayDeliveryResult, RelayDeliveryResultKind, conversation_id,
221};
222#[cfg(feature = "sqlite-storage")]
223pub use runtime_facade::AccountRuntimeHandle as SoftchatAccount;
224pub use session::{
225    DeliveryDecision, DeliveryState, IngestionBatch, IngestionResult, MAX_INFLIGHT_INGESTION,
226    MAX_OUTBOUND_RELAY_FRAMES, MAX_PENDING_PUBLISHES, MAX_RELAY_SUBSCRIPTIONS, OutboundRelayFrame,
227    RelayConnectionState, RelaySession, RelaySessionAction, RelaySessionActionKind,
228    RelaySessionSnapshot, classify_delivery_acknowledgement, validate_ingestion_result,
229};
230#[cfg(feature = "sqlite-storage")]
231pub use storage::{
232    AccountDatabase, AccountDatabaseInfo, AccountDiagnostics, AccountEventNode, AccountMessage,
233    AccountMutationResult, AccountOperationResult, AccountProjection, ClaimedDelivery,
234    ConversationSummary, ConversationView, DeliveryPayload, LinkPreviewRecord, LocalContactRecord,
235    RelayCatalogMutation, RelayFailoverMutation, StickerRecord, StoredEventJson, StoredIngestion,
236    StoredRelayCatalogPlan,
237};
238pub use sync::{
239    SYNC_BASE_PAGE_LIMIT, SYNC_EVENT_REQUEST_CHUNK, SYNC_MAX_PAGE_LIMIT, SyncAction,
240    SyncActionKind, SyncEngine, SyncEngineSnapshot, SyncPhase,
241};
242
243/// Return the Softchat semantic version compiled into this library.
244#[cfg_attr(feature = "native-bindings", uniffi::export)]
245#[must_use]
246pub fn version() -> String {
247    env!("CARGO_PKG_VERSION").to_owned()
248}
249
250#[cfg(feature = "native-bindings")]
251uniffi::setup_scaffolding!();