Skip to main content
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 through PersistenceManager: A type implementing all five automatically implements Backend. Do not omit MsgSecretStore from a custom adapter.

Select account scope

Use SqliteStore::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 shared Device 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 idempotent InboundDurabilityHook 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 adapter

Cache 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

Tune SqliteDatabaseConfig 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.

Migrate persisted data

Back up the database before an incompatible format migration. Follow the storage migration reference for supported migrations and legacy session interoperability. Changing a library feature does not retroactively validate arbitrary stored bytes. Do not hand-rewrite Signal sessions or drop account-scoped state to bypass an invalid-data error.