Skip to content

Support for attaching files to notes #23

@careck

Description

@careck

Attaching of binary and text files, eg. images, audio, video, docx, csv, etc. to a note should be possible.

Here is a design proposed by Claude Opus, please consider this for the implementation/brainstorming:

Encrypted File Attachments — Technical Design

Overview

Allow users to attach an arbitrary number of files (images, documents, audio, etc.) to any note. Attachments must be encrypted at rest, consistent with the existing SQLCipher-encrypted database.

Architecture: Encrypted Sidecar Files

Store attachments as individually encrypted files on disk alongside the database. The database holds only metadata — never the file contents.

~/.krillnotes/
  └── workspaces/
      ├── <workspace-id>/
      │   ├── notes.db              (SQLCipher encrypted database)
      │   └── attachments/
      │       ├── a1b2c3d4.enc      (encrypted attachment)
      │       ├── e5f6g7h8.enc
      │       └── ...
      ├── <workspace-id>/
      │   ├── notes.db
      │   └── attachments/
      │       └── ...
      └── ...

Encryption Scheme

  • Algorithm: ChaCha20-Poly1305 (authenticated encryption)
  • Key derivation: HKDF from the master passphrase + a unique random salt per file
  • No keys or passwords stored in the database — only the per-file salt is stored. The master passphrase (same one that unlocks SQLCipher) is the single secret.
  • Rust crate: ring (provides ChaCha20-Poly1305 and HKDF)

Why ChaCha20-Poly1305?

  • Industry standard (used by TLS 1.3, WireGuard, Signal)
  • Authenticated encryption — detects tampering, not just encrypts
  • Fast in software without requiring hardware AES support
  • Excellent Rust ecosystem support

Encrypted File Format

Each .enc file on disk:

[ 12-byte nonce ][ 32-byte salt ][ encrypted payload + 16-byte auth tag ]

Encrypt Flow

  1. Generate random 32-byte salt
  2. Derive file-specific key: HKDF(master_passphrase, salt, info="krillnotes-attachment")
  3. Generate random 12-byte nonce
  4. Encrypt file contents with ChaCha20-Poly1305
  5. Write nonce || salt || ciphertext+tag to <uuid>.enc
  6. Store metadata (salt, uuid, original filename, mime type, size, hash) in DB

Decrypt Flow

  1. Read nonce and salt from .enc file header
  2. Derive key from master passphrase + salt via HKDF
  3. Decrypt and authenticate payload
  4. Return plaintext bytes

Database Schema

CREATE TABLE attachments (
    id          TEXT PRIMARY KEY,       -- UUID, matches filename on disk
    note_id     TEXT NOT NULL,          -- FK to notes table
    filename    TEXT NOT NULL,          -- original filename (e.g. "photo.jpg")
    mime_type   TEXT,                   -- e.g. "image/jpeg"
    size_bytes  INTEGER NOT NULL,       -- original unencrypted size
    hash_sha256 TEXT NOT NULL,          -- hash of original file (integrity check)
    salt        BLOB NOT NULL,          -- 32-byte salt for HKDF key derivation
    created_at  TEXT NOT NULL,          -- ISO 8601 timestamp
    FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE
);

CREATE INDEX idx_attachments_note_id ON attachments(note_id);

Attachment Lifecycle

Add

  1. User selects file(s) via file picker
  2. For each file: encrypt → write .enc → insert DB row
  3. Wrap in a transaction so DB and disk stay in sync

Retrieve / View

  1. Query DB for attachment metadata by note_id
  2. Read .enc file, decrypt in memory
  3. Serve to UI (display inline for images, download/open for others)

Delete

  1. Delete DB row
  2. Delete .enc file from disk
  3. Wrap in transaction; if file deletion fails, log for cleanup

Orphan Cleanup

On startup (or periodically), scan the attachments/ directory and compare against DB records. Remove any .enc files that have no matching DB row. Also check for DB rows pointing to missing files and flag/remove them.

Considerations

  • Large files: ChaCha20-Poly1305 can encrypt/decrypt in streaming chunks to keep memory usage bounded. Consider a chunked approach for files over a configurable threshold (e.g. 10MB).
  • Thumbnails: For image attachments, consider generating and storing an encrypted thumbnail for quick preview in the notes list without decrypting the full file.
  • Backup: A complete backup requires both notes.db and the attachments/ folder. Document this clearly for users.
  • Migration: Existing databases without the attachments table will need a migration step to add the table and create the attachments directory.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions