# Stack Research **Domain:** Personal Task/Notes Web App (self-hosted, containerized, single-user) **Researched:** 2026-01-29 **Confidence:** HIGH ## Recommended Stack ### Core Technologies | Technology | Version | Purpose | Why Recommended | |------------|---------|---------|-----------------| | **SvelteKit** | 2.50.x | Full-stack framework | Best DX for solo/small projects. Svelte 5's runes provide explicit reactivity. No virtual DOM = smaller bundles and faster performance. Built-in routing, SSR, and API routes. Perfect for self-hosted single-user apps. | | **Svelte** | 5.49.x | UI framework | Compiles to vanilla JS with minimal runtime. 30% faster load times vs React/Vue per 2026 benchmarks. Smaller bundle = faster on any device. | | **SQLite** | 3.x | Database | Zero-config, serverless, single-file database. Ideal for single-user apps. FTS5 provides full-text search built-in. No separate database service to manage in Docker. | | **better-sqlite3** | 12.x | SQLite driver | 20-39% faster than alternatives. Synchronous API is simpler for single-user apps. Native bindings outperform WASM alternatives. | | **TypeScript** | 5.x | Language | Type safety catches bugs early. First-class Svelte/SvelteKit support. Industry standard for modern web development. | ### Supporting Libraries | Library | Version | Purpose | When to Use | |---------|---------|---------|-------------| | **sharp** | 0.34.x | Image processing | Always - for thumbnail generation, image optimization. 4-5x faster than ImageMagick. Handles JPEG, PNG, WebP, AVIF. | | **Drizzle ORM** | 0.45.x (stable) | Database ORM | Recommended for type-safe SQL. Code-first schema in TypeScript. Lightweight (~7.4kb), no runtime overhead. Excellent SQLite support. | | **Tailwind CSS** | 4.1.x | Styling | Always - utility-first CSS with zero runtime overhead. Automatic purging keeps CSS tiny. Great for rapid prototyping. | | **Zod** | 3.x | Validation | Form validation, API input validation. Integrates with SvelteKit's form actions. TypeScript-first schema validation. | | **nanoid** | 5.x | ID generation | URL-safe unique IDs for tasks/notes. Smaller and faster than UUID. | ### Development Tools | Tool | Purpose | Notes | |------|---------|-------| | **Vite** | Build tool | Built into SvelteKit. Fast HMR, optimized builds. | | **ESLint + Prettier** | Code quality | Use `eslint-plugin-svelte` for Svelte-specific rules. | | **Vitest** | Testing | Vite-native testing. Fast, compatible with SvelteKit. | | **Docker** | Containerization | Multi-stage builds for minimal production images. Use `node:22-alpine` base. | ### Infrastructure (Self-Hosted) | Technology | Version | Purpose | Why Recommended | |------------|---------|---------|-----------------| | **Node.js** | 22 LTS | Runtime | Current LTS. Required for better-sqlite3 native bindings. | | **Docker** | Latest | Container | Multi-stage builds reduce image size 10x. Alpine base keeps footprint small (~70MB). | | **Caddy** | 2.x | Reverse proxy (optional) | Simple config, automatic HTTPS. Built-in basic auth if needed. | ## Installation ```bash # Create SvelteKit project npx sv create taskplaner cd taskplaner # Core dependencies npm install better-sqlite3 drizzle-orm sharp nanoid zod # Dev dependencies npm install -D drizzle-kit @types/better-sqlite3 tailwindcss @tailwindcss/vite ``` ## File Storage Strategy For image attachments, use **local filesystem storage**: ``` /data/ database.sqlite # SQLite database attachments/ # Image files {year}/{month}/ # Organized by date {nanoid}.webp # Optimized images ``` **Why filesystem over object storage:** - Single-user app doesn't need S3 complexity - Easy to backup (just copy the /data folder) - No external dependencies - Docker volume mount makes it simple **Image processing pipeline:** 1. Accept upload (validate file type server-side, not just Content-Type header) 2. Generate thumbnail with sharp (resize to max 1920px width, convert to WebP) 3. Store original and thumbnail 4. Save path reference in SQLite ## Alternatives Considered | Recommended | Alternative | When to Use Alternative | |-------------|-------------|-------------------------| | SvelteKit | Next.js | If you need React ecosystem libraries or team already knows React. | | SvelteKit | Hono + separate frontend | If building API-first architecture for mobile apps later. | | SQLite | PostgreSQL | If you expect multiple concurrent users or need advanced features like JSONB operators. | | better-sqlite3 | Drizzle with LibSQL | If you want Turso cloud sync in the future. | | Drizzle ORM | Prisma | If you prefer schema-first approach and don't mind slower cold starts. | | Tailwind CSS | CSS Modules | If you dislike utility classes and prefer scoped traditional CSS. | | sharp | Thumbor | If you need a dedicated image service with caching CDN. Overkill for single-user. | ## What NOT to Use | Avoid | Why | Use Instead | |-------|-----|-------------| | **Express.js** | Outdated, slow, development stalled. No need for separate backend with SvelteKit. | SvelteKit API routes (or Hono if you must separate) | | **Prisma** | Heavier runtime, slower cold starts, schema-first adds build step. | Drizzle ORM - lighter, faster, code-first | | **MongoDB** | Overkill for structured task/note data. No ACID by default. Adds container complexity. | SQLite - simpler, faster for this use case | | **Redis** | Unnecessary for single-user app. SQLite is fast enough for session/cache. | SQLite in-memory or just application state | | **CSS-in-JS (styled-components, Emotion)** | Runtime overhead, larger bundles, complexity. | Tailwind CSS - zero runtime, tiny output | | **TypeORM** | Buggy, inconsistent TypeScript support, heavy. | Drizzle ORM | | **Sequelize** | Old patterns, poor TypeScript support. | Drizzle ORM | | **sql.js** | 50-60% slower than native SQLite. Only use if browser-side SQLite needed. | better-sqlite3 for Node.js server | | **node-sqlite3** | Callback-based API, slower than better-sqlite3. | better-sqlite3 | | **Full auth systems (Keycloak, Authentik)** | Massive overkill for single-user. Adds container complexity. | Simple session with hashed password in SQLite, or Caddy basic auth | ## Stack Patterns by Variant **If you want maximum simplicity:** - Skip Drizzle, use better-sqlite3 directly with raw SQL - Use SvelteKit's built-in `$app/stores` for state instead of external state management - Single Dockerfile with embedded SQLite **If you might add mobile app later:** - Consider separating API with Hono - Add JWT-based auth from the start - Design REST API endpoints alongside SvelteKit routes **If you want offline-first/PWA:** - Add service worker (SvelteKit supports this) - Consider IndexedDB for client-side cache - Implement sync strategy for when online ## Authentication Recommendation For **single-user self-hosted**, keep it simple: ```typescript // Simple password hash stored in SQLite or environment variable // Use Argon2 or bcrypt for password hashing import { hash, verify } from '@node-rs/argon2'; // Session stored as httpOnly cookie // SvelteKit hooks handle session validation ``` **Do NOT:** - Use full OAuth providers (Google, GitHub) - unnecessary for single-user - Implement JWT tokens - session cookies are simpler and more secure for web-only - Use external auth services - adds complexity and external dependencies **Consider:** - Caddy's built-in basic auth as the simplest option (one line of config) - Environment variable for password hash (no database needed for auth) ## Version Compatibility | Package | Compatible With | Notes | |---------|-----------------|-------| | better-sqlite3@12.x | Node.js 18.17+ or 20.3+ | Requires native compilation or prebuilt binaries | | sharp@0.34.x | Node.js 18.17+ or 20.3+ | Uses libvips, prebuilts available for common platforms | | Drizzle@0.45.x | better-sqlite3@11+ | Use `drizzle-orm/better-sqlite3` adapter | | SvelteKit@2.50.x | Svelte 5.x | Svelte 5 is peer dependency | | Tailwind@4.1.x | Vite (built into SvelteKit) | Use `@tailwindcss/vite` plugin | ## Docker Configuration ```dockerfile # Multi-stage build for minimal production image FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:22-alpine AS production WORKDIR /app # Install only production dependencies COPY package*.json ./ RUN npm ci --omit=dev # Copy built application COPY --from=builder /app/build ./build # Create non-root user RUN adduser -D appuser && chown -R appuser /app USER appuser # Data volume for SQLite and attachments VOLUME /app/data EXPOSE 3000 CMD ["node", "build"] ``` **Key practices:** - Multi-stage build reduces image from ~500MB to ~70MB - Non-root user for security - Volume mount for persistent data - Alpine base for minimal attack surface - Use `npm ci` for reproducible builds ## Search Implementation Use **SQLite FTS5** (built-in full-text search): ```sql -- Create FTS5 virtual table CREATE VIRTUAL TABLE notes_fts USING fts5( title, content, content='notes', content_rowid='id' ); -- Search with ranking SELECT * FROM notes_fts WHERE notes_fts MATCH 'search query' ORDER BY rank; ``` **Why FTS5 over Meilisearch/Typesense:** - Zero dependencies - built into SQLite - 6-8x smaller index size than Meilisearch - Much lower memory usage (~75MB vs 800MB) - Simpler architecture - just SQL queries - Sufficient for personal notes (thousands, not millions of documents) ## Sources ### HIGH Confidence (Official Documentation) - [Svelte 5.49.0 Release](https://github.com/sveltejs/svelte/releases) - Current stable version verified - [SvelteKit 2.50.1 Release](https://github.com/sveltejs/kit/releases) - Current stable version verified - [Hono 4.11.7 Release](https://github.com/honojs/hono/releases) - Current stable version verified - [Tailwind CSS v4.1 Documentation](https://tailwindcss.com/docs/installation) - Current version verified - [sharp Documentation](https://sharp.pixelplumbing.com/) - API and requirements verified - [better-sqlite3 GitHub](https://github.com/WiseLibs/better-sqlite3) - Performance claims verified - [SQLite FTS5 Documentation](https://www.sqlite.org/fts5.html) - Built-in full-text search - [Docker Node.js Best Practices](https://docs.docker.com/guides/nodejs/containerize/) - Official guide ### MEDIUM Confidence (Multiple Sources Agree) - [Drizzle vs Prisma Comparison](https://betterstack.com/community/guides/scaling-nodejs/drizzle-vs-prisma/) - Performance characteristics - [React vs Vue vs Svelte 2026](https://www.frontendtools.tech/blog/best-frontend-frameworks-2025-comparison) - Framework comparison benchmarks - [SQLite vs PostgreSQL Comparison](https://www.selecthub.com/relational-database-solutions/postgresql-vs-sqlite/) - Use case recommendations - [Node.js Backend Framework Comparison](https://betterstack.com/community/guides/scaling-nodejs/hono-vs-fastify/) - Express vs Fastify vs Hono - [SQLite Driver Benchmark](https://sqg.dev/blog/sqlite-driver-benchmark) - better-sqlite3 performance verified ### LOW Confidence (Single Source, Verify Before Using) - Drizzle ORM 1.0 release timing - still in beta, stable is 0.45.x - Sharp 0.35.0 - currently RC, not stable --- *Stack research for: Personal Task/Notes Web App* *Researched: 2026-01-29*