A local-first, hierarchical note-taking application. Notes live in a tree, each note has a schema-defined type, and every change is recorded in an operation log — laying the groundwork for offline-first sync.
Built with Rust, Tauri v2, React, and SQLCipher (encrypted SQLite).
- Hierarchical notes — Organize notes in an infinite tree. Each note can have children, with configurable sort order (alphabetical ascending/descending, or manual positioning).
- Typed note schemas — Note types are defined as Rhai scripts. The built-in
TextNotetype ships out of the box; custom types support fields of typetext,textarea,number,boolean,date,email,select,rating,file, andnote_link. - User scripts — Each workspace stores its own Rhai scripts in the database. Scripts come in two categories: Schema (
.schema.rhai) define note types viaschema(), and Library/Presentation (.rhai) define views, hover tooltips, and context-menu actions viaregister_view(),register_hover(), andregister_menu(). Create, edit, enable/disable, reorder, and delete scripts from a built-in script manager — no file system access required. - Template gallery — Ready-to-use templates live in the
templates/folder: a book collection organiser and a Zettelkasten atomic-note system. Copy the Rhai source into the Script Manager to activate a template in any workspace. - Tags — Attach free-form tags to any note. Tags are displayed as colour-coded pills in the note view, shown in the tree's tag cloud panel, and matched by the search bar. Scripts can read
note.tagsin view closures and query all notes carrying a given tag withget_notes_for_tag(). - On-save hooks — Rhai
on_savehooks use a transactional API (set_field,set_title,reject,commit) to compute derived fields safely. Field-levelvalidateclosures run on every keystroke. Field groups with optionalvisibleclosures keep complex schemas organised. - Tabbed note views — Schemas with
register_view()registrations show a tab bar in the detail panel. Custom view tabs appear in registration order;display_first: truemoves a tab to the leftmost position. The Fields tab is always present. Types with no registered views show the plain field grid. - Schema versioning and migrations — Schemas declare a
versionnumber and optionalmigrateclosures. When the workspace opens, stale notes (those at an olderschema_version) are migrated automatically in a single transaction per schema type. A toast notification reports how many notes were updated. - Search — A live search bar with debounced fuzzy matching across note titles and all text fields. Keyboard-navigable results; selecting a match expands collapsed ancestors and scrolls the note into view.
- Export / Import — Export an entire workspace as a
.krillnotesarchive (notes + attachments + user scripts), with an optional AES-256 password. Import an archive into a new workspace; the app detects encrypted archives and prompts for the password before importing. - File attachments — Attach any file to a note. Attachments are encrypted at rest alongside the database. Images render as thumbnails; all file types can be downloaded or opened. Attachment size limit is configurable per workspace.
- Undo / Redo — Cmd+Z / Cmd+Shift+Z (toolbar buttons also available). Undoes note creates, edits, deletes, and moves. Multi-step tree actions collapse into a single undo step. History limit is configurable per workspace (default 50, max 500). The script editor has its own independent undo stack that does not mix with the note-tree history.
- Operations log viewer — Browse the full mutation history, filter by operation type or date range, and purge old entries to reclaim space.
- Operation log — Every mutation (create, update, move, delete, script changes, undo/redo) is appended to an immutable log before being applied, enabling device sync when it ships.
- Identity system — A cryptographic identity (Ed25519 keypair, passphrase-protected via Argon2id) manages workspace access. Unlock your identity once per session with your passphrase; all bound workspaces open without additional prompts. Identities are portable via
.swarmidexport/import — move your identity to another device and all your workspaces follow. - Workspace Manager — Browse, open, duplicate, and delete workspaces from a dedicated manager. Each entry shows name, size, last-modified date, note count, and attachment count — all without needing to unlock the workspace.
- Internationalisation — 7 language packs ship out of the box: English, German, French, Spanish, Japanese, Korean, and Simplified Chinese. The active language is chosen from Settings and takes effect immediately, including the native application menu.
- Tree keyboard navigation — Arrow keys to move between nodes, Right/Left to expand/collapse, Enter to edit the selected note.
- Resizable panels — Drag the divider between the tree and the detail panel to resize.
- Context menu — Right-click on any tree node for quick actions (Add Note, Edit, Delete).
- Multi-window — Open multiple workspaces simultaneously, each in its own window.
- Encrypted workspaces — Every workspace is encrypted at rest with SQLCipher (AES-256-CBC, PBKDF2-HMAC-SHA512 key derivation). Workspace passwords are randomly generated and managed by the identity system — you never type a workspace password directly.
- Local-first — All data is stored on disk. No account, no cloud dependency, no internet connection required.
- Cross-platform — Runs on macOS, Linux, and Windows via Tauri.
| Tool | Version |
|---|---|
| Rust | 1.78+ |
| Node.js | 20+ |
| Tauri CLI | v2 |
Install the Tauri prerequisites for your platform by following the Tauri v2 setup guide.
# Clone the repository
git clone <repo-url>
cd Krillnotes
# Install Node dependencies
cd krillnotes-desktop
npm install
# Run in development mode (hot-reload frontend + Rust backend)
npm run tauri dev
# Build a release binary
npm run tauri buildThe compiled application is placed in krillnotes-desktop/src-tauri/target/release/bundle/.
# Core library unit tests
cargo test -p krillnotes-coreEach workspace is a folder on disk containing:
notes.db— a SQLCipher-encrypted SQLite databaseattachments/— per-attachment encrypted files (ChaCha20-Poly1305)info.json— unencrypted metadata sidecar (name, note count, size, workspace UUID) readable without a password
The database contains six tables:
| Table | Purpose |
|---|---|
notes |
The note tree (id, title, type, parent, position, fields, schema_version) |
note_tags |
Many-to-many junction between notes and tags |
operations |
Append-only mutation log (CRDT-style, includes UpdateSchema migration entries) |
workspace_meta |
Per-device metadata (device ID, selection state, undo limit) |
user_scripts |
Per-workspace Rhai scripts (id, name, source code, load order, enabled flag, category) |
attachments |
Attachment metadata (id, note_id, filename, MIME type, size, hash) |
The database uses AES-256-CBC encryption (SQLCipher v4 defaults: PBKDF2-HMAC-SHA512, 256,000 iterations). Workspace passwords are randomly generated and stored encrypted under your identity key — you need SQLCipher-aware tooling and the correct randomly-generated password to open the file outside of Krillnotes.
Migrating from v0.2.x: Workspaces created with v0.2.x used a user-supplied password. Export them in v0.2.x via File → Export Workspace, then import the
.krillnotesarchive into v0.3.0 using New Workspace from Archive.
Migrating from v0.1.x: Unencrypted workspaces are rejected with a migration message. Open them in v0.1.x, export via File → Export Workspace, then import here.
macOS Gatekeeper blocks unsigned apps with an "app is damaged and can't be opened" message. To bypass this after installing from the .dmg:
xattr -cr /Applications/Krillnotes.appThis removes the quarantine flag macOS adds when mounting a DMG. The app will open normally afterwards.
MPL-2.0 — see LICENSE.
