1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Client for the Spot secure messaging network.
//!
//! Spotlib enables secure, end-to-end encrypted communication between clients
//! through the Spot network. It handles connection management, cryptographic
//! identity, message routing, and provides both request-response and
//! fire-and-forget messaging patterns.
//!
//! This crate is a Rust port of the Go package
//! [`github.com/KarpelesLab/spotlib`](https://github.com/KarpelesLab/spotlib).
//! It is built on [`bottlers`] for cryptography, [`purecrypto`] for TLS, and
//! [`rsurl`](https://crates.io/crates/rsurl) for the platform REST API —
//! pure Rust all the way down, no async runtime required (connections are
//! handled by background threads).
//!
//! # Basic usage
//!
//! Create a new client (with an ephemeral identity) and wait for it to come
//! online:
//!
//! ```no_run
//! use std::time::Duration;
//!
//! let client = spotlib::Client::new()?;
//! client.wait_online(Duration::from_secs(30))?;
//! # Ok::<(), spotlib::Error>(())
//! ```
//!
//! # Sending messages
//!
//! Send an encrypted query and wait for the response:
//!
//! ```no_run
//! # use std::time::Duration;
//! # let client = spotlib::Client::new()?;
//! let response = client.query("k.targetID/endpoint", b"payload", Duration::from_secs(30))?;
//! # Ok::<(), spotlib::Error>(())
//! ```
//!
//! Send a one-way encrypted message:
//!
//! ```no_run
//! # use std::time::Duration;
//! # let client = spotlib::Client::new()?;
//! client.send_to("k.targetID/endpoint", b"payload", Duration::from_secs(30))?;
//! # Ok::<(), spotlib::Error>(())
//! ```
//!
//! # Receiving messages
//!
//! Register a handler for incoming messages on an endpoint (decrypted
//! automatically; the returned bytes are sent back as the response):
//!
//! ```no_run
//! let client = spotlib::Client::builder()
//! .handler("myendpoint", |msg| Ok(Some(msg.body.clone())))
//! .build()?;
//! # Ok::<(), spotlib::Error>(())
//! ```
//!
//! # Identity and addressing
//!
//! Each client has a cryptographic identity represented by an ID card. The
//! client's address ([`Client::target_id`]) is derived from the SHA-256 hash
//! of its public key and has the format `k.<base64url hash>`. Messages to
//! key-based addresses are automatically encrypted and signed; the
//! recipient's public key is retrieved and cached automatically. Use
//! [`DiskStore`] to persist the identity key across runs.
//!
//! # wasm32 (browser)
//!
//! spotlib also runs in the browser on `wasm32-unknown-unknown`. Because the
//! browser event loop cannot block and has no threads, the network-facing API
//! there is **async** and driven on the browser event loop: `Client::query`,
//! `Client::wait_online`, `Client::send_to`, the `get_*`/`store_blob`/
//! `fetch_blob`/`get_time` methods, and `PacketConn::recv` become `async fn`.
//! `Client::new`/`build`, `close`, `target_id`, `subscribe_events`,
//! `set_handler` and the other non-networking methods keep their synchronous
//! signatures. Connections use rsurl's `aio` WebSocket/Fetch backend over the
//! browser's native APIs.
//!
//! Building for wasm requires disabling the default `native` feature:
//!
//! ```sh
//! cargo build --no-default-features --target wasm32-unknown-unknown
//! ```
//!
//! Two integration requirements are the embedder's responsibility:
//! - **Randomness** — purecrypto's `OsRng` draws entropy through an imported
//! host function `purecrypto.random_get(ptr, len)`. Wire it to
//! `crypto.getRandomValues` (browser) or `crypto.randomFillSync` (Node).
//! - **Key persistence** — [`DiskStore`] is native-only. On wasm, supply keys
//! via [`ClientBuilder::key`]/[`ClientBuilder::keychain`] and persist them
//! yourself (e.g. in `localStorage`).
// The `native` feature selects the blocking, thread-backed implementation and
// is on by default. It may only be disabled on wasm32, where the async browser
// implementation is compiled instead.
compile_error!;
compile_error!;
// Native (blocking, thread-backed) transport, connection management and disk
// key storage.
// Browser (wasm32) async transport and connection management.
pub use ;
pub use ;
pub use ;
pub use clone_private_key;
pub use InstantMessage;
pub use PacketConn;
/// Disk-backed key storage (native only; not available on wasm32, where key
/// persistence is the embedder's responsibility).
pub use DiskStore;
// re-export the protocol crate and message type handlers receive
pub use spotproto;
pub use Message;