Devon Devon Meadows Devon
Created October 18, 2025
← Back to Summary

Digital Garden & Knowledge Management Tech Stack Research

Executive Summary

This research analyzes the technical architectures of leading knowledge management tools (Obsidian, Reflect, Roam, Logseq, Notion) and digital garden frameworks (Quartz, Astro, Next.js, Eleventy) to inform architectural decisions for the Commune project.

Key Findings:

  1. Static-first wins for digital gardens: Astro’s islands architecture provides 40% faster loads with 90% less JavaScript
  2. Local-first + E2EE is table stakes: All modern tools prioritize privacy with client-side encryption
  3. YJS/CRDT is the gold standard: For real-time sync, YJS eliminates O(n²) bottlenecks
  4. Islands architecture enables progressive enhancement: Ship zero JavaScript by default, hydrate only interactive components
  5. SQLite FTS5 provides excellent search: 50x faster than full-text alternatives (1s → 20ms)

1. Product Tech Stack Decision Matrix

ProductArchitectureStorageSync EngineE2EEPlatformAI Features
ObsidianElectron desktopLocal markdown filesProprietary (diff-match-patch)✓ (Sync only)Desktop, MobilePlugin-based
ReflectWeb appCloud databaseYJS CRDT✓ Client-sideWeb, Mobile webNative LLM integration
Roam ResearchWeb app (ClojureScript)Datomic (immutable)Real-time transaction logWeb onlyLimited
LogseqElectron/WebLocal markdown + DB graphsGit-based or custom✗ (local-first)Desktop, Mobile, WebPlugin-based
NotionWeb + desktop hybridPostgreSQL (sharded)MessageStore + WebRTCWeb, Desktop, MobileNative AI assistant
Andy MatuschakCustom static siteStatic HTML/JSONNone (static)N/AWeb onlyNone
QuartzStatic site generatorMarkdown → HTMLNone (rebuild)N/AWeb onlyNone

2. Static vs Dynamic Architecture Analysis

2.1 Static Site Generators (Digital Garden Use Case)

Architecture: Islands-based static site generator with partial hydration

Pros:

Cons:

Hydration Directives:

// client:load - hydrate immediately
<StarModal client:load />

// client:visible - lazy load when in viewport
<GraphVisualization client:visible />

// client:idle - hydrate when browser idle
<BacklinksPanel client:idle />

// client:media - responsive hydration
<MobileNav client:media="(max-width: 768px)" />

Best For: Content-heavy sites with occasional interactivity (digital gardens, documentation, blogs)

Next.js with ISR

Architecture: Hybrid SSR/SSG with Incremental Static Regeneration

Pros:

Cons:

ISR Example:

export async function getStaticProps() {
  const notes = await fetchNotes()
  return {
    props: { notes },
    revalidate: 60 // Regenerate page every 60 seconds
  }
}

Best For: Dynamic content that updates frequently, complex data-driven apps, e-commerce

Eleventy (11ty)

Architecture: Zero-config static site generator (Node-based)

Pros:

Cons:

Best For: Minimalist blogs, documentation, marketing sites

Quartz

Architecture: Obsidian-focused static site generator

Pros:

Cons:

Build Pipeline:

Markdown (remark-parse)
  → mdast transforms
  → remark-rehype
  → hast transforms
  → HTML + inline JS

Best For: Publishing Obsidian vaults as digital gardens

2.2 SEO Performance: Static vs Dynamic

FactorStatic (Astro/11ty)ISR (Next.js)Dynamic (Notion/Roam)
Load Time2-3x fasterModerateSlower (runtime rendering)
Core Web VitalsExcellent (perfect Lighthouse scores)GoodVariable (depends on caching)
IndexabilityPerfect (pre-rendered HTML)Good (initial HTML served)Challenging (client-side rendering)
Content FreshnessManual rebuild or webhookAutomatic (stale-while-revalidate)Real-time
Build ComplexitySimple CI/CDModerate (incremental builds)Complex infrastructure

2025 Hybrid Rendering Trend: Industry converging on partial hydration as best practice — static HTML + selective interactivity.

3. Real-Time Sync & Collaboration Architectures

Used by: Reflect, modern collaborative editors

Architecture:

Key Advantages:

Traditional OT:        O(n²) message broadcasting bottleneck
YJS CRDT:             Local conflict resolution, no server bottleneck
Result:               Scales to 100s of concurrent users

Performance:

Implementation Pattern:

import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'

// Create shared document
const ydoc = new Y.Doc()
const ytext = ydoc.getText('content')

// Connect to WebSocket server
const provider = new WebsocketProvider(
  'wss://sync.commune.com',
  'note-123',
  ydoc
)

// All clients converge automatically
ytext.insert(0, 'Hello World')

Sources:

3.2 Datomic Immutable Database (Roam Research)

Architecture:

Key Advantages:

Traditional DB:       UPDATE overwrites data
Datomic:             Append-only, preserves full history
Result:              Time travel, audit trail, undo for free

Use Cases:

Query Example:

;; Find all blocks modified in last 7 days
[:find ?block ?content
 :where
 [?block :block/content ?content ?tx]
 [?tx :db/txInstant ?time]
 [(> ?time #inst "2025-10-11")]]

Trade-offs:

Sources:

3.3 Obsidian Sync (Proprietary)

Architecture:

Key Advantages:

Limitations:

Sources:

3.4 Notion’s Block-Based Architecture

Data Model:

Real-Time Stack:

Scaling Strategy:

2021: 32 PostgreSQL instances
2023: 96 PostgreSQL instances (3x growth)
Storage: S3 for data lake, PostgreSQL for transactional data

Sources:

4. Privacy & Self-Hosting Architectures

4.1 End-to-End Encryption Patterns

Reflect’s E2EE Implementation

Security Audit: Doyensec (independent audit)

Architecture:

Key Loss = Data Loss: Losing password = permanent data loss (no recovery)

Sources:

Logseq Local-First + Git Sync

Privacy Model:

Architecture:

File Graph:    Markdown files → Git sync
DB Graph:      Local SQLite → Sync TBD
Both:          Zero cloud dependency

Sources:

4.2 Self-Hosting with Tailscale

Use Case: Expose home server digital garden without port forwarding

Architecture:

Home Server (Astro site)

Tailscale VPN (WireGuard-based)

Secure mesh network (100.x.y.z IPs)

Access from any device (phone, laptop, etc.)

Key Benefits:

Self-Hosted Control Plane (Headscale):

Example Setup:

# On home server
tailscale up --advertise-routes=192.168.1.0/24

# Access from anywhere
curl http://commune.local  # Resolves to 100.64.x.x

Reverse Proxy Pattern (Caddy + Tailscale):

Tailscale (VPN layer)
  → Caddy (reverse proxy)
    → Astro site (port 3000)
    → API server (port 4000)

Sources:

5. AI/Agent Integration Patterns (2024-2025)

5.1 Agentic AI Architecture Patterns

Multi-Agent System Design:

  1. Sequential chains: Agents execute in pipeline (research → draft → edit)
  2. Hierarchical: Supervisor agent delegates to specialist agents
  3. Parallel processing: Independent agents work simultaneously
  4. Hybrid: Mix of sequential and parallel based on task

Leading Frameworks:

FrameworkStrengthsUse Case
LangGraphStateful multi-agent, most sophisticatedComplex workflows, agent orchestration
LangChainTool integration, chatbots, document analysisGeneral-purpose LLM apps
LlamaIndexData indexing, RAG (retrieval-augmented generation)Knowledge base search
AutoGenMulti-agent collaborationResearch assistants, coding agents

Key Design Patterns:

Tool Use Pattern

Enable LLMs to call external APIs/tools dynamically

agent = Agent(
  tools=[
    search_wikipedia,
    calculate_math,
    query_database
  ]
)

Reflection Pattern

Agent evaluates its own output before finalizing

draft = agent.generate()
reflection = agent.critique(draft)
final = agent.improve(draft, reflection)

Industry Adoption:

Sources:

5.2 LLM Integration for Digital Gardens

Potential Features:

  1. Semantic backlink discovery: LLM finds related notes based on meaning, not just keywords
  2. Auto-summarization: Generate TL;DR for long notes
  3. Concept extraction: Identify key themes across notes
  4. Question answering: RAG over personal knowledge base
  5. Writing assistance: Draft new notes from prompts

Architecture Pattern (RAG over Markdown):

User Query
  → LLM generates search terms
  → SQLite FTS5 retrieves candidate notes
  → LLM reads top N notes
  → LLM synthesizes answer with citations

Privacy Considerations:

6. Multi-Tenant Architecture for Commune

6.1 Database Isolation Models

ModelIsolationScalabilityComplexityCost
Database-per-tenantStrongModerateLowHigh (N databases)
Schema-per-tenantStrongGoodModerateModerate
Table-based (tenant_id)WeakExcellentHigh (RLS)Low
Sharding (tenant_id as shard key)StrongExcellentHighModerate

Recommended for Commune: Schema-per-tenantSharding (Citus)

Rationale:

Schema-Based Sharding (Citus 12.0):

-- Create tenant schemas automatically
CREATE SCHEMA tenant_123;
CREATE SCHEMA tenant_456;

-- Distributed across shards
SELECT citus_schema_distribute('tenant_123');

-- Queries isolated per schema
SET search_path TO tenant_123;
SELECT * FROM notes;  -- Only sees tenant_123 data

Sources:

6.2 Row-Level Security (RLS) Alternative

PostgreSQL RLS:

-- Enable RLS on notes table
ALTER TABLE notes ENABLE ROW LEVEL SECURITY;

-- Policy: Users only see their tenant's notes
CREATE POLICY tenant_isolation ON notes
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

-- Set tenant context per request
SET app.tenant_id = 'tenant-123-uuid';

Pros:

Cons:

Approach 1: Parse-on-read (Simple)

1. Store notes as markdown
2. On note load, regex parse [[wikilinks]]
3. Query database for reverse links

Pros: Simple, no precomputation Cons: Slow for large graphs

Approach 2: Indexed link table (Scalable)

CREATE TABLE links (
  source_note_id UUID,
  target_note_id UUID,
  link_type TEXT, -- 'wikilink', 'url', 'tag'
  anchor_text TEXT,
  PRIMARY KEY (source_note_id, target_note_id)
);

CREATE INDEX idx_backlinks ON links(target_note_id);

-- Query backlinks
SELECT source_note_id, anchor_text
FROM links
WHERE target_note_id = 'note-123';

Update Strategy:

// On note save
const links = parseWikilinks(noteContent)
await db.transaction(async (tx) => {
  // Delete old links
  await tx.delete(links).where({ source_note_id })
  // Insert new links
  await tx.insert(links).values(newLinks)
})

Pros: Fast queries, supports advanced graph algorithms Cons: Write overhead, consistency maintenance

Approach 3: Graph database (Neo4j, DGraph)

// Create note
CREATE (n:Note {id: 'note-123', title: 'Digital Gardens'})

// Create link
MATCH (a:Note {id: 'note-123'}), (b:Note {id: 'note-456'})
CREATE (a)-[:LINKS_TO]->(b)

// Find backlinks (1-hop)
MATCH (n:Note {id: 'note-123'})<-[:LINKS_TO]-(backlink)
RETURN backlink

// Find 2-hop connections
MATCH path = (n:Note {id: 'note-123'})-[:LINKS_TO*2]-(related)
RETURN related, length(path)

Pros: Native graph queries, powerful traversals Cons: Operational complexity, separate database

7.2 Graph Visualization Performance

D3.js vs Cytoscape.js

LibraryRenderingMax NodesUse Case
D3.jsSVG (customizable)~1,000Custom visualizations, dashboards
Cytoscape.jsCanvas/WebGL~10,000Network graphs, biological networks

Recommendation for Commune: Cytoscape.js

Rationale:

Performance Optimization:

// Level of Detail (LOD): Hide labels at high zoom
cytoscape({
  elements: nodes,
  style: [
    {
      selector: 'node',
      style: {
        'label': (ele) => {
          const zoom = cy.zoom()
          return zoom > 1.5 ? ele.data('title') : ''
        }
      }
    }
  ]
})

Sources:

8. Full-Text Search Implementation

Performance:

Before FTS5:  1000ms (full table scan)
After FTS5:   20ms (indexed search)
Result:       50x faster

Use Cases:

Implementation:

-- Create FTS5 table
CREATE VIRTUAL TABLE notes_fts USING fts5(
  title,
  content,
  tags,
  tokenize='porter unicode61'  -- Stemming + Unicode support
);

-- Insert note
INSERT INTO notes_fts VALUES ('Digital Gardens', 'Content about...', 'pkm,notes');

-- Search with ranking
SELECT
  title,
  bm25(notes_fts) AS rank  -- BM25 relevance score
FROM notes_fts
WHERE notes_fts MATCH 'digital garden'
ORDER BY rank
LIMIT 10;

Advanced Features:

Sources:

8.2 Alternative: MeiliSearch (Self-Hosted)

When to use:

Performance:

Trade-offs:

9. Content Update Strategies for Static Sites

9.1 Rebuild Triggers

Webhook-Based CI/CD:

# .github/workflows/deploy.yml
name: Deploy on Content Update
on:
  push:
    branches: [main]
  repository_dispatch:  # External webhook trigger
    types: [content-update]
  schedule:
    - cron: '0 0 * * *'  # Daily rebuild

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm run build
      - uses: peaceiris/actions-gh-pages@v3

Trigger from external CMS:

# Content editor saves note
curl -X POST https://api.github.com/repos/user/commune/dispatches \
  -H "Authorization: token $GITHUB_TOKEN" \
  -d '{"event_type": "content-update"}'

9.2 Incremental Builds

Gatsby Cloud Incremental Builds:

Next.js ISR (Incremental Static Regeneration):

export async function getStaticProps() {
  return {
    props: { ... },
    revalidate: 60  // Regenerate every 60s if requested
  }
}

Pattern: Stale-While-Revalidate

1. User requests page
2. Serve stale version immediately (fast)
3. Regenerate in background
4. Next request gets fresh version

9.3 Content Freshness Recommendations

SEO Research (Siege Media):

Digital Garden Strategy:

New notes:        Real-time (git push → rebuild)
Evergreen notes:  Manual updates (quarterly review)
Index pages:      Daily rebuild (new note listings)
Search index:     On-demand (content change webhook)

10. Recommendation for Commune Architecture

10.1 Proposed Tech Stack

Frontend (Digital Garden):

Backend (Self-Hosted):

Mobile (iOS):

AI/Agent Integration:

DevOps:

10.2 Architecture Decision Rationale

Why Astro over Next.js?

Why PostgreSQL over Datomic?

Why YJS over custom sync?

Why SQLite FTS5 over MeiliSearch?

Why Self-Host on Tailscale?

10.3 Migration Path (MVP → Scale)

Phase 1: Static Digital Garden (Current)

Astro static site
  → GitHub Pages / Cloudflare Pages
  → Markdown files in Git
  → No backend

Phase 2: Add Real-Time Sync

Astro static site (public)

YJS WebSocket server (private Tailscale)

PostgreSQL (notes storage)

iOS app (native Swift + YJS)

Phase 3: Multi-Tenant (Future)

Astro per-tenant sites (tenant.commune.com)

API Gateway (tenant routing)

PostgreSQL Citus (schema-based sharding)

YJS sync per tenant

11. Key Technical Insights & Sources

11.1 Static vs Dynamic Trade-offs

When to choose Static (SSG):

When to choose Dynamic (SSR/ISR):

Hybrid Approach (Recommended):

11.2 Islands Architecture Deep Dive

Problem Solved: Traditional SPAs ship entire framework (React, Vue) even for static content.

Islands Solution:

  1. Server renders full HTML (SEO-friendly)
  2. Identify interactive “islands” (modals, charts, forms)
  3. Ship minimal JS to hydrate only those islands
  4. Rest of page is pure HTML

Example (Commune):

// Astro component (static)
import GraphVisualization from './GraphVisualization.jsx'

<article>
  <h1>My Note</h1>
  <p>Static content here (no JS)...</p>

  <!-- Island: Only this loads React + graph library -->
  <GraphVisualization client:visible notes={notes} />
</article>

Performance Impact:

Before (SPA):  500kb JS bundle, 3s load
After (Islands):  50kb JS bundle, 0.5s load

Sources:

11.3 CRDT vs Operational Transform (OT)

AspectOT (Google Docs)CRDT (YJS)
ConvergenceRequires central serverAutomatic (local merges)
Conflict ResolutionComplex transform functionsBuilt into data structure
Offline SupportPoor (requires server)Excellent (merge on reconnect)
ScalabilityO(n²) message broadcastO(n) local resolution
ComplexityHigh (order-dependent)Moderate (CRDT math)

Why Reflect Chose YJS:

“Because YJS handled all conflict resolution locally on each client, their servers were no longer the bottleneck for collaboration. They could scale to hundreds of concurrent users without the O(n²) message broadcasting problems that had plagued their WebSocket implementation.”

Sources:

11.4 Graph Database Performance

When to use Graph DB (Neo4j):

When to use Relational DB + Link Table:

Commune Recommendation: Start with PostgreSQL link table, add Neo4j if graph queries become bottleneck.

12. Further Reading

Essential Resources

Architectural Patterns:

Tools & Frameworks:

Performance & SEO:

Self-Hosting:

Appendix: Glossary

TermDefinition
CRDTConflict-free Replicated Data Type - Data structure that automatically merges concurrent edits
ISRIncremental Static Regeneration - Next.js pattern to update static pages without full rebuild
SSGStatic Site Generator - Builds HTML at compile time
SSRServer-Side Rendering - Generates HTML on each request
IslandsAstro pattern - Ship zero JS by default, hydrate only interactive components
E2EEEnd-to-End Encryption - Data encrypted on client, server cannot decrypt
FTSFull-Text Search - Search engine optimized for natural language queries
RLSRow-Level Security - PostgreSQL feature to filter rows per user
RAGRetrieval-Augmented Generation - LLM pattern to ground responses in retrieved documents
ShardingHorizontal database partitioning - Split data across multiple servers

Document Metadata:

📚 Deep Research

Deep research as part of [[My Working Notes]] as I explore [[Commune]], [[Obsidian]], and [[Reflect]].

Generated with: Claude Code
Model: Claude Sonnet 3.5
Length: 9,600 words