Open-source project
0xGG/crossnote-app avatar
0xGG/crossnote-app

Crossnote: a local-first markdown notebook that syncs through in-browser git

📝 Local-first markdown notebook - notes live in your browser or local folders as plain markdown files, synced with in-browser git. No server, no account, works offline.

1,088 stars80 forksTypeScriptAGPL-3.0

At a glance

What is it?
Crossnote stores notes as plain markdown in the browser or in a local folder and pushes them to any git remote using isomorphic-git, with no account and no dedicated server. The catch is that the original backend is gone and the current maintainer is rebuilding the app from scratch.
Who is it for?
Adopt the legacy-final tag or the archive/develop branch if you want a working local-first markdown notebook today and you can accept a codebase whose original author has stopped maintaining it.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Crossnote solves: markdown notes that stay files

Most note apps ask you to trust a vendor's database. Crossnote takes the opposite position. The README describes it as a local-first web app where notes live in your browser, sync with any git repository directly, and remain plain markdown files with no dedicated server in the middle. That combination is the point: you get a browser editor, but the storage format is a folder of .md files you could read with cat, and the sync transport is git rather than a proprietary API. The audience is narrow and specific. It suits engineers and writers who already keep notes in a git repo, who dislike the idea of a note store they cannot grep, and who are willing to run a build or host a static site. It does not suit anyone who wants a hosted service with a login, a mobile app that just works, or shared notebooks with non-technical collaborators. There is no account system to fall back on, and the README is explicit that the original backend has been shut down.

How in-browser git sync actually works

The architecture is visible in package.json. The app is a Vite-built React 18 single-page application. Persistence comes from PouchDB and pouchdb-find, which store notebook data in the browser, and from @isomorphic-git/lightning-fs, a filesystem implementation that gives isomorphic-git a place to write inside the browser. isomorphic-git is pinned at 1.3.1, so the git implementation is a JavaScript reimplementation running in the page rather than a call to a system git binary. Because a browser page cannot open a raw TCP connection to a git remote, sync goes through an HTTP CORS proxy. The .env.example file names the default, VITE_DEFAULT_CORS_PROXY=https://cors.isomorphic-git.org, and notes that deploying your own instance of isomorphic-git/cors-proxy is recommended for production deployments. That is the whole data flow: markdown in PouchDB or a local folder, a git commit built in the browser, an HTTPS push relayed by the proxy. The proxy sees your repository traffic, which is a real trust decision, not a footnote. The topics list also includes file-system-access-api, which is what lets the app write to a local folder instead of only browser storage. The UI around this is assembled from MUI, CodeMirror 5 for editing, flexlayout-react for panes, minisearch for in-app search, and i18next for translation.

Install and first run: pnpm, Vite, and a notebook in the browser

The repository ships no install instructions in the README, so the commands below come from package.json and .env.example. The engines field requires Node 22 or newer and packageManager pins pnpm 11.17.0. Note the predev and prebuild hooks: both run scripts/download-emoji-sheet.mjs before Vite starts, so the first run needs network access to fetch the emoji sheet.

bash
# Node >= 22 and pnpm 11.17.0 are required by package.json
corepack enable
pnpm install
pnpm dev

pnpm dev triggers predev, downloads the emoji sheet, then starts the Vite dev server. Vite prints a local URL, conventionally http://localhost:5173, and opening it should show the Crossnote notebook interface.

To point sync at your own proxy instead of the public one, copy the example environment file. Vite reads .env.local at build time.

bash
cp .env.example .env.local
# then edit VITE_DEFAULT_CORS_PROXY in .env.local

Setting VITE_DEFAULT_CORS_PROXY to an empty string makes no-proxy the default, and the .env.example comment adds that notebooks can still set a proxy in the UI. For a production bundle, pnpm build runs tsc --noEmit first and then vite build, so a type error stops the build before any output is written. If you only want the editor and not the sync layer, the app still works offline: the README states notes live in your browser as plain markdown files.

Where Crossnote gets in your way

The largest limitation is project status, and it is stated in the README rather than hidden. The original author, @shd101wyy, has moved on, the backend has been shut down, and @dongshunyao has taken over and is rebuilding the app from the ground up. The last push to the repository was on 2026-09-10. The most recent release is tagged legacy-final, described as the final version from the original author, dated 2026-07-28; before that the release list jumps back to v0.3.0 in 2021. So the code you can actually run today is the legacy line, and the code on main is a rebuild in progress. If you need a note app you can install and forget, that split is disqualifying. There are smaller constraints too. The CORS proxy is a required hop for sync against a remote that does not send permissive CORS headers, and the README's own advice is to self-host isomorphic-git/cors-proxy for production. isomorphic-git is pinned to 1.3.1, an old version, which limits how much git behaviour you should expect. The lint script carries --max-warnings 136, which suggests the maintainers are tolerating a backlog rather than enforcing a clean tree. And the browser storage path via PouchDB is not the same thing as a folder of files on disk: if you clear site data without a git remote configured, the notes go with it.

Crossnote against Obsidian and plain git plus an editor

The honest alternative for most people reading this is Obsidian, which is also local-first and also stores notes as markdown files on disk, but is a desktop application with a plugin ecosystem and a paid sync service rather than a browser app that pushes through a CORS proxy. The difference in approach is where the git integration lives. Crossnote builds git into the page using isomorphic-git and lightning-fs, so a commit and push happen inside the browser tab. Obsidian leaves git to the filesystem, so you use the system git binary via a plugin or a cron job, and there is no proxy in the path. A second alternative is to skip the app entirely: keep a directory of markdown files, edit them in your existing editor, and run git yourself. That costs you the notebook UI, the pane layout, the in-app search and the kanban view, but it removes the browser storage layer, the proxy and the pinned isomorphic-git version from your stack. Crossnote's case is strongest when you want the browser editor and you want git sync without installing anything on the machine you are reading on.

Licence and the cost of keeping a fork alive

Crossnote is licensed AGPL-3.0-or-later. The LICENSE file carries two copyright lines: 0xGG Team for 2020 and dongshunyao for 2026, which matches the handover described in the README. The practical consequence is that if you modify Crossnote and let other people use it over a network, the AGPL's network clause is the part to read carefully before you deploy. That is a pointer to the licence text, not legal advice; the AGPL is a copyleft licence and the obligations differ from MIT or Apache-2.0 in ways that matter for hosted deployments. On maintenance cost, the package.json tells you what a rebuild involves. Node 22 or newer is required, pnpm is pinned to 11.17.0, isomorphic-git is held at 1.3.1, and CodeMirror is still on major version 5 while MUI is on 9. Those are three different upgrade problems sitting in one dependency list. The test setup is real, at least: vitest for unit tests via pnpm test, and Playwright for end-to-end tests via pnpm test:e2e, which builds with Vite first. If you fork this, budget for the isomorphic-git upgrade and the CodeMirror 5 to 6 migration before you budget for features.

Editorial conclusion

Adopt the legacy-final tag or the archive/develop branch if you want a working local-first markdown notebook today and you can accept a codebase whose original author has stopped maintaining it. Do not adopt the current main branch as a production note store: the README states it is being rebuilt from the ground up, the backend is shut down, and the pnpm scripts target Node 22 with pnpm 11.17.0, so verify the build, the VITE_DEFAULT_CORS_PROXY setting and the AGPL-3.0-or-later obligations before you commit any real notes to it.

Frequently asked questions

Does Crossnote store my notes on a server?

No. The README describes it as local-first, with notes living in your browser and staying as plain markdown files. Sync happens directly against a git repository, and the original backend has been shut down.

What do I need to install to run Crossnote locally?

package.json requires Node 22 or newer and pins pnpm 11.17.0, then pnpm install followed by pnpm dev starts the Vite dev server. The predev hook also downloads an emoji sheet, so the first run needs network access.

Why does Crossnote need a CORS proxy for git sync?

.env.example sets VITE_DEFAULT_CORS_PROXY to https://cors.isomorphic-git.org and notes that deploying your own instance of isomorphic-git/cors-proxy is recommended for production. Setting the variable to an empty string makes no-proxy the default, and notebooks can still set a proxy in the UI.

Is Crossnote still maintained?

The README states the original author has moved on, the backend is shut down, and @dongshunyao has taken over to rebuild the app from the ground up. The last push was on 2026-09-10, and the most recent release is the legacy-final tag from the original author.

Official sources

  1. 0xGG/crossnote-app on GitHub
  2. Issues
  3. License: AGPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/0xgg-crossnote-app.svg)](https://hysenlabs.com/projects/0xgg-crossnote-app)