✏️ InkNote

A cross-platform note-taking application with rich text editing, freehand drawing, PDF annotation, and encrypted sync — inspired by Samsung Notes.


Features

Core

  • Rich Text Notes — Bold, italic, underline, headings, bullet/numbered lists, checkboxes
  • Freehand Drawing — Pen, highlighter, eraser with adjustable thickness and color palette (20 colors, 7 pen sizes)
  • Undo/Redo — Full history stack for drawing operations
  • PDF Import & Annotation — Import PDFs, overlay drawing/highlighting, persistent annotations
  • Notebooks & Folders — Create notebooks with custom gradient covers, organize notes by folder
  • Star/Favorite — Quick access to important notes
  • Dark & Light Mode — Full theme support with refined color schemes

Sync & Storage

  • Offline-First — Works fully offline, syncs when connection returns
  • Conflict-Aware Sync — Vector clock–based conflict detection; notifies user of merge conflicts
  • WebSocket Real-Time Push — Changes appear on other devices instantly
  • Encrypted at Rest — AES-256-CBC encryption for all note content on the server
  • JWT Authentication — Secure user accounts with bcrypt password hashing

Architecture

  • Self-Hosted Backend — Node.js + Express + SQLite (or PostgreSQL)
  • Docker Ready — docker-compose up and you're running
  • Client Sync Engine — Portable JS library for any frontend (React Native, Flutter JS bridge, Electron, web)

Project Structure

inknote/
├── note-app.jsx              # Full React UI (artifact / web)
├── docker-compose.yml        # Container orchestration
├── backend/
│   ├── server.js             # Express API + WebSocket server
│   ├── sync-client.js        # Client-side sync engine library
│   ├── package.json          # Node dependencies
│   ├── Dockerfile            # Server container
│   └── .env.example          # Environment variable template
└── README.md                 # This file

Quick Start

1. Run the UI (Web / Artifact)

The note-app.jsx file is a self-contained React component. It runs directly as a Claude artifact, or you can embed it in any React project:

import NoteApp from './note-app';

function App() {
  return <NoteApp />;
}

2. Run the Backend

cd backend
cp .env.example .env        # Edit secrets!
npm install
npm start                   # → http://localhost:4000

3. Docker Deployment

# Generate a secure encryption key
export ENCRYPTION_KEY=$(openssl rand -hex 32)
export JWT_SECRET=$(openssl rand -base64 32)

docker-compose up -d

The server runs on port 4000, the web UI on port 3000.


API Reference

Authentication

Method Endpoint Body Response
POST /api/auth/register {email, password, displayName} {token, user}
POST /api/auth/login {email, password} {token, user}

Notebooks

Method Endpoint Description
GET /api/notebooks List all notebooks
POST /api/notebooks Create notebook
PUT /api/notebooks/:id Update notebook
DELETE /api/notebooks/:id Soft-delete notebook

Notes

Method Endpoint Description
GET /api/notes List all notes (metadata)
GET /api/notes/:id Get note with decrypted content
POST /api/notes Create note
PUT /api/notes/:id Update note (conflict-aware)
DELETE /api/notes/:id Soft-delete note
PUT /api/notes/:id/drawing Save drawing data (base64)
POST /api/notes/pdf Upload PDF (multipart/form)

Sync

Method Endpoint Description
POST /api/sync Bidirectional sync with conflict reports

WebSocket

Connect to ws://localhost:4000/ws, then send:

{"type": "auth", "token": "JWT_TOKEN", "deviceId": "device-123"}

Receive real-time events: note_created, note_updated, note_deleted, notebook_created, etc.


Sync Engine (Client)

import { SyncEngine } from './sync-client';

const sync = new SyncEngine('https://your-server.com', 'device-abc');

// Authenticate
await sync.authenticate('user@example.com', 'password');

// Listen for events
sync.on('status', (status) => console.log('Sync:', status));
sync.on('conflict', (conflict) => {
  // Present both versions to user, let them choose
  console.log('Conflict:', conflict);
});
sync.on('remote_change', (change) => {
  // Update local state with remote changes
  console.log('Remote change:', change);
});

// Start syncing (every 30s + WebSocket push)
sync.start();

// CRUD operations auto-queue for sync
await sync.createNote({ type: 'text', title: 'Hello', content: '<p>World</p>' });
await sync.updateNote('note-id', { title: 'Updated' });
await sync.deleteNote('note-id');
await sync.uploadPDF(fileBlob);
await sync.saveDrawing('note-id', canvasDataUrl);

Conflict Resolution

InkNote uses vector clocks to detect conflicts:

  1. Each device maintains a logical clock counter
  2. On save, the device increments its counter and includes the full vector clock
  3. The server compares clocks — if the client clock is strictly newer, the update applies
  4. If clocks are concurrent (both have changes the other doesn't), a 409 Conflict is returned
  5. The client receives both versions and can present a merge UI to the user

Extending to Native Platforms

React Native

Use note-app.jsx components with React Native adaptations (replace HTML canvas with react-native-canvas or @shopify/react-native-skia for drawing). The sync engine works as-is.

Flutter

Use the sync engine via a JavaScript bridge, or port the API calls to Dart. For drawing, use Flutter's CustomPainter. For PDF, use flutter_pdfview.

Electron

Wrap the React app directly in Electron for desktop. Add native file system access for PDF import/export.


Security Notes

  • All note content is encrypted with AES-256-CBC before database storage
  • Passwords are hashed with bcrypt (12 rounds)
  • JWT tokens expire after 30 days
  • HTTPS should be enforced in production (use a reverse proxy like Nginx or Pangolin)
  • The encryption key should be stored securely (e.g., environment variable, secrets manager)

Tech Stack

Layer Technology
Frontend React (web), adaptable to RN/Flutter
Backend Node.js, Express
Database SQLite (dev), PostgreSQL (prod)
Real-time WebSocket (ws library)
Auth JWT + bcrypt
Encryption AES-256-CBC
Container Docker + docker-compose
S
Description
No description provided
Readme
3.1 MiB
0 Stars 0 Watchers 0 Forks
Languages
JavaScript 70.5%
TypeScript 27.7%
CSS 1.2%
HTML 0.4%
Shell 0.2%