Skip to content

Latest commit

 

History

21,173 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Epicenter

Epicenter

Local-first apps over a store you own.

An app's whole data set is one CRDT document on your machine, complete enough to work with the network off. Sign in on a second device and the two converge. No server holds the only copy, and no app owns your storage.

Honeycrisp, a local-first notes app, is the app built on it today.

Run the apps freely under AGPL-3.0; build on the developer toolkit freely under MIT. What that means.

GitHub stars Apps license: AGPL-3.0 Toolkit license: MIT Discord

The Store | Toolkit | Status | Trust | Repo Map | Development | License


The Store

The hard problem with local-first apps is synchronization. If each device has its own SQLite file, how do you keep them in sync?

Epicenter's answer: an application is one Yjs document, replayed in full before any handle exists, and the surface over it is synchronous. A read is a property access, not a round trip, so nothing is awaited and nothing needs cache invalidation or race protection.

import { openDevice } from '@epicenter/data/browser';
import { defineTable, defineWorkspace } from '@epicenter/workspace';

const notes = defineTable({
  title: 'string',
  pinned: 'boolean',
  folderId: 'string|null = null',
});

const notesWorkspace = defineWorkspace({
  namespace: 'com.example.notes',
  tables: { notes },
});

// Every verb returns a Result; the error arm is elided here for length.
const { data: db } = await openDevice(notesWorkspace);  // the only await

db.tables.notes.create({ title: 'Hello', pinned: false }, { document: ['body'] });

const listed = db.tables.notes.list();          // synchronous: { rows, nonconforming }
const stop = db.tables.notes.subscribe(read);   // fires with the row ids a commit touched

A workspace is one application's declaration of its durable data: pure JSON, arktype expression strings, no storage and no lifecycle of its own. It is release-local and never migrates your data. A row it cannot read is reported beside the rows it can, with the reason and the raw values intact, and an ordinary write repairs it.

Prose merges per character: a row is allocated a document container when it is created, and db.tables.notes.document(id)?.get('body') hands an editor a live type to bind to. Epicenter never looks inside it.

Sync is one Cloudflare Durable Object per (account, application). Being signed in on two devices is the entire sharing model: nothing is paired, invited, or approved.

Read the data package docs | What it replaced, and why

Build With The Toolkit

The developer toolkit is MIT: build anything on it, including closed-source and commercial products, and you own what you build, with no obligation back to Epicenter. @epicenter/data, @epicenter/workspace, and @epicenter/ui are the packages meant to leave this repo. They are pre-1.0 and tuned for our own apps, so treat them as fork-and-own rather than a stability-guaranteed SDK for now.

Status

There is one runtime: a desktop SPA in a WebView, over a store the client owns. A host serves bundles and brokers credentials; it owns no application data. A hosted web runtime with a host-owned replica is refused, and so are third-party installed apps, for now.

Honeycrisp is the app running on the store, and its README is the worked example.

Whispering, vocab, skills, and the Epicenter host do not compile right now. The superseded data stack was deleted before they were migrated, deliberately: the new store has no row-document HTTP path and no multi-process observation carrier, so there was nowhere for them to move until those refusals landed. Data they held on the old stack is accepted as lost; there is no importer.

Matter edits user-owned Markdown folders directly and keeps a disposable matter.sqlite query mirror beside them. Local Books and Local Mail are headless mirrors that pull a hosted account into local SQLite. Those three do not use the store.

Trust Boundaries

Pick the trust model you want.

Path What leaves your device
Signed out Nothing. The store is complete on the machine it opened on, and every read comes from a document already in memory.
Signed in Your application's document, as opaque update bytes, to one authority per account.
Hosted Epicenter That authority is ours, along with account and session data and any hosted feature you enable.
Self-hosted instance You control the server, secrets, deployment, and infrastructure boundary.
A provider an app calls Whatever that app sends it: transcript text to an LLM, audio to a transcription provider. Epicenter servers are not in that path.

Signed-in sync sends your data to a trusted server that reads it in plaintext. On hosted Epicenter the authority is ours, so that data sits inside our trust boundary; self-hosting puts it on infrastructure you control, so Epicenter never holds it. See the trust model for the details, including where this is heading with the anchor.

Repo Map

Apps

App Status Notes
Honeycrisp Runs on the store Local-first notes. Folders and notes are rows; a note's prose is a rich-text type inside its row.
Matter Runs, separately Typed grid over user-owned Markdown folders. It edits ordinary .md files directly; matter.sqlite is a disposable query mirror.
Local Books, Local Mail Run, separately Headless CLI mirrors that pull QuickBooks and Gmail into local SQLite.
API Hosted infrastructure Personal cloud Worker. Owns the store authority binding, hosted-only billing, and the dashboard.
Self-host Reference deployable Community-supported single-partition instance without hosted billing.
Whispering, vocab, skills, Epicenter Do not compile Awaiting migration onto the store. See Status.
Other app folders Research and prototypes Useful history and experiments, not the current product lineup.

Packages

These packages carry the main architecture.

Package Role License
@epicenter/data The store: one Yjs document per application, a synchronous surface over it, and the transport that carries it. MIT
@epicenter/workspace The inert workspace declaration vocabulary: arktype JSON, row addresses, and nonconformance. MIT
@epicenter/sqlite Neutral embedded-SQLite driver with Browser, Bun, and Durable Object adapters. It owns no product schema. MIT
@epicenter/sync The WebSocket subprotocol vocabulary both halves of a handshake must agree on. MIT
@epicenter/ui Shared Svelte component library used by multiple apps. MIT
@epicenter/server Shared Hono server library composed by the hosted API and the self-host reference deployable. AGPL-3.0-or-later

Architecture

The server side is split into one shared library and two deployable folders:

packages/server
  shared Hono library
  route composition for auth, sessions, store sync, blobs,
  and provider-backed inference and transcription

apps/api
  hosted personal Cloudflare Worker
  composes packages/server with a Better Auth principal resolver
  owns hosted-only dashboard and billing code

apps/self-host
  self-hosted single-partition instance reference deployable
  composes packages/server with the instance principal resolver
  community-supported
  no hosted billing surface

Full architecture walkthrough | Trust model

Development

Use Bun in this repo.

git clone https://github.com/EpicenterHQ/epicenter.git
cd epicenter
bun install

Every app starts from the repo root. bun dev:<app> runs every process the app needs; for apps that talk to the hosted API, that includes the API worker on localhost:8787. bun dev:<app>:ui runs the app's frontend alone when that split exists, and bun dev:api runs just the backend. Bare bun dev is bun dev:honeycrisp, and bun run with no arguments lists every target.

Command Starts App port
bun dev:honeycrisp API + Honeycrisp desktop 5175
bun dev:honeycrisp:ui Honeycrisp in the browser, no API or Tauri shell 5175
bun dev:api Hosted API worker alone 8787
bun dev:api-dashboard API + dashboard UI 5178
bun dev:landing Landing site, standalone 4321
bun dev:matter Matter desktop, standalone 5180
bun dev:posthog-reverse-proxy PostHog reverse proxy Worker wrangler default
bun dev:self-host Self-host server (needs INSTANCE_TOKEN) 8787

bun dev:whispering, bun dev:vocab, bun dev:skills, and bun dev:epicenter still exist, but those apps do not compile until they are migrated onto the store.

The API needs local Postgres and Infisical; see apps/api/README.md. Rust is needed for Tauri apps such as Honeycrisp and Matter. Local Books and Local Mail run their own multi-process dev flows; their READMEs document them.

bun run check is the gate. It runs lint, typecheck, every workspace test, and the structural checks, and it is the same gate CI runs, so a green local run predicts a green pull request. Formatting is handled separately by the autofix workflow.

bun run check

Run the pieces on their own while you work:

bun run format          # rewrite formatting (CI autofixes this for you)
bun run lint:check
bun run typecheck
bun run test
bun run check:structure # doc paths, catalog pins, API paths, licenses, UI boundary, boot purity

Two checks sit outside the gate on purpose. bun run check:doc-hygiene flags specs and ADRs that time has made stale, so it belongs to review rather than to merge. bun run smoke:local boots the API against local services.

Design Notes

Durable decisions and their reasoning live in docs/adr/. Specs in specs/ are in-flight design scaffolding rather than current truth; when a spec and an ADR disagree, the ADR wins. Start with docs/README.md.

Contributing

Contributions are welcome. Good entry points are docs, local-first infrastructure, Svelte interfaces, migrating a broken app onto the store, and small changes that make the repo easier to understand.

Read the Contributing Guide

Contributors coordinate in Discord.

License

Epicenter uses a two-tier split by how you use the code:

  • MIT for code you build with: the toolkit roots (@epicenter/data, @epicenter/workspace, @epicenter/ui) and the toolkit-internal contracts they carry (@epicenter/field, @epicenter/sqlite, @epicenter/sync, @epicenter/identity, @epicenter/agent-protocol, @epicenter/chat).
  • AGPL-3.0 or later for code we ship or run: every app, the shared server library, and the rest of the internal packages.
  • There is no proprietary tier today. Revenue is intended to come from hosting and services, not from selling closed licenses.

Every dependency of the toolkit packages is MIT-compatible, enforced by bun run check:licenses. The license split follows the same broad pattern as Plausible and PostHog for hosted open-source services, and Yjs for MIT core libraries with copyleft server pieces.

See the root LICENSE, FINANCIAL_SUSTAINABILITY.md, and the licensing strategy for the full model.


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

Your data outlives the app that wrote it. Local-first, open source, built on Yjs.

About

Open-source, local-first apps.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4.8k stars

Watchers

14 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages