Skip to content

EpicenterHQ/epicenter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14,741 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Epicenter

Epicenter

Local-first, open-source apps

One folder of plain text and SQLite on your machine, synced across all your devices.
Grep it, query it, host it wherever you want.

GitHub stars License Discord macOS Windows Linux

AppsArchitecturePackagesFor DevelopersQuick StartContributingDiscord


What is Epicenter?

Epicenter is an ecosystem of open-source, local-first apps. Your notes, transcripts, and chat histories live in a single folder of plain text and SQLite on your machine. Every tool we build reads and writes to the same place. It's open, tweakable, and yours. Grep it, open it in Obsidian, version it with Git, host it wherever you want.

Under the hood, Yjs CRDTs are the single source of truth. They materialize down to SQLite (for fast queries) and markdown (for human-readable files). Sync happens over the Yjs protocol; the server is a relay, not an authority. It never sees your content.

The library that powers this, @epicenter/workspace, is something other developers can build on too. Define a typed schema, get CRDT-backed tables with multi-device sync handled for you.

Architecture

Epicenter has three different backend boundaries on purpose:

Domains split by public protocol role.
Deployables split by infrastructure and operational boundary.
Hono modules split by code composition boundary.

Hosted Epicenter is moving toward three public domains served by two deployables:

accounts.epicenter.so
  OAuth issuer, sign-in, consent, token issuance
  served by apps/server

sync.epicenter.so
  workspace identity, workspace sync, document sync
  served by apps/server

api.epicenter.so
  hosted Cloud APIs, billing, storage registry, dashboard
  served by apps/cloud

Inside each deployable, route groups are mountable Hono modules:

apps/server
  createAccountsRoutes()
  createSyncRoutes()

apps/cloud
  createCloudResourceRoutes()
  createDashboardRoutes()

This keeps the self-hostable server free of Postgres and billing dependencies, while still making accounts, sync, Cloud APIs, and dashboard routes easy to split later if their operational needs diverge.

                              ┌──────────────────────────────────┐
                              │         @epicenter/api           │
                              │   Cloudflare Workers + DO hub    │
                              │   auth · sync relay · AI chat    │
                              └──────────┬───────────────────────┘
                                         │ y-websocket protocol
                    ┌────────────────────┼──────────────────────┐
                    │                    │                      │
               ┌────▼──────┐      ┌─────▼───────┐      ┌──────▼──────┐
               │ Whispering│      │  Opensidian  │      │ Tab Manager │
               │  (Tauri)  │      │ (SvelteKit)  │      │  (WXT ext)  │
               └────┬──────┘      └─────┬────────┘      └──────┬──────┘
                    │                    │                      │
          ┌─────────┴────────────────────┴──────────────────────┘
          │              All apps share these layers:
          │
    ┌─────▼───────────────────────────────────────────────────────────┐
    │                     MIDDLEWARE / ADAPTERS                        │
    │  @epicenter/svelte    : Svelte integration, auth, persistence   │
    │  @epicenter/filesystem : POSIX file layer over Yjs              │
    │  @epicenter/skills    : skill/reference tables                  │
    │  @epicenter/ai        : LLM tool bridging                      │
    └─────────────────────────────┬───────────────────────────────────┘
                                  │
    ┌──────────────────────────────▼───────────────────────────────────┐
    │                           CORE                                   │
    │  @epicenter/workspace : typed schemas, Yjs CRDTs, extensions,   │
    │                         E2E encryption, lifecycle, materializers │
    │  @epicenter/sync      : protocol encoding/decoding, V2 updates  │
    │  @epicenter/constants : app URLs, versions, shared config       │
    │  @epicenter/ui        : shadcn-svelte component library         │
    │  @epicenter/cli       : TypeBox→yargs CLI, auth/session APIs    │
    └─────────────────────────────────────────────────────────────────┘

The dependency flow is strict: core has zero upward dependencies, middleware only reaches into core, and apps compose both. @epicenter/workspace is the gravitational center; every middleware package and most apps depend on it. The sync server is a relay, not an authority; it never sees your content because encryption happens client-side before anything leaves the device.

Full architecture walkthrough → · Encryption design →

Apps

Press shortcut, speak, get text. Desktop transcription that cuts out the middleman. Bring your own API key or run locally with Whisper C++.

Source · Install

Local-first note-taking with a built-in bash terminal, end-to-end encryption, and real-time sync. Your notes live in a CRDT-backed virtual filesystem.

Source · Try it

Browser extension side panel for managing tabs with workspace sync and AI chat that can call workspace tools with inline approval.

Source

Apple Notes-style local-first notes app. Folders, rich-text editing with ProseMirror, and collaborative sync via Yjs.

Source

The hub server. Auth, real-time sync via Durable Objects, and AI inference. Everything that needs a single authority across devices.

Source

Build your own

The @epicenter/workspace library makes it straightforward to build apps that share the same CRDT-backed data. Define a schema, get tables, add sync.

Also in the repo: Fuji (personal CMS), Zhongwen (Mandarin learning chat), Skills Editor (agent skill manager), Dashboard (billing UI), and Landing (public site).

Packages

Package Description License
@epicenter/workspace Core library. Typed schemas, Yjs CRDTs, extension builder, E2E encryption, materializers. Everything builds on this. MIT
@epicenter/sync Yjs sync protocol encoding/decoding. Dumb server, smart client; protocol framing is separate from transport. AGPL-3.0
@epicenter/ui shadcn-svelte component library shared across all apps. MIT
@epicenter/svelte Svelte 5 integration: persisted state, auth, workspace gate, TanStack Query helpers. MIT
@epicenter/filesystem POSIX-style virtual filesystem over Yjs workspace tables. mkdir, mv, rm, stat. MIT
@epicenter/skills Skill and reference tables for AI-enhanced workspace apps. MIT
@epicenter/ai Bridges workspace actions with LLM tool calling. MIT
@epicenter/cli The epicenter command. TypeBox schemas become CLI flags automatically. MIT
@epicenter/constants Shared URLs, ports, and version info across the monorepo. MIT

For Developers

The hard problem with local-first apps is synchronization. If each device has its own SQLite file, how do you keep them in sync? If each device has its own markdown folder, same question. We ended up using Yjs CRDTs as the single source of truth, then materializing that data down to SQLite (for fast SQL reads) and markdown (for human-readable files). Yjs handles the sync; SQLite and markdown handle the reads.

The @epicenter/workspace package wraps this into a single API. Define a schema, get CRDT-backed tables, attach providers to materialize to SQLite or markdown, and add sync when you're ready.

import { type } from 'arktype';
import * as Y from 'yjs';
import {
  attachIndexedDb,
  attachSync,
  attachTables,
  defineDocument,
  defineTable,
  toWsUrl,
} from '@epicenter/workspace';

const posts = defineTable(
  type({ id: 'string', title: 'string', published: 'boolean', _v: '1' }),
);

const blog = defineDocument((id: string) => {
  const ydoc = new Y.Doc({ guid: id });
  const tables = attachTables(ydoc, { posts });
  const idb = attachIndexedDb(ydoc);
  const sync = attachSync(ydoc, {
    url: (docId) => toWsUrl(`http://localhost:3913/rooms/${docId}`),
    waitFor: idb.whenLoaded,
  });

  return {
    id, ydoc, tables, idb, sync,
    [Symbol.dispose]() { ydoc.destroy(); },
  };
});

const workspace = await blog.load('epicenter.blog');
workspace.tables.posts.set({ id: '1', title: 'Hello', published: false, _v: 1 });

Each user gets their own database. Schema definitions are plain JSON, so they work with MCP and OpenAPI out of the box. Write to Yjs and SQLite updates; edit a markdown file and the CRDT merges it in.

Read the full workspace docs →

Where We're Headed

More apps are in progress. Each one shares the same workspace, so data flows between them without import/export. The @epicenter/workspace library handles the hard parts (schemas, CRDT sync, materialization), so each new app is mostly UI.

Epicenter Cloud will provide hosted sync for people who don't want to run their own server. Same model as Supabase selling hosted Postgres or Liveblocks selling hosted collaboration. Self-hosting is and will remain first-class. The sync server is open source under AGPL, and when you run it yourself, you control the encryption keys and trust boundary.

Quick Start

Install Whispering

brew install --cask whispering

Or download directly from GitHub Releases for macOS (.dmg), Windows (.msi), or Linux (.AppImage, .deb, .rpm).

Full installation guide →

Build from Source

# Prerequisites: Bun, local Postgres, and Infisical access for API secrets
git clone https://github.com/EpicenterHQ/epicenter.git
cd epicenter
bun install
bun dev

Root bun dev starts one local workflow: the API and Tab Manager. See apps/api/README.md for local Postgres and Infisical setup. Use bun run dev:api for only the local API, or bun run dev:tab-manager:ui for only the extension UI. App folders still support bun dev for focused local work on that app. Rust is only needed for Tauri apps like Whispering.

Troubleshooting

If things break after switching branches or pulling changes:

bun clean    # Clears caches and node_modules
bun install  # Reinstall dependencies

For a full reset including Rust build artifacts (~10GB, takes longer to rebuild):

bun nuke     # Clears everything including Rust target
bun install

You rarely need bun nuke. Cargo handles incremental builds well. Use bun clean first.

Contributing

We're looking for contributors who are passionate about open source, local-first software, or just want to build with Svelte and TypeScript.

Read the Contributing Guide →

Contributors coordinate in our Discord.

Tech Stack

Svelte 5 Tauri TypeScript Rust Yjs Cloudflare Workers Tailwind CSS

Design Decisions

We publish our implementation specs. These are the reasoning behind non-obvious architectural choices: alternatives considered, trade-offs made, and why we landed where we did.

Spec What it decided
Encrypted Workspace Storage XChaCha20-Poly1305 at the CRDT value level; server-managed keys with self-hosting as the trust boundary
Y-Sweet Persistence Architecture How Yjs documents persist and compact in Durable Objects
Simple Definition-First Workspace API The defineTabledefineDocument + attach* composition pattern
Resilient Client Architecture How workspace clients handle offline, reconnect, and extension failures
Migrate to @epicenter/sync Custom sync protocol replacing Y-Sweet with our own framing layer

All 112 implemented specs live in specs/.

License

Most packages and all apps are MIT. Use them however you want, no strings attached. The sync server (apps/api) and sync protocol (packages/sync) are AGPL-3.0, which means anyone hosting a modified version shares their changes. This follows the same pattern as Yjs (MIT core, AGPL y-redis), Liveblocks (Apache clients, AGPL server), and Bitwarden (GPL clients, AGPL server).

See FINANCIAL_SUSTAINABILITY.md for the full reasoning behind the split.


Contact: github@bradenwong.com | Discord | @braden_wong_

Local-first · CRDT · Own your data · Open source