From 14eff29bb7020e273cb45a4a1cf3412c5ea6d9c4 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 22 Sep 2026 20:47:59 +0000 Subject: [PATCH] docs: align every document with the code at 045c24e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verified each checkable claim in the READMEs, crate-level rustdoc, docs/ reference tree, SECURITY/CONTRIBUTING and the architecture baseline against the workspace and corrected what drifted. No behaviour changes; the only Rust edits are doc comments (//! and ///) and intra-doc link paths. - BASELINE-solid-pod-rs.md: version 0.5.0-alpha.9, re-recorded every rotted file:line citation (git_mark_write 3462, no-op shim 3668, handle_patch 1619, seed_graph 1896, replay.rs/replay_store.rs, multitenant.rs), bumped to 0.1.1 / verified_commit 045c24e per its own change process. - solid-pod-rs-server README + http-endpoints.md: `git` is not a default feature; dpop-replay-cache/nip98-schnorr/nip98-replay/acl-origin/mrc20/ embedded-docs are always on via the dependency; admin provision returns podUrl `/pods/{pk}/` and is registered unconditionally; git-panel routes and OPTIONS preflight live at `/pods/{pubkey}/_git/…`; real CORS header set; real WWW-Authenticate challenge; crates.io install is live. - NIP-98 freshness window is ±60 s (TIMESTAMP_TOLERANCE), not ±120 s. - Sibling READMEs: LOC and test counts refreshed (2026-09-22), git/nostr "will close when this crate lands" → shipped, activitypub module table matches src/, idp PARITY path fixed, forge marks forks/PRs, NIP-34 announce, anchoring and marks manager as planned and states that the bundled server gates the forge with the ownership guard, not WAC. - Root README: fixed broken link to the archived ADR-059; FsBackend::new is async; git-marks are always-on only in a --features git build. - env-vars.md: dropped JSS_WORKERS / JSS_LOG_LEVEL / JSS_DISABLE_DOTFILES (no Rust consumer), added JSS_MAX_ACL_JSON_DEPTH; migrate-from-jss.md: JSS_MASHLIB is a runtime flag; ecosystem-integration.md: S3 scaffold removed in alpha.8; comparison-vs-jss: cargo install path. - examples-index.md / tutorial 03: no `standalone` example exists; use the bundled server. benchmarks.md: five benches (dpop_replay_bench added). - SECURITY.md dotfile allowlist adds .acl.meta and .account; CONTRIBUTING no longer points at a vendored JSS checkout that is not in the repo. - api.md / solid-oidc-compatibility-matrix.md version pins → alpha.9 (no oidc source change since the alpha.8 tag). - Rustdoc: crate-qualified five intra-doc links in the server crate that fail under -D rustdoc::broken-intra-doc-links (module docs merged into crate scope), so `cargo doc --no-deps` is clean for all members. Gates run locally: cargo test --workspace --all-features (1896 passed, 114 binaries), cargo doc --no-deps -D warnings -D broken-intra-doc-links for every crate, cargo test --doc --workspace --all-features, cargo fmt --check, scripts/check-diagram-staleness.sh, scripts/adr-index-gen.js. Co-Authored-By: jjohare --- CHANGELOG.md | 17 ++++++ README.md | 9 +-- crates/solid-pod-rs-activitypub/README.md | 7 ++- crates/solid-pod-rs-forge/README.md | 30 +++++++--- crates/solid-pod-rs-forge/src/lib.rs | 13 ++-- crates/solid-pod-rs-git/README.md | 7 ++- crates/solid-pod-rs-idp/README.md | 5 +- crates/solid-pod-rs-nostr/README.md | 10 ++-- crates/solid-pod-rs-server/README.md | 59 ++++++++++++------- crates/solid-pod-rs-server/src/cli/mod.rs | 3 +- crates/solid-pod-rs-server/src/lib.rs | 2 +- crates/solid-pod-rs-server/src/mempool.rs | 8 +-- crates/solid-pod-rs/CONTRIBUTING.md | 7 ++- crates/solid-pod-rs/README.md | 29 +++++---- crates/solid-pod-rs/SECURITY.md | 2 + .../docs/BASELINE-solid-pod-rs.md | 45 +++++++------- crates/solid-pod-rs/docs/benchmarks.md | 14 ++++- crates/solid-pod-rs/docs/examples-index.md | 6 +- .../docs/explanation/comparison-vs-jss.md | 2 +- .../docs/explanation/ecosystem-integration.md | 4 +- .../docs/how-to/migrate-from-jss.md | 2 +- crates/solid-pod-rs/docs/reference/api.md | 2 +- .../solid-pod-rs/docs/reference/env-vars.md | 10 ++-- .../docs/reference/http-endpoints.md | 44 ++++++++------ .../solid-oidc-compatibility-matrix.md | 2 +- .../tutorials/03-adding-access-control.md | 6 +- crates/solid-pod-rs/src/lib.rs | 14 +++-- 27 files changed, 227 insertions(+), 132 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46a0061..0571988 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Documentation + +- **Docs re-aligned to the code at `045c24e` (2026-09-22).** Every checkable + claim in the READMEs, crate-level rustdoc, `docs/` reference and the + architecture baseline was verified against the tree: stale `file:line` + citations in `BASELINE-solid-pod-rs.md` re-recorded (`verified_commit` + now `045c24e`, version `0.1.1`); the server README no longer claims the + `git` feature is on by default or that `dpop-replay-cache` / + `nip98-schnorr` are opt-in; the admin-provision `podUrl` and git-panel + routes are documented at their real `/pods/{pubkey}/…` paths; the NIP-98 + freshness window is stated as ±60 s; sibling-crate LOC and test counts + refreshed; `solid-pod-rs-forge` marks forks/PRs, NIP-34 announce and + anchoring as planned rather than shipped; broken in-repo link to the + archived ADR-059 fixed; phantom `standalone` example and the JSS-only + `JSS_WORKERS` / `JSS_LOG_LEVEL` / `JSS_DISABLE_DOTFILES` variables removed + from the docs. + ### Security - **`rustls` 0.23.45.** RUSTSEC-2026-0285 (TLS 1.3 handshake messages accepted diff --git a/README.md b/README.md index 71b56b0..8f86294 100644 --- a/README.md +++ b/README.md @@ -185,10 +185,11 @@ solid-pod-rs = { version = "0.5.0-alpha.9", features = ["fs-backend", "oidc"] } ```rust,no_run use solid_pod_rs::storage::fs::FsBackend; -use std::path::PathBuf; -let storage = FsBackend::new(PathBuf::from("./pod-root")); +# async fn demo() -> Result<(), solid_pod_rs::PodError> { +let storage = FsBackend::new("./pod-root").await?; // Wire your HTTP framework of choice; see examples/embed_in_actix.rs. +# Ok(()) } ``` All configuration keys accept either a JSON/TOML file entry or a `JSS_*` environment variable — names identical to JSS, so existing deployment scripts work unchanged. See [`env-vars.md`](crates/solid-pod-rs/docs/reference/env-vars.md) for the full list. @@ -203,7 +204,7 @@ Each subsystem below is a one-paragraph summary; the linked docs carry the row-l **WAC — Web Access Control.** Deny by default: no ACL means no access. `.acl` sidecars specify who (by WebID, agent class, or group) may Read / Write / Append / Control, inheriting down the container tree via `acl:default`. Parser bounds cap Turtle at 1 MiB and JSON-LD depth at 32 levels (CWE-400). See [`wac-modes.md`](crates/solid-pod-rs/docs/reference/wac-modes.md) and [`debug-acl-denials.md`](crates/solid-pod-rs/docs/how-to/debug-acl-denials.md). -**Provenance & trust ledger.** A pod records who changed what, when, and on whose authority. **git-marks** (cheap, always-on) turn every write into a git commit persisted as a PROV-O sidecar. **block-trails** (opt-in) anchor a hash-chained state trail to Bitcoin taproot, batched under an epoch Merkle root so one transaction notarises an epoch of writes. Default network is `testnet4`; mainnet is an explicit operator choice. See [ADR-059](crates/solid-pod-rs/docs/adr/ADR-059-provenance-primitives-block-trails-git-marks.md) and the [provenance upgrade master plan](crates/solid-pod-rs/docs/design/provenance-upgrade-master-plan.md). +**Provenance & trust ledger.** A pod records who changed what, when, and on whose authority. **git-marks** (cheap; always on once the server is built with `--features git` — the binary ships `default = []`) turn every write into a git commit persisted as a PROV-O sidecar. **block-trails** (opt-in) anchor a hash-chained state trail to Bitcoin taproot, batched under an epoch Merkle root so one transaction notarises an epoch of writes. Default network is `testnet4`; mainnet is an explicit operator choice. See [legacy ADR-059](crates/solid-pod-rs/docs/archive/adr/ADR-059-provenance-primitives-block-trails-git-marks.md) (archived; the [architecture baseline](crates/solid-pod-rs/docs/BASELINE-solid-pod-rs.md) is authoritative) and the [provenance upgrade master plan](crates/solid-pod-rs/docs/design/provenance-upgrade-master-plan.md). **Payments & web ledger.** solid-pod-rs inherits JSS's HTTP-402 economy: a `PaymentCondition` in a WAC ACL gates a resource behind a price, the client pays, the read succeeds. Settlement is sovereign and Bitcoin-native (sats, no EVM), sharing one verified taproot core with block-trail anchors — deposits, withdrawals, a routed order book and constant-product AMM, all through `PaymentStore` as the sole ledger I/O path, with replay protection on every settlement proof. @@ -234,7 +235,7 @@ Honest, pre-1.0, dated. Version pins here match `Cargo.toml` - **8 crates, not 7.** `solid-pod-rs-forge` is real and test-green: Phases 0–3 (XSS-safe content-type spine, Tier-1 git hosting + browse porcelain, Tier-2 issues over an atomic spine store, and the Tier-2.5 HMAC push-token path for podless did:nostr identities) shipped per CHANGELOG's `0.5.0-alpha.5` entry (2026-07-15). Phases 4–7 — forks/PRs, Bitcoin anchors (`forge-anchoring`), and NIP-34 discovery (`forge-announce`) — are feature-scaffolded and compiling, not implemented. - **97.6% strict JSS parity.** Ground truth is [`PARITY-CHECKLIST.md`](crates/solid-pod-rs/PARITY-CHECKLIST.md): 230 rows tracked through JSS `0.0.220` (`f9f7a4d`) — no row remains classified as missing. The remaining strict-gap rows are partial implementations; architectural exclusions stay outside the denominator. The Rust port adds a single static binary, no Node.js dependency, deterministic RDF serialisation, and compile-time feature gating on top of that parity. -- **Provenance is git-mark-first.** git-marks are always-on; Bitcoin block-trail anchors are opt-in behind the `mrc20` feature and default to `testnet4`. The Bitcoin write side (P2TR construction, BIP-341 TapSighash, BIP-340 Schnorr) is validated against the official test vectors. +- **Provenance is git-mark-first.** git-marks are always on in a `--features git` build (and a no-op without it); Bitcoin block-trail anchors are opt-in behind the `mrc20` feature and default to `testnet4`. The Bitcoin write side (P2TR construction, BIP-341 TapSighash, BIP-340 Schnorr) is validated against the official test vectors. - **Supply-chain gates are green; the code audit is not.** As of 2026-09-21, formatting, strict Clippy, compilation, the complete all-feature workspace test command, `cargo audit --deny warnings` and `cargo deny --all-features diff --git a/crates/solid-pod-rs-activitypub/README.md b/crates/solid-pod-rs-activitypub/README.md index b7a5e1b..02d11c4 100644 --- a/crates/solid-pod-rs-activitypub/README.md +++ b/crates/solid-pod-rs-activitypub/README.md @@ -3,7 +3,8 @@ **Status: 0.5.0-alpha.9 — functional ActivityPub federation crate.** Rust port of the JSS ActivityPub surface (`JavaScriptSolidServer/src/ap/*`). -4,453 LOC across 9 modules; 53 tests. Integrators may take a dependency +3,615 LOC across 9 modules; 94 unit tests plus the `federation_flows` +integration suite (counts at 2026-09-22). Integrators may take a dependency today. ## What ships @@ -17,8 +18,8 @@ today. | `http_sig` | `HttpSignatureVerifier` + `HttpActorKeyResolver` implementing draft-cavage v12 over RSA-SHA256. | | `store` | SQLite-backed follower/following/outbox/delivery-queue persistence. Actor cache with 24-hour freshness. | | `error` | Typed error hierarchy (`InboxError`, `OutboxError`, `DeliveryError`, `StoreError`). | -| `webfinger` | AP-specific WebFinger JRD rendering. | -| `nodeinfo` | NodeInfo 2.1 document emission. | +| `discovery` | NodeInfo 2.1 document emission + WebFinger JRD re-export from the core crate. | +| `ssrf` | Outbound-delivery SSRF guard (private / metadata address classes). | ## Federation flow diff --git a/crates/solid-pod-rs-forge/README.md b/crates/solid-pod-rs-forge/README.md index 8899e9f..23a4faa 100644 --- a/crates/solid-pod-rs-forge/README.md +++ b/crates/solid-pod-rs-forge/README.md @@ -14,12 +14,20 @@ The forge is ~90% composition of existing solid-pod-rs primitives and - **write-as-commit provenance + Bitcoin anchoring** — `solid-pod-rs::provenance` / `mrc20` / `bitcoin_tx` - **NIP-98 auth** — `solid-pod-rs::auth::nip98` - **`did:nostr` identity** — `solid-pod-rs::did_nostr_types` -- **WAC gating** — enforced by the embedding server before `handle` - -New in this crate: the spine index (issue/PR/label pointers), the -two-phase pod-write coordinator, hosted storage for podless `did:nostr` -agents, the forge push token, a NIP-34 announcement builder/signer, the -marks manager, and fork/PR/browse porcelain that extends the git API. +- **ownership gating** — the embedding server gates the forge scope before + `handle`; the bundled `solid-pod-rs-server` applies the forge's own + fail-closed namespace-ownership guard (`ownership`) rather than the pod's + WAC ACL evaluator + +New in this crate: the spine index (`spine`, issues today), the pod-hosted +body reader/loopback fetch (`bodies`), hosted storage for podless +`did:nostr` agents (`hosted`), the forge push token (`token`), the +namespace-ownership guard (`ownership`), the HTML rendering layer (`html`) +and the repo browse porcelain (`repo`) that extends the git API. Forks and +pull requests, the NIP-34 announcement builder/signer and the marks manager +are **planned** (Phases 4–7): their `pulls/` and `marks/` directories are +created and the `anchoring` / `announce` features compile, but no code yet +uses them. ## Architecture rule @@ -42,16 +50,20 @@ adds zero dependencies to the core crate. ## Features +Both features are scaffolded (they add the dependencies) and are not yet +wired to any code path in this crate: + | Feature | Effect | |---|---| -| `anchoring` | Tier 3.5 Blocktrails anchoring (pulls `solid-pod-rs/mrc20` + `k256`) | -| `announce` | NIP-34 discovery publication over WS (pulls `solid-pod-rs-nostr` + `k256`) | +| `anchoring` | Tier 3.5 Blocktrails anchoring — planned (pulls `solid-pod-rs/mrc20` + `k256`) | +| `announce` | NIP-34 discovery publication over WS — planned (pulls `solid-pod-rs-nostr` + `k256`) | ## Entry point Framework-agnostic: `ForgeService::handle(ForgeRequest, ForgeAgent) -> Result`. The embedding server translates its -native types at the edge and WAC-gates the forge scope before dispatch. +native types at the edge and gates the forge scope before dispatch (the +bundled server uses the namespace-ownership guard, not WAC). ## Licence diff --git a/crates/solid-pod-rs-forge/src/lib.rs b/crates/solid-pod-rs-forge/src/lib.rs index 7427cbd..c6e9d8d 100644 --- a/crates/solid-pod-rs-forge/src/lib.rs +++ b/crates/solid-pod-rs-forge/src/lib.rs @@ -7,8 +7,11 @@ //! ([`solid_pod_rs_git`]), write-as-commit provenance and Bitcoin //! anchoring ([`solid_pod_rs::provenance`], [`solid_pod_rs::mrc20`]), //! NIP-98 auth ([`solid_pod_rs::auth::nip98`]), `did:nostr` identity -//! ([`solid_pod_rs::did_nostr_types`]), and WAC gating (enforced by the -//! embedding server before [`ForgeService::handle`] is reached). +//! ([`solid_pod_rs::did_nostr_types`]), and access gating enforced by the +//! embedding server before [`ForgeService::handle`] is reached (the bundled +//! `solid-pod-rs-server` applies this crate's fail-closed +//! namespace-ownership guard, [`ownership`], rather than the pod's WAC +//! evaluator). //! //! ## IP posture //! @@ -37,8 +40,10 @@ //! The crate is framework-agnostic: [`ForgeService::handle`] consumes a //! [`ForgeRequest`] and produces a [`ForgeResponse`]; the embedding //! server (actix/axum/hyper) translates its native types at the edge and -//! WAC-gates the forge scope *before* dispatch — exactly as the server's -//! `handle_git` gates before invoking [`solid_pod_rs_git::GitHttpService`]. +//! gates the forge scope *before* dispatch — the same shape as the server's +//! `handle_git`, which WAC-gates before invoking +//! [`solid_pod_rs_git::GitHttpService`]; the forge scope uses the +//! [`ownership`] guard instead. #![forbid(unsafe_code)] #![warn(missing_docs)] diff --git a/crates/solid-pod-rs-git/README.md b/crates/solid-pod-rs-git/README.md index b59abac..36e5f70 100644 --- a/crates/solid-pod-rs-git/README.md +++ b/crates/solid-pod-rs-git/README.md @@ -3,7 +3,7 @@ **Status: 0.5.0-alpha.9 — functional Git HTTP backend.** Integrators may depend on this crate today; see the workspace audit for current quality gates. -## Target scope +## Scope - Git HTTP smart-protocol backend (`info/refs`, `upload-pack`, `receive-pack`) mounted as a pod sub-scope. @@ -15,11 +15,12 @@ depend on this crate today; see the workspace audit for current quality gates. - WAC integration so repo `.git/` trees honour the enclosing pod's ACL. -Target LOC: ~450 + 12 integration tests at first landing. +Shipped: 3,240 LOC (`src/`, 2026-09-22), 60 unit tests plus the +`git_service_sprint10` integration suite. ## Parity rows -Rows that will close when this crate lands (see +Rows closed by this crate (see [`../solid-pod-rs/PARITY-CHECKLIST.md`](../solid-pod-rs/PARITY-CHECKLIST.md)): - **69** — `Basic nostr:` HTTP Basic bridge to NIP-98. diff --git a/crates/solid-pod-rs-idp/README.md b/crates/solid-pod-rs-idp/README.md index 10cd7b9..94f79b9 100644 --- a/crates/solid-pod-rs-idp/README.md +++ b/crates/solid-pod-rs-idp/README.md @@ -10,7 +10,7 @@ or plug `Provider` into any router you like). ## What landed in Sprint 10 Parity rows flipped from `missing` → `present` (tracked in -`../../docs/PARITY-CHECKLIST.md`): +`../solid-pod-rs/PARITY-CHECKLIST.md`): | Row | Endpoint / feature | JSS ref | |----:|--------------------------------------|--------------------------------| @@ -182,7 +182,8 @@ should be zero): ## Tests -91 unit tests cover: +The unit suite (84 tests in `src/` at 2026-09-22, plus the +`key_provisioning_smoke` integration test) covers: - Discovery document shape (`webid` in scopes, `none` auth method, DPoP algs, PKCE S256, issuer trailing-slash normalisation). diff --git a/crates/solid-pod-rs-nostr/README.md b/crates/solid-pod-rs-nostr/README.md index a32f503..1c52325 100644 --- a/crates/solid-pod-rs-nostr/README.md +++ b/crates/solid-pod-rs-nostr/README.md @@ -1,14 +1,16 @@ # solid-pod-rs-nostr **Status: 0.5.0-alpha.9 — functional did:nostr + embedded relay.** -2,177 LOC, 45 tests. Integrators may depend on this crate today. +2,666 LOC (`src/`), 63 unit tests plus the `did_nostr_resolver` and +`relay_nip11` integration suites (counts at 2026-09-22). Integrators may +depend on this crate today. Note: the did:nostr bidirectional resolver also ships inside the core library at `interop::did_nostr` (feature `did-nostr`). This sibling crate adds the **embedded relay** and the Tier 3 DID surface on top of that core resolver. -## Target scope +## Scope - did:nostr DID Document publication at `/.well-known/did/nostr/:pubkey.json` (Tier 1 / Tier 3) — Tier 1 @@ -20,11 +22,9 @@ of that core resolver. (`auth::nip98::verify_schnorr_signature` under `nip98-schnorr`); this crate does not re-implement it. -Target LOC: 800–1,200 at first landing. - ## Parity rows -Rows that will close when this crate lands (see +Rows closed by this crate (see [`../solid-pod-rs/PARITY-CHECKLIST.md`](../solid-pod-rs/PARITY-CHECKLIST.md)): - **89** — Embedded Nostr relay (NIP-01). diff --git a/crates/solid-pod-rs-server/README.md b/crates/solid-pod-rs-server/README.md index 6c5d92f..6539dd7 100644 --- a/crates/solid-pod-rs-server/README.md +++ b/crates/solid-pod-rs-server/README.md @@ -5,14 +5,14 @@ JSS replacement that runs as a single static-ish Rust binary. ## Install -Once published to crates.io (target: v0.4.0): +From crates.io (published; every workspace crate is at `0.5.0-alpha.9`): ```bash cargo install solid-pod-rs-server solid-pod-rs-server --config config.json ``` -Until then, build from source: +Or build from source: ```bash cargo build --release -p solid-pod-rs-server @@ -146,7 +146,8 @@ Verification is hardened three ways (closeout 0.5.0-alpha.4): - **Single-use replay guard** — every accepted NIP-98 token id is recorded in a shared process-local `Nip98ReplayCache`; a re-presented token is treated as unauthenticated (`extract_pubkey` returns `None`) so the WAC gate denies with - `401`, closing the ±120s replay window the stateless verifier leaves open. + `401`, closing the ±60 s (120 s total) replay window the stateless verifier + leaves open. Tunable via `SOLID_POD_NIP98_REPLAY_TTL_SECS` / `SOLID_POD_NIP98_REPLAY_MAX_SIZE`; per-process (multi-replica deployments share no state). - **Fail-open compile guard** — the binary references a `const fn` that exists @@ -169,14 +170,16 @@ The optional `export-jsonld` build adds `GET /api/exports/all`, an ### Provision endpoint `POST /_admin/provision/{pubkey}` creates a new pod for a Nostr pubkey in one -atomic step: pod directory, owner-only `.acl`, and a `git init` that sets +atomic step: pod directory (under `data_root/pods/{pubkey}/`), owner-only +`.acl`, and — when built with `--features git` — a `git init` that sets `receive.denyCurrentBranch=updateInstead` so the pod is immediately pushable -over HTTP via `/_git/{pubkey}/`. +over git smart-HTTP at `/pods/{pubkey}/info/refs`. The endpoint itself is +registered unconditionally. ```bash curl -X POST https://pods.example.com/_admin/provision/ \ -H "X-Pod-Admin-Key: $SOLID_ADMIN_KEY" -# → { "podUrl": "https://pods.example.com//", "ok": true } +# → { "podUrl": "https://pods.example.com/pods//", "ok": true } ``` This endpoint is the CF Workers ↔ agentbox handshake: `auth-worker` calls it @@ -192,8 +195,9 @@ openssl rand -hex 32 ### CORS allowlist for the forum git client -The forum's Source Control panel (`components/git_panel.rs`) drives -`/_git/{pubkey}/` over HTTP from a cross-origin browser context. +The forum's Source Control panel (`components/git_panel.rs`) drives the git +control-panel REST API at `/pods/{pubkey}/_git/{status,log,diff,stage,unstage,commit,branches,…}` +over HTTP from a cross-origin browser context. `SOLID_ALLOWED_ORIGINS` / `--allowed-origins` is a comma-separated list of origins that will receive `Access-Control-Allow-Origin` headers. @@ -204,8 +208,10 @@ SOLID_ALLOWED_ORIGINS=https://dreamlab-ai.com,https://pods.dreamlab-ai.com # Development default — empty = wildcard (*) ``` -OPTIONS preflights for `/_git/{pubkey}/**` are handled automatically -(feature `git` required, which is on by default in this binary). +OPTIONS preflights for `/pods/{pubkey}/_git/**` are handled automatically +and are registered unconditionally; the panel routes themselves require +`--features git` (this binary ships `default = []`, so `git` is **not** on by +default). ### Deployment @@ -315,7 +321,8 @@ runtime error telling the operator to rebuild with `--features install`. ## Feature flags -This binary enables the following `solid-pod-rs` features by default: +This binary always enables the following `solid-pod-rs` features through its +dependency declaration (`Cargo.toml`): | Feature | Purpose | |---|---| @@ -323,10 +330,19 @@ This binary enables the following `solid-pod-rs` features by default: | `memory-backend` | In-memory storage (test / dev) | | `config-loader` | F6 layered config loader | | `legacy-notifications` | F3 `solid-0.1` WS notifications adapter | - -Other feature flags (`oidc`, `dpop-replay-cache`, and `nip98-schnorr`) can be -opted into by the operator via a custom build. No stock object-store backend -ships; unknown storage types are rejected during configuration loading. +| `dpop-replay-cache` | DPoP `jti` replay cache (pulls `oidc`) | +| `embedded-docs` | Embedded Diátaxis docs for the MCP docs tools | +| `mrc20` | Block-trail anchor verification + taproot tx build/sign | +| `nip98-schnorr` | BIP-340 Schnorr verification for NIP-98 | +| `nip98-replay` | NIP-98 single-use replay guard | +| `acl-origin` | WAC `acl:origin` enforcement | + +The binary's own feature set is empty by default (`default = []`); `tls`, +`git`, `forge`, `forge-anchoring`, `forge-announce`, `install`, `rate-limit`, +`quota`, `did-nostr`, `security-primitives`, `provision-keys`, +`nip05-endpoint` and `export-jsonld` are opt-in at build time. No stock +object-store backend ships; unknown storage types are rejected during +configuration loading. ## Licence @@ -335,8 +351,11 @@ network service triggers AGPL §13 source-disclosure obligations. ## Sibling crates (all functional) -- [`solid-pod-rs-activitypub`](../solid-pod-rs-activitypub/) — ActivityPub federation (4,453 LOC) -- [`solid-pod-rs-git`](../solid-pod-rs-git/) — Git HTTP backend (1,685 LOC) -- [`solid-pod-rs-idp`](../solid-pod-rs-idp/) — Solid-OIDC identity provider (6,160 LOC) -- [`solid-pod-rs-nostr`](../solid-pod-rs-nostr/) — did:nostr + embedded Nostr relay (2,177 LOC) -- [`solid-pod-rs-didkey`](../solid-pod-rs-didkey/) — did:key (Ed25519/P-256/secp256k1) + JWT (1,167 LOC) +`src/` line counts as of 2026-09-22: + +- [`solid-pod-rs-activitypub`](../solid-pod-rs-activitypub/) — ActivityPub federation (3,615 LOC) +- [`solid-pod-rs-git`](../solid-pod-rs-git/) — Git HTTP backend (3,240 LOC) +- [`solid-pod-rs-forge`](../solid-pod-rs-forge/) — pod-native git forge (5,376 LOC) +- [`solid-pod-rs-idp`](../solid-pod-rs-idp/) — Solid-OIDC identity provider (6,080 LOC) +- [`solid-pod-rs-nostr`](../solid-pod-rs-nostr/) — did:nostr + embedded Nostr relay (2,666 LOC) +- [`solid-pod-rs-didkey`](../solid-pod-rs-didkey/) — did:key (Ed25519/P-256/secp256k1) + JWT (864 LOC) diff --git a/crates/solid-pod-rs-server/src/cli/mod.rs b/crates/solid-pod-rs-server/src/cli/mod.rs index 9f79eb5..4400504 100644 --- a/crates/solid-pod-rs-server/src/cli/mod.rs +++ b/crates/solid-pod-rs-server/src/cli/mod.rs @@ -1,6 +1,7 @@ //! Operator CLI subcommands — Sprint 11 rows 138, 163, 168. //! -//! Three thin wrappers over library primitives: +//! Three thin wrappers over library primitives, plus the `install` +//! subcommand (JSS `src/cli/install.js` port, see [`run_install`](crate::cli::run_install)): //! //! | Subcommand | JSS ref | Primitive | //! |--------------------------------------|------------------------|------------------------------------| diff --git a/crates/solid-pod-rs-server/src/lib.rs b/crates/solid-pod-rs-server/src/lib.rs index 3932d17..c2e1bac 100644 --- a/crates/solid-pod-rs-server/src/lib.rs +++ b/crates/solid-pod-rs-server/src/lib.rs @@ -89,7 +89,7 @@ //! //! - **NIP-98 single-use replay guard** — every request runs through a //! shared process-local `Nip98ReplayCache`, so a captured token cannot be -//! replayed within the ±120s NIP-98 tolerance window (`extract_pubkey` +//! replayed within the ±60 s NIP-98 tolerance window (`extract_pubkey` //! returns `None` on a replayed id → the WAC gate denies with 401). TTL / //! size via `SOLID_POD_NIP98_REPLAY_TTL_SECS` / `SOLID_POD_NIP98_REPLAY_MAX_SIZE`. //! The cache is per-process; multi-replica deployments share no state. diff --git a/crates/solid-pod-rs-server/src/mempool.rs b/crates/solid-pod-rs-server/src/mempool.rs index 3d514a4..0fe883c 100644 --- a/crates/solid-pod-rs-server/src/mempool.rs +++ b/crates/solid-pod-rs-server/src/mempool.rs @@ -32,12 +32,12 @@ //! enabled crate-wide, so responses are read as text and parsed with //! `serde_json` (matching the proxy handler's manual-parse style). //! -//! The endpoint choice is not silent: [`select_mempool_endpoint`] resolves the -//! base URL, [`infer_network`] classifies the Bitcoin network it serves, and -//! [`log_mempool_selection`] records both (plus whether the URL was +//! The endpoint choice is not silent: [`select_mempool_endpoint`](crate::mempool::select_mempool_endpoint) resolves the +//! base URL, [`infer_network`](crate::mempool::infer_network) classifies the Bitcoin network it serves, and +//! [`log_mempool_selection`](crate::mempool::log_mempool_selection) records both (plus whether the URL was //! operator-supplied or defaulted) in the startup log — with a warning when the //! pod would otherwise be anchoring against an unchosen or unclassifiable -//! chain. [`MempoolSelection::to_manifest_json`] renders the same facts for a +//! chain. [`MempoolSelection::to_manifest_json`](crate::mempool::MempoolSelection::to_manifest_json) renders the same facts for a //! manifest (ADR-2007). use async_trait::async_trait; diff --git a/crates/solid-pod-rs/CONTRIBUTING.md b/crates/solid-pod-rs/CONTRIBUTING.md index 392ff87..9dd286f 100644 --- a/crates/solid-pod-rs/CONTRIBUTING.md +++ b/crates/solid-pod-rs/CONTRIBUTING.md @@ -16,9 +16,10 @@ and themselves. Concurrency-safety is required. ## Parity checklist -Any feature port from the JavaScriptSolidServer reference (under -`references/javascript-solid-server/`) must update -`PARITY-CHECKLIST.md` to reflect its new status. +Any feature port from the JavaScriptSolidServer reference (upstream +`gh-pages`; the pinned comparator commit and package version are recorded +at the top of `PARITY-CHECKLIST.md` — there is no vendored checkout in this +repository) must update `PARITY-CHECKLIST.md` to reflect its new status. ## Testing diff --git a/crates/solid-pod-rs/README.md b/crates/solid-pod-rs/README.md index 185bb7a..c502e86 100644 --- a/crates/solid-pod-rs/README.md +++ b/crates/solid-pod-rs/README.md @@ -22,10 +22,11 @@ solid-pod-rs = "0.5.0-alpha.9" ```rust,no_run use solid_pod_rs::storage::fs::FsBackend; -use std::path::PathBuf; -let storage = FsBackend::new(PathBuf::from("./pod-root")); +# async fn demo() -> Result<(), solid_pod_rs::PodError> { +let storage = FsBackend::new("./pod-root").await?; // Compose with your framework; see examples/embed_in_actix.rs. +# Ok(()) } ``` ## What's new in 0.5.0-alpha.0 (2026-06-13, provenance primitives — ADR-059) @@ -44,7 +45,7 @@ the [master plan](docs/design/provenance-upgrade-master-plan.md). `provenance`; native `GitMarker` in `solid-pod-rs-git`, no-op on wasm. **Not a default build:** `solid-pod-rs-server` ships `default = []` (`Cargo.toml:123`), so without `--features git` `git_mark_write` compiles to a - no-op shim (`lib.rs:3490`) and records zero marks. See + no-op shim (`lib.rs:3668`) and records zero marks. See [BASELINE divergence #3](docs/BASELINE-solid-pod-rs.md). - **block-trails** (high-value, opt-in, feature `mrc20`) — a Bitcoin-taproot -anchored, hash-chained MRC20 state trail. Verify **and** write side @@ -117,7 +118,7 @@ a small WAC Turtle serializer quirk is tracked in | `oidc` | off | Solid-OIDC 0.1 + DPoP. | | `dpop-replay-cache` | off | DPoP `jti` replay cache (pulls `oidc`). | | `nip98-schnorr` | off | BIP-340 Schnorr signature verification for NIP-98 via `verify_raw()` (raw 32-byte message, no tagged pre-hash). Verification is unconditional and fail-closed — without this feature the verifier returns `PodError::Unsupported` rather than accepting a forged pubkey. | -| `nip98-replay` | off | NIP-98 single-use replay guard (`auth::replay::Nip98ReplayCache`) — bounded process-local LRU on the canonical event id; closes the ±120s replay window. | +| `nip98-replay` | off | NIP-98 single-use replay guard (`auth::replay::Nip98ReplayCache`) — bounded process-local LRU on the canonical event id; closes the ±60 s (120 s total) replay window. | | `acl-origin` | off | WAC `acl:origin` enforcement (request `Origin` threaded into the evaluator by `solid-pod-rs-server`). | | `security-primitives` | off | SSRF guard + dotfile allowlist. | | `legacy-notifications` | off | `solid-0.1` WebSocket adapter (SolidOS). | @@ -156,13 +157,15 @@ Seven sibling crates live alongside the core library in the eight-crate workspace. All compile and pass the workspace test suite; feature-specific deployment readiness still follows each crate's own status notes. -| Crate | LOC | Parity rows | JSS source refs | +| Crate | LOC (`src/`, 2026-09-22) | Parity rows | JSS source refs | |----------------------------|-------|------------------------|-------------------------------------| -| `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 | +| `solid-pod-rs-server` | 11,436 | server/operator rows | `bin/jss.js`, `src/server.js` | +| `solid-pod-rs-activitypub` | 3,615 | 102–108, 131, 169–172 | `src/ap/{index,routes/inbox,routes/outbox,store}.js` | +| `solid-pod-rs-git` | 3,240 | 69, 100 | `src/handlers/git.js` | +| `solid-pod-rs-forge` | 5,376 | forge extension rows | JSS `forge` plugin (behaviour only) | +| `solid-pod-rs-idp` | 6,080 | 74–81, 130 | `src/idp/{index,provider,passkey,interactions,credentials}.js` | +| `solid-pod-rs-nostr` | 2,666 | 89, 90, 101, 132 | `src/{did/resolver,nostr/relay,auth/did-nostr}.js` | +| `solid-pod-rs-didkey` | 864 | 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 @@ -206,8 +209,8 @@ flowchart TD - **NIP-98 single-use replay guard** (`nip98-replay`) — the verifier returns a signature-bound canonical event id, and `auth::replay::Nip98ReplayCache` (a bounded process-local LRU) rejects a - re-presented token, closing the ±120s window the stateless verifier - leaves open. Wired into every request by `solid-pod-rs-server`. + re-presented token, closing the ±60 s (120 s total) window the stateless + verifier leaves open. Wired into every request by `solid-pod-rs-server`. - **WAC `acl:origin` enforcement** (`acl-origin`) — the request `Origin` header is threaded into the evaluator by `solid-pod-rs-server`, so an ACL bearing `acl:origin` triples gates cross-origin reads and writes. @@ -225,7 +228,7 @@ flowchart TD 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`, +- **Dotfile allowlist** — only `.acl`, `.meta`, `.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 diff --git a/crates/solid-pod-rs/SECURITY.md b/crates/solid-pod-rs/SECURITY.md index efde1fa..4ed0b76 100644 --- a/crates/solid-pod-rs/SECURITY.md +++ b/crates/solid-pod-rs/SECURITY.md @@ -114,6 +114,8 @@ return 404 regardless of storage-layer presence: - `.meta` — RDF metadata sidecars. - `.well-known` — the standard discovery tree. - `.quota.json` — per-pod quota sidecar (when `quota` is enabled). +- `.acl.meta` — meta sidecar of an ACL document. +- `.account` — IdP account endpoints (login, registration, password reset). The allowlist is enforced at the storage boundary (`security::dotfile`) and again by the server's `DotfileGuard` middleware. diff --git a/crates/solid-pod-rs/docs/BASELINE-solid-pod-rs.md b/crates/solid-pod-rs/docs/BASELINE-solid-pod-rs.md index ac57ba8..802323f 100644 --- a/crates/solid-pod-rs/docs/BASELINE-solid-pod-rs.md +++ b/crates/solid-pod-rs/docs/BASELINE-solid-pod-rs.md @@ -1,9 +1,9 @@ --- title: solid-pod-rs Architecture Baseline doc_id: SPR-BASELINE -version: 0.1.0 +version: 0.1.1 status: active-normative -verified_commit: 0ccad60 +verified_commit: 045c24e sources: - crates/solid-pod-rs/src/lib.rs - crates/solid-pod-rs/src/oidc/mod.rs @@ -19,7 +19,7 @@ sources: - crates/solid-pod-rs-git/src/auth.rs - crates/solid-pod-rs-server/Cargo.toml - Cargo.toml -date: 2026-08-31 +date: 2026-09-22 --- # solid-pod-rs Architecture Baseline @@ -41,8 +41,8 @@ never as authority. The workspace ships eight crates (`Cargo.toml`), library-first: `solid-pod-rs` (the core library), the embedded HTTP server `solid-pod-rs-server`, and the siblings `-git`, `-idp`, `-activitypub`, `-nostr`, `-didkey`, `-forge`. -Workspace version is `0.5.0-alpha.8` (`Cargo.toml:15`) — four alphas past the -`0.5.0-alpha.4` at which legacy ADR-060 was written, and eight past the +Workspace version is `0.5.0-alpha.9` (`Cargo.toml:15`) — five alphas past the +`0.5.0-alpha.4` at which legacy ADR-060 was written, and nine past the `0.5.0-alpha.0` legacy ADR-059 set as its acceptance tag. ### NIP-98 authentication (single-sourced) @@ -56,11 +56,12 @@ policy (`solid-pod-rs-git/src/auth.rs:144,157`), and `solid-pod-rs-idp` reuses `verify_schnorr_signature` (`solid-pod-rs-idp/src/schnorr.rs:11`). Replay protection is a single-use nonce cache, `Nip98ReplayCache` -(`auth/replay.rs:70`), now formalised behind a `trait ReplayStore` -(`auth/replay_store.rs:68`) with `Nip98ReplayCache` as the reference implementor -(`auth/replay.rs:178`). The cache is **process-local**: replay protection does +(`auth/replay.rs:117`), now formalised behind a `trait ReplayStore` +(`auth/replay_store.rs:90`) with `Nip98ReplayCache` as the reference implementor +(`auth/replay.rs:235`). The cache is **process-local**: replay protection does not span replicas, and the out-of-repo forum/CF edge tier keeps its own -datastore — the documented edge-local exception (`auth/replay.rs:20,174`). +datastore — the documented edge-local exception (`auth/replay_store.rs:19-32`, +`auth/replay.rs:231`). ### OIDC / DPoP surface @@ -79,7 +80,7 @@ enumerated is unshipped (see divergences). (`security/dotfile.rs:24`) — `.account` is present. - Pod-label sanitisation scrubs `..` with an **iterative double-pass** `scrub_dotdot` loop that runs until the string stops changing, defeating the - `....//` bypass (`multitenant.rs:181-198`), guarded further by an explicit + `....//` bypass (`multitenant.rs:184-193`), guarded further by an explicit `!safe.contains("..")` check (`multitenant.rs:118`). - WAC ACL parsing enforces a byte cap `MAX_ACL_BYTES` (`JSS_MAX_ACL_BYTES`, default 1 MiB) at the parse boundary, rejecting oversized bodies before serde @@ -105,14 +106,14 @@ enumerated is unshipped (see divergences). The provenance composition shipped (legacy ADR-059 Phase 5, commit `182ed31`): `ProvenanceLog::record` in `crates/solid-pod-rs/src/provenance.rs` is the single canonical write path, invoked from the server's `git_mark_write` -(`solid-pod-rs-server/src/lib.rs:3317`) on every LDP `PUT`/`POST`/`PATCH` -(`lib.rs:1374,1486,1582`). The "SINGLE canonical path" comment sits at -`lib.rs:3400`. It composes a cheap git-mark (always) with an opt-in Bitcoin +(`solid-pod-rs-server/src/lib.rs:3462`) on every LDP `PUT`/`POST`/`PATCH` +(`lib.rs:1493,1606,1703-1771`). The "SINGLE canonical path" comment sits at +`lib.rs:3550`. It composes a cheap git-mark (always) with an opt-in Bitcoin block-trail anchor (per resolved `AnchorPolicy`). **But it is off in a default build.** The server's default feature set is empty (`solid-pod-rs-server/Cargo.toml:123`), so `git_mark_write` compiles to the -no-op shim `#[cfg(not(feature = "git"))]` (`lib.rs:3490`) and a default build +no-op shim `#[cfg(not(feature = "git"))]` (`lib.rs:3668`) and a default build records **zero** provenance marks. Marks require `--features git`. **Outcomes are typed and reported** (ADR-2004, 2026-09-05). @@ -149,8 +150,8 @@ to store and re-serve it. ### Non-destructive PATCH -`handle_patch` (`solid-pod-rs-server/src/lib.rs:1498`) seeds the working graph -from the stored resource via `seed_graph_from_patch_target` (`lib.rs:1769`) +`handle_patch` (`solid-pod-rs-server/src/lib.rs:1619`) seeds the working graph +from the stored resource via `seed_graph_from_patch_target` (`lib.rs:1896`) before applying the patch, failing closed on an unparseable existing body. This is the REC-1 fix (commit `791977a`). @@ -168,11 +169,11 @@ is the REC-1 fix (commit `791977a`). 2. **Legacy ADR-058's security "gaps" are all closed.** ADR-058 lists `.account` allowlist (P1), iterative `..` sanitisation (P0) and size-capped ACL parse (`safeJsonParse`, P0) as gaps; all three shipped - (`security/dotfile.rs:24`, `multitenant.rs:181-198`, `wac/parser.rs:23-39`). + (`security/dotfile.rs:24`, `multitenant.rs:184-193`, `wac/parser.rs:23-39`). ADR-058 was never updated post-Sprint 12. 3. **Provenance is off by default (legacy ADR-059 D1).** The "always-on git-mark" wording is contradicted by the empty default feature set - (`solid-pod-rs-server/Cargo.toml:123`, no-op shim `lib.rs:3490`). Every + (`solid-pod-rs-server/Cargo.toml:123`, no-op shim `lib.rs:3668`). Every provenance claim must carry the `--features git` caveat. 4. **No pod-wide `_prov` enumeration (REC-11).** The provenance query surface is point-lookup only — `GET /{pod}/_prov/{commit_sha}` plus per-resource @@ -203,10 +204,12 @@ is the REC-1 fix (commit `791977a`). `mempool.space/testnet4`. The LAN URL is deployment config, gated on the acceptance checklist, not a crate default. 7. **Legacy ADR line citations rotted.** `solid-pod-rs-server/src/lib.rs` grew - to ~5,059 lines; ADR-060's cited offsets (`git_mark_write` @2838, + to ~5,243 lines; ADR-060's cited offsets (`git_mark_write` @2838, `handle_patch` @1210, `seed_graph_from_patch_target` @1467, "SINGLE canonical - path" @2917) no longer match the code (actual: 3317, 1498, 1769, 3400). This - baseline supersedes those numbers. + path" @2917) no longer match the code (actual at `verified_commit`: 3462, + 1619, 1896, 3550). This baseline supersedes those numbers; the 0.1.0 baseline's + own offsets (3317, 1498, 1769, 3400, shim @3490) were re-recorded on + 2026-09-22. ## Invariants (must not silently change) diff --git a/crates/solid-pod-rs/docs/benchmarks.md b/crates/solid-pod-rs/docs/benchmarks.md index 0f648e7..b5d8ecb 100644 --- a/crates/solid-pod-rs/docs/benchmarks.md +++ b/crates/solid-pod-rs/docs/benchmarks.md @@ -1,6 +1,6 @@ # solid-pod-rs — benchmarks -Four criterion-based benches live in `crates/solid-pod-rs/benches/`. +Five criterion-based benches live in `crates/solid-pod-rs/benches/`. Each is registered as `harness = false` so criterion drives the executable directly. @@ -12,6 +12,7 @@ cargo bench -p solid-pod-rs --bench storage_backend_bench cargo bench -p solid-pod-rs --bench wac_eval_bench cargo bench -p solid-pod-rs --bench ldp_content_negotiation_bench cargo bench -p solid-pod-rs --bench nip98_verify_bench +cargo bench -p solid-pod-rs --bench dpop_replay_bench # requires --features dpop-replay-cache # Run everything cargo bench -p solid-pod-rs @@ -58,6 +59,17 @@ linear scan in `StaticGroupMembership`. | Valid token, body with SHA-256 check | <30 µs (dominated by SHA-256) | | Tampered body, fail path | <30 µs (fails at hash compare) | +### `dpop_replay_bench` + +Requires `--features dpop-replay-cache`. Exercises the `DpopReplayCache` +hot path (`check_and_record`), which sits on every DPoP-authenticated +request. + +| Scenario | Target | +|----------|--------| +| Fresh `jti`, single-threaded (`dpop_replay_fresh`) | <1 µs at 10k steady-state entries | +| 10 tasks × 1 000 unique `jti`s (`dpop_replay_concurrent`) | mutex-contention cost on the shared cache | + ## Hardware context Targets above are "what modern x86_64 laptops running Linux with an diff --git a/crates/solid-pod-rs/docs/examples-index.md b/crates/solid-pod-rs/docs/examples-index.md index 7868f87..a2893cf 100644 --- a/crates/solid-pod-rs/docs/examples-index.md +++ b/crates/solid-pod-rs/docs/examples-index.md @@ -5,11 +5,15 @@ Every example lives in `crates/solid-pod-rs/examples/` and runs with each source file repeats the invocation, the expected output, and any prerequisites. +The quickest way to see a working pod is the bundled binary +(`cargo run -p solid-pod-rs-server`, see +[tutorial 01](tutorials/01-your-first-pod.md)); there is no standalone +example in this crate. + ## Server-side | Example | Purpose | |---------|---------| -| `standalone` | Minimal actix-web server wiring `FsBackend`, NIP-98 auth, LDP headers, and WAC-Allow. The quickest way to see a working pod. | | `embed_in_actix` | Same pod, but mounted under `/pod/*` inside a larger actix-web app that also serves `/health` and `/api/whoami`. Shows how to share `SharedState` (storage + auth) between the host app and the pod sub-scope. | | `custom_storage` | Implements the `Storage` trait for a `BTreeMap`-backed backend to demonstrate the extension point. Useful if you want to back a pod with Redis, SQLite, S3, IPFS, etc. | | `webhook_receiver` | Minimal Axum server that receives `WebhookChannel2023` POSTs from a pod and logs them. Good template for building downstream consumers. | diff --git a/crates/solid-pod-rs/docs/explanation/comparison-vs-jss.md b/crates/solid-pod-rs/docs/explanation/comparison-vs-jss.md index 0bf8491..0d2d91d 100644 --- a/crates/solid-pod-rs/docs/explanation/comparison-vs-jss.md +++ b/crates/solid-pod-rs/docs/explanation/comparison-vs-jss.md @@ -16,7 +16,7 @@ feature-level status table. | | JSS 0.0.220 (`f9f7a4d`) | solid-pod-rs 0.5.0-alpha.7 | |--------------------------------|-----------------------|---------------------------| | Language | JavaScript (Node 18+) | Rust 2021 / MSRV 1.88 | -| Binary distribution | `npm install -g javascript-solid-server` → `jss` | Build from source; drop-in crate | +| Binary distribution | `npm install -g javascript-solid-server` → `jss` | `cargo install solid-pod-rs-server` (crates.io) or build from source | | Licence | AGPL-3.0-only | AGPL-3.0-only (inherited) | | HTTP framework | Fastify | Agnostic; actix/axum/hyper| | Configuration | `JSS_*` env vars + optional config | Typed library config plus server JSON/TOML/env loader | diff --git a/crates/solid-pod-rs/docs/explanation/ecosystem-integration.md b/crates/solid-pod-rs/docs/explanation/ecosystem-integration.md index 9429b30..510c860 100644 --- a/crates/solid-pod-rs/docs/explanation/ecosystem-integration.md +++ b/crates/solid-pod-rs/docs/explanation/ecosystem-integration.md @@ -102,8 +102,8 @@ Repository: [DreamLab-AI/dreamlab-ai-website](https://github.com/DreamLab-AI/dre - LDP semantics (containers, Link headers, PATCH). - WAC evaluator. - NIP-98 + Solid-OIDC verification. -- In-memory and filesystem backends. S3 is configuration/dependency - scaffolding only; no implementation ships yet. +- In-memory and filesystem backends. No object-store backend ships; the + former S3 dependency scaffold was removed in 0.5.0-alpha.8. - Solid Notifications 0.2 channel managers. - DID:nostr resolution + WebID generation. - did:key resolution (Ed25519, P-256, secp256k1). diff --git a/crates/solid-pod-rs/docs/how-to/migrate-from-jss.md b/crates/solid-pod-rs/docs/how-to/migrate-from-jss.md index 8d3f5ae..e00a82a 100644 --- a/crates/solid-pod-rs/docs/how-to/migrate-from-jss.md +++ b/crates/solid-pod-rs/docs/how-to/migrate-from-jss.md @@ -59,7 +59,7 @@ document**, or every request will 401. See | `JSS_READ_ONLY` | enforce at the HTTP framework layer | — | | `JSS_NOTIFICATIONS` | notification feature selection and server wiring | WebSocket, webhook, and legacy adapters are separate Rust features | | `JSS_NOSTR` / `JSS_ACTIVITYPUB` | sibling crates / compile-time features | Rebuild required; there is no runtime plugin loader | -| `JSS_MASHLIB` | core `mashlib` feature and server route wiring | Compile-time rather than runtime selection | +| `JSS_MASHLIB` | `JSS_MASHLIB` / `--mashlib` on the bundled server (plus `JSS_MASHLIB_CDN`, `JSS_MASHLIB_MODULE`) | Runtime flag, same name; core `mashlib` module is always compiled | | `JSS_BODY_LIMIT` | `server.body_cap` (`JSS_MAX_REQUEST_BODY` alias) | Rust default 50 MiB; JSS `0.0.220` default 20 MB | | `JSS_MCP` | server MCP enable flag | Off by default | diff --git a/crates/solid-pod-rs/docs/reference/api.md b/crates/solid-pod-rs/docs/reference/api.md index ae0ce81..080a653 100644 --- a/crates/solid-pod-rs/docs/reference/api.md +++ b/crates/solid-pod-rs/docs/reference/api.md @@ -5,7 +5,7 @@ trait, and function exported from `lib.rs` is listed. Private items are not documented here. - Crate root: [`src/lib.rs`](../../src/lib.rs) -- Crate version: `0.5.0-alpha.7` +- Crate version: `0.5.0-alpha.9` ## Modules diff --git a/crates/solid-pod-rs/docs/reference/env-vars.md b/crates/solid-pod-rs/docs/reference/env-vars.md index 33c60c3..3d4bc0e 100644 --- a/crates/solid-pod-rs/docs/reference/env-vars.md +++ b/crates/solid-pod-rs/docs/reference/env-vars.md @@ -41,16 +41,18 @@ unchanged. | `JSS_BASE_URL` | URL | `config::ConfigLoader` | Externally visible base URL. | | `JSS_STORAGE_ROOT` | path | `config::ConfigLoader` | Filesystem root for the FS backend. | | `JSS_OIDC_ISSUER` | URL | `config::ConfigLoader` | Identity provider discovery URL. | -| `JSS_WORKERS` | usize, default CPUs | `config::ConfigLoader` | actix-web worker count. | -| `JSS_LOG_LEVEL` | string | `config::ConfigLoader` | `trace` / `debug` / `info` / `warn` / `error`. | | `JSS_LIVE_RELOAD` | bool, default `false` | `solid-pod-rs-server` | Injects the development reload WebSocket script into HTML responses. Do not enable it on a public production service. | -| `JSS_DISABLE_DOTFILES` | bool | `config::ConfigLoader` | If set, no dotfiles served even on allowlist. | | `JSS_MAX_ACL_BYTES` | bytes, default `1048576` (1 MiB) | `wac::parse_turtle_acl_with_limit`, `wac::parse_jsonld_acl_with_limits` | Maximum ACL document size before rejection (CWE-400 DoS protection). Added Sprint 12. | +| `JSS_MAX_ACL_JSON_DEPTH` | integer, default `32` | `wac::parse_jsonld_acl` | Maximum JSON-LD nesting depth accepted for an ACL document. | | `DOTFILE_ALLOWLIST` | comma-separated | `security::dotfile::DotfileAllowlist::from_env` | Override the default dotfile allowlist (`.acl`, `.meta`, `.account`). | None of the `POD_*` vars above are parsed by the library. The `JSS_*` vars are consumed by the config loader when the `config-loader` feature -is enabled. This table is a suggested vocabulary so multi-pod +is enabled; the loader's complete `JSS_*` table lives in +[`src/config/sources.rs`](../../src/config/sources.rs). `JSS_WORKERS`, +`JSS_LOG_LEVEL` and `JSS_DISABLE_DOTFILES` are JSS-only names with no Rust +consumer — use `RUST_LOG` for log filtering and `DOTFILE_ALLOWLIST` for the +dotfile policy. This table is a suggested vocabulary so multi-pod deployments can share config conventions. ## solid-pod-rs-server (alpha.15+) diff --git a/crates/solid-pod-rs/docs/reference/http-endpoints.md b/crates/solid-pod-rs/docs/reference/http-endpoints.md index 8907786..2926dd3 100644 --- a/crates/solid-pod-rs/docs/reference/http-endpoints.md +++ b/crates/solid-pod-rs/docs/reference/http-endpoints.md @@ -58,10 +58,11 @@ status mapping. ### `401` ``` -WWW-Authenticate: Nostr -WWW-Authenticate: DPoP algs="ES256 RS256" +WWW-Authenticate: Nostr realm="Solid", DPoP realm="Solid", Bearer realm="Solid" ``` +(the bundled server emits the three schemes as one comma-joined challenge) + ## Request headers the server honours | Header | Effect | @@ -185,8 +186,9 @@ A first push to a not-yet-initialised git-backed pod auto-runs ## Admin / provisioning endpoints These endpoints are only present in the `solid-pod-rs-server` binary and -require the `git` feature (or the standalone binary build with admin routes -compiled in). They are **not** part of the core library surface. +are registered unconditionally (no feature gate); the `git init` step runs +only when the binary is built with `--features git`. They are **not** part +of the core library surface. ### `POST /_admin/provision/{pubkey}` @@ -195,23 +197,24 @@ Creates a new pod for the given owner public key. | Attribute | Value | |---|---| | Auth | PSK — `X-Pod-Admin-Key: ` header. Requests without a valid key receive `403 Forbidden`. The key must match `SOLID_ADMIN_KEY` / `--admin-key`. | -| Feature gate | Compiled only when `--features git` is passed (or the default server build that includes it). | +| Feature gate | None for the route; the `git init` step requires `--features git` (this binary ships `default = []`). | | Path parameter | `pubkey` — hex-encoded Nostr/secp256k1 public key of the future pod owner. | | Request body | None. | **Response `200 OK`:** ```json -{ "podUrl": "https://pods.example.com//", "ok": true } +{ "podUrl": "https://pods.example.com/pods//", "ok": true } ``` **What it does:** -1. Creates the pod directory under the configured storage root. +1. Creates the pod directory at `pods/{pubkey}/` under the configured storage root. 2. Writes an owner-only `.acl` granting full control to the pubkey. -3. Runs `git init -b main` and sets `receive.denyCurrentBranch=updateInstead` - so the pod directory is a bare-ish working-tree repo that can receive - `git push` over HTTP via `/_git/{pubkey}/`. +3. (`--features git` only) Runs `git init -b main` and sets + `receive.denyCurrentBranch=updateInstead` so the pod directory is a + bare-ish working-tree repo that can receive `git push` over HTTP via the + smart-protocol routes at `/pods/{pubkey}/info/refs` etc. **Error responses:** @@ -228,26 +231,31 @@ with a firewall; the PSK is a defence-in-depth measure, not a public API. ## Git Control Panel endpoints -Present only when built with `--features git`. +The panel REST API (`GET /pods/{pubkey}/_git/{status,log,diff,branches}`, +`POST /pods/{pubkey}/_git/{stage,unstage,commit,…}`) is present only when +built with `--features git`; the preflight handler below is registered +unconditionally. -### `OPTIONS /_git/{pubkey}/{tail}` +### `OPTIONS /pods/{pubkey}/_git/{tail}` -CORS preflight handler for the Git HTTP smart-protocol routes used by the +CORS preflight handler for the git control-panel routes used by the forum's VS Code-style Source Control panel. | Attribute | Value | |---|---| | Auth | None — OPTIONS responses are unauthenticated by design. | -| Feature gate | `git` feature. | -| Path | `/_git/{pubkey}/{tail}` — matches any sub-path under a pubkey's git namespace. | +| Feature gate | None (registered before the `git` feature block). | +| Path | `/pods/{pubkey}/_git/{tail}` — matches any sub-path under a pubkey's git-panel namespace. | | Request body | None. | -**Response `204 No Content`** with the following headers: +**Response `204 No Content`** with the shared CORS header set +(`add_cors_headers`): ``` Access-Control-Allow-Origin: | * -Access-Control-Allow-Methods: GET, POST, OPTIONS -Access-Control-Allow-Headers: Content-Type, Authorization, X-Pod-Admin-Key +Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS +Access-Control-Allow-Headers: Accept, Authorization, Content-Type, DPoP, Git-Protocol, If-Match, If-None-Match, Link, Range, Slug, Origin +Access-Control-Expose-Headers: Accept-Patch, Accept-Post, Accept-Ranges, Allow, Content-Length, Content-Range, Content-Type, ETag, Link, Location, Updates-Via, WAC-Allow, X-Cost, X-Balance, X-Pay-Currency Access-Control-Max-Age: 86400 ``` diff --git a/crates/solid-pod-rs/docs/reference/solid-oidc-compatibility-matrix.md b/crates/solid-pod-rs/docs/reference/solid-oidc-compatibility-matrix.md index 79a55fc..b6e10b7 100644 --- a/crates/solid-pod-rs/docs/reference/solid-oidc-compatibility-matrix.md +++ b/crates/solid-pod-rs/docs/reference/solid-oidc-compatibility-matrix.md @@ -1,6 +1,6 @@ # Solid-OIDC compatibility matrix -**Version: `solid-pod-rs` 0.5.0-alpha.8** (workspace `Cargo.toml`, +**Version: `solid-pod-rs` 0.5.0-alpha.9** (workspace `Cargo.toml`, `[workspace.package] version`). This matrix describes the OIDC surface of *that* crate version. It is the compatibility contract referenced by [ADR-2003](../adr/ADR-2003-solid-oidc-01-defer-lws10.md) — "Hold the diff --git a/crates/solid-pod-rs/docs/tutorials/03-adding-access-control.md b/crates/solid-pod-rs/docs/tutorials/03-adding-access-control.md index 69a1445..232a0bf 100644 --- a/crates/solid-pod-rs/docs/tutorials/03-adding-access-control.md +++ b/crates/solid-pod-rs/docs/tutorials/03-adding-access-control.md @@ -7,8 +7,8 @@ minutes. ## Prerequisites - Tutorial 2 complete. -- A running example server (`cargo run --example standalone -p - solid-pod-rs`). +- The bundled server from tutorial 01 still running on `127.0.0.1:8765` + (`cargo run -p solid-pod-rs-server -- --host 127.0.0.1 --port 8765`). ## Step 1 — Observe the baseline (no ACL) @@ -175,7 +175,7 @@ for the walk-up algorithm. Re-request the container with a trace: ```bash -RUST_LOG=solid_pod_rs=debug cargo run --example standalone -p solid-pod-rs +RUST_LOG=solid_pod_rs=debug cargo run -p solid-pod-rs-server -- --host 127.0.0.1 --port 8765 # in another terminal: curl -sI http://127.0.0.1:8765/notes/ ``` diff --git a/crates/solid-pod-rs/src/lib.rs b/crates/solid-pod-rs/src/lib.rs index 94dc4ed..5788f8b 100644 --- a/crates/solid-pod-rs/src/lib.rs +++ b/crates/solid-pod-rs/src/lib.rs @@ -6,15 +6,17 @@ //! to a specific HTTP framework. Wire it into actix-web, axum, hyper, or //! anything else; the crate never mounts routes itself. On top of the Solid //! core it adds two composable **provenance primitives** ([`provenance`]): -//! cheap, always-on **git-marks** (every pod write captured as a git commit -//! + a PROV-O sidecar) and expensive, opt-in **block-trails** (a -//! Bitcoin-taproot-anchored, hash-chained state trail — [`mrc20`] / -//! `bitcoin_tx`) — and a routed, sovereign HTTP-402 economy: a `did:nostr`-keyed +//! cheap **git-marks** (every pod write captured as a git commit + a PROV-O +//! sidecar; always on once the server is built with `--features git`) and +//! expensive, opt-in **block-trails** (a Bitcoin-taproot-anchored, +//! hash-chained state trail — [`mrc20`] / `bitcoin_tx`) — and a routed, +//! sovereign HTTP-402 economy: a `did:nostr`-keyed //! [Web Ledger](payments), `acl:PaymentCondition` ([`wac`]) access gating, an MRC20 //! deposit path, and a peer order book + constant-product AMM ([`trading`]). //! The HTTP routing for the 402 economy and the `_prov` provenance API lives in //! the sibling [`solid-pod-rs-server`](https://docs.rs/solid-pod-rs-server). See -//! [ADR-059](https://docs.rs/crate/solid-pod-rs/latest/source/docs/adr/ADR-059-provenance-primitives-block-trails-git-marks.md). +//! [legacy ADR-059](https://docs.rs/crate/solid-pod-rs/latest/source/docs/archive/adr/ADR-059-provenance-primitives-block-trails-git-marks.md) +//! (archived; `docs/BASELINE-solid-pod-rs.md` is authoritative). //! //! For a turnkey binary, use the sibling crate //! [`solid-pod-rs-server`](https://docs.rs/solid-pod-rs-server). @@ -33,7 +35,7 @@ //! | `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. Verification is **unconditional and fail-closed**: without this feature the verifier returns [`PodError::Unsupported`] rather than accepting a forged pubkey after structural checks alone. | -//! | `nip98-replay` | off | NIP-98 single-use replay guard (`auth::replay::Nip98ReplayCache`) — bounded process-local LRU keyed on the canonical event id; closes the ±120s replay window the stateless verifier leaves open. | +//! | `nip98-replay` | off | NIP-98 single-use replay guard (`auth::replay::Nip98ReplayCache`) — bounded process-local LRU keyed on the canonical event id; closes the ±60 s (120 s total) replay window the stateless verifier leaves open. | //! | `jss-v04` | off | JSS-parity umbrella (ADR-056); no-op alone — sub-features below switch one bounded context each on. | //! | `acl-origin` | off | WAC `acl:origin` enforcement (pulls `jss-v04`). Wired into the request path in `solid-pod-rs-server` (the request `Origin` is threaded into the evaluator `RequestContext`). Note: `acl:origin` is the only WAC 2.0 condition satisfiable end-to-end today — `client_id`/`issuer` conditions still evaluate deny (no authenticated OIDC client_id/issuer is surfaced into the context yet). | //! | `security-primitives` | off | SSRF guard + dotfile allowlist (pulls `jss-v04`). |