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.
8//!
9//! For a turnkey binary, use the sibling crate
10//! [`solid-pod-rs-server`](https://docs.rs/solid-pod-rs-server).
11//!
12//! ## Feature flags
13//!
14//! | Flag | Default | Purpose |
15//! |-------------------------|:-------:|-----------------------------------------------|
16//! | `fs-backend` | on | POSIX filesystem storage. |
17//! | `memory-backend` | on | In-process `HashMap` storage (tests/demos). |
18//! | `s3-backend` | off | AWS S3 / S3-compatible object stores. |
19//! | `oidc` | off | Solid-OIDC 0.1 + DPoP. |
20//! | `dpop-replay-cache` | off | DPoP `jti` replay cache (pulls `oidc`). |
21//! | `nip98-schnorr` | off | BIP-340 signature verification for NIP-98. |
22//! | `acl-origin` | off | WAC `acl:origin` enforcement. |
23//! | `security-primitives` | off | SSRF guard + dotfile allowlist. |
24//! | `legacy-notifications` | off | `solid-0.1` WebSocket adapter (SolidOS). |
25//! | `config-loader` | off | Layered config loader with `JSS_*` env vars. |
26//! | `webhook-signing` | off | RFC 9421 Ed25519 webhook signing. |
27//! | `did-nostr` | off | did:nostr resolver in `interop`. |
28//! | `rate-limit` | off | Sliding-window LRU rate limiter. |
29//! | `quota` | off | Per-pod `.quota.json` sidecar (atomic writes). |
30//!
31//! ## Module overview
32//!
33//! | Module | Responsibility |
34//! |-----------------|--------------------------------------------------------------|
35//! | [`storage`] | `Storage` trait + FS / Memory / S3 backends. |
36//! | [`ldp`] | Resources, containers, content negotiation, PATCH, `Prefer`. |
37//! | [`wac`] | Access control evaluator + WAC 2.0 conditions framework. |
38//! | [`webid`] | WebID profile documents (emits `solid:oidcIssuer` + CID). |
39//! | [`auth`] | NIP-98 HTTP authentication. |
40//! | [`notifications`] | WebSocket, Webhook (RFC 9421 signed), legacy adapter. |
41//! | [`error`] | Crate-wide [`PodError`] error type. |
42//! | [`config`] | Layered configuration schema. |
43//! | [`security`] | SSRF guard, dotfile allowlist, CORS, rate limiter. |
44//! | [`quota`] | Per-pod byte-quota enforcement. |
45//! | [`multitenant`] | `PodResolver` trait; path + subdomain modes. |
46//! | [`interop`] | `.well-known/solid`, WebFinger, NodeInfo, did:nostr. |
47//! | [`provision`] | Pod bootstrap (WebID + containers + type indexes + ACL). |
48//!
49//! ## Quick start
50//!
51//! ```rust,no_run
52//! use solid_pod_rs::storage::memory::MemoryBackend;
53//! use solid_pod_rs::{Storage, evaluate_access, AccessMode};
54//! use bytes::Bytes;
55//! use std::sync::Arc;
56//!
57//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
58//! // 1. Create a storage backend.
59//! let store = Arc::new(MemoryBackend::new());
60//!
61//! // 2. PUT a resource.
62//! store.put("/hello.txt", Bytes::from("world"), "text/plain").await.unwrap();
63//!
64//! // 3. GET it back.
65//! let (body, meta) = store.get("/hello.txt").await.unwrap();
66//! assert_eq!(&body[..], b"world");
67//! assert_eq!(meta.content_type, "text/plain");
68//!
69//! // 4. WAC evaluation (no ACL document = deny by default).
70//! let allowed = evaluate_access(None, Some("https://alice.example/profile/card#me"),
71//! "/hello.txt", AccessMode::Read, None);
72//! assert!(!allowed);
73//! # });
74//! ```
75//!
76//! ## Attribution
77//!
78//! Rust port of JavaScriptSolidServer. See NOTICE for provenance.
79
80#![doc = include_str!("../README.md")]
81#![deny(unsafe_code)]
82#![warn(rust_2018_idioms)]
83
84pub mod auth;
85pub mod config;
86pub mod error;
87pub mod interop;
88pub mod ldp;
89pub mod metrics;
90pub mod multitenant;
91pub mod notifications;
92pub mod provision;
93pub mod quota;
94pub mod security;
95pub mod storage;
96pub mod wac;
97pub mod webid;
98
99#[cfg(feature = "oidc")]
100pub mod oidc;
101
102/// Transport-agnostic HTTP / WebSocket handler drivers. Consumers wire
103/// these into their HTTP framework of choice. Feature-gated; present
104/// only when at least one handler is enabled. Respects the F7
105/// library-server boundary — this crate never mounts routes itself.
106#[cfg(feature = "legacy-notifications")]
107pub mod handlers;
108
109// Re-exports for ergonomic consumers.
110pub use auth::nip98::Nip98Verifier;
111pub use auth::self_signed::{
112 CidVerifier, ProofEnvelope, SelfSignedError, SelfSignedVerifier, VerifiedSubject,
113};
114pub use error::PodError;
115pub use metrics::SecurityMetrics;
116pub use security::{
117 is_path_allowed, is_safe_url, resolve_and_check, DotfileAllowlist, DotfileError,
118 DotfilePathError, IpClass, SsrfError, SsrfPolicy,
119};
120pub use storage::{ResourceMeta, Storage, StorageEvent};
121pub use wac::{
122 check_origin, evaluate_access, evaluate_access_with_groups, extract_origin_patterns,
123 method_to_mode, mode_name, parse_turtle_acl, serialize_turtle_acl, wac_allow_header,
124 AccessMode, AclDocument, GroupMembership, Origin, OriginDecision, OriginPattern,
125 StaticGroupMembership,
126};
127pub use ldp::{
128 apply_json_patch, apply_n3_patch, apply_patch_to_absent, apply_sparql_patch,
129 cache_control_for, evaluate_preconditions, is_rdf_content_type, link_headers,
130 negotiate_format, not_found_headers, options_for, parse_range_header, parse_range_header_v2,
131 patch_dialect_from_mime, server_managed_triples, slice_range, vary_header, ByteRange,
132 ConditionalOutcome, ContainerRepresentation, Graph, OptionsResponse, PatchCreateOutcome,
133 PatchDialect, PatchOutcome, PreferHeader, RangeOutcome, RdfFormat, Term, Triple, ACCEPT_PATCH,
134 ACCEPT_POST, CACHE_CONTROL_RDF,
135};
136pub use interop::{
137 dev_session, nip05_document, verify_nip05, webfinger_response, well_known_solid, DevSession,
138 Nip05Document, SolidWellKnown, WebFingerJrd, WebFingerLink,
139};
140pub use multitenant::{PathResolver, PodResolver, ResolvedPath, SubdomainResolver};
141pub use provision::{
142 check_admin_override, provision_pod, AdminOverride, ProvisionOutcome, ProvisionPlan,
143 QuotaTracker,
144};
145pub use quota::{QuotaExceeded, QuotaPolicy, QuotaUsage};
146
147#[cfg(feature = "quota")]
148pub use quota::FsQuotaStore;
149pub use webid::{
150 extract_oidc_issuer, generate_webid_html, generate_webid_html_with_issuer,
151 validate_webid_html,
152};