# solid-pod-rs
Framework-agnostic Rust library for serving [Solid Protocol 0.11]
pods: LDP resources and containers, Web Access Control, WebID,
Solid Notifications 0.2, Solid-OIDC 0.1, and NIP-98 HTTP auth.
**Parity vs JSS: ~100 % spec-normative** (~98 % strict on the full
132-row tracker — see [`PARITY-CHECKLIST.md`](PARITY-CHECKLIST.md)).
702 tests pass across the workspace as of Sprint 12 close (2026-05-06).
The library has no opinions about the HTTP runtime; wire it into
actix-web, axum, hyper, or any other server. For a turnkey binary
use the sibling crate [`solid-pod-rs-server`](../solid-pod-rs-server/).
```toml
[dependencies]
solid-pod-rs = "0.4.0-alpha.1"
```
```rust,no_run
use solid_pod_rs::storage::fs::FsBackend;
use std::path::PathBuf;
let storage = FsBackend::new(PathBuf::from("./pod-root"));
// Compose with your framework; see examples/embed_in_actix.rs.
```
## Feature flags
| `fs-backend` | on | POSIX filesystem storage. |
| `memory-backend` | on | In-process `HashMap` storage (tests/demos). |
| `s3-backend` | off | AWS S3 / S3-compatible object stores. |
| `oidc` | off | Solid-OIDC 0.1 + DPoP. |
| `dpop-replay-cache` | off | DPoP `jti` replay cache (pulls `oidc`). |
| `nip98-schnorr` | off | BIP-340 signature verification for NIP-98. |
| `acl-origin` | off | WAC `acl:origin` enforcement. |
| `security-primitives` | off | SSRF guard + dotfile allowlist. |
| `legacy-notifications` | off | `solid-0.1` WebSocket adapter (SolidOS). |
| `config-loader` | off | Layered config loader with `JSS_*` env vars. |
| `webhook-signing` | off | RFC 9421 Ed25519 webhook signing. |
| `did-nostr` | off | did:nostr resolver in `interop`. |
| `rate-limit` | off | Sliding-window LRU rate limiter. |
| `quota` | off | Per-pod `.quota.json` sidecar (atomic writes).|
## Modules
| `storage` | `Storage` trait + FS / Memory / S3 backends. |
| `ldp` | Resources, containers, content negotiation, PATCH, `Prefer`. |
| `wac` | Access control evaluator + WAC 2.0 conditions framework. |
| `webid` | WebID profile documents (emits `solid:oidcIssuer` + CID). |
| `auth` | NIP-98 HTTP authentication. |
| `oidc` | Solid-OIDC 0.1 + DPoP (verified) + JWKS + replay cache. |
| `notifications` | WebSocket, Webhook (RFC 9421 signed), legacy adapter. |
| `security` | SSRF guard + dotfile allowlist + CORS + rate limiter. |
| `quota` | Per-pod `.quota.json` sidecar with atomic writes. |
| `multitenant` | `PodResolver` trait; path + subdomain modes. |
| `config` | Layered configuration schema. |
| `interop` | `.well-known/solid`, WebFinger, NodeInfo 2.1, did:nostr. |
| `provision` | Pod bootstrap (WebID + containers + type indexes + ACL). |
## Sibling crate ecosystem
Five sibling crates live in the workspace — all **functional and
shipping** as of Sprint 12. Integrators may depend on them today.
| `solid-pod-rs-activitypub` | 4,453 | 102–108, 131, 169–172 | `src/ap/{index,routes/inbox,routes/outbox,store}.js` |
| `solid-pod-rs-git` | 1,685 | 69, 100 | `src/handlers/git.js` |
| `solid-pod-rs-idp` | 6,160 | 74–81, 130 | `src/idp/{index,provider,passkey,interactions,credentials}.js` |
| `solid-pod-rs-nostr` | 2,177 | 89, 90, 101, 132 | `src/{did/resolver,nostr/relay,auth/did-nostr}.js` |
| `solid-pod-rs-didkey` | 1,167 | 153 | W3C did:key spec + LWS 1.0 SSI |
The did:nostr resolver shipped in Sprint 6 lives inside the core
library (`interop::did_nostr` under `did-nostr`) as well as the
`solid-pod-rs-nostr` crate, so the Tier 1 + Tier 3 DID flow is
available either way.
## WAC inheritance model
```mermaid
flowchart TD
REQ["Request for<br/>/pod/notes/2024/entry.ttl"] --> Q1{".acl sidecar<br/>at entry.ttl.acl?"}
Q1 -->|found| EVAL["Evaluate ACL rules<br/>against AuthContext"]
Q1 -->|not found| Q2{".acl at<br/>/pod/notes/2024/.acl?"}
Q2 -->|"found (acl:default)"| EVAL
Q2 -->|not found| Q3{".acl at<br/>/pod/notes/.acl?"}
Q3 -->|"found (acl:default)"| EVAL
Q3 -->|not found| Q4{".acl at<br/>/pod/.acl?"}
Q4 -->|"found (acl:default)"| EVAL
Q4 -->|not found| DENY["DENY<br/>(no ACL = no access)"]
EVAL --> CHK{"Agent match?<br/>acl:agent / agentClass<br/>/ agentGroup"}
CHK -->|"matched + mode ok"| ALLOW["ALLOW<br/>+ WAC-Allow header"]
CHK -->|"no match"| DENY2["DENY<br/>+ WAC-Allow header"]
style REQ fill:#4a90d9,stroke:#2c5f8a,color:#fff
style ALLOW fill:#2ecc71,stroke:#1a9850,color:#fff
style DENY fill:#e74c3c,stroke:#c0392b,color:#fff
style DENY2 fill:#e74c3c,stroke:#c0392b,color:#fff
style EVAL fill:#9b59b6,stroke:#7d3c98,color:#fff
```
## Security posture
- **DPoP signature verification** against the proof's embedded JWK
(RFC 9449 §4.3), with an algorithm allowlist (`ES256`/`ES384`,
`RS256`/`RS384`/`RS512`, `PS256`/`PS384`/`PS512`, `EdDSA`); `alg=none`
and HMAC hard-rejected. `ath` access-token hash binding enforced.
`jti` replay cache under `dpop-replay-cache`.
- **SSRF guard** — RFC 1918, loopback, link-local, and cloud metadata
endpoints are rejected on every outbound fetch (JWKS discovery,
webhook delivery, did:nostr resolution). DNS-rebinding is closed by
pinning the resolved IP on the per-call reqwest client.
- **Dotfile allowlist** — only `.acl`, `.meta`, `.well-known`,
`.quota.json`, and `.account` are served. All other dotfiles return
404 regardless of storage-layer presence.
- **RFC 7638 canonical JWK thumbprints** — replaces the previous
hand-rolled JSON template; verified byte-for-byte against the
spec's appendix-A vector.
- **WAC parser bounds** — 1 MiB Turtle input cap via
`parse_turtle_acl_with_limit` (`JSS_MAX_ACL_BYTES`); 32-level
JSON-LD depth cap via `parse_jsonld_acl_with_limits`. Returns
`PodError::PayloadTooLarge` on oversized input (CWE-400, Sprint 12).
- **Atomic quota writes** — temp-file + rename so concurrent writers
cannot observe a torn `.quota.json`.
- **RFC 9421 webhook signing** — Ed25519 over `@method`,
`@target-uri`, `content-type`, `content-digest` (RFC 9530),
`date`, `x-solid-notification-id`.
See [`SECURITY.md`](SECURITY.md) for disclosure policy and a full
cryptographic verification matrix.
## Documentation
- Workspace README: [`../../README.md`](../../README.md)
- Diátaxis docs: [`docs/`](docs/)
- Agent integration guide: [`docs/reference/agent-integration-guide.md`](docs/reference/agent-integration-guide.md)
- Parity vs JSS: [`PARITY-CHECKLIST.md`](PARITY-CHECKLIST.md)
- Gap analysis: [`GAP-ANALYSIS.md`](GAP-ANALYSIS.md)
- Changelog: [`CHANGELOG.md`](CHANGELOG.md)
## Licence
AGPL-3.0-only — see [`../../LICENSE`](../../LICENSE) and
[`NOTICE`](NOTICE).
[Solid Protocol 0.11]: https://solidproject.org/TR/protocol