offline-protocol 0.30.0

Main protocol engine for the Offline Protocol SDK
Documentation
[package]
name = "offline-protocol"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
# AGPL-3.0-only requires the license text to accompany the program, and a
# published crate ships on its own. Rather than `license-file = "../../LICENSE"`
# (which makes cargo warn that `license` and `license-file` are redundant on
# every invocation), each publishable crate carries its own byte-identical
# LICENSE copy; cargo packages every git-tracked file under the crate root, so
# it lands in the .crate archive. scripts/check-license-consistency.sh asserts
# the copies never drift.
repository.workspace = true
rust-version.workspace = true
description = "Main protocol engine for the Offline Protocol SDK"

[lints]
workspace = true

[features]
default = ["data", "telemetry-pipe", "file-store"]
# Replicated documents. Gates the engine dependency and the store, never the
# configuration section, the storage categories, or the events: those are
# inert without the engine and gating them would fork the config surface and
# the events guard test for no binary saving.
data = ["dep:offline-protocol-data"]
# The telemetry pipe: classification, the durable batch queue, and the HTTPS
# uploader that ships accepted events to the ingest. Gates the only network
# egress, the only background thread and the only TLS stack in the workspace,
# so a build that wants none of those (a bench, a bare embedder) opts out with
# `default-features = false`. Default on for the same reason `data` is: the
# mobile bindings ship one FFI artifact, and a feature-gated UDL method would
# be a runtime checksum mismatch rather than a build error.
telemetry-pipe = ["dep:ureq", "dep:rustls", "dep:offline-protocol-telemetry-wire"]
# Test seams: a capturing stand-in for the telemetry pipe's HTTPS client, so
# a test in another crate can see every upload without a network. Enabled by
# dev-dependencies only; a shipped binary never carries it.
test-utils = []
# The built-in file stores (`offline_protocol::file_store`): protocol state in
# the bindings' shared file format, and MLS material sealed under an
# operator-supplied store key. For a host with a filesystem and no platform
# keystore. Default on, like `data`, so the FFI entry that exposes them cannot
# become a checksum mismatch; a mobile build that never calls them opts out
# with `default-features = false` and drops the HKDF, HMAC and lock code.
file-store = ["dep:hkdf", "dep:hmac", "dep:rustix"]

[dependencies]
offline-protocol-core = { workspace = true }
offline-protocol-transport = { workspace = true }
offline-protocol-router = { workspace = true }
offline-protocol-reliability = { workspace = true }
# The sealed layer: the envelope codec this crate's send and receive paths
# encode with, and the canonical signing-payload construction the control
# plane signs and verifies over. Shared with a leaf node rather than
# restated here; see ADR 0022.
offline-protocol-sealed = { workspace = true }
offline-protocol-mls = { workspace = true }
offline-protocol-services = { workspace = true }
# Optional: the replicated-document layer. Default on, because the mobile
# bindings ship one FFI artifact and a feature-gated UDL object would be a
# runtime checksum mismatch rather than a build error. Native consumers that
# only want messaging opt out with `default-features = false` and pay none of
# the engine's bytes.
offline-protocol-data = { workspace = true, optional = true }
# Optional: the telemetry pipe's wire types and its HTTPS client. `ureq` with
# exactly `rustls-no-provider` is rustls and no optional extra (no gzip, no
# SOCKS, no charset) and no bundled root set: the uploader trusts only the
# roots that can issue the ingest's certificate (`ingest-roots.pem`), never the
# device's store and never the whole Mozilla set, so a compromise of any other
# authority cannot reach it. `rustls` with ring is the crypto provider the
# uploader names; it is the same rustls ureq links, so it adds no crate. It
# must stay on the rustls series ureq uses (0.23): the provider crosses into
# ureq through `unversioned_rustls_crypto_provider`, and two series would be
# two crates whose types do not match, a build error at that call.
# HTTP CONNECT proxy support is part of ureq's core rather than a feature: the
# uploader can use an HTTP CONNECT proxy selected through the environment, read
# when telemetry is enabled, and TLS still runs end to end to the ingest,
# validated against the pinned roots. The uploader names that proxy source,
# the redirect limit, the root store and the crypto provider in its agent
# configuration rather than inheriting ureq's defaults; see docs/telemetry.md.
offline-protocol-telemetry-wire = { workspace = true, optional = true }
ureq = { version = "3.4", default-features = false, features = ["rustls-no-provider"], optional = true }
rustls = { version = "0.23", default-features = false, features = ["ring", "std"], optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
uuid = { workspace = true }
chrono = { workspace = true }
tracing = { workspace = true }
base64 = { workspace = true }
# The Nostr author key is hex on the wire and hex in the discovery record, so
# the comparison that binds the two never crosses an encoding.
hex = { workspace = true }
sha2 = "0.10"
chacha20poly1305 = { workspace = true }
rand_core = { workspace = true }
zeroize = { workspace = true }
# The built-in sealed file store for MLS material derives its sealing and
# naming keys from the operator's store key (HKDF) and names its files with a
# keyed digest (HMAC), so a stolen directory does not confirm which peers and
# groups it holds. Both are already in the graph through the transport crate.
hkdf = { workspace = true, optional = true }
hmac = { workspace = true, optional = true }

# The built-in file stores hold an exclusive `flock` on their directory for
# their lifetime, so a second store over it is refused rather than diverging
# the MLS state. Already in the graph (and in the notices) through tempfile;
# std's `File::try_lock` needs Rust 1.89, above the 1.87 MSRV. Windows uses
# std's open with read-only sharing instead and needs nothing.
[target.'cfg(unix)'.dependencies]
rustix = { version = "1", default-features = false, features = ["fs", "std"], optional = true }

[lib]

# The example drives the document API, which the `data` feature gates. Declared
# so that a `--no-default-features` build skips it instead of failing to
# compile a target that cannot exist in that configuration.
[[example]]
name = "replicated_notes"
required-features = ["data", "file-store"]