Skip to main content

solid_pod_rs/
lib.rs

1//! Framework-agnostic Rust library for serving Solid Protocol 0.11 pods.
2//!
3//! `solid-pod-rs` provides LDP resource and container semantics, Web Access
4//! Control (WAC 1.x + 2.0), WebID profile documents, Solid-OIDC 0.1,
5//! NIP-98 HTTP auth, and Solid Notifications 0.2 -- all without coupling
6//! to a specific HTTP framework. Wire it into actix-web, axum, hyper, or
7//! anything else; the crate never mounts routes itself. On top of the Solid
8//! core it adds two composable **provenance primitives** ([`provenance`]):
9//! cheap, always-on **git-marks** (every pod write captured as a git commit
10//! + a PROV-O sidecar) and expensive, opt-in **block-trails** (a
11//! Bitcoin-taproot-anchored, hash-chained state trail — [`mrc20`] /
12//! [`bitcoin_tx`]) — and a routed, sovereign HTTP-402 economy: a `did:nostr`-keyed
13//! [Web Ledger](payments), `acl:PaymentCondition` ([`wac`]) access gating, an MRC20
14//! deposit path, and a peer order book + constant-product AMM ([`trading`]).
15//! The HTTP routing for the 402 economy and the `_prov` provenance API lives in
16//! the sibling [`solid-pod-rs-server`](https://docs.rs/solid-pod-rs-server). See
17//! [ADR-059](https://docs.rs/crate/solid-pod-rs/latest/source/docs/adr/ADR-059-provenance-primitives-block-trails-git-marks.md).
18//!
19//! For a turnkey binary, use the sibling crate
20//! [`solid-pod-rs-server`](https://docs.rs/solid-pod-rs-server).
21//!
22//! ## Feature flags
23//!
24//! | Flag | Default | Purpose |
25//! |-------------------------|:-------:|-----------------------------------------------|
26//! | `core` | off | Pure-logic surfaces only — wasm32 / CF Workers. |
27//! | `std` | on | std lib (always; reserved for future no_std).  |
28//! | `embedded-docs` | off | Embed the Diataxis `docs/` tree as `pub static DOCS_DIR` (re-exports `include_dir`); consumed by `solid-pod-rs-server`'s MCP docs tools + docs.rs. |
29//! | `tokio-runtime` | on | Tokio + tokio-tungstenite + futures-util.       |
30//! | `notifications` | on | WebSocketChannel2023 + WebhookChannel2023.      |
31//! | `fs-backend` | on | POSIX filesystem storage. |
32//! | `memory-backend` | on | In-process `HashMap` storage (tests/demos). |
33//! | `s3-backend` | off | AWS S3 / S3-compatible object stores. |
34//! | `oidc` | off | Solid-OIDC 0.1 + DPoP. |
35//! | `dpop-replay-cache` | off | DPoP `jti` replay cache (pulls `oidc`). |
36//! | `nip98-schnorr` | off | BIP-340 signature verification for NIP-98. |
37//! | `jss-v04` | off | JSS-parity umbrella (ADR-056); no-op alone — sub-features below switch one bounded context each on. |
38//! | `acl-origin` | off | WAC `acl:origin` enforcement (pulls `jss-v04`). |
39//! | `security-primitives` | off | SSRF guard + dotfile allowlist (pulls `jss-v04`). |
40//! | `legacy-notifications` | off | `solid-0.1` WebSocket adapter (SolidOS). |
41//! | `config-loader` | off | Layered config loader with `JSS_*` env vars + YAML/TOML. |
42//! | `webhook-signing` | off | RFC 9421 Ed25519 webhook signing. |
43//! | `rate-limit` | off | Sliding-window LRU rate limiter + CORS. |
44//! | `quota` | off | Per-pod `.quota.json` sidecar (atomic writes). |
45//! | `did-nostr-types` | off | Canonical did:nostr types (wasm32-safe). |
46//! | `did-nostr` | off | did:nostr DID-Doc ↔ WebID resolver in [`interop`]. |
47//! | `mrc20` | off | BIP-341 taproot key chaining + anchor verify/build for MRC20 / block-trails. |
48//! | `lws-cid` | off | LWS 1.0 CID self-signed JWT verifier (ES256K). |
49//! | `lws-cid-p256` | off | LWS-CID + ES256 (P-256) algorithm. |
50//! | `lws-cid-eddsa` | off | LWS-CID + EdDSA (Ed25519) algorithm. |
51//! | `lws-cid-full` | off | LWS-CID with all algorithms. |
52//! | `provision-keys` | off | Gates the optional `ProvisionPlan::provision_keys` field (IdP key provisioner extension point). |
53//! | `nip05-endpoint` | off | Pod-resident `GET /.well-known/nostr.json` (pulls `did-nostr`). |
54//! | `export-jsonld` | off | JSON-LD time-chain pod export (`GET /api/exports/all`). |
55//! | `git-auto-init` | off | `GitInitHook` trait + `provision_pod_ext` (on-demand `git init` on first push; impl in `solid-pod-rs-git`). |
56//!
57//! On `wasm32-unknown-unknown` targets the `getrandom` crate is pulled in with
58//! its `js` feature (transitively required by `uuid`/`rand`) so randomness
59//! resolves via `crypto.getRandomValues()`; this is a target-gated dependency,
60//! not a cargo feature you enable.
61//!
62//! `core` consumers wire the crate via `default-features = false,
63//! features = ["core"]` and get only the pure-logic surfaces (no
64//! tokio, no reqwest, no DNS resolver, no filesystem). See
65//! `RELEASE_NOTES.md` v0.4.0-alpha.3 for the absorbed surfaces map.
66//!
67//! ## Module overview
68//!
69//! | Module | Responsibility |
70//! |-----------------|--------------------------------------------------------------|
71//! | [`storage`] | `Storage` trait + FS / Memory / S3 backends. |
72//! | [`ldp`] | Resources, containers, content negotiation, PATCH, `Prefer`. |
73//! | [`wac`] | Access control evaluator + WAC 2.0 conditions framework. |
74//! | [`webid`] | WebID profile documents (emits `solid:oidcIssuer` + CID). |
75//! | [`mashlib`] | SolidOS data-browser HTML wrapper + data-island embed.    |
76//! | [`auth`] | NIP-98 HTTP auth + LWS-CID self-signed JWT verifier. |
77//! | [`payments`] | HTTP 402, Web Ledgers, multi-chain TXO, payment store. |
78//! | [`mrc20`] | MRC20 state chains, JCS, BIP-341 key chaining.       |
79//! | [`provenance`] | git-mark / block-trail provenance primitives + PROV-O.  |
80//! | [`trading`] | Peer-to-peer order book + AMM constant-product pool.  |
81//! | [`notifications`] | WebSocket, Webhook (RFC 9421 signed), legacy adapter. |
82//! | [`error`] | Crate-wide [`PodError`] error type. |
83//! | [`config`] | Layered configuration schema. |
84//! | [`security`] | SSRF guard, dotfile allowlist, CORS, rate limiter. |
85//! | [`quota`] | Per-pod byte-quota enforcement. |
86//! | [`multitenant`] | `PodResolver` trait; path + subdomain modes. |
87//! | [`interop`] | `.well-known/solid`, WebFinger, NodeInfo, did:nostr. |
88//! | [`did_nostr_types`] | Canonical `did:nostr` types (wasm32-safe, `core`). |
89//! | [`provision`] | Pod bootstrap (WebID + containers + type indexes + ACL). |
90//!
91//! ## Quick start
92//!
93//! ```rust,no_run
94//! use solid_pod_rs::storage::memory::MemoryBackend;
95//! use solid_pod_rs::{Storage, evaluate_access, AccessMode};
96//! use bytes::Bytes;
97//! use std::sync::Arc;
98//!
99//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
100//! // 1. Create a storage backend.
101//! let store = Arc::new(MemoryBackend::new());
102//!
103//! // 2. PUT a resource.
104//! store.put("/hello.txt", Bytes::from("world"), "text/plain").await.unwrap();
105//!
106//! // 3. GET it back.
107//! let (body, meta) = store.get("/hello.txt").await.unwrap();
108//! assert_eq!(&body[..], b"world");
109//! assert_eq!(meta.content_type, "text/plain");
110//!
111//! // 4. WAC evaluation (no ACL document = deny by default).
112//! let allowed = evaluate_access(None, Some("https://alice.example/profile/card#me"),
113//!     "/hello.txt", AccessMode::Read, None);
114//! assert!(!allowed);
115//! # });
116//! ```
117//!
118//! ## Attribution
119//!
120//! Rust port of JavaScriptSolidServer. See NOTICE for provenance.
121
122#![doc = include_str!("../README.md")]
123#![deny(unsafe_code)]
124#![warn(rust_2018_idioms)]
125
126// ---------------------------------------------------------------------------
127// Always-compiled (`core`) modules.
128//
129// Pure-logic surfaces: parsers, validators, type definitions. None of
130// these reach for tokio, reqwest, or notify directly. Wasm32 / CF
131// Workers consumers wire these via
132// `default-features = false, features = ["core"]`.
133// ---------------------------------------------------------------------------
134pub mod auth;
135/// Bitcoin taproot transaction building (block-trail write-side). The module's
136/// own inner `#![cfg(all(feature = "mrc20", not(target_arch = "wasm32")))]`
137/// gates it: it compiles to nothing on wasm or without the `mrc20` feature, so
138/// the tx-building never leaks into the wasm `core` surface (ADR-059 D4).
139pub mod bitcoin_tx;
140pub mod config;
141pub mod error;
142pub mod interop;
143pub mod ldp;
144pub mod mashlib;
145pub mod metrics;
146pub mod mrc20;
147pub mod multitenant;
148pub mod payments;
149pub mod provenance;
150pub mod security;
151pub mod trading;
152pub mod wac;
153pub mod webid;
154
155// ---------------------------------------------------------------------------
156// `did-nostr-types`-gated module.
157//
158// Canonical did:nostr types (NostrPubkey, DID-Doc renderers, ServiceEntry)
159// behind a lightweight feature flag. No runtime deps — wasm32 / CF Workers
160// consumers get these via `core`.
161// ---------------------------------------------------------------------------
162#[cfg(feature = "did-nostr-types")]
163pub mod did_nostr_types;
164
165// ---------------------------------------------------------------------------
166// `tokio-runtime`-gated modules.
167//
168// These pull tokio (mpsc, fs, broadcast) or reqwest (HTTP client) and
169// are unavailable to `core` consumers. They are wired in by the
170// `default` feature set so the existing surface from 0.4.0-alpha.2 is
171// preserved bit-for-bit on native builds.
172// ---------------------------------------------------------------------------
173#[cfg(feature = "notifications")]
174pub mod notifications;
175#[cfg(feature = "tokio-runtime")]
176pub mod provision;
177#[cfg(feature = "tokio-runtime")]
178pub mod quota;
179#[cfg(feature = "tokio-runtime")]
180pub mod storage;
181
182#[cfg(feature = "oidc")]
183pub mod oidc;
184
185// ---------------------------------------------------------------------------
186// JSS v0.0.190 Phase 1 port (issue #437) — scaffolds.
187//
188// Parity row 198. Default-off; function body is `todo!()`. Type
189// surface is stable for downstream consumers (NRF,
190// dreamlab-ai-website).
191// ---------------------------------------------------------------------------
192#[cfg(feature = "export-jsonld")]
193pub mod export;
194
195/// Transport-agnostic HTTP / WebSocket handler drivers. Consumers wire
196/// these into their HTTP framework of choice. Feature-gated; present
197/// only when at least one handler is enabled. Respects the F7
198/// library-server boundary — this crate never mounts routes itself.
199#[cfg(feature = "legacy-notifications")]
200pub mod handlers;
201
202// ---------------------------------------------------------------------------
203// `core` re-exports — always available.
204// ---------------------------------------------------------------------------
205pub use auth::nip98::Nip98Verifier;
206pub use auth::self_signed::{
207    CidVerifier, ProofEnvelope, SelfSignedError, SelfSignedVerifier, VerifiedSubject,
208};
209pub use error::PodError;
210pub use interop::{
211    dev_session, nip05_document, verify_nip05, webfinger_response, well_known_solid, DevSession,
212    Nip05Document, SolidWellKnown, WebFingerJrd, WebFingerLink,
213};
214pub use ldp::{
215    apply_json_patch, apply_n3_patch, apply_patch_to_absent, apply_sparql_patch, cache_control_for,
216    evaluate_preconditions, is_rdf_content_type, link_headers, negotiate_format, not_found_headers,
217    options_for, parse_range_header, parse_range_header_v2, patch_dialect_from_mime,
218    server_managed_triples, slice_range, vary_header, ByteRange, ConditionalOutcome,
219    ContainerRepresentation, Graph, OptionsResponse, PatchCreateOutcome, PatchDialect,
220    PatchOutcome, PreferHeader, RangeOutcome, RdfFormat, Term, Triple, ACCEPT_PATCH, ACCEPT_POST,
221    CACHE_CONTROL_RDF, SPARQL_UPDATE_MAX_BYTES,
222};
223pub use mashlib::{MashlibConfig, MashlibMode, DATA_ISLAND_MAX_BYTES};
224pub use metrics::SecurityMetrics;
225pub use multitenant::{PathResolver, PodResolver, ResolvedPath, SubdomainResolver};
226pub use provenance::{
227    prov_ttl, AnchorPolicy, BlockAnchorer, BlockTrailAnchor, BlocktrailEnvelope, BlocktrailTxo,
228    ClosedEpoch, EpochAccumulator, GitMark, GitMarkEnvelope, GitMarker, MerkleProof,
229    ProvenanceError, ProvenanceLog, ProvenanceMark, WriteRecord,
230};
231pub use security::{is_path_allowed, DotfileAllowlist, DotfileError, DotfilePathError};
232pub use wac::{
233    check_origin, evaluate_access, evaluate_access_with_groups, extract_origin_patterns,
234    method_to_mode, mode_name, parse_turtle_acl, serialize_turtle_acl, wac_allow_header,
235    AccessMode, AclDocument, GroupMembership, Origin, OriginDecision, OriginPattern,
236    StaticGroupMembership,
237};
238pub use webid::{
239    extract_nostr_pubkey, extract_oidc_issuer, generate_webid_html,
240    generate_webid_html_with_issuer, validate_webid_html,
241};
242
243// ---------------------------------------------------------------------------
244// `tokio-runtime`-gated re-exports.
245// ---------------------------------------------------------------------------
246#[cfg(feature = "tokio-runtime")]
247pub use provision::{
248    check_admin_override, provision_pod, AdminOverride, ProvisionOutcome, ProvisionPlan,
249    QuotaTracker,
250};
251#[cfg(feature = "quota")]
252pub use quota::FsQuotaStore;
253#[cfg(feature = "tokio-runtime")]
254pub use quota::{QuotaExceeded, QuotaPolicy, QuotaUsage};
255#[cfg(feature = "tokio-runtime")]
256pub use security::{is_safe_url, resolve_and_check, IpClass, SsrfError, SsrfPolicy};
257#[cfg(feature = "tokio-runtime")]
258pub use storage::{ResourceMeta, Storage, StorageEvent};
259
260// ---------------------------------------------------------------------------
261// JSS v0.0.190 Phase 1 port — re-exports (scaffolds).
262// ---------------------------------------------------------------------------
263#[cfg(feature = "export-jsonld")]
264pub use export::{
265    export_pod_jsonld, ExportOptions, PodExportBundle, PodExportEntry, EXPORT_CONTENT_TYPE,
266    EXPORT_JSONLD_CONTEXT,
267};
268
269// ---------------------------------------------------------------------------
270// Embedded documentation tree (feature = "embedded-docs")
271// ---------------------------------------------------------------------------
272
273/// Re-exported so dependants can name `include_dir::Dir` without taking a
274/// direct dependency on the embedding crate.
275#[cfg(feature = "embedded-docs")]
276pub use include_dir;
277
278/// The crate's Diataxis `docs/` tree, embedded at compile time. It lives
279/// here — inside the owning crate's package root — so dependants (notably
280/// `solid-pod-rs-server`'s MCP `list_docs`/`read_docs` tools) serve it from
281/// a self-contained binary even when built from the registry tarball, where
282/// a `../solid-pod-rs/docs` path escape does not exist.
283#[cfg(feature = "embedded-docs")]
284pub static DOCS_DIR: include_dir::Dir<'static> =
285    include_dir::include_dir!("$CARGO_MANIFEST_DIR/docs");