18 KiB
Cairnquire - Project Plan
Version: 1.1
Date: 2026-04-29
Status: In Progress — Milestone 1 Complete, Milestone 2 Started
Vision
A minimal-dependency, ultra-fast documentation platform that publishes markdown notes as a responsive web application. Features wiki-style linking, collaborative comments, offline-capable sync, and fine-grained access control — all deployable via a single docker-compose.yml with zero vendor lock-in.
Design Principles
- Paranoid Security: Cryptographic content verification, battle-tested dependencies only, supply-chain attack mitigation through minimal external code
- Operational Simplicity: Single binary, single SQLite database, filesystem storage — nothing that can't be understood in an afternoon
- Performance First: Sub-100ms initial page loads, instant client-side navigation, no JavaScript required for reading
- Offline Capability: Read cached, sync when connected
- No Vendor Lock-in: Works on any Docker host; no cloud-specific APIs
Milestones
Milestone 1: Foundation ✅ COMPLETE
Goal: Working server that can serve markdown files as HTML
- Go HTTP server with chi router
- libsql database with schema migration system
- Markdown-to-HTML pipeline with most common extensions enabled
- Static file serving for attachments
- Basic Dockerfile and docker-compose.yml
- Dev mode with
airlive reload + Vite HMR proxy - Miller column document browser with folder navigation
index.mddefault rendering for folders- Content-addressed attachment storage with secure serving
- WebSocket real-time updates (document change notifications)
Acceptance Criteria:
- Server starts and listens on configurable port
- Database auto-migrates on startup
- Markdown files with wiki-links, tags, callouts, mermaid, math render correctly
- Attachments served securely with content-type headers
- Docker compose brings up full stack with one command
- All dependencies pinned with checksum verification
- Build reproducible (same input → same binary hash)
Notes:
- Mermaid and math blocks render as code blocks (no client-side JS rendering yet). Will be addressed in Milestone 5 with client-side hydration.
- Dev mode uses
airfor Go live reload and Vite proxy for frontend HMR.
Milestone 2: Sync Protocol (Weeks 3-4) — IN PROGRESS
Goal: Bidirectional file sync between server and filesystem/web client
- Content-addressed storage (SHA-256 filesystem layout)
- WebSocket sync protocol implementation
- File watcher for local filesystem sync
- Client-side IndexedDB cache
- Offline queue with conflict detection
Acceptance Criteria:
- File changes on disk automatically sync to server within 5 seconds
- Web client can sync after reconnection, even with changes made offline
- Concurrent edits to different files succeed without conflict
- Concurrent edits to same file detected and queued for resolution
- All synced content verified against SHA-256 hashes
- Sync protocol documented with state machine diagrams
What's Done:
- Real-time file watcher using
fsnotify— detects create/write/remove/rename events on.mdfiles - Changes trigger immediate
SyncSourceDir()and broadcastdocument_versionevents via WebSocket - Browser shows reload notification when current document or folder structure changes
- Fallback polling every 30 seconds ensures nothing is missed
What's Next:
- Client-side IndexedDB cache for offline reading
- Offline edit queue with conflict resolution UI
- State machine diagram for sync protocol
Milestone 3: Authentication & Authorization (Weeks 5-6)
Goal: Secure access control with passkeys and granular permissions
- Passkey (WebAuthn) primary authentication
- Password fallback with Argon2id
- Session management with signed cookies
- Role-based access control (RBAC)
- Content signing with Ed25519 (optional per-user)
Acceptance Criteria:
- Users can register and log in with passkeys on modern browsers
- Password fallback works with proper hashing
- Sessions expire securely and are revocable
- Read/write/admin permissions work per-document and per-collection
- All auth endpoints rate-limited and logged
- Security audit: no plaintext secrets, no timing attacks on auth
Milestone 4: Collaboration Features (Weeks 7-8)
Goal: Comments, notifications, and conflict resolution
- Comment system linked to file versions (by hash)
- Email notifications via Postmark adapter
- Web-based conflict resolution UI
- Notification preferences (per-file, per-folder, global, digest)
- Comment threading and email replies
Acceptance Criteria:
- Users can comment on specific lines/sections of a document
- Comments persist and are linked to document versions
- Email notifications sent for watched file changes
- Replying to notification email creates a comment
- Conflicts display side-by-side diff with resolution actions
- Digest mode batches notifications by time window
Milestone 5: Search & UI Polish (Weeks 9-10)
Goal: Fast search, design system, and production readiness
- Full-text search with SQLite FTS5
- Client-side search index sync
- Design system with browser-based CSS editor
- Responsive mobile layout
- Error boundaries and loading states
- Performance benchmarking
- Client-side Mermaid and KaTeX rendering
Acceptance Criteria:
- Search returns results in <50ms on client
- Search works offline after initial sync
- Design system documented at
/designroute - All screens responsive down to 320px width
- Lighthouse score >90 on all metrics
- Initial page load <100ms (server-rendered HTML)
- Mermaid diagrams render client-side
- Math blocks render with KaTeX
Milestone 6: Production Hardening (Weeks 11-12)
Goal: Deploy with confidence
- Comprehensive test suite (unit, integration, e2e)
- Security audit and penetration testing guide
- Backup and restore procedures
- Monitoring and health checks
- Documentation for operators
Acceptance Criteria:
- 80% test coverage
- No critical or high-severity security findings
- Backup script exports database + attachments as tar.gz
- Health check endpoint returns status of all dependencies
- Operator docs cover: install, upgrade, backup, security incident response
- Load test: 1000 concurrent readers, 100 concurrent editors
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Client Browser │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Preact UI │ │ File Sync │ │ Search Index │ │
│ │ (hydrated) │ │ (IndexedDB) │ │ (FTS5) │ │
│ └──────────────┘ └──────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
WebSocket / HTTP
│
┌─────────────────────────────────────────────────────────────┐
│ Go Application Server │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐ │
│ │ HTTP │ │ WebSocket │ │ Markdown │ │ Auth │ │
│ │ Router │ │ Sync │ │ Renderer │ │ (WebAuthn)│ │
│ └────────────┘ └────────────┘ └────────────┘ └──────────┘ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Email │ │ Search │ │ File Watch │ │
│ │ (Postmark)│ │ (FTS5) │ │ (fsnotify)│ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌──────────────────┴──────────────────┐
│ │
┌──────────────┐ ┌──────────────┐
│ libsql │ │ Content- │
│ (SQLite) │ │ Addressed │
│ │ │ Filesystem │
└──────────────┘ └──────────────┘
See architecture-overview.md for detailed component design.
Technology Stack
Backend (Go 1.24+)
| Component | Library | Justification |
|---|---|---|
| HTTP Router | github.com/go-chi/chi |
Minimal, idiomatic, stdlib-compatible |
| Database | github.com/tursodatabase/go-libsql |
Embedded SQLite with replication option |
| Migrations | Internal migration runner | Simple, no extra dependency |
| Auth/Passkeys | github.com/go-webauthn/webauthn |
Reference implementation, well-audited |
| Password Hash | golang.org/x/crypto/argon2 |
Winner of Password Hashing Competition |
| Markdown | github.com/yuin/goldmark + extensions |
Fast, extensible, CommonMark compliant |
| WebSocket | github.com/gorilla/websocket |
Most widely used Go WS library |
| File Watch | github.com/fsnotify/fsnotify |
Cross-platform, stdlib-adjacent |
github.com/keighl/postmark + net/smtp |
Postmark adapter + SMTP fallback | |
| Crypto | crypto/ed25519, crypto/sha256 |
Standard library, no external deps |
| Templates | html/template |
Stdlib, auto-escaping, SSR-safe |
Frontend (Preact + TypeScript)
| Component | Library | Justification |
|---|---|---|
| Framework | preact (10KB) |
React API, tiny bundle, fast hydration |
| Routing | preact-iso |
Lightweight isomorphic routing |
| Markdown | marked + micromark |
Server+client compatible |
| Search | flexsearch |
Client-side full-text, zero server load |
| Styling | Native CSS + custom properties | No build-time CSS processing needed |
| Icons | Inline SVG | No icon font dependency |
| Build | vite |
Fast HMR, modern output, minimal config |
Infrastructure
| Component | Technology | Justification |
|---|---|---|
| Container | Docker + Compose | Universal deployment target |
| Reverse Proxy | Traefik (optional) | Auto-HTTPS, included in compose |
| Database | libsql/SQLite | Single file, zero external dependency |
| Storage | Local filesystem | Content-addressed, immutable, backup-friendly |
Security Model
Threats Addressed
- Supply Chain Attacks: Minimal external dependencies; all pinned with go.sum; vendored if needed
- Data Tampering: All document versions SHA-256 hashed; optional Ed25519 signatures
- Unauthorized Access: RBAC with principle of least privilege; content never served without auth check
- Session Hijacking: Signed cookies with HttpOnly, Secure, SameSite=Strict; short expiry with refresh
- CSRF: Double-submit cookie pattern for state-changing operations
- XSS: Go html/template auto-escaping; strict CSP headers; no inline scripts except nonce-tagged
- DoS: Rate limiting on auth endpoints; file upload size limits; query timeouts
Content Security
- Attachment uploads: Magic number validation, extension whitelist, size limits
- Markdown rendering: Sanitized HTML output; no raw HTML injection
- File paths: Canonicalization prevents directory traversal
Audit & Compliance
- Every data modification logged with user, timestamp, IP, and content hash
- Immutable history table — updates are inserts
- GDPR-compliant: full data export and deletion capability
Performance Targets
| Metric | Target | Measurement |
|---|---|---|
| Initial Page Load | <100ms | TTFB + First Contentful Paint |
| Time to Interactive | <200ms | Hydration complete |
| Search Query | <50ms | Client-side after initial sync |
| File Sync | <5s | From disk save to server persist |
| Bundle Size | <100KB | Gzipped JS + CSS |
| Concurrent Users | 1000 readers, 100 editors | Load tested |
Monorepo Structure
cairnquire/
├── README.md
├── docker-compose.yml
├── Dockerfile
├── mise.toml
├── docs/ # Project documentation
│ ├── project-plan.md # This file
│ ├── architecture-overview.md # System architecture
│ ├── adrs/ # Architecture Decision Records
│ ├── milestones/ # Detailed milestone plans
│ └── specs/ # Technical specifications
├── apps/
│ ├── server/ # Go HTTP application
│ │ ├── cmd/cairnquire/
│ │ ├── internal/
│ │ │ ├── app/ # Application orchestration
│ │ │ ├── config/ # Configuration management
│ │ │ ├── database/ # Database & migrations
│ │ │ ├── docs/ # Document service & repository
│ │ │ ├── httpserver/ # HTTP handlers & templates
│ │ │ ├── markdown/ # Markdown rendering pipeline
│ │ │ ├── realtime/ # WebSocket hub
│ │ │ └── store/ # Content-addressed filesystem
│ │ ├── static/ # Static assets
│ │ └── templates/ # Go HTML templates
│ └── web/ # Preact frontend application
│ ├── src/
│ ├── index.html
│ └── vite.config.ts
├── content/ # Markdown source files
├── data/ # SQLite database & attachments
└── packages/
└── protocol/ # Shared sync protocol spec
Risk Register
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| WebAuthn/passkey adoption issues | Medium | Medium | Password fallback always available; extensive browser testing |
| SQLite performance at scale | Low | High | libsql replication path; FTS5 for search; benchmark early |
| Markdown extension parser bugs | Medium | Medium | Fuzz testing; strict CommonMark base; gradual feature rollout |
| Email deliverability | Medium | Medium | Postmark reputation monitoring; SPF/DKIM setup; SMTP fallback |
| Frontend bundle bloat | Medium | Medium | Bundle analysis in CI; tree-shaking enforced; lazy loading |
| File sync conflicts in teams | High | Medium | Clear UX for conflict resolution; email notification; automatic merge when possible |
Next Steps
- ✅ Complete Milestone 1: Foundation
- 🔄 Continue Milestone 2: Sync Protocol
- Implement client-side IndexedDB cache
- Build offline edit queue
- Create sync protocol state machine diagram
- Begin Milestone 3: Authentication & Authorization
- Set up WebAuthn/passkey infrastructure
- Implement session management
- Add client-side Mermaid and KaTeX rendering (Milestone 5)
- Implement SQLite FTS5 search (Milestone 5)
- Add Renovate with dependency update cooldown policies
Glossary
- ADR: Architecture Decision Record
- CRDT: Conflict-free Replicated Data Type (not used, but considered)
- CSRF: Cross-Site Request Forgery
- CSP: Content Security Policy
- FTS5: SQLite Full-Text Search version 5
- RBAC: Role-Based Access Control
- SSR: Server-Side Rendering
- TTFB: Time to First Byte
- WebAuthn: Web Authentication API (passkeys)