Skip to content

GKaszewski/k-notes

Repository files navigation

K-Notes

A self-hosted note-taking engine built in Rust with a strict hexagonal + DDD architecture.

K-Notes Logo

Features

  • Authentication — JWT-based login and registration (disable registration via ALLOW_REGISTRATION=false)
  • Note Management — create, update, pin, archive, delete, version history
  • Markdown — content stored and served as Markdown
  • Tagging — user-scoped tags with get-or-create semantics
  • Search — full-text search via SQLite FTS5
  • Smart Features — semantic similarity links between notes using local embeddings (fastembed) and Qdrant; enabled when QDRANT_URL is set
  • Export / Import — portable JSON backup and restore
  • API Docs — Swagger UI at /docs, Scalar at /scalar
  • SPA — React frontend served at / by the same process

Tech Stack

Backend

Layer Technology
Language Rust
HTTP Axum 0.8
Database SQLite (sqlx + FTS5)
Events NATS JetStream (prod) · in-memory bus (dev)
Embeddings fastembed (AllMiniLML6V2)
Vector store Qdrant
Auth JWT (jsonwebtoken + argon2)
API docs utoipa + Scalar + Swagger UI

Frontend

Layer Technology
Framework React + Vite
Language TypeScript
Styling Tailwind CSS + shadcn/ui
State TanStack Query
Package manager Bun

Architecture

The backend follows Hexagonal Architecture + CQRS:

crates/
  domain/               # Entities, value objects, ports (traits)
  application/          # Use cases (commands + queries), WorkerService
  adapters/
    sqlite/             # NoteRepository, TagRepository, UserRepository, LinkRepository
    auth/               # Argon2PasswordHasher, JwtValidator, OidcService
    nats/               # NatsEventPublisher, NatsEventConsumer (JetStream)
    event-publisher-memory/  # In-memory bus for dev/test
    event-payload/      # DomainEvent ↔ wire format (JSON)
    fastembed/          # EmbeddingGenerator implementation
    qdrant/             # VectorStore implementation
  wiring/               # Assembles AppContext from env vars
  presentation/         # Axum routes, OpenAPI, SPA serving
  api-types/            # Request/response DTOs (no domain dependency)
  bootstrap/            # HTTP server binary
  worker/               # Background event processor binary

Dependency direction: domain ← application ← {presentation, worker}. Adapters depend on domain only. wiring assembles everything.

Getting Started

Docker (recommended)

docker compose up -d

Local development

Prerequisites

  • Rust stable (rustup update stable)
  • Bun (curl -fsSL https://bun.sh/install | bash)

Quickstart

cp .env.example .env        # edit JWT_SECRET at minimum
make dev                    # API server on :3000
make dev-frontend           # Vite dev server on :5173 (separate terminal)
make dev-worker             # background worker (separate terminal, optional)

The API server also serves the SPA if you run make build-frontend first and SPA_DIR points at the dist directory. For active frontend development, use the Vite dev server instead — it hot-reloads and proxies API calls to :3000.

Environment variables

Variable Process Required Default Description
DATABASE_URL both yes SQLite path, e.g. sqlite:data.db?mode=rwc
JWT_SECRET backend yes HS256 signing secret — generate with openssl rand -hex 32
NATS_URL both no NATS JetStream URL; in-memory bus used if unset
QDRANT_URL both no Enables smart features (semantic links)
ENABLE_EMBEDDINGS worker only no false Set true in the worker to load the fastembed model (~150 MB). Leave unset in the backend to save memory.
QDRANT_COLLECTION both no notes Qdrant collection name
QDRANT_VECTOR_SIZE both no 384 Must match the embedding model output dimension
ALLOW_REGISTRATION backend no true Set false to close public registration
SPA_DIR backend no k-notes-frontend/dist Path to built frontend; set empty for API-only mode
CORS_ORIGINS backend no Comma-separated allowed origins
PORT backend no 3000 HTTP listen port
HOST backend no 0.0.0.0 HTTP listen address
NATS_MAX_DELIVER worker no 5 JetStream dead-letter threshold
SMART_NEIGHBOUR_LIMIT worker no 10 Max semantic links per note
SMART_MIN_SIMILARITY worker no 0.7 Cosine similarity threshold

See .env.example for a commented template.

API

All endpoints are under /api/v1. Full interactive docs at /docs (Swagger) or /scalar after starting the server.

Method Path Auth Description
POST /auth/login Login, returns JWT
POST /auth/register Register (if enabled)
GET /auth/me Current user
GET /config Server capabilities
GET /notes List notes (filter: pinned, archived, tag)
POST /notes Create note
GET /notes/:id Get note
PATCH /notes/:id Update note
DELETE /notes/:id Delete note
PATCH /notes/:id/pin Pin / unpin
PATCH /notes/:id/archive Archive / unarchive
GET /notes/:id/versions Version history
GET /notes/:id/related Semantically related notes
POST /notes/:id/tags Add tag by name
DELETE /notes/:id/tags/:tag_id Remove tag
GET /search?q= Full-text search
GET /tags List tags
POST /tags Create tag
DELETE /tags/:id Delete tag
PATCH /tags/:id Rename tag
GET /export Export all data as JSON
POST /import Import from backup JSON

Deployment

# Build and push image
make deploy

# Or manually
IMAGE=your-registry/k-notes:latest make docker-build docker-push

The CMD in the Dockerfile starts bootstrap (HTTP server). Run ./worker as a separate container or process for background event processing.

Docker Compose volumes to mount:

  • /app/data — SQLite database file
  • /app/data/model-cache — fastembed model cache (avoids re-download on restart)

Project Structure

crates/                     # New architecture (active)
  adapters/                 # Infrastructure adapters
  api-types/                # HTTP DTOs
  application/              # Use cases + WorkerService
  bootstrap/                # API server binary
  domain/                   # Core domain
  presentation/             # Axum + OpenAPI + SPA
  wiring/                   # Dependency assembly
  worker/                   # Event worker binary
k-notes-frontend/           # React SPA
migrations/                 # Legacy migration files (see crates/adapters/sqlite/migrations/)

notes-api/                  ⚠ DEPRECATED — will be removed in a future release
notes-domain/               ⚠ DEPRECATED — will be removed in a future release
notes-infra/                ⚠ DEPRECATED — will be removed in a future release
notes-worker/               ⚠ DEPRECATED — will be removed in a future release

Note: The notes-* directories contain the original monolithic implementation and are kept for reference only. They are excluded from the workspace and are not built. All active development happens in crates/.

License

MIT — Copyright (c) 2025-2026 Gabriel Kaszewski

About

Google Keep replica

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors