Notez iconNotez

About Notez

Notez is a desktop application for organizing notes, documents, and files in a hierarchical tree structure. It is built as an Electron app backed by a centralized REST API server. This page documents the full technical architecture.

1. Architecture Overview

Notez is a three-tier client-server application organized as an npm workspaces monorepo with three packages:

Package Role
@notez/client Electron + React desktop application (renderer = React SPA, main process = Node.js)
@notez/server Express REST API handling authentication, authorization, business logic, and file I/O
@notez/shared Shared TypeScript types, constants, and API route definitions used by both client and server
Electron Desktop App React 18 + TipTap Zustand + React Query Win / macOS / Linux HTTPS Express API Server TypeScript + Node.js JWT Auth + Zod + Helmet REST JSON API TDS Microsoft SQL Server Structured data & metadata Local Filesystem HTML, attachments, images

The Electron client is not served by the Express server. It is a standalone desktop application that communicates with the API over HTTPS. In production, the API runs at notezapi.qstatecode.com behind a reverse proxy that terminates TLS.

2. Technology Stack

Server

ComponentTechnology
LanguageTypeScript, compiled with tsc, dev via ts-node-dev
FrameworkExpress 4.18
DatabaseMicrosoft SQL Server 2019+ via the mssql npm package
ORMNone — raw parameterized SQL queries via mssql connection pools
ValidationZod 3.22 for all request bodies, params, and query strings
Authjsonwebtoken (JWT, HS256)
Password hashingbcrypt with 12 salt rounds
LoggingWinston 3.11 (file + console transports, structured JSON)
File uploadsMulter 1.4 (memory storage)
HTTP securityHelmet 7.1, CORS, express-rate-limit 7.1, compression
HTML sanitizationDOMPurify 3.0 (server-side via jsdom)
TestingVitest + Supertest

Client

ComponentTechnology
UI frameworkReact 18.2
RoutingReact Router DOM 6.22 (HashRouter for Electron file:// compatibility)
State managementZustand 4.5 (4 stores: auth, project, tree, UI)
Server stateTanStack React Query 5.24
Rich text editorTipTap 3.17 (built on ProseMirror)
TipTap extensionsStarterKit, Image, Placeholder, Table, TableRow, TableCell, TableHeader
Drag and drop@dnd-kit/core 6.1 + @dnd-kit/sortable 8.0
Tree virtualization@tanstack/react-virtual 3.1
CSSTailwind CSS 3.4 with @tailwindcss/typography plugin
IconsLucide React
NotificationsSonner 1.4
Document previewmammoth 1.6 (.docx), pdfjs-dist 4.0 (PDF)
Build toolVite 7.3 with vite-plugin-electron
TestingVitest + Testing Library

Electron Shell

ComponentDetail
Electron version40.0.0
Build toolelectron-builder 23.x
Context isolationEnabled (contextIsolation: true, nodeIntegration: false)
Preload bridgeExposes electronAPI: platform info, window controls (minimize, maximize, close)
App IDcom.notez.app
TargetsWindows (NSIS x64), macOS (DMG x64 + arm64), Linux (AppImage x64)

3. Data Model & Schema

The database runs on Microsoft SQL Server 2019+. All primary keys are UNIQUEIDENTIFIERs (GUIDs) generated with NEWID(). All major entities use a soft-delete pattern via a nullable deleted_at column — records are never physically removed through the application.

TablePurposeKey Columns
Users User accounts id, username (case-insensitive unique), password_hash
Permissions Permission definitions id, name (create, read, update, delete)
Settings App configuration (key-value) key, value, description
Projects Top-level containers id, owner_id (FK Users), name
ProjectUsers Project membership (junction) project_id + user_id (composite PK)
ProjectUserPermissions Per-user, per-project permissions project_id + user_id + permission_id (composite PK)
Nodes Tree folders/items id, project_id, parent_id (self-FK, NULL = root), name, sort_order
TextObjects Rich text documents id, node_id (FK), name, stored_filename, file_path, file_size
FileAttachments Binary file attachments id, node_id (FK), name, original_filename, stored_filename, file_type (MIME), file_size
Images Inline images for rich text id, text_object_id (FK), original_filename, stored_filename, file_size
Tags Project-scoped labels id, project_id (FK), label (lowercase, unique per project)
NodeTags Node-tag junction (many-to-many) node_id + tag_id (composite PK, cascade delete)

Entity Relationships

Indexes

Filtered indexes on name/label columns (where deleted_at IS NULL) optimize search queries: idx_nodes_name, idx_textobjects_name, idx_attachments_name, idx_tags_label.

4. Data Flow & API

All client-server communication is over REST (JSON over HTTPS). There are no WebSocket connections. File uploads use multipart/form-data via Multer. Every endpoint lives under the /api/v1 prefix.

Client-Side Request Pipeline

  1. UI components dispatch actions through React Query mutation/query hooks.
  2. Hooks call the ApiClient singleton, which wraps fetch() and automatically attaches the Authorization: Bearer <JWT> header.
  3. On 401 Unauthorized responses, the ApiClient triggers the auth store’s logout flow automatically.
  4. React Query manages caching, deduplication, and invalidation. Mutations invalidate related queries to keep the UI in sync.

Server-Side Request Pipeline

  1. Rate limiter — global 100 req/min/IP; auth endpoints 10 req/min/IP.
  2. Helmet — sets security headers (CSP, HSTS, X-Content-Type-Options, etc.).
  3. CORS — validates origin against an allowlist (Electron null origin, notez:// protocol, configurable domains).
  4. Body parser — JSON with a 10 MB limit; Multer for multipart uploads.
  5. Auth middleware — verifies JWT signature and expiry, extracts user identity and project permissions from the token payload.
  6. Permission middleware — checks the token’s embedded permissions for the target project (no database round-trip required).
  7. Zod validation — validates request body, params, and query against typed schemas.
  8. Controller — executes business logic, calls the database and storage service.
  9. Error handler — catches all exceptions and returns structured JSON error responses.

API Endpoints

GroupEndpointsDescription
Auth POST register, login, logout, refresh Account creation, JWT issuance and refresh. Auth endpoints have a stricter rate limit (10/min).
Projects GET list, POST create, PUT update, DELETE delete/leave Project CRUD. Owners can delete; members can leave. Requires appropriate permissions.
Tree GET full tree Returns the entire node hierarchy for a project, including text objects, attachments, tags, and node-tag mappings in a single response.
Nodes CRUD + move + reorder + batch delete Create, read, rename, soft-delete, drag-to-move (validates against circular references), reorder within parent.
Text Objects CRUD + content read/write + move + reorder + batch delete Metadata and HTML content are separate endpoints. Content is sanitized with DOMPurify on every write.
Attachments Upload + CRUD + stream/download + move + reorder + batch delete Multipart upload with magic-byte validation. Stream and download endpoints serve file bytes directly.
Images POST upload, GET stream For inline images embedded in rich text. JPEG, PNG, GIF, WebP only. SVG blocked (XSS risk).
Tags CRUD + node-tag association Project-scoped labels. Add/remove tags from nodes. Labels stored lowercase.
Search GET search Full-text search across nodes, text objects, attachments, and tags with optional tag filtering and subtree scoping.
Health GET /health Health check endpoint, exempt from rate limiting.

5. Security

Authentication

Password Security

Transport Security

HTTP Security Headers

Rate Limiting

Input Validation & Sanitization

File Upload Security

Path Traversal Prevention

Electron Security

CORS

Authorization

6. File Storage

All files are stored on the local filesystem of the server machine. There is no S3 or cloud object storage. The storage root is configurable via the storage_root key in the Settings table (default: C:\NotezStorage on Windows, or /app/storage in Docker).

Content TypeDirectoryNaming
Rich text documents {storage_root}/text-objects/ {name}_{guid}.html
File attachments {storage_root}/attachments/ {name}_{guid}.{ext}
Inline images {storage_root}/images/ {uuid}.{ext}

The StorageService is a singleton that handles all file I/O operations. It provides path traversal prevention, DOMPurify sanitization of HTML before writing, magic byte validation, extension whitelist enforcement, file size limits, and idempotent delete operations.

Metadata (filenames, paths, sizes, MIME types) is stored in the database. The actual file bytes live on disk. Soft-deleting a record in the database does not remove the file from disk.

Data Storage Notice: All data is currently stored on QState Code's servers. A future update will provide a configurable server endpoint so users can point the client at their own infrastructure and retain full control over where data is stored.

7. Feature Details

Rich Text Editor

The editor is built on TipTap 3.17 (a headless ProseMirror wrapper). It supports bold, italic, strikethrough, inline code, headings (H1–H6), blockquotes, bullet lists, ordered lists, code blocks, horizontal rules, and undo/redo (100 levels of history). Tables with resizable columns are available via the TipTap Table extension. Inline images are uploaded to the server and referenced by URL — no base64 data URIs are stored in the document HTML.

Content is saved with Ctrl+S. The UI tracks unsaved changes and warns before navigating away.

Hierarchical Tree

Nodes form an unlimited-depth tree via a self-referencing parent_id foreign key. The full tree for a project is fetched in a single API call and assembled server-side into a nested structure. The client stores it in a Zustand store as a Map<GUID, TreeNode> for O(1) lookups.

The tree supports: expand/collapse, expand-all/collapse-all, range selection (Shift+click), multi-selection (Ctrl+click), keyboard navigation, inline rename editing, and drag-and-drop reordering and moving via @dnd-kit. The tree is virtualized with @tanstack/react-virtual for performance with large node counts.

File Attachments & Previews

Any node can have file attachments uploaded via multipart form data. The server validates uploads with magic byte checks and an extension whitelist. Files are stored on the local filesystem under {storage_root}/attachments/.

The client provides built-in previews for: PDF (rendered page-by-page with pdfjs-dist), Word .docx (converted to HTML via mammoth), images (native rendering), and plain text. Unsupported types show a download fallback.

Tagging & Search

Tags are project-scoped labels stored in lowercase. They can be attached to any node through the NodeTags junction table. The search system runs SQL LIKE pattern matching (case-insensitive via MSSQL collation) across nodes, text objects, attachments, and tags simultaneously using UNION ALL queries.

Search supports scoping to the entire project, a specific subtree (via recursive CTE), or filtering by selected tags. Results include breadcrumb paths showing the full ancestor chain for each match.

Multi-Project & Permissions

Users can create multiple projects and be invited to others. Each membership carries independent CRUD permissions (create, read, update, delete) via the ProjectUserPermissions table. The project owner always has full access and is the only user who can delete the project. Other members can leave voluntarily.

8. Deployment & Distribution

Server Deployment

The production server targets Windows Server 2019+ or Linux. Prerequisites are Node.js 20+ LTS and MSSQL Server 2019+. The deployment process is:

  1. Create the MSSQL database and run the schema and migration scripts.
  2. Configure environment variables: NODE_ENV, PORT, DB_SERVER, DB_NAME, DB_USER, DB_PASSWORD, JWT_SECRET.
  3. Create storage directories (text-objects/, attachments/, images/) and set the storage_root value in the Settings table.
  4. Build the server with npm run build:server (TypeScript compilation).
  5. Start with node dist/server.js.

Process Management

Two options are supported:

Docker

A docker-compose.yml is provided with two services:

Docker volumes persist file storage (notez-storage) and database data (mssql-data).

HTTPS

TLS is terminated at the reverse proxy (IIS URL Rewrite or nginx) rather than in the Node.js process. The production API runs at notezapi.qstatecode.com.

Desktop App Distribution

PlatformFormatBuild Command
WindowsNSIS installer (.exe), x64npm run build:win
macOSDMG, x64 + arm64npm run build:mac
LinuxAppImage, x64npm run build:linux

Code Quality

Open Source — Coming Soon

The full Notez source code will be released as open source. Once published, you will be able to self-host the entire stack — server, database, and storage — on your own infrastructure.