research: local-first workspaces cohort deep dive (Anytype/AFFiNE/AppFlowy) — CRDT substrate; UC-64/65

Combined cohort memo for the three open-source local-first all-in-one
workspaces, studied together because they share the trait none of the
prior eleven systems had: the store is a CRDT (Anytype any-sync, AFFiNE
Yjs/y-octo, AppFlowy Yrs). The cohort thesis: CRDT changes the adapter
math — the backend performs conflict-free merge itself (shard-wiki must
not impose git/text merge; speak the CRDT via a replica or stay
projection/overlay), history is a CRDT update log (supplement not git),
and the attach surface is a replica or self-host sync endpoint, with
Anytype adding P2P (no single endpoint) + E2EE (opaque). Added UC-64
(CRDT-synced shard with native merge), UC-65 (P2P/decentralized shard,
no single canonical endpoint); enriched UC-36/47/48/51/55/57/58/61.
Catalog now 65 UCs. Architecture for SHARD-WP-0002 T11/T14: merge-model
capability (proposed 13th spectrum: none/git/native-CRDT), CRDT-replica
substrate + P2P/no-central-endpoint attach mode, endpoint-model
capability, MCP as an integration surface. Boundary: CRDT local-first
candidate shards — attach a replica/projection as pages, respect native
merge, never re-drive their sync; not substrates and not the federation
layer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 16:21:08 +02:00
parent 6ccf349209
commit 3e0c05a2b3
5 changed files with 419 additions and 14 deletions

View File

@@ -0,0 +1,46 @@
# 260614 — Local-first workspaces cohort deep dive (Anytype · AFFiNE · AppFlowy)
Date: 2026-06-14
## What this is
A **combined cohort deep dive** of three open-source, local-first "all-in-one workspace"
tools — **Anytype**, **AFFiNE**, **AppFlowy** — studied together because they share the
one trait none of the prior eleven systems had: **the store is a CRDT** (Anytype's
*any-sync*, AFFiNE's *Yjs/y-octo*, AppFlowy's *Yrs*). The substrate is the contribution;
studying them together isolates it from each tool's surface features.
Per-tool highlights:
- **Anytype** — `any-sync`: **P2P + E2EE** CRDT (sync/file/consensus/coordinator nodes;
encrypted backup), a **typed object graph** (user-editable types + relations), IPFS
files, an **open API + MCP**.
- **AFFiNE** — **Yjs/y-octo** over **OctoBase**; **BlockSuite** editor framework; docs,
whiteboards, and databases as **views of the same block set**; WebSocket sync,
self-host (Docker, MPL-2.0).
- **AppFlowy** — **Yrs** via **AppFlowy-Collab** (Rust); Flutter UI; Notion-style
**databases + views**; pluggable storage/sync (RocksDB/IndexedDB; AppFlowy Cloud/
Supabase).
## Contents
| Path | Role |
|------|------|
| `findings.md` | Per-tool sections, the CRDT cohort thesis, comparative matrix, combined capability profile, INTENT mapping, UC seeds, architecture notes, sources |
## Status
Cohort dive complete. Two new use cases promoted to `spec/UseCaseCatalog.md` (UC-64
attach a CRDT-synced local-first shard with native conflict-free merge; UC-65 attach a
peer-to-peer / decentralized shard with no single canonical endpoint); UC-36/47/48/51/55/
57/58/61 enriched. Logged for `SHARD-WP-0002` (T11/T14): a **merge-model** capability
(proposed thirteenth spectrum: `none / git / native-CRDT`), a **CRDT-replica** substrate +
**P2P/no-central-endpoint** attach mode, an **endpoint-model** capability, and MCP as an
integration surface.
**Cohort thesis recorded:** CRDT changes the adapter math — the backend performs
conflict-free merge itself (so shard-wiki must not impose git/text merge), history is a
CRDT update log (supplement, not git), and the attach surface is a **replica or sync
endpoint** (Anytype adds **P2P + E2EE**). **Boundary:** CRDT local-first candidate shards
— attach a replica/projection as pages, respect native merge, never re-drive their sync;
not substrates and not the federation layer.
</content>

View File

@@ -0,0 +1,261 @@
# Findings — Anytype, AFFiNE, AppFlowy: the CRDT local-first workspace cohort
Date: 2026-06-14
Source kind: **modern shipped products (cohort dive)** — three open-source, local-first
"all-in-one workspace" tools studied together; candidate shards of a **new substrate
family: CRDT-synced local-first stores**
Lens: shard-wiki — the CRDT substrate and its native-merge / P2P / E2EE implications for
the adapter contract
> Why one combined memo. Anytype, AFFiNE, and AppFlowy are the open-source local-first
> Notion/Miro alternatives, and they share the one trait the prior eleven systems never
> had: **the store is a CRDT** (Anytype's *any-sync*, AFFiNE's *Yjs/y-octo*, AppFlowy's
> *Yrs*). That single fact changes the adapter math — **the backend performs conflict-
> free merge itself**, history is a CRDT update log (not git), and content arrives as a
> replica/export, not files or rows. Studying them together isolates the *substrate*
> contribution from each tool's surface features and avoids triplicating boilerplate.
> Per-tool sections (§1§3), then the cohort thesis and what it means for shard-wiki
> (§4 onward).
Contrast set: file-store (Obsidian/Logseq), client-DB (Roam), hosted-DB (Notion),
SQLite-local+file-sync (Joplin). This cohort adds **CRDT-replica** as a substrate.
---
## 1. Anytype — P2P, E2EE, typed object graph (any-sync)
- **Substrate:** the open **any-sync** protocol — **local-first, peer-to-peer,
end-to-end encrypted** CRDT. Infrastructure is four node types: **sync, file,
consensus, coordinator**; **backup nodes store ciphertext they cannot read**. Every
change is **encrypted and signed with the user's private key**, verified via a shared
public key. Files via **IPFS**.
- **Data model:** **spaces** = graph-based databases; users define **custom object
types with relations** ("everything is an object"; a user-editable **ontology**).
Closest to Notion's typed model but **local-first + E2EE + user-owned schema**.
- **Extension:** an **open API** for integrations, **plus MCP support** (AI-agent
collaboration) — a notable modern surface (a shard speaking MCP).
- shard-wiki read: the **most decentralized + encrypted** shard yet — no single
canonical endpoint (attach a local replica or a node), content opaque without keys
(UC-61), typed object graph (UC-58), CRDT merge (UC-64).
## 2. AFFiNE — docs + whiteboard + DB as views of one block set (Yjs/BlockSuite)
- **Substrate:** **Yjs CRDT** via **y-octo** (native Rust Yjs) over **OctoBase** (Rust
local-first engine). Local-first by default; optional **WebSocket realtime** sync to
AFFiNE Cloud or self-host (MPL-2.0, Docker).
- **Editor:** **BlockSuite** — an open block-editor framework providing the **page
editor, the edgeless canvas, and block types (incl. databases/embeds)**.
- **Headline:** **docs, whiteboards, and databases are *views of the same underlying
data*** — one block set rendered as page mode, edgeless canvas, or DB views. CRDT
guarantees offline edits "merge mathematically perfectly" on reconnect.
- shard-wiki read: the **one-data-many-renderings** model shipped (ZigZag dimensions,
UC-47/48), with **non-Markdown spatial content** (edgeless canvas, UC-55) and CRDT
merge (UC-64).
## 3. AppFlowy — Rust/Flutter, Notion-style DBs over Yrs CRDT
- **Substrate:** **Yrs** (Rust Yjs) wrapped as **AppFlowy-Collab** — CRDT primitives +
persistence + domain objects (documents, **databases**, folders, importers, plugins,
user state). **Pluggable storage/sync:** RocksDB (native) / IndexedDB (wasm) locally;
**Supabase or AppFlowy Cloud** (self-hostable) for sync, via **trait-based local-only
vs cloud**.
- **Data model:** Notion-like **databases with views** (grid/board/calendar), typed
fields. Flutter UI + Rust core, modular.
- shard-wiki read: a **self-hostable CRDT Notion** — typed DBs + views (UC-58, UC-54),
CRDT merge (UC-64), self-host sync endpoint (UC-57).
---
## 4. The cohort thesis — CRDT changes the adapter math
All three converge on **CRDT-synced, local-first, open-source, self-hostable** stores
with **typed/database object models** and **non-Markdown content** (canvas/DB). The
*substrate* is the new thing, and it has four consequences shard-wiki must absorb:
1. **Native, conflict-free merge (UC-64).** The backend resolves concurrent edits
itself (Yjs/Yrs/any-sync). shard-wiki must **not impose git/text merge** on a CRDT
shard; it either speaks the CRDT (replica) or stays a **projection/overlay** that
respects CRDT semantics. This adds a **merge-model** capability:
`none → git/text → native-CRDT` — a candidate **thirteenth spectrum** for the
contract (after Joplin's content-opacity twelfth).
2. **History is a CRDT update log, not git (UC-36).** No portable commit history; the
coordination journal **supplements** (like Notion/Joplin), or shard-wiki snapshots
the replica over time.
3. **The attach surface is a replica or a sync endpoint, not files/rows.** Attach a
**local CRDT replica** (offline, full state) or the **self-host sync server's API**
(AFFiNE Cloud / AppFlowy Cloud / any-sync node). Anytype adds **P2P with no single
canonical endpoint** (UC-65) and **E2EE content opacity** (UC-61).
4. **Typed object graphs + multi-view (UC-58, UC-47/48, UC-55).** User-editable
ontologies (Anytype types/relations), Notion-style DBs (AppFlowy), and one-block-set-
many-renderings (AFFiNE page/canvas/DB) — the structured + dimensional demands the
prior dives raised, now local-first.
These are **sync layers** (any-sync, Yjs sync, AppFlowy Cloud) — like Joplin, the
**not-a-file-sync-daemon** boundary applies: shard-wiki attaches a **replica/projection
as pages** and does **not** re-implement or re-drive their CRDT sync.
---
## 5. Comparative matrix
| | Anytype | AFFiNE | AppFlowy |
|---|---------|--------|----------|
| CRDT | any-sync (custom) | Yjs (y-octo) | Yrs |
| Sync | **P2P + nodes, E2EE** | WebSocket, cloud/self-host | RocksDB local + cloud/Supabase |
| Encryption | **E2EE (default)** | optional (transport/self-host) | self-host boundary |
| Local engine | local + IPFS files | OctoBase (Rust) | RocksDB / IndexedDB |
| Data model | **typed object graph (ontology)** | blocks; **doc/canvas/DB = one data, many views** | Notion-style **DBs + views** |
| Non-MD content | objects, files | **edgeless canvas** | board/calendar/grid |
| Extension | **open API + MCP** | BlockSuite framework | Flutter/Rust modules; plugins (roadmap) |
| Self-host | backup/sync nodes | Docker (MPL-2.0) | AppFlowy Cloud |
| Markdown | export (lossy) | import/export (lossy) | import/export (lossy) |
---
## 6. Combined capability profile (as a shard)
| Capability | This cohort | Notes for the adapter contract |
|------------|-------------|--------------------------------|
| Read | yes (replica or sync API) | local CRDT replica (offline) or self-host/P2P endpoint |
| Write | yes, **CRDT-mediated** | writes are CRDT ops; respect merge — don't overwrite |
| Merge | **native CRDT (conflict-free)** | **new capability**: backend owns merge; no external git merge (UC-64) |
| Write granularity | block/object | fine-grained |
| Identity / addressing | object/block IDs (CRDT-assigned) | fine-grained, store-minted (UC-51) |
| Structure | **typed object graph / DBs + relations** | user-editable ontology (Anytype); DB views (AppFlowy) (UC-58) |
| History | **CRDT update log, not git** | supplement / snapshot (UC-36) |
| Native query | varies (graph/DB filters) | delegate where present (UC-52) |
| Subscribe | realtime CRDT sync / P2P | push by nature; attach as replica (UC-31) |
| Content opacity | **Anytype E2EE → ciphertext** | opaque without keys (UC-61) |
| Endpoint model | **replica / self-host server / P2P (Anytype)** | no single canonical URL for P2P (UC-65) |
| Content types | docs + **canvas/DB/objects** | non-Markdown (UC-55); multi-view (UC-47/48) |
| Markdown | lossy export | fidelity report (UC-59) |
| Attach modes | CRDT replica · self-host API · (Anytype) MCP/open API | new substrate row in the taxonomy |
Verdict: a **demanding but coherent new family** — best attached as a **local replica
(offline, full CRDT state)** projected to pages, or via a **self-host sync endpoint**;
Anytype additionally **P2P + E2EE** (replica-only/opaque). Standout demands:
**native-CRDT merge** (UC-64) and **P2P/no-central-endpoint + E2EE** (UC-65/UC-61).
---
## 7. Mapping to shard-wiki INTENT (compare, do not equate)
### 7.1 Reinforcements
- **Local-first sovereignty** is the cohort's whole pitch — strong INTENT alignment;
each is a legitimate sovereign shard with user-owned data.
- **Self-hostable + open-source** means shard-wiki can attach within a user's trust
boundary (good for the L0→L4 authz ladder).
- **One-data-many-views** (AFFiNE) and **typed object graphs** (Anytype/AppFlowy) are
the dimensional (UC-47/48) and structured (UC-58) demands, now validated local-first.
### 7.2 Deliberate divergences (design bugs if conflated)
1. **Do not impose git/text merge on a CRDT shard.** The backend merges; shard-wiki
respects CRDT semantics or stays a projection/overlay (UC-64). Treating a CRDT shard
like a file shard corrupts it.
2. **Not a sync daemon.** any-sync/Yjs/AppFlowy Cloud already sync; shard-wiki attaches
a **replica as pages**, never re-drives their sync (INTENT not-a-file-sync-daemon).
3. **P2P has no canonical endpoint.** Don't model the shard as a URL; attach a replica
or a named peer/node (UC-65).
4. **E2EE shards are opaque** without keys — backup/structure-shell only (UC-61).
5. **Proprietary CRDT/object formats are lossy to Markdown** — translate with a fidelity
report (UC-59), one workspace = one shard, not the federation layer.
### 7.3 What the cohort teaches that shard-wiki should keep
- Add a **merge-model** capability (`none / git / native-CRDT`) so CRDT shards are
handled correctly (UC-64) — a thirteenth capability spectrum.
- Add **CRDT-replica** to the substrate/attachment taxonomy, and **P2P/no-central-
endpoint** as a binding mode (UC-64/65).
- **MCP as an integration surface** (Anytype) is worth noting — a shard may expose an
AI-agent protocol shard-wiki could consume or coexist with.
---
## 8. Use-case seeds → catalog (promoted 2026-06-14)
Last existing UC is **UC-63**. New UCs **UC-64, UC-65** added; existing UCs enriched.
| Seed | Catalog action |
|------|----------------|
| **Attach a CRDT-synced local-first shard** whose backend performs native conflict-free merge; respect CRDT semantics (no git merge), consume a replica/export, CRDT-log history (supplement) | **UC-64 (new)** |
| **Attach a peer-to-peer / decentralized shard with no single canonical endpoint** (Anytype any-sync: replica or peer/backup node; E2EE) | **UC-65 (new)** |
| Typed object graph / user-editable ontology + Notion-style DBs, local-first | **enriches UC-58** |
| E2EE content opacity (Anytype) reinforced; self-host trust boundary | **enriches UC-61** |
| CRDT update log ≠ portable git history → supplement | **enriches UC-36** |
| One block set rendered as page / canvas / DB views (AFFiNE) | **enriches UC-47, UC-48** |
| Edgeless canvas / board / objects = non-Markdown content | **enriches UC-55** |
| Self-host sync server (AFFiNE/AppFlowy Cloud) + Anytype open API/MCP as endpoints | **enriches UC-57** |
| Object/block IDs CRDT-assigned (fine-grained) | **enriches UC-51** |
| Lossy Markdown export | links UC-59 |
---
## 9. Architecture notes for SHARD-WP-0002 (no UC)
- **Merge-model capability** (`none / git-or-text / native-CRDT`) — proposed
**thirteenth spectrum** (with Joplin's content-opacity twelfth). CRDT shards reject
external merge; shard-wiki overlays must be CRDT-aware or remain projections. (T11.)
- **Substrate taxonomy gains CRDT-replica**; **attach modes gain "local CRDT replica"
and "P2P/no-central-endpoint"** beside file-store / in-engine-host / external-API.
(T14.)
- **Endpoint model** as a capability: single URL vs self-host server vs **P2P peer set**
(Anytype) — affects how a binding is addressed and how freshness/sync work. (T14.)
- **MCP / AI-agent protocol** as a possible shard integration surface (Anytype). Note
for the extension-surface catalogue. (T14.)
- These three plus Joplin and Logseq all argue the contract must treat **substrate as a
spectrum that tools migrate along and that shard-wiki must not hard-code**.
---
## 10. Open questions (for spec / workplans)
1. For a **CRDT shard** (UC-64), does shard-wiki embed a CRDT client (Yjs/Yrs) to hold a
live replica, or only consume periodic **exports/snapshots**? The former gives
live/writable; the latter is simpler but lossy on merge timing.
2. How are **overlays** expressed against a CRDT shard — as CRDT ops (requires the
client) or as out-of-band patches reconciled later (projection-only)?
3. For **P2P shards** (UC-65), what is the "address" of the shard — a space ID + a peer/
node, a local replica path, or an invite/key? How does this fit shard attachment
config?
4. Does shard-wiki ever hold **E2EE keys** (Anytype) to project content, or only ever
treat such shards as opaque replicas/backups (UC-61)?
5. Is the **self-host sync server** (AFFiNE/AppFlowy Cloud) attached as an external API
(UC-57) or by running a co-located replica?
---
## 11. Sources
| Source | Used for |
|--------|----------|
| Anytype any-sync — Protocol overview (https://tech.anytype.io/any-sync/overview) | Local-first/P2P/E2EE CRDT; sync/file/consensus/coordinator nodes; encrypted backup |
| anyproto/any-sync (https://github.com/anyproto/any-sync) | Protocol scope; keys signed/verified; IPFS files |
| Anytype Docs (https://doc.anytype.io/anytype-docs) | Spaces, object types + relations (ontology); open API + MCP |
| toeverything/AFFiNE + DeepWiki (https://deepwiki.com/toeverything/AFFiNE) | BlockSuite (page/edgeless/DB); y-octo Yjs; OctoBase; one-data-many-views; self-host |
| AFFiNE — interactive whiteboard / blog (https://affine.pro/blog/interactive-whiteboard-app) | Edgeless canvas; CRDT offline merge |
| AppFlowy-IO/AppFlowy-Collab (https://github.com/AppFlowy-IO/AppFlowy-Collab) | Yrs CRDT; collab domain objects (documents/databases/folders); persistence |
| AppFlowy Architecture Overview — DeepWiki (https://deepwiki.com/AppFlowy-IO/AppFlowy/2-architecture-overview) | Flutter/Rust layers; pluggable storage/sync (RocksDB/IndexedDB; Supabase/AppFlowy Cloud); local vs cloud trait |
| AFFiNE vs AppFlowy vs Anytype (https://affine.pro/blog/affine-vs-appflowy-vs-anytype) | Cohort comparison framing |
Cross-references: `research/260614-notion-deep-dive/findings.md` (typed DB, hosted
contrast), `research/260614-joplin-deep-dive/findings.md` (E2EE/content opacity, sync-
daemon boundary), `research/260614-shard-spectrum-synthesis/findings.md` (the spectra
this extends), `spec/UseCaseCatalog.md` (UC-36, UC-47, UC-48, UC-51, UC-55, UC-57,
UC-58, UC-61), `workplans/SHARD-WP-0002-federation-architecture.md` (T11, T14).
---
## 12. Traceability
- New UCs: **UC-64, UC-65**`spec/UseCaseCatalog.md`.
- Enriched UCs: **UC-36, UC-47, UC-48, UC-51, UC-55, UC-57, UC-58, UC-61** (links UC-59).
- Architecture (no UC): **merge-model** capability (proposed thirteenth spectrum);
CRDT-replica substrate + P2P/no-central-endpoint attach mode; endpoint-model capability;
MCP integration surface → `SHARD-WP-0002` (T11, T14).
- Boundary recorded: Anytype/AFFiNE/AppFlowy are **CRDT local-first candidate shards**
(attach a replica/projection as pages; respect native CRDT merge; never re-drive their
sync; P2P/E2EE shards are replica-only/opaque) — not substrates, not the federation
layer (INTENT not-a-sync-daemon, graceful degradation, no silent mutation).
</content>

View File

@@ -24,4 +24,5 @@ when multiple files or sources are involved. Findings here inform `spec/` and
| 2026-06-14 | `260614-notion-deep-dive/` | Notion — closed block-DB SaaS, external REST API only, database-as-pages; UC-57/58/59 |
| 2026-06-14 | `260614-shard-spectrum-synthesis/` | Synthesis — shard family matrix + eleven capability spectra across nine systems; feeds SHARD-WP-0002 T11T16 |
| 2026-06-14 | `260614-joplin-deep-dive/` | Joplin — SQLite-local/Markdown-on-sync, interchange-format attach, E2EE content opacity; UC-60/61 |
| 2026-06-14 | `260614-logseq-deep-dive/` | Logseq — block-graph on plain Markdown files, in-file block IDs, derived Datalog index; UC-62/63 |
| 2026-06-14 | `260614-logseq-deep-dive/` | Logseq — block-graph on plain Markdown files, in-file block IDs, derived Datalog index; UC-62/63 |
| 2026-06-14 | `260614-localfirst-workspaces-deep-dive/` | Anytype · AFFiNE · AppFlowy — CRDT local-first workspaces (any-sync/Yjs/Yrs), native merge, P2P/E2EE; UC-64/65 |