Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions crates/solid-pod-rs-activitypub/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
30 changes: 21 additions & 9 deletions crates/solid-pod-rs-forge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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<ForgeResponse, ForgeError>`. 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

Expand Down
13 changes: 9 additions & 4 deletions crates/solid-pod-rs-forge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
Expand Down Expand Up @@ -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)]
Expand Down
7 changes: 4 additions & 3 deletions crates/solid-pod-rs-git/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:<token>` HTTP Basic bridge to NIP-98.
Expand Down
5 changes: 3 additions & 2 deletions crates/solid-pod-rs-idp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
|----:|--------------------------------------|--------------------------------|
Expand Down Expand Up @@ -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).
Expand Down
10 changes: 5 additions & 5 deletions crates/solid-pod-rs-nostr/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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).
Expand Down
59 changes: 39 additions & 20 deletions crates/solid-pod-rs-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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/<hex-pubkey> \
-H "X-Pod-Admin-Key: $SOLID_ADMIN_KEY"
# → { "podUrl": "https://pods.example.com/<hex-pubkey>/", "ok": true }
# → { "podUrl": "https://pods.example.com/pods/<hex-pubkey>/", "ok": true }
```

This endpoint is the CF Workers ↔ agentbox handshake: `auth-worker` calls it
Expand All @@ -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.

Expand All @@ -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

Expand Down Expand Up @@ -315,18 +321,28 @@ 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 |
|---|---|
| `fs-backend` | Filesystem storage (JSS 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

Expand All @@ -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)
Loading
Loading