Skip to content

About

Self-hosted end-to-end encrypted Obsidian sync server: gateway, web notes, sharing, Send to Kindle and an AI administrator

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Tabularium

English · 中文 · Lingua Latīna

Tabularium is a self-hosted server that synchronises note vaults with end-to-end encryption, together with the Obsidian plug-in and the web console that connect to it. Notes are encrypted on each device before they are uploaded; the server stores them in CouchDB and cannot read them. Registration is closed: an administrator creates the accounts.

The web pages also include web notes, which let you read and edit notes in the browser and share them on the public internet in several forms (viewable by anyone or only with a password, through a link that expires or one that does not), and you can send your notes to your Kindle. Sharing notes on the internet and sending them to Kindle are also available in the Obsidian plug-in.

Besides synchronisation, the server's web pages provide server administration, scheduled backups, and Tabularius, an AI administrator (the vault's clerk) that a vault owner can enable for one vault at a time. It works with several APIs (OpenAI, Anthropic, Google Gemini, DeepSeek) and with inference engines for locally deployed models (vLLM, Ollama, TensorRT-LLM); each recipe can set every parameter its provider's API accepts. You can also give the clerk RAG retrieval, if you run a RAGFlow server.

Features

Synchronisation

Synchronisation uses the CouchDB replication protocol. The client's sync core is @tabularium/sync-core in the plug-in repository, shared by the Obsidian plug-in, web notes and the command-line client.

  • Data model: each vault is a separate CouchDB database on the server, and each device keeps a local database of its own. Changes to files are written to the local database first and then replicated with the server in both directions. A note is stored as one metadata document (prefixed f:, listing the chunks it is made of) and a set of content chunks (prefixed h:, named after their content, so identical chunks are stored once); editing one passage transfers only the chunks that changed.
  • Live: clients follow the server's change feed (_changes as a continuous stream or long poll), so edits from other devices arrive within seconds. The plug-in can sync live, on a timer, or on events (start-up, save, switching files). Live sync can be paused per device: edits stay on the device and are uploaded on resume or with "Sync now".
  • Conflicts: when two devices edit the same note on different lines, the changes merge automatically; when they edit the same passage, every version is kept. The plug-in can be set to keep the newer version, and web notes show the versions side by side so you can keep one or edit a merged text. The plug-in's "Version history" context menu lists a note's versions on the device, to compare, copy or restore.
  • Gateway: clients connect to /sync/<vault ID>/, which the gateway maps to a physical database name generated by the server. It passes only the endpoints replication needs (the change feed, _revs_diff, _bulk_get, _bulk_docs, _local and so on), reads the body of every write, accepts only documents in the encrypted, path-obfuscated form, and rejects other entries one by one. Requests are rate-limited per device; long connections end after five minutes and the client reconnects.
  • Accounts and devices: administrators create accounts on the web; the initial password is changed at first sign-in. Each client (an Obsidian vault or a browser profile) receives its own device credential when it signs in, valid for 30 days and renewed automatically when fewer than 15 days remain; a device unused for 30 days expires. "Account → Devices" lists devices by client with their last sync time, and lets you rename or revoke them; revoking a device closes its sync connections at once.
  • Rebuilding a vault: to overwrite the server with one device's notes, the vault owner first authorises that device for a 15-minute window. The gateway turns the rebuild into a new physical database and switches to it, keeping the old one so you can switch back; the other devices then report "Vault locked", fetch again from the server, and the owner unlocks the vault.
  • Command-line client: the same sync core is also packaged as a command-line client, which can mirror a vault on the server as a folder of plain files for git auditing or scripts.

Encryption

  • Notes are encrypted on the device before they enter the sync database. The E2EE passphrase is stretched with PBKDF2-SHA256 (310,000 iterations) into a master key, HKDF derives a key for each chunk from it, and chunks are encrypted with AES-256-GCM. The PBKDF2 salt is stored with the vault on the server; the passphrase stays on each device.
  • Path obfuscation encrypts file paths as well, so document IDs and metadata on the server carry no file names or folder structure.
  • While connected to Tabularium, the plug-in enforces encryption (E2EE V2) and path obfuscation, and the server accepts only documents in that form, so the server holds ciphertext throughout. What the server sees is accounts, devices, request times and traffic.
  • The sync credential and passphrase in the plug-in's data.json are encrypted with a key kept only on the device. The connection is bound to the Obsidian vault it signed in from; if the plug-in folder is copied to another vault, the connection is cleared at start-up.
  • Enabling Tabularius for a vault is the step that gives the server that vault's passphrase (stored encrypted with the server key) and a plain-text copy; the page explains this before you enable it, and disabling it deletes them.

File sorting

Write a Collation.md at the vault root; the indented list in its body is the order of the file list:

- Project plan —— read the plan first
  - Requirements
  - ...
  - Archive
- Meeting notes
- Collation
  • One item per file or folder, indentation marks the folder it is in. Listed items come first in the listed order, the rest follow in their usual order, and ... marks where the unlisted items go (above, "Archive" always comes last).
  • Text after —— is a note for people; in reading view it folds into an ⓘ icon after the name. Items can be written as [[name]] to open them with a click; any other text in the note is free.
  • A subfolder can have its own Collation.md, which overrides the parent's rules for that folder.
  • The file list's "Sorting" context menu has "Move to the top", "Move to the bottom" (they rewrite the list) and "Show in Collation.md".
  • Sorting changes only the display order. The rule note syncs like any other note, and the Obsidian file list, the web notes file tree and the contents of shared folders all follow it.

Web notes

A note client in the browser, for reading and editing notes on a computer that has no Obsidian.

  • Synchronisation and decryption run in the browser, and each browser counts as a device. Notes are stored in the browser's private file system (OPFS) and IndexedDB, and the passphrase lives only in the page's memory. The first visit fetches the whole vault; after that only changes are synced.
  • Reading looks like Obsidian: callouts, task lists, tables, KaTeX formulas, ==highlights==, Mermaid diagrams, and code blocks coloured by language (more than sixty languages).
  • The editor has three modes, live rendering (like Obsidian's Live Preview), source and split preview. Selecting text brings up a formatting toolbar, and the "+" menu inserts tables, code blocks, Mermaid diagrams, formulas, callouts and attachments. Notes save two seconds after you stop typing, and the title shows "Saved locally", "Waiting to upload", "Synchronised" or "Conflict".
  • Create, rename, move and delete notes and folders; renaming or moving updates the wikilinks and Markdown links in other notes. Attachments can be dropped into the editor.
  • The browser caches the pages, so a vault fetched in full can be unlocked, read and edited offline; changes made offline upload once the connection returns.
  • "Clear local data" deletes the copy in this browser and revokes its device; signing out offers to clear it too.

Sharing

Share a note or a folder on the internet: visitors need no account and read it in the browser. "Share…" is in the "⋯" menu of web notes and in the Obsidian context menu.

  • Two kinds: an encrypted link (the default) is encrypted on the device before upload, the key lives only in the part of the link after #, and the server stores ciphertext. A public page is stored as plain text and can allow search engines to index it (the page then carries the title and a description of about 160 characters).
  • Link: https://<domain>/s/<address>, with an address you choose or a random one.
  • Expiry: 1 day, 7 days, 30 days, no expiry, or until a chosen date; on expiry the server deletes the shared content.
  • Access password: optional. For encrypted links the password takes part in decryption; for public pages the server checks it.
  • Linked notes and attachments: the dialog lists notes and attachments that the shared content refers to but that lie outside it, and you tick them level by level; embedded images and attachments are shared by default. Unticked references appear on the shared page as "No access", with their names, paths and link text hidden.
  • Folders: every note and attachment in the folder is shared, the contents are ordered by Collation.md, and each note has a stable address.
  • Automatic updates: about ten seconds after a file in the share is edited, created, renamed or deleted, the share is regenerated under the same link. The device that last saved the share does the updating; the share follows the note when it is renamed or moved, and is withdrawn when it is deleted.
  • No copying (optional): the shared page draws text, formulas, diagrams and images onto a canvas, and public pages are also transferred encrypted; selecting, copying, right-clicking, dragging, printing and the usual developer-tools shortcuts are blocked.
  • Display: shared pages use the sharer's own colour scheme and render like web notes, Mermaid diagrams included.
  • Management: "Account → Shares" lists all your shares with their visit counts, where you can change expiry, withdraw them or clean up expired ones; administrators manage everyone's shares under "Administration → Shares".

Sensitive and secret content

Select a password, token or account name in the editor and press "Sensitive" on the formatting toolbar, or run the command of the same name: invisible marker characters are placed around the text. Reading view shows it as normal text; in Obsidian's editing modes the marked text shows a tinted background and a small red eye, and the cursor steps over the markers in one move. Applying it again removes the mark; across several lines each line is marked separately and the Markdown syntax is kept.

The markers are three invisible mathematical operators from Unicode: U+2061 (function application), U+2062 (invisible times) and U+2063 (invisible separator). The opening marker is U+2061 U+2062 U+2063 in that order and the closing marker is the reverse, U+2063 U+2062 U+2061. A marked span stays within one line; without a closing marker it is hidden to the end of the line.

  • Sharing: "Desensitize" is on by default, and marked text is replaced with •••••• before it leaves the device (in code blocks too), so the server and visitors receive only the placeholder. The dialog shows how many places are affected.
  • Tabularius: marked text becomes [REDACTED] before it is sent to the model and is restored when a proposal writes the note back.
  • Send to Kindle: the original text is shown, without the marker characters.

Send to Kindle

The plug-in and web notes build an EPUB 3 from the notes on the device (title, a table of contents from second-level headings, PNG/JPEG/GIF images, code blocks, checked with EPUBCheck), and the server mails it to the user's Kindle address through the SMTP account the administrator configured. You can send one note, a folder (each subfolder becomes a part), or several selected items as one book each. The server checks the Kindle address's domain, the 50 MB limit and a daily count per user, and records only the time, file name, size and result of each send.

Obsidian plug-in

The plug-in id is tabularium; it needs Obsidian 1.13 or later and works on desktop and mobile.

  • Sign-in: enter the server address, user name and password, choose or create a vault, and type the E2EE passphrase; the plug-in issues a device and fills in the connection. A device that already holds notes can follow the server or compare modification times.
  • Settings: Connection, Sync, File sorting, Send to Kindle, Editor, Appearance, Maintenance and Help; every item can be found with Obsidian's settings search. Help pages and version history ship with the plug-in.
  • Maintenance: fetch again from the server, overwrite the server with this device, unlock, diagnostic report, find incomplete files, keep the newer version in all conflicts. Destructive actions share one confirmation dialog that states the effect on other devices.
  • Editing: the same formatting toolbar as web notes ("Sensitive" included) and an "Insert" context menu; every item is a command you can bind to a hotkey.
  • Broken links: "Check broken links" lists links to missing files and proposes files with the same name as targets; "Repair broken links automatically" can be turned on for one device that stays on.
  • File sorting, sharing, sending to Kindle and pausing live sync are all in the file list's context menu.

Tabularius (the clerk)

An AI note administrator enabled per vault, under "Clerk" in the web app.

  • Questions and search: the server runs a hosted client for the vault, syncs a plain-text copy and indexes it (SQLite FTS5 full-text search plus optional embedding search, with the two result lists merged). Answers stream in, and cited notes open in web notes with a click.
  • Tidying and proposals: to tidy, rename, move or delete notes, it submits a proposal with a diff for each file; you review it and apply or discard the whole batch. Before applying, each file is checked for edits made elsewhere; moves and renames update the links that point at them; modification times are the time of applying. The tidying rules live in the server and vault prompts and can include keeping each folder's Content.md and the root Collation.md up to date.
  • Providers and recipes: Anthropic, OpenAI, Google Gemini, DeepSeek, and local vLLM, Ollama and TensorRT-LLM. Model lists are read from the providers' APIs. A recipe combines a model, parameters and an extra prompt; its form lists every parameter each provider's official API accepts, and a conversation can adjust them on top of the recipe.
  • Conversations: image, text-file and PDF attachments; projects and archiving; memory across conversations (per account, managed on the page); search over earlier conversations; long conversations compacted or trimmed in each provider's own way.
  • Web verification: checks a note's versions, commands, parameters and links one by one, rates each as matching, outdated, wrong or unverifiable, and lists its sources. Claude, OpenAI and Gemini use their built-in search; other models use the server's SearXNG.
  • Sandbox and SSH: runs code and commands from notes in a throwaway container (limited resources and time, offline by default), and runs commands over SSH on hosts the administrator registered, within the permission level.
  • Permission levels: read-only, restricted (allow-listed commands and registered internal addresses) and full, capped by the server. Hosts registered as "forbidden" are unreachable at every level, and changes to notes always go through proposals.
  • What is sent: excluded paths are left out of the index and of requests to the provider; redaction rules (RE2 regular expressions, covering common API keys, private keys and values such as password: by default) replace credentials with [REDACTED].
  • Local knowledge base: RAGFlow retrieval over MCP, and embeddings from a local service.

Web app and administration

One sign-in page and one shell: Home, Web notes, Clerk and Account, plus Administration for administrators.

  • Home: vault status, each device's last sync time, storage used, recent notes and conversations, a server summary.
  • Account: vaults (rebuild authorisation, unlocking), devices, my shares, Kindle address, password, and the interface (language and colour scheme: Sakura, Forest, Lavender, and any number of custom schemes).
  • Administration: accounts, vaults, devices, usage, server status and the activity log; "Server settings" (mail and Kindle, sharing, session and device lifetimes, rate limits, audit retention); "LLM settings" (providers, recipes, prompts, embeddings, web access, permission cap, SSH hosts, sandbox); "Shares"; "Backups". Every runtime setting is changed here and takes effect at once.
  • Scheduled backups: the server takes online incremental backups daily, weekly or monthly, with time, time zone, number kept and full-backup interval; each backup point is published after its size and SHA-256 are verified, and a failure is flagged on the Overview page.
  • The plug-in and every web page are available in English, Chinese and Latin.

Repositories

This is the parent repository. The server and the plug-in are submodules with their own repositories:

Path Repository Contents
apps/server Tabularium-Server Node.js gateway, SQLite application database, web app, Tabularius
apps/obsidian Tabularium-Obsidian Obsidian plug-in; also the source of the browser client and of the command-line client the server runs
deploy/ here Compose files, backup, restore, upgrade and rollback tools, release build
tests/ here Tests across components: real-client synchronisation, Tabularius end to end, backups, release packages

The server image is built from both submodules, so build and test from this repository.

Building

Node.js 24.10.0 and npm 11.6.1 are required (see .nvmrc and engines in package.json); Docker with Compose 2.24 or later is needed for the server and most tests.

git clone --recurse-submodules git@github.com:Violaria/Tabularium.git
cd Tabularium
npm run bootstrap   # dependencies of the workspace and the plug-in
npm run build       # server, plug-in main.js, CLI
npm run check       # type checks and lint for everything

Tests:

Command Covers
npm run test:server gateway and API against a real CouchDB
npm run test:server:browser administration console in a headless browser
npm run test:unit plug-in unit tests
npm run test:tabularius Tabularius end to end, with scripted model providers
npm run test:backup backup, restore and scheduled backups
npm run test:gateway synchronisation regression with real clients (needs desktop Obsidian)
npm run test:release a clean install from the built release package

Running locally

cp deploy/local/.env.example deploy/local/.env
# set the three passwords; the administrator's initial password needs at least 12 characters
docker compose --env-file deploy/local/.env -f deploy/local/compose.yaml up --build -d --wait
curl http://127.0.0.1:3000/health/ready

Open http://127.0.0.1:3000/, sign in as admin and change the password. Runtime settings (email, sharing, AI providers, backups) are configured on the web pages; .env only holds what the containers need before they start.

Releases and installation

npm run release writes release/<version>/: the server image as a tar file, the plug-in as a zip, the deployment directory (compose.yaml, couchdb.ini, .env.example, backup/), third-party licences and checksums. On the target host:

cd release/<version>
sha256sum -c SHA256SUMS
docker load -i tabularium-server-<version>.tar
docker tag tabularium-server:<version> tabularium-server
cd deploy
cp .env.example .env    # fill in the passwords and TABULARIUM_PUBLIC_URL
docker compose -p tabularium --env-file .env up -d --wait

The gateway listens on 127.0.0.1 only. Put an HTTPS reverse proxy in front of it that does not buffer responses (the synchronisation change feed is a long-lived stream), has a read timeout longer than the gateway's five-minute connection limit, and accepts request bodies of 64 MiB. For nginx: proxy_buffering off, proxy_request_buffering off, proxy_read_timeout 600s, client_max_body_size 64m.

Install the plug-in by copying main.js, manifest.json and styles.css from the zip into .obsidian/plugins/tabularium/ and enabling it.

Backups and upgrades

deploy/backup/cli.ts runs with Node.js 24.10.0 and no other dependencies:

node deploy/backup/cli.ts backup   --compose <file>... --env-file <file> --project <name> --output <dir>
node deploy/backup/cli.ts verify   <backup-dir>
node deploy/backup/cli.ts restore  <backup-dir> --project <name> --target <dir> --port <port>
node deploy/backup/cli.ts restore-scheduled <backups-dir> <point-id> --image <image> --compose <file>... --env-file <file> --project <name> --target <dir> --port <port>
node deploy/backup/cli.ts upgrade  --image <image> --compose <file>... --env-file <file> --project <name> --output <dir>
node deploy/backup/cli.ts rollback <backup-dir> --compose <file>... --env-file <file> --project <name> --output <dir>

backup stops the services briefly and archives both data volumes and the image. upgrade takes such a backup, starts the new image on a copy first, then switches; rollback returns to the backup taken before an upgrade. Restores always go into a new Compose project, never over the running one. Scheduled backups are written by the server itself and configured under Admin → Backups.

About

Self-hosted end-to-end encrypted Obsidian sync server: gateway, web notes, sharing, Send to Kindle and an AI administrator

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages