Design review: @jeswr/y-solid persistence model (log vs snapshot), scope model, live-sync seam
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
**Design summary for maintainer review/steer** — I made the design choices below autonomously (per the no-block-on-greenlights rule) and documented them; raising them here so you can steer later.
## What it is
`@jeswr/y-solid` is a Yjs persistence provider that stores/loads a Yjs CRDT document in a Solid pod. Yjs is the dominant CRDT behind collaborative editors (TipTap, BlockNote, Monaco-collab, Excalidraw, …); this makes any Yjs app store its collaborative doc in the user's own pod via an injectable authenticated `fetch` (no concrete auth lib imported). cfa-epic OSS-integration target.
## Decision 1 — append-only update **LOG**, not a single snapshot resource (CHOSEN: log)
Each Yjs binary update is its own `application/octet-stream` LDP resource under one container; loading merges them. Rationale:
- **No read-modify-write race** — appending is a pure create, so concurrent writers/tabs never clobber each other (a single snapshot needs RMW → last-write-wins data loss).
- **Offline/crash-safe** — each queued update is one independent PUT; a partial flush leaves a valid smaller log, never a corrupt snapshot.
- **Order-independent + idempotent** by Yjs CRDT semantics — robust to out-of-order/duplicate delivery. We still mint lexicographically-sortable resource names (zero-padded ms timestamp + random suffix) for a *deterministic* load/compaction order; correctness does not depend on it.
- **`compact()`** is the GC: folds the whole log into one merged resource (write-before-delete for crash-safety), bounding storage.
The binary CRDT payload is stored as binary — **no bespoke RDF encoding of the payload**. The only RDF touched is the LDP container **listing** (read-only) via `@jeswr/fetch-rdf` parse + `@solid/object` `ContainerDataset`.
Open question for you: is per-update-resource granularity acceptable on the target servers (container listing cost grows with log length until `compact()`), or would you prefer an auto-compact threshold / a periodic compaction policy baked in?
## Decision 2 — scope model (fail-closed, container-confined)
`assertWithinBase` (ported from `@jeswr/solid-memory`) is applied on **every** read/write/delete target: same-origin + path-prefixed, container root rejected for resource ops, foreign-origin/escaping members skipped on read and rejected on write. The container is the single security boundary. No crypto/DPoP in-package — the consumer injects an authed `fetch`.
## Decision 3 — live cross-client sync = a DOCUMENTED SEAM (follow-up, not built)
P1 is persistence (load on init + persist local edits). Live remote-edit propagation is exposed via a manual `provider.sync()` hook (reads + applies updates appended since the last load). Two documented ways to drive it: Solid Notifications `WebSocketChannel2023` (recommended, push) or polling. **Follow-up:** wire a notifications channel into the provider so live sync is automatic.
## Status
- Public repo, `@jeswr` namespace, package `@jeswr/y-solid`.
- `yjs` is a peerDependency (not bundled). All other deps on npm → self-contained committed `dist/`, GitHub-installable now (`npm install github:jeswr/y-solid#main yjs`); npm publish deferred (`needs:user`).
- Gate green: lint (Biome) + typecheck (tsc) + **38 vitest tests** (round-trip create→persist→fresh-doc-loads-same-state, scope guard rejects foreign origin, binary-update integrity over full 0..255 byte range, append-log merge ordering, compaction, no-echo-on-load, sync seam, error/destroy lifecycle) + build + check:dist + check:lockfile-transport.
- roborev (codex) on HEAD `417131f`: **No issues found.**
## Follow-ups I'd track
1. Auto-compaction policy (threshold/periodic) — pending your steer on Decision 1.
2. Live-sync: wire `WebSocketChannel2023` into the provider (Decision 3).
3. npm publish (`needs:user` — npm login).
🤖 PSS agent — @jeswr's agent for `prod-solid-server` / the Solid app + Pod-Manager suite.
Contributor guide
No contributing guide indexed for this repository
Research direction
Review the provider entry points around persistence, `assertWithinBase`, `compact()`, and `provider.sync()`, then read the existing 38 Vitest tests and the documented log, scope, and sync decisions. Done requires maintainer direction on compaction and live-sync policy, rather than a self-contained code change.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100