Examples on this page target source snapshot
35a69a14, not a crates.io release. Use the current API guide to select the matching dependency.Storage domains
The client coordinates five backend domains throughPersistenceManager:
A type implementing all five automatically implements
Backend. Do not omit MsgSecretStore from a custom adapter.
Select account scope
UseSqliteStore::open for the default account or the SqliteDatabase facade to provision, select and enumerate accounts. An account store scopes state to its device ID. A database-wide reader or adapter has a different lifetime and scope; do not treat its resource report as an account-only measurement.
Register a concrete backend with the bot/client builder or an existing Arc<dyn Backend> with the corresponding with_backend_arc method. See SQLite storage and custom backends.
Change device state through the manager
The manager owns mutable device state and publishes a cached immutable snapshot. Reads use the snapshot; updates go through the manager’s commands or mutation API so publication, dirty tracking and persistence ordering stay consistent. Directly changing a sharedDevice bypasses those obligations.
The implementation uses portable synchronization. Its internal fields and locks are not a second public storage API. Use the source reference for current details rather than rebuilding the manager from a copied struct definition.
Validate stored secrets
get_stored_msg_secret returns a validated MessageSecret and the parent’s optional Unix-seconds timestamp. Unknown time is None. A malformed persisted byte length is a typed error, not a secret to pad or truncate. The byte-only read is an explicit projection of that validated read.
expires_at is an independent retention deadline. A zero expiry means never; upserts keep the later deadline, with never winning. Parent timestamps use the later non-zero value and cannot be replaced by an unknown zero. See MsgSecretStore and current contracts.
Preserve commit boundaries
Persist Signal changes before sending an envelope that advances their ratchets. An adapter’s in-memory cache does not replace that write barrier. For durable incoming delivery, store pending payloads and implement the protocol-store replay methods. Register an idempotentInboundDurabilityHook to commit application data before acknowledgement. Use (chat, sender, id) as the identity, since IDs alone can collide across chats or senders. A crash between the application’s commit and ACK can replay an already committed message.
Read inbound durability for commit order, offline batches, PDO recovery and failure boundaries. On current main, the builder probes bounded exact/group-alias pending reads and conditional replacement/deletion, alongside the pending-store contract. It rejects unsupported adapters with UnsupportedDurabilityBackend. See the ProtocolStore reference and bounded backend semantics. A successful probe does not establish batch atomicity or crash durability.
Source reference
Storage traits · PersistenceManager · SQLite adapterCache coherence
Signal session/sender-key caches buffer mutations for the durable flush boundary. They are distinct from public lookup caches and cannot be discarded as a substitute for committing ratchet state. Fine-grained notification patches keep known cached metadata coherent; unknown entries or unsupported patches may require invalidation and a later fetch. See the source’s cache implementation and state management. LID/PN mappings preserve both addressing namespaces. Learning a mapping does not make the identities cryptographically interchangeable for an existing poll, event or encrypted add-on. Use the exact recorded creator with a creation reference, and let each facade perform its documented address resolution.Tune database resources
TuneSqliteDatabaseConfig at database scope. Keep writer pool_size at 1 to avoid deferred read-to-write upgrade deadlocks; busy_timeout cannot resolve them. Reader connections can increase read concurrency under WAL while consuming their own page-cache resources; adding write concurrency does not make SQLite a multi-writer database. Use the current SQLite configuration and resource reports instead of old per-account pool or thread-count tables.