solid-pod-rs 0.4.0-alpha.4

Rust-native Solid Pod server library — LDP, WAC, WebID, Solid-OIDC, Solid Notifications, NIP-98. Framework-agnostic.
Documentation
[package]
name = "solid-pod-rs"
version.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
rust-version.workspace = true
description = "Rust-native Solid Pod server library — LDP, WAC, WebID, Solid-OIDC, Solid Notifications, NIP-98. Framework-agnostic."
keywords = ["solid", "pod", "wac", "ldp", "webid"]
categories = ["web-programming::http-server"]
readme = "README.md"

[lib]
name = "solid_pod_rs"
path = "src/lib.rs"

[dependencies]
# `tokio` is optional so wasm32 / no-runtime consumers can opt out via
# `default-features = false, features = ["core"]`. Default builds still
# pull it in transitively through `tokio-runtime`.
tokio = { version = "1", features = ["fs", "io-util", "sync", "macros", "rt", "time"], optional = true }
async-trait = "0.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
tracing = "0.1"
uuid = { version = "1", features = ["v4", "serde"] }
bytes = "1"
sha2 = "0.10"
hex = "0.4"
url = "2"
chrono = { version = "0.4", features = ["serde"] }
base64 = "0.22"
notify = { version = "6", optional = true }

# SPARQL-Update parsing for PATCH bodies (pure-logic; always compiled).
spargebra = "0.3"

# WebSocketChannel2023 (Solid Notifications 0.2). Optional: only needed
# when a tokio runtime is configured for the notifications feature.
tokio-tungstenite = { version = "0.24", optional = true }
futures-util = { version = "0.3", optional = true }

# HTTP client for WebhookChannel2023 delivery + did:nostr resolver.
# Optional so `core` consumers don't pull a TLS stack.
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"], optional = true }

# Optional backends
aws-sdk-s3 = { version = "1", optional = true }

# Solid-OIDC (0.1) — DPoP, dynamic client registration, discovery
openidconnect = { version = "4", optional = true }
jsonwebtoken = { version = "9", optional = true }

# NIP-98 Schnorr verification (Phase 3 close)
k256 = { version = "0.13", features = ["schnorr"], optional = true }

# F5 (Sprint 4): DPoP jti replay cache — Solid-OIDC §5.2 / RFC 9449.
# Gated behind `dpop-replay-cache` (which implies `oidc`).
lru = { version = "0.12", optional = true }

# Sprint 7 A: rate-limit primitive — parking_lot for the LRU bucket
# mutex (low contention, Send + Sync, already in dep tree via reqwest).
parking_lot = { version = "0.12", optional = true }

# Sprint 6 C: RFC 9421 HTTP Message Signatures for outgoing webhook
# deliveries (Solid Notifications 0.2 §5). Gated behind
# `webhook-signing` (which implies `jss-v04`).
ed25519-dalek = { version = "2", features = ["rand_core"], optional = true }
httpdate = { version = "1", optional = true }
rand = { version = "0.8", optional = true }

# Sprint 11 (row 120-124): multi-format config loader. YAML + TOML are
# optional so consumers that only care about JSON aren't dragged in.
# Gated behind `config-loader`.
serde_yaml = { version = "0.9", optional = true }
toml = { version = "0.8", optional = true }

[features]
# `default` preserves the surface from 0.4.0-alpha.2: filesystem +
# in-memory backends, the tokio runtime, the notifications stack
# (WebSocketChannel2023 + WebhookChannel2023 — `reqwest`-backed), plus
# the JSS-parity umbrella.
default = ["std", "fs-backend", "memory-backend", "tokio-runtime", "notifications"]

# ---------------------------------------------------------------------------
# 0.4.0-alpha.3: feature flags for wasm32 / CF-Workers consumers.
# Per ADR-076/078 absorption, `nostr-bbs-pod-worker` consumes the pure
# logic surfaces (wac, webid, dotfile, nip98 verifier, ldp parsers,
# interop types, security path/URL primitives) without dragging in tokio
# or reqwest. `core` is the entry point for those consumers.
# ---------------------------------------------------------------------------
core = ["std"]
std = []

# Activate the tokio runtime + the websocket/futures stack. Pulled in
# transitively by every async-IO feature below. Pure-logic consumers
# leave this off via `default-features = false, features = ["core"]`.
tokio-runtime = ["dep:tokio", "dep:tokio-tungstenite", "dep:futures-util"]

fs-backend = ["tokio-runtime", "dep:notify"]
memory-backend = ["tokio-runtime"]
s3-backend = ["dep:aws-sdk-s3", "tokio-runtime"]
oidc = ["dep:openidconnect", "dep:jsonwebtoken", "tokio-runtime", "dep:reqwest"]
nip98-schnorr = ["dep:k256"]
notifications = ["tokio-runtime", "dep:reqwest"]

# ---------------------------------------------------------------------------
# Sprint 4 / JSS parity umbrella (ADR-056 / PRD §F).
# `jss-v04` is the parent flag. Sub-features enable one bounded
# context each. Enabling `jss-v04` alone is a no-op.
# ---------------------------------------------------------------------------
jss-v04 = []

# F1/F2: SSRF guard + dotfile allowlist (lightweight structs always
# compiled; integration gated at call sites).
security-primitives = ["jss-v04", "tokio-runtime"]

# F3: legacy `solid-0.1` WebSocket notifications adapter for SolidOS
# data-browser compat. Implies the tokio runtime + the new
# `notifications` feature it sits on top of.
legacy-notifications = ["jss-v04", "tokio-runtime", "notifications"]

# F4: WAC `acl:origin` enforcement per WAC §4.3. When off, the
# evaluator ignores the Origin header for backward compat.
acl-origin = ["jss-v04"]

# F5: DPoP jti replay cache (Solid-OIDC §5.2 / RFC 9449).
# Enables `oidc::replay` and pulls in `lru`.
dpop-replay-cache = ["oidc", "jss-v04", "dep:lru", "tokio-runtime"]

# F6: JSS-compatible layered config loader. The `config` module is
# always compiled (lightweight struct definitions); this feature flag
# is the consumer-facing toggle for binary-layer wiring.
#
# Sprint 11 (row 120-124): extends with auto-detected YAML/TOML file
# loading, env overlay, and CLI overlay. YAML/TOML deps are pulled in
# only when this feature is enabled.
config-loader = ["jss-v04", "dep:serde_yaml", "dep:toml"]

# Sprint 6 C: RFC 9421 HTTP Message Signatures on outgoing webhook
# deliveries, Retry-After handling, circuit breaker. Consumers opt in.
webhook-signing = ["jss-v04", "dep:ed25519-dalek", "dep:httpdate", "dep:rand", "tokio-runtime", "dep:reqwest"]

# Sprint 6 D: did:nostr resolver (DID-Doc ↔ WebID bidirectional
# alsoKnownAs/owl:sameAs verification). Surfaces under
# `solid_pod_rs::interop::did_nostr`. No new crate dependencies: uses
# the reqwest + serde + url stack already pulled in by the library,
# and std::sync::RwLock for the cache.
did-nostr = ["jss-v04", "security-primitives", "tokio-runtime", "dep:reqwest"]

# Sprint 7 A: rate-limit primitive. Library exposes a `RateLimiter`
# trait plus an LRU-backed reference implementation. CORS support is
# gated under `jss-v04` (no new deps). Enabling `rate-limit` pulls in
# `lru` + `parking_lot`.
rate-limit = ["jss-v04", "dep:lru", "dep:parking_lot", "tokio-runtime"]

# Sprint 7 B: pod-level quota policy. Ships `FsQuotaStore` backed by
# `.quota.json` sidecars; integrates with the config loader via
# `JSS_DEFAULT_QUOTA`. `multitenant` and `parse_size` live in the
# always-on `jss-v04` umbrella; this flag only gates the FS adapter
# (which shares serde_json + tokio::fs already pulled in by the crate,
# no new deps).
quota = ["jss-v04", "config-loader", "tokio-runtime"]

[dev-dependencies]
tokio = { version = "1", features = ["full"] }
tempfile = "3"
actix-web = "4"
wiremock = "0.6"
criterion = { version = "0.5", features = ["html_reports"] }
# Sprint 7 D: integration tests drive the server binary's actix `App`
# through `test::init_service`. Dev-dependency only — never ships in
# the library's public surface.
# Path-only dev-dependency: dropping the version pin avoids a publish
# cycle (solid-pod-rs-server depends on solid-pod-rs at the same
# version, so cargo can't resolve the dev-dep against crates.io until
# the parent itself publishes). cargo strips dev-deps from the
# published metadata, so this is the supported pattern.
solid-pod-rs-server = { path = "../solid-pod-rs-server" }
axum = "0.7"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
rand = { version = "0.8", features = ["small_rng"] }

# Sprint 5 P0: real ES256 keypairs for DPoP / access-token JWT tests.
# Not pulled into the main library — tests only.
p256 = { version = "0.13", features = ["pem", "ecdsa", "pkcs8"] }
pkcs8 = { version = "0.10", features = ["pem"] }
# HMAC-SHA256 for DPoP replay test fixtures (post-Sprint-5 the
# signature is now verified so fixtures must produce real MACs).
hmac = "0.12"
# Sprint 11 row 152: real BIP-340 Schnorr signer for CID verifier
# integration tests, which run with `nip98-schnorr` enabled via the
# workspace's default feature cascade.
k256 = { version = "0.13", features = ["schnorr"] }

# ---------------------------------------------------------------------------
# Examples — runnable via `cargo run --example <name>`.
# ---------------------------------------------------------------------------

[[example]]
name = "embed_in_actix"
path = "examples/embed_in_actix.rs"

[[example]]
name = "custom_storage"
path = "examples/custom_storage.rs"

[[example]]
name = "nip98_client"
path = "examples/nip98_client.rs"

[[example]]
name = "notifications_consumer"
path = "examples/notifications_consumer.rs"

[[example]]
name = "webhook_receiver"
path = "examples/webhook_receiver.rs"

[[example]]
name = "wac_admin"
path = "examples/wac_admin.rs"

[[example]]
name = "oidc_client"
path = "examples/oidc_client.rs"
required-features = ["oidc"]

# ---------------------------------------------------------------------------
# Benchmarks — criterion-based, see `docs/benchmarks.md`.
# ---------------------------------------------------------------------------

[[bench]]
name = "storage_backend_bench"
harness = false

[[bench]]
name = "wac_eval_bench"
harness = false

[[bench]]
name = "ldp_content_negotiation_bench"
harness = false

[[bench]]
name = "nip98_verify_bench"
harness = false

[[bench]]
name = "dpop_replay_bench"
harness = false
required-features = ["dpop-replay-cache"]