macula-rust 0.7.0

Rust SDK for the macula 12 mesh: ML-DSA-87 and LAMPS composite node keys, a post-quantum QUIC transport — mobile first, not mobile-only.
Documentation
[workspace]
members = ["macula-rust-ffi"]

[package]
name = "macula-rust"
version = "0.7.0"
edition = "2021"
rust-version = "1.89"
authors = ["Macula <raf.lefever@erlef.org>"]
description = "Rust SDK for the macula 12 mesh: ML-DSA-87 and LAMPS composite node keys, a post-quantum QUIC transport — mobile first, not mobile-only."
license = "Apache-2.0"
repository = "https://github.com/macula-io/macula-rust"
homepage = "https://github.com/macula-io/macula-rust"
readme = "README.md"
keywords = ["mesh-network", "quic", "p2p", "post-quantum", "decentralized"]
categories = ["network-programming", "cryptography"]

[lints.rust]
unsafe_code = "forbid"

[lints.clippy]
all = { level = "deny", priority = -1 }

[dependencies]
sha2 = "0.11"
quinn = "0.11"
# The key exchange for every connection this crate dials: every TLS
# configuration starts from macula-pqc's client_builder(), which offers
# SecP384r1MLKEM1024 then SecP256r1MLKEM768 and nothing classical, and whose
# KeyPossessionVerifier accepts one ML-DSA-87 station certificate. So rustls
# selects no crypto provider of its own here. See transport.rs.
macula-pqc = "0.3"
# ML-DSA-87 for every node key signature (profile.rs, node_key.rs): the same
# implementation macula-pqc signs TLS with.
macula-mldsa = "0.3"
# The RSA-PSS-4096 half of pq_hybrid's LAMPS composite: already linked through
# rustls for the key exchange, constant-time, and with a FIPS path.
aws-lc-rs = "1"
rustls = { version = "0.23", default-features = false, features = ["logging", "std"] }
tokio = { version = "1", features = ["full"] }
# keystore.rs: platform-native secure storage for a node key
# (Keychain via Security.framework on macOS/iOS, Secret Service via D-Bus
# on Linux, Credential Manager on Windows, Keystore via JNI on Android —
# each selected automatically per target by keyring's own Cargo.toml
# target-cfg blocks, not by any feature switching we do here). `v1`
# (default) covers macOS/Windows/Linux, but NOT iOS despite its own docs
# implying otherwise -- `v1` only forwards apple-native-keyring-store's
# `keychain` feature, and that backend is explicitly "Ignored on iOS" per
# apple-native-keyring-store's own module docs (iOS has only the
# "protected data" store, no legacy keychain at all); building for iOS
# without `protected` hits apple-native-keyring-store's own
# compile_error!("The `protected` feature is required on iOS"). Verified
# 2026-09-05 against real iOS CI (macula-cam2me's ios.yml), not assumed.
# `android-native-keyring-store` is added explicitly since it is NOT part
# of `v1`'s default set either.
keyring = { version = "4", features = ["android-native-keyring-store"] }
# keystore.rs's LinuxKeyutilsStore: a second, independently-selectable
# KeyStore backend demonstrating the trait is genuinely overridable, not
# just declared to be — uses the kernel's own keyutils facility directly
# (no D-Bus/secret-service daemon required), which is what makes it real-
# world useful on headless Linux (containers, CI, this dev sandbox) where
# no secret-service provider is running. Built via keyring-core's
# CredentialStoreApi::build() directly against one Store instance, NOT
# through keyring's own v1::Entry/set_default_store (a process-global,
# would collide with KeyringStore's own default-store selection if both
# were used in one process).
[target.'cfg(target_os = "linux")'.dependencies]
keyring-core = "1"
linux-keyutils-keyring-store = "1"

# Unifies apple-native-keyring-store's `protected` feature into the same
# optional dependency keyring (v1, above) already pulls in for
# cfg(any(macos, ios)) -- see the comment on the main `keyring` line.
# Version constraint ("1") deliberately matches keyring 4.2.0's own exactly
# so Cargo resolves both to the SAME crate instance and unifies features,
# rather than risking two separate resolved versions.
[target.'cfg(target_os = "ios")'.dependencies]
apple-native-keyring-store = { version = "1", features = ["protected"] }

[target.'cfg(unix)'.dependencies]
# node_key/key_file.rs: a key file must belong to the effective user, and is
# opened without blocking; std has neither geteuid nor O_NONBLOCK without libc.
rustix = { version = "1", features = ["fs", "process"] }

[dev-dependencies]
hex = "0.4"
tempfile = "3"
serde_json = "1"
# Test-only, transport.rs's own tests: a station with a classical certificate,
# which a dial must refuse.
rcgen = { version = "0.14", default-features = false, features = ["pem", "ring"] }
# Test-only, transport.rs's own tests: the stations the dialler must refuse
# are built from aws-lc-rs's own groups.
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }