Proposed
2026-05-16
VisionClaw is being refactored. The current Solid integration uses
JavaScriptSolidServer (JSS, Node.js) as a sidecar container
(solid-sidecar-architecture.md)
reached over HTTP via src/handlers/solid_proxy_handler.rs against
JSS_URL=http://visionclaw-jss:3030. Every pod read/write crosses a
process boundary, a TCP socket, and a JSON-LD parse round-trip per call.
Three forces converge on this refactor:
-
DreamLab-AI's
solid-pod-rsis now feature-mapped to JSS at ~98% strict parity (132 rows tracked inPARITY-CHECKLIST.md). It is a Rust-native port of JSS, AGPL-3.0, co-maintained with Melvin Carvalho's JSS upstream. New JSS features land in JSS first and are ported to solid-pod-rs as Cargo features. Library surface is framework-agnostic and consumable in-process. -
JSS Phase 1 (v0.0.190, May 2026) shipped pod-native Nostr identity (
--provision-keys,/private/privkey.jsonld, NIP-05 endpoint, JSON-LD data export). These features are being ported intosolid-pod-rsas Cargo features in the current sprint cycle. VisionClaw needs them to consolidate three keygen paths (VISIONCLAW_NOSTR_PRIVKEYenv, JSS sidecar bootstrap, broker actor signing) into a single pod-resident identity. -
Agentbox proved the binary-aggregation pattern in agentbox ADR-010. That ADR rejected library-linking for AGPL reasons because agentbox's first-party Rust code is not AGPL. VisionClaw's situation is different: AGPL is acceptable here because DreamLab-AI co-maintains both
solid-pod-rsandJavaScriptSolidServer(the upstream from which solid-pod-rs inherits AGPL). The licence-as-blocker argument that constrains agentbox does not constrain VisionClaw.
| Component | Current state |
|---|---|
src/handlers/solid_proxy_handler.rs |
NIP-98-signed HTTP proxy to JSS sidecar |
src/config.js |
Reads 18 JSS_* env vars; orchestrates sidecar lifecycle |
docker-compose.yml |
solidproject/community-server:latest container exposed at :3030 |
src/services/nostr_bridge.rs |
Loads signing key from VISIONCLAW_NOSTR_PRIVKEY env var |
src/services/nostr_bead_publisher.rs |
Same env-key dependency |
src/actors/broker_actor.rs |
Same env-key dependency (via ServerNostrActor) |
Failure modes today:
- Sidecar restart loses in-flight WebSocket subscriptions (Solid Notifications 0.2 reconnect handled by client, but bead-publisher drops events).
- Three keygen surfaces: env var, sidecar
--provision-keys(not yet wired), broker actor signer. No single source of truth. - Pod data is in a Docker volume opaque to the Rust process. Backups require a separate path.
- HTTP+JSON-LD parse on every WAC check (~3-5 ms p50, 15 ms p99 against loopback) accumulates against the bead-publishing hot path.
Replace the JSS sidecar with an in-process embedding of solid-pod-rs
Rust crates linked directly into the webxr workspace. Pod storage,
WAC enforcement, NIP-98 verification, LDP container semantics, and Solid
Notifications become library calls, not HTTP round-trips.
Six crates from the solid-pod-rs workspace, all from a single pinned
git rev (initially v0.4.0-alpha.7+sprint10 once the JSS Phase 1 ports
land):
| Crate | Role in VisionClaw |
|---|---|
solid-pod-rs (core) |
LDP resources/containers, WAC, NIP-98 verification, atomic-rename fs-backend, Solid Notifications 0.2 |
solid-pod-rs-nostr |
did:nostr resolver, NIP-05 endpoint, Schnorr verifier |
solid-pod-rs-idp |
Pod provisioning, account scaffolding, key provisioning (Phase 1) |
solid-pod-rs-server |
Not depended on directly — reference only for HTTP route templates |
solid-pod-rs-activitypub |
Deferred to follow-up ADR (only needed if VisionClaw federates) |
solid-pod-rs-git |
Deferred (graph-as-git is a separate workstream) |
Cargo features enabled by default in the embedding:
[dependencies.solid-pod-rs]
git = "https://github.com/DreamLab-AI/solid-pod-rs"
rev = "<pinned>"
default-features = false
features = [
"fs-backend",
"nip98-schnorr",
"did-nostr",
"security-primitives",
"rate-limit",
"quota",
"webhook-signing",
"notifications",
"provision-keys", # JSS Phase 1
"nip05-endpoint", # JSS Phase 1
"export-jsonld", # JSS Phase 1
]BEFORE: AFTER:
+----------------+ HTTP +-------+ +----------------------------+
| VisionClaw |---------->| JSS | | VisionClaw |
| (Rust, MIT) | :3030 | (Node)| | (Rust, AGPL-3.0-only) |
| | | | | ├── solid-pod-rs (lib) |
| | +-------+ | ├── ActixWeb routes |
| | | | │ mounted at /solid/* |
+----------------+ v | └── Pod storage: fs |
Docker vol | (mounted as Solid |
| directory tree) |
+----------------------------+
Pod storage path under /var/lib/visionclaw/pods/ (POSIX, atomic-rename
guarantees from fs-backend). solid-pod-rs is invoked via its
framework-agnostic library API; Actix-web handlers in VisionClaw wrap
the library's request/response primitives. No internal HTTP loopback.
Linking AGPL-3.0 solid-pod-rs as a library makes the combined
VisionClaw binary AGPL-3.0-only. VisionClaw is relicensed from MIT
to AGPL-3.0-only as part of this refactor:
Cargo.tomllicense = "MIT"→license = "AGPL-3.0-only"LICENSEreplaced with AGPL-3.0 text;LICENSE.MITretained for the pre-cut history.NOTICEfile added preserving solid-pod-rs / JSS attribution per AGPL §5(a-c).- AGPL §13 (network corresponding source) satisfied by the public
GitHub repo URL exposed in
/.well-known/solidand the server-info HTTP headerX-Source-URL.
Acceptable because DreamLab-AI co-maintains both solid-pod-rs and
upstream JSS. The AGPL boundary at the library link is consistent with
the rest of the sovereign data stack (agentbox aggregates the same
upstream as a binary; nostr-rust-forum and dreamlab-ai-website
federate via HTTP). No external dependency forces a non-AGPL link.
Five PRs, each independently reviewable, totalling ~1500 LoC delta:
M1 — Relicense + crate add (small). Cargo.toml license switch,
LICENSE swap, NOTICE file, solid-pod-rs git dep added behind feature
flag embedded-pod (off by default). Builds both ways. No behavioural
change.
M2 — Replace solid_proxy_handler.rs. New src/handlers/pod/
module with sub-handlers per LDP verb, each calling into solid-pod-rs
library entry points. The proxy handler retained behind the
embedded-pod = false flag as a fallback for one minor release.
M3 — Key consolidation. src/services/nostr_bridge.rs,
nostr_bead_publisher.rs, actors/broker_actor.rs switch from
VISIONCLAW_NOSTR_PRIVKEY to a PodResidentSigner that loads
pods/<webid>/private/privkey.jsonld via solid-pod-rs-idp. Env var
demoted to test override (SERVER_NOSTR_PRIVKEY_OVERRIDE,
test/CI only).
M4 — Pod NIP-05 wiring. VisionClaw's WebID profile (profile/card)
gains a nostr:pubkey triple seeded at pod creation; the embedded
solid-pod-rs-nostr serves /.well-known/nostr.json?name=<local> from
the same pod tree. Forum auth-worker NIP-05 federation (separate ADR in
nostr-rust-forum) consumes this endpoint.
M5 — Sidecar removal. docker-compose.yml drops the
solidproject/community-server service. JSS_URL removed from .env.
The embedded-pod feature flag becomes default-on; the proxy fallback
code is deleted. src/config.js (entire JS stub) deleted.
Default flag flip happens at M5; pre-M5 the embedded path is opt-in via env so dev/staging can validate behavioural parity before cut.
The forum (nostr-rust-forum / dreamlab-ai-website) continues to
federate to VisionClaw's pod over HTTP — the embed is an
in-process optimisation, not a topology collapse. Federated reads via
GET /solid/<path> against VisionClaw's Actix routes resolve through
the in-process solid-pod-rs library. External clients see no
difference except lower latency and consistent WAC enforcement.
The agentbox container (solid-pod-rs aggregated as a supervisord
binary per agentbox ADR-010) is unchanged. Agentbox and VisionClaw both
host solid-pod-rs, but at different boundaries (binary vs library) for
different licence reasons (agentbox AGPL-aggregation, VisionClaw
AGPL-derivative). The shared code path means feature parity is free.
- One keygen surface.
provision-keyswrites the canonicalprivkey.jsonldat pod creation; every signing path reads from there. Three env-var-driven paths collapse to one. - WAC actually enforces in-process. Today, JSS WAC checks happen inside the sidecar but VisionClaw cannot consult them at the Rust layer; access control logic is duplicated. Post-embed, the WAC engine is a function call from any handler.
- Latency drops materially on the bead-publishing hot path. Eliminates the loopback HTTP + JSON-LD parse on every pod write. The sidecar p50 of 3-5 ms / p99 of 15 ms becomes sub-millisecond library calls.
- Atomic-rename durability is real.
fs-backendguarantees apply directly to VisionClaw's pod tree. No more "filesystem says yes, sidecar says no" inconsistencies during crashes. - Single binary deployable.
docker-composeshrinks by one service. Operational surface area drops. - Future feature work lands once. When JSS Phase 2 ships (BIP-39
seed wrapping, key rotation, passkey-wrap), the port lands in
solid-pod-rs; VisionClaw and agentbox absorb viacargo update. - NIP-05 federation becomes free. Forum federates to VisionClaw's pod endpoint; pod owns its own identity record.
- Cargo build cost.
solid-pod-rsworkspace adds ~120 transitive dependencies; cold build adds ~3-4 minutes tocargo build --release(warm/incremental is unchanged). Mitigation: pin viaCargo.lockand usesccachein CI. - Tighter coupling to
solid-pod-rsrev cadence. A breaking change upstream forces a VisionClaw fix in the same sprint. Mitigation: co-maintenance gives early signal; pin by git rev (not semver range) until upstream cuts v1.0. - AGPL imposes operator obligations. Anyone running a VisionClaw instance on a public network must offer source. The public GitHub repo satisfies §13 today; future closed-source forks are not viable without a separate licence.
src/config.jsdeletion is a real cut. Operators with custom JSS_* env tuning must migrate to the new[integrations.solid_pod_rs]TOML section (or the matching env namespace, which solid-pod-rs'sconfig-loaderfeature preserves drop-in via the same JSS_* names).- The Phase 1 features must land in solid-pod-rs before M4. If the upstream port slips, VisionClaw's pod-resident NIP-05 wiring slips with it. Mitigation: the port plan is six sprints (A-F in the internal port plan) and provision-keys is the first port.
- Forum federation unchanged in shape. Still HTTP, still NIP-98 signed. Only the server-side implementation changes.
- WebSocket Solid Notifications surface unchanged. Library exposes the same WebSocket channel; routes mount at the same paths.
- Neo4j is untouched. This ADR is about the Solid layer. Graph data continues to live in Neo4j; pod data is the LDP-shaped surface on top.
- Docker volume layout unchanged. Pod data still under
/var/lib/visionclaw/pods/(renamed from/data/pods/). One-line volume rename indocker-compose.yml.
Keep JSS sidecar; upgrade to v0.0.190. Rejected: every additional sprint extends the duplicate-keygen problem and the HTTP loopback cost. The refactor window is now; deferring loses leverage. Also: JSS is Node.js — embedding a Node runtime into a Rust binary is not viable, so the choice is sidecar-forever vs library-once.
Aggregate solid-pod-rs as a binary (agentbox pattern). Rejected for VisionClaw because the licence-blocker argument that drove agentbox to binary-aggregation does not apply: VisionClaw is fine going AGPL. Library linking gives in-process latency and a unified WAC surface that aggregation cannot.
Fork solid-pod-rs into VisionClaw's tree. Rejected: forks decay, co-maintenance breaks, parity tracking against JSS becomes a two-repository problem. Pin via git rev is sufficient.
Hand-roll a minimal Solid impl in VisionClaw's Rust. Rejected: reinvents LDP semantics, WAC evaluation, atomic-rename storage, NIP-98 verification, Notifications. solid-pod-rs already exists and is co-maintained.
Wait for solid-pod-rs v0.5.0 stable. Rejected: v0.4.x is already
running in agentbox production. v0.5.0 is the umbrella version for the
JSS Phase 1 absorption; the embed work starts now and tracks the rev
forward.
- agentbox ADR-010: solid-pod-rs as first-class pod server (binary aggregation pattern). This ADR is the library-link counterpart for VisionClaw.
- ADR-027: pod-backed graph views (consumer of LDP semantics)
- ADR-028: SPARQL-patch ontology (consumer of PATCH semantics)
- ADR-029: type-index discovery (consumer of WebID + type-index)
- ADR-030: agent memory pods (consumer of WAC + LDP containers)
- ADR-041: Judgment Broker Workbench (consumer of
PodResidentSignerper M3; broker decisions sign with the pod-resident key, replacing the env-var path that exists today)
- Pod root for VisionClaw's server identity? Proposal:
/var/lib/visionclaw/pods/<server-webid-npub>/. Confirm with ops before M5. - Migration of existing pod data from
solid-datavolume. solid-pod-rsfs-backendreads existing JSS pod trees, but.metasidecars may need a one-shot migration script. Out of scope for M1-M5; tracked separately. - Should the
solid-pod-rs-activitypubcrate land in a follow-up ADR? ActivityPub federation is out of scope here; flagged as follow-up. - AGPL relicense — any contributors who never agreed to AGPL?
Audit
git shortlog -sagainst contributor records before the cut; contact any external contributors for re-licence consent.
- agentbox ADR-010 — solid-pod-rs as first-class pod server
- JSS upstream: https://github.com/JavaScriptSolidServer/JavaScriptSolidServer
- solid-pod-rs upstream: https://github.com/DreamLab-AI/solid-pod-rs
- JSS Phase 1 feature roadmap: JSS issue #437
- solid-pod-rs parity tracking:
PARITY-CHECKLIST.md - AGPL-3.0 text:
LICENSE docs/explanation/solid-sidecar-architecture.md— current sidecar shape (will be superseded post-M5)