Self-hosted service
shuaiplus/inkstone avatar
shuaiplus/inkstone

Inkstone: a Markdown notebook that lives inside your Cloudflare account

A self-hosted Markdown notebook that runs entirely on Cloudflare Workers.

816 stars506 forksTypeScriptNOASSERTION

At a glance

What is it?
Inkstone is a self-hosted, browser-based Markdown notebook deployed as a Cloudflare Worker, with D1, R2 or KV, Durable Objects and optional Workers AI behind it. It is a good fit for people who want plain-text notes and control of the runtime, and a poor fit for anyone who does not want to operate Cloudflare infrastructure.
Who is it for?
Adopt Inkstone if you already run Cloudflare Workers and want your notes to stay plain Markdown in storage you control; skip it if you want a single binary on your own server or cannot accept the LGPL-3.0-only licence and the unresolved NOASSERTION metadata. Before committing, run npm run dev:kv locally, check the deploy command against the R2 versus KV binding you intend to use, and confirm in the repository that the licence file says LGPL-3.0-only.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

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

DEEP OPEN-SOURCE ANALYSIS

The problem Inkstone picks, and the people it picks it for

Most hosted note apps hold your text in a format you cannot read without them. Inkstone takes the opposite position: notes stay plain Markdown, and the application is a layer on top. The README states the deployer retains control of the database, attachments and runtime environment. That sentence is the whole pitch, and it also defines the audience. This is for someone who already has a Cloudflare account, is comfortable with Workers and Wrangler, and wants a notebook with backlinks, tags, search and offline editing without renting that capability from a vendor. The starter notes are a small signal of intent: every new account receives two notes, one in Chinese and one in English, and the browser-only demo reuses the same content rather than shipping separate demo data. The project also targets bilingual users directly, with Simplified Chinese and English locale resources and an npm run i18n:check script that verifies parity between them. If you write in one language and never touch infrastructure, Inkstone's advantages are mostly invisible to you.

What actually runs when you open a notebook

The architecture is split three ways in the repository layout: src/client holds the React interface, editor, preview and local state; src/shared holds types, limits, locale resources and Markdown utilities; src/worker holds the Hono API, authentication, D1 access, sync, sharing and backups. Storage is deliberately separated by workload. Cloudflare D1 keeps accounts, notes, folders, tags, settings, versions, shares, lexical indexes, per-account AI embeddings and background indexing queues. Attachments and uploaded avatars go to R2 or Workers KV through the FILES or FILES_KV binding. A separate OAUTH_KV namespace holds OAuth client registrations, authorization codes, tokens and grants, and the README is explicit that note bodies are not stored there. Two Durable Objects do narrow jobs: SyncHub pushes realtime change notifications between active clients, and CredentialVault isolates the key used to encrypt backup credentials. The browser keeps an IndexedDB cache and a queue of pending offline writes. That split is the reason the project can offer offline editing and realtime sync at the same time, and it is also why a partial deployment (say, D1 without the Durable Objects) is not really a supported configuration.

Offline writes, conflicts, and what happens when two tabs disagree

The reliability row of the feature table is the most interesting part of the README, and the most under-explained. It lists an offline write queue, optimistic concurrency control, immediate local mutations with rollback, stale-sync protection, conflict copies, realtime notifications and an elected-tab polling fallback. Read together, those items describe a system that assumes the network will fail and that two clients will edit the same note. The chosen resolution is conflict copies rather than a merge algorithm, which is a defensible call for a personal notebook: a duplicated note is annoying, a silently merged paragraph is worse. Optimistic concurrency control means writes carry a revision, and the MCP section confirms the same idea on the API side with revision-safe writes. The elected-tab polling fallback suggests SyncHub is not treated as the only delivery path, which matters because a Durable Object connection can drop without the user noticing. What the README does not describe is how conflicts are surfaced in the interface, or how long the offline queue survives a browser restart. Those are the questions to answer by reading the client source before you trust the notebook on a plane.

Deploying it: fork, connect, pick a binding

Deployment is aimed at people who would rather click through a dashboard than run a server. The README's steps are: fork the repository to your GitHub account, open the Cloudflare Workers and Pages creation flow, choose Continue with GitHub and select the fork. For R2 mode, set the build command to npm run build and the deploy command to npm run deploy. For KV mode, change the deploy command to npm run deploy:kv. After the deployment completes, open the generated Workers URL. Local work uses the same scripts: npm run dev starts the local Worker and client, npm run dev:kv starts locally with the KV attachment configuration, and npm run dev:demo starts the reset-on-refresh browser-only demo. Verification is scripted rather than manual: npm run typecheck, npm run test:unit, npm run i18n:check, npm run comments:check, npm run build. There is also npm run test:e2e, which exercises the API against a running disposable local instance at http://localhost:7712, and the README warns to run it only against a fresh local state dedicated to testing because the script creates, changes and deletes data. Existing databases are upgraded through versioned, idempotent migrations, and the README tells you to keep a current backup before updating any self-hosted deployment. That is the right instruction and also a hint about where the risk sits.

MCP, API keys, and the permission model you have to reason about

Inkstone exposes a private remote MCP server, which is the feature most likely to change how you use the notebook and the one most likely to be misconfigured. The README lists OAuth 2.1 with PKCE, revocable ink_... API keys, standard search and fetch tools, bounded reads, revision-safe writes, a separate trash permission, and per-account grant management. The design is conservative in the right places: reads are bounded, writes check revisions, and deleting to trash is a distinct permission rather than an implied consequence of write access. That last point is unusual and worth noting, because many integrations treat delete as part of the write scope. The practical consequence is that an MCP client can be granted read and write without being able to empty your trash, and grants can be revoked per account. What the README does not give is a worked example of the OAuth flow or a list of which MCP clients have been tried. If you plan to connect an assistant to your notebook, budget time for that integration rather than assuming it is a copy-paste step.

Backups, exports, and the credentials that are deliberately missing

Portability is handled in three ways. JSON export preserves legacy structured notebook data for re-import. ZIP export and remote backups share one verified Markdown snapshot format that includes readable notes, archived notes, trashed notes, attachments and a completion marker. Remote targets are WebDAV or S3-compatible storage, configured per target, run manually or on a schedule, with duplicate attachment content stored only once inside each snapshot. Restoring a large backup works by selecting the backup folder rather than loading one complete archive into memory, which is the detail that decides whether this is usable for a notebook with years of attachments. The README also states what is excluded from exports: login passwords, active sessions, share passwords and backup-service credentials. That exclusion is correct from a security standpoint and it means a restored instance is not a full clone. You will re-authenticate, re-create shares and re-enter backup credentials after a restore. The CredentialVault Durable Object exists precisely because those backup credentials need a key that is not sitting next to the data they protect.

Where Inkstone is the wrong tool, and what to compare it against

The clearest limitation is environmental. Inkstone is not a program you install on a VPS. It is a Cloudflare Worker with D1, R2 or KV, Durable Objects and optionally Workers AI behind it, and the README's own deployment path runs through the Cloudflare dashboard and a GitHub fork. If your requirement is a single binary on your own hardware, or a host that is not Cloudflare, this project does not meet it, and no amount of configuration changes that. The optional semantic search makes the dependency sharper: it uses Workers AI embeddings, and the README notes that deployments where the AI binding is unavailable continue to use lexical search. So semantic search is a feature you may simply not have, depending on your account. The natural alternative is Obsidian, whose difference in approach is worth stating precisely. Obsidian is a local application that reads a folder of Markdown files on your machine and syncs through whatever you point at it; Inkstone is a server application whose Markdown lives in D1 and whose clients are browsers. Obsidian keeps working with no network and no account. Inkstone gives you a shared, always-available notebook with server-side full-text search, public share links and MCP access, at the cost of a runtime you must operate. Pick based on whether you want a folder or a service. A second consideration is licence. The README links to LICENSE and labels it LGPL-3.0-only, but the repository metadata reports NOASSERTION, meaning the automated licence detection did not resolve it. Those two signals disagree, and the discrepancy should be settled by reading the LICENSE file itself rather than by trusting either label. LGPL-3.0-only is a copyleft licence with specific obligations if you distribute modified versions; that is a question for your own legal review, not something to infer from a badge.

Maintenance cost and what to check before you commit

The upgrade story is documented and low-drama: versioned, idempotent migrations run automatically, and when a newer stable release is available the owner receives a reminder without interrupting regular members. Releases arrive at a steady pace, with v0.5.0, v0.6.0 and v0.7.0 all landing in August 2026 according to the release list. That cadence means you are tracking a moving project, and the README's instruction to keep a current backup before updating is the real maintenance cost: a self-hosted deployment here is something you update deliberately, with a snapshot in hand, not something that patches itself. The verification scripts are your safety net, but note that npm run test:e2e writes and deletes data against http://localhost:7712, so it belongs in a disposable environment and nowhere else. Before adopting, confirm three things in the repository rather than in this article. First, the actual licence text, because the LGPL-3.0-only label and the NOASSERTION metadata do not agree. Second, whether your Cloudflare account can bind Durable Objects and, if you want semantic search, Workers AI. Third, the R2 versus KV decision, since it changes the deploy command to npm run deploy:kv and the binding to FILES_KV. Get those three right and the rest of the deployment is a build command and a URL.

Editorial conclusion

Adopt Inkstone if you already run Cloudflare Workers and want your notes to stay plain Markdown in storage you control; skip it if you want a single binary on your own server or cannot accept the LGPL-3.0-only licence and the unresolved NOASSERTION metadata. Before committing, run npm run dev:kv locally, check the deploy command against the R2 versus KV binding you intend to use, and confirm in the repository that the licence file says LGPL-3.0-only.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. shuaiplus/inkstone on GitHub
Community notes

Community notes