Skip to content

kalmigs/trilkeep

Repository files navigation

Trilkeep

Trilkeep logo

License: MIT VS Code Marketplace Open VSX VS Code Engine TypeScript CI GitHub Release GitHub Downloads GitHub Stars

Mirror your notes to Trilium.

A VS Code extension that mirrors your notes workspace into Trilium via the ETAPI (Trilium's External API).

The local workspace stays the source of truth; Trilkeep writes a one-way copy into Trilium.

Running Trilkeep: Back Up Workspace in VS Code, then the notes appearing as a tree in Trilium

Features

  • One-way backup into Trilium. A batched full backfill on the first run, then incremental hash-diff runs that upload only changed files and never duplicate notes.
  • Lossless Markdown. Files are stored as Trilium code notes (text/x-markdown) byte-for-byte; folders become container notes that recreate your tree.
  • Backup on save (opt-in). Back up changed files automatically as you save.
  • Backup on open (opt-in). Run a full backup when the workspace opens, to catch up edits made while it was closed.
  • Read-only mirror (on by default). The mirror renders read-only in Trilium so you don't accidentally edit it. You can make it editable (trilkeep.readOnly: false), but the workspace stays the source of truth, so any changes you make in Trilium are overwritten on the next backup. (True two-way sync is a longer-term roadmap idea, not a current capability.)
  • Secure token storage. Your ETAPI token is kept in VS Code SecretStorage, never written to settings.json or any file you could commit by accident.
  • Zero runtime dependencies. Built on Node's fetch and crypto, with a 7-day dependency cooldown on the dev toolchain.

Requirements

  • VS Code 1.90 or later.
  • Desktop VS Code. The extension uses Node APIs (filesystem + crypto), so it doesn't run in the browser-only web editor (vscode.dev / github.dev).
  • A reachable Trilium server with ETAPI enabled (see Setup).

Setup

Install Trilkeep from the VS Code Marketplace or Open VSX, then:

  1. Run Trilium and open Options → ETAPI, then generate a token.
  2. In VS Code, run Trilkeep: Setup, a quick wizard for the essentials (instance name, token, server URL, on-save). For the rest (globs, grouping, read-only, hard-delete, backup on open), run Trilkeep: Setup (Advanced), which walks every setting. Both pre-fill the current values, so you can re-run either any time to review or change config. The token is stored in VS Code SecretStorage (never in settings) and is never displayed. Give each Trilium instance a distinct instance name (the token + backup state are keyed by it, so the URL can change freely). Instance names are stable: pick an existing instance or create a new one, but you don't rename them (a new name is simply a new backup).
  3. Run Trilkeep: Back Up Workspace to back up.

Prefer to configure by hand? Set trilkeep.serverUrl in VS Code Settings (Cmd/Ctrl+,, switch to the Workspace tab so it saves per-repo like Setup does, then search "trilkeep", or edit .vscode/settings.json directly), run Trilkeep: Set ETAPI Token, then Trilkeep: Test Connection to confirm.

How the backup works

A state manifest at <workspace>/.trilkeep/state.json maps every backed-up file to the Trilium noteId it became plus a sha256 of its content. That manifest is what makes incremental backup possible:

  • New file → create a Trilium note and record it.
  • Changed file (hash differs) → PUT new content to the existing note.
  • Unchanged file (hash matches) → skipped.
  • Removed file → logged, kept in Trilium by default (soft). Set trilkeep.hardDeleteRemovedFiles: true to delete instead.

Markdown files are stored as Trilium code notes with mime text/x-markdown so the raw Markdown is preserved byte-for-byte (text notes would force a lossy HTML conversion). Folders become container (book) notes, recreating the tree.

Commands

Command Action
Trilkeep: Setup Quick wizard for the essentials: instance, server URL, token, on-save (re-runnable).
Trilkeep: Setup (Advanced) Guided walk-through of every setting (re-runnable).
Trilkeep: Back Up Workspace Full/incremental backup of the open workspace.
Trilkeep: Back Up Workspace (Dry Run) Show what would be backed up (new/changed/unchanged/skipped/removed) without writing to Trilium. No token needed.
Trilkeep: Test Connection Verify server URL + token via /app-info.
Trilkeep: Set ETAPI Token Store the ETAPI token for the current instance.
Trilkeep: Clear ETAPI Token Remove the stored token for the current instance.
Trilkeep: Forget Instance Stop tracking any known instance: clears its (global) token and optionally this repo's backup state. Trilium is left intact.

Settings

Setting Default Description
trilkeep.instanceName default Stable name for this Trilium instance (e.g. real, test). Keys the token + backup state, so serverUrl can change without losing them.
trilkeep.serverUrl http://localhost:8080 Trilium base URL (just the address; change it freely).
trilkeep.include ["**/*.md"] Globs to back up.
trilkeep.exclude node_modules, .git, .trilkeep Globs to skip.
trilkeep.backupOnSave false Incremental backup on each save.
trilkeep.backupOnActivation false Full backup when the workspace opens (the extension activates). Runs in the background; never blocks startup.
trilkeep.rootNoteTitle (empty) Title of this workspace's root note. Blank = the workspace folder name.
trilkeep.group Trilkeep Slash-path of container notes to nest the backup root under (e.g. Trilkeep/work/repo). Blank = no grouping.
trilkeep.parentNoteId (empty) Existing Trilium noteId to use as the base parent instead of Trilium's root. Blank = Trilium root.
trilkeep.readOnly true Mark the mirrored tree read-only in Trilium (inheritable #readOnly label). Backups still update it.
trilkeep.hardDeleteRemovedFiles false Delete Trilium notes for removed files.
trilkeep.excludeSecrets true Skip files whose name looks like a secret (.env, *.pem, id_rsa, credentials, …) even if include matches them. A manual backup lists any matches and lets you include them for that run; set false to always back them up.

Security posture

  • Token lives in VS Code SecretStorage, not settings.json, and is keyed by instanceName (a stable name you choose), not by serverUrl. Distinct instances (e.g. test vs real) never share a token, and changing a server's address, such as a churning LAN IP, never loses or misroutes the token. The backup-state manifest is keyed the same way (.trilkeep/state.<instance>.json), so two instances keep independent trees. (serverUrl/instanceName are workspace-scoped settings; the token is global SecretStorage.)
  • Secret files are skipped by default. trilkeep.excludeSecrets (on) keeps credential-looking files (.env, *.pem, *.key, id_rsa, credentials, .npmrc, …) out of the backup even when include matches them, so widening include to **/* doesn't sweep secrets into Trilium. A manual backup lists any matches and lets you include them for that run; automatic runs (on save / on open) skip them, log each one, and show a once-per-session notice, so nothing is silently uploaded. It is a filename heuristic, not secret scanning: it misses a renamed secret and does not catch a secret living inside a tracked note (a .md file with a password), so still add trilkeep.exclude patterns for anything sensitive it wouldn't recognize. Set trilkeep.excludeSecrets to false to back these files up.
  • Zero runtime dependencies. Uses Node's built-in fetch and crypto, so there's no third-party supply-chain surface in what ships.
  • Dependency cooldown. pnpm-workspace.yaml sets minimumReleaseAge: 10080 (7 days), so we never install a version published in the last week, which closes the window where a freshly-compromised release is live before it's caught. Dependabot is configured with a matching 7-day cooldown.

Notes & known limitations

  • Verified against Trilium 0.103.x, over localhost and LAN. Trilkeep uses the ETAPI, Trilium's stable external API, so it should hold across Trilium versions and over remote connections, but it has only been exercised against Trilium 0.103.x reached at http://localhost and on a local network. Other versions and remote setups (HTTPS, a reverse proxy, or a tunnel) are expected to work but are not yet tested. Please report any trouble on a different version or a remote URL.
  • Remote instances may rate-limit large backups. A reverse proxy or CDN in front of a remote Trilium (nginx, Caddy, Cloudflare, and the like) can throttle the rapid sequential requests a big backfill makes. Trilkeep retries a throttled or transient response (429, 502, 503, 504) up to three times, honoring the server's Retry-After header and otherwise backing off exponentially. (A 429 is retried for any request; a 5xx only for idempotent ones, so a create is never duplicated.) If it persists past those retries the file is reported as an error; re-running the backup picks it up (failed files aren't recorded, so nothing is silently skipped). Direct localhost/LAN connections have no such limit.
  • One window per workspace at a time. Overlapping backups are guarded within a single VS Code window, but that lock doesn't span processes. If you open the same folder in two windows and back up from both at once, they can race the shared .trilkeep/state.json and create duplicate notes in Trilium. Back up from one window at a time.
  • The mirror is not protected/encrypted in Trilium. Trilium's protected notes are encrypted with a key from your protected-session password, which only exists in an interactive UI session. The ETAPI token never holds that key, so the API cannot create or modify protected notes (isProtected is read-only over ETAPI; writing to a protected note returns NOTE_IS_PROTECTED). Trilkeep's notes are therefore stored unprotected, and unencrypted at rest, even if the originals you keep in Trilium are protected. Do not manually "Protect subtree" on the backup tree either: the next incremental backup would fail on every changed note. Your local .md files are already plaintext on disk, so the copy matches the source, but it does not inherit Trilium's at-rest encryption.

Roadmap

Planned and under consideration (directional, not commitments):

  • Automatic backup. Opt-in backup when the workspace opens, plus scheduled/periodic runs and a status-bar indicator (last backup time, in progress, errors).
  • Standalone multi-repo daemon. Back up several repos at the OS level, beyond the one open in VS Code.
  • Two-way sync (opt-in, longer-term). Pull edits made in Trilium back into the workspace, not just push. A distinct mode from the default one-way backup (which keeps the workspace as the source of truth); the hard part is conflict handling when a note and its file both change, plus tracking remote-side changes and deletes.

Development

pnpm install          # respects the 7-day cooldown
pnpm run typecheck    # tsc against src + test
pnpm run lint         # eslint
pnpm run test         # node:test unit suite (pure logic)
pnpm run compile      # build to out/

Press F5 in VS Code to launch an Extension Development Host with the extension loaded.

Feedback

Bug reports and feature ideas are welcome. Open an issue on the GitHub repo. If something feels clunky in daily use, describe your workflow so it can be reproduced.

License

MIT. See LICENSE.

About

One-way backup of a VS Code notes workspace into Trilium, via the ETAPI.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors