# Product Requirements Document (PRD) ## Ironpad - Personal Project & Knowledge Management System **Version:** 3.0 **Date:** 2026-02-02 **Status:** Active Development **Author:** Internal / Personal **Changelog:** v3.0 - Addressed concurrency, file watching, git conflicts, port handling, and frontmatter automation --- ## 1. Executive Summary **Ironpad** is a **local-first, browser-based personal project management and note-taking system** powered by a Rust backend. The system combines: - Free-form markdown notes - Task management per project - Project organization - Full-text search - Git-based versioning - **Real-time file system watching** for external edits - **Automatic frontmatter management** for low-ceremony UX **Core Philosophy:** Simplicity first, power through composition, not rigid workflows. **Key Innovation:** Single-binary Rust executable that auto-opens your browser - no bundled Chromium, no Node.js runtime required. --- ## 2. Goals ### Primary Goals - Provide a single place to store thoughts, notes, and tasks - Enable lightweight project organization without heavy structure - Support incremental evolution of features - Keep the system easy to understand, modify, and extend - Learn Rust through practical application - **Eliminate manual metadata management** (auto-update timestamps, IDs) - **Support external editing** (VS Code, Obsidian, etc.) ### Technical Goals - Zero-dependency data storage (plain files only) - Fast search (<100ms for 1000 notes) - Responsive UI (<16ms frame time) - Startup time <500ms - Tiny binary size (<15 MB) - No browser bundling (use system browser) - **Graceful port conflict handling** - **Real-time sync between UI and filesystem** - **Robust git conflict handling** ### Non-Goals (v1) - Multi-user collaboration - Cloud sync (Git remote is optional) - Permissions/roles - Complex workflow automation - Enterprise-grade task management - Mobile app (mobile access not required) --- ## 3. Design Principles ### 1. Local-First - No external services required - Works fully offline - Data stored on local file system - **File system is the source of truth** (not UI state) ### 2. Notes-First, Tasks-Per-Project - Notes are the primary unit - Each project has its own task list - Tasks don't float independently ### 3. File-Based Storage - Data stored as Markdown files - Editable outside the app - Human-readable formats only - **External editors fully supported** (VS Code, Obsidian, Vim) ### 4. Low Ceremony - Minimal configuration - Minimal UI friction - No complex setup wizards - **Automatic metadata management** (no manual timestamps/IDs) ### 5. Future-Proof - Easy to migrate to other tools - Git-friendly formats - No vendor lock-in ### 6. Simplicity Principles - **No abstraction layers** - Files are the database - **No build step for data** - Markdown is human-readable - **No proprietary formats** - Everything is standard - **No server required** - Runs entirely on localhost - **No complex workflows** - Write, save, done --- ## 4. Architecture ### Overview ``` User launches executable ↓ Rust Backend (Axum) - HTTP server on dynamic port (3000-3010) - Serves Vue frontend (static files) - REST API for file operations - Git operations with conflict handling - Full-text search - File system watcher (notify crate) - WebSocket server for real-time updates ↓ Auto-opens default browser → http://localhost:{port} ↓ Vue 3 Frontend (in browser) - Markdown editor - Task management - Project switching - WebSocket client (receives file change events) ↓ Bidirectional real-time sync ← WebSocket → File System Changes ``` ### Why Not Electron? | **Electron** | **Ironpad (Rust)** | |-------------|-------------------| | 150-300 MB bundle | 5-15 MB binary | | Bundles Chromium | Uses system browser | | 200-500 MB RAM | 10-50 MB RAM | | 2-5s startup | <500ms startup | | Complex distribution | Single executable | **Rationale:** No need to bundle an entire browser when every user already has one. ### Technology Stack #### Backend (Rust) - **Axum** - Web framework (simple, fast, learning-friendly) - **Tokio** - Async runtime - **Tower** - Middleware - **Serde** - JSON serialization - **markdown-rs** - Markdown parsing with frontmatter (CommonMark compliant) - **git2** - Git operations with lock handling - **webbrowser** - Cross-platform browser launching - **notify** - File system watching for external changes - **axum-ws** or **tower-websockets** - WebSocket support for real-time updates - **ripgrep** library or **tantivy** - Fast search #### Frontend (Vue 3) - **Vue 3** - UI framework (Composition API) - **Vite** - Build tool - **CodeMirror 6** - Markdown editor with syntax highlighting - **Pinia** - State management (minimal caching, trust filesystem) - **markdown-it** - Markdown rendering (CommonMark mode for consistency) - **Native WebSocket API** - Real-time file change notifications #### Data Storage - **Markdown files** (.md) on local file system - **YAML frontmatter** for metadata (auto-managed by backend) - **Git repository** for versioning - **No database** - files are the database --- ## 5. User Experience ### Mental Model - Similar to a notebook system (OneNote / Obsidian) - A **front page** (index.md) acts as entry point - Users can create notes and projects - Projects contain notes + dedicated task list - Simple navigation via sidebar - **Edit anywhere** - changes in VS Code/Obsidian auto-sync to UI - **Conflict-free** - UI prevents simultaneous edits of same file ### Core UI Areas #### 1. Sidebar (Left) - **Projects** section - List of all projects - Quick switch between projects - **Notes** section - List of standalone notes - Daily notes - **Quick actions** - New note - New project - Search - **Git status indicator** (changes pending commit) #### 2. Main Area (Center) - **Editor view** - CodeMirror markdown editor - Syntax highlighting - Auto-save (2s debounce) - **File lock indicator** (shows if file open in Task View) - **External edit notification** (shows banner if file changed externally) - **Split view option** - Editor on left - Preview on right #### 3. Task View (Separate Page) - View: `/projects/:id/tasks` - Shows tasks for currently selected project - Parse checkboxes from `data/projects/{id}/tasks.md` - Sections: Active, Completed, Backlog - Quick checkbox toggle - **Prevents editor access** - If Task View open, editor shows "Read-Only" mode --- ## 6. Data Model ### File Structure ``` data/ .git/ # Git repository index.md # Front page / landing inbox.md # Quick capture daily/ # Daily notes (optional) 2026-02-02.md 2026-02-03.md projects/ # Project folders ironpad/ index.md # Project overview tasks.md # Task list notes.md # Miscellaneous notes (optional) assets/ # Images, attachments screenshot.png homelab/ index.md tasks.md assets/ notes/ # Standalone notes ideas.md rust-learning.md assets/ # Shared assets diagram.png archive/ # Completed/archived items old-project/ ``` ### Frontmatter Schema #### Standard Fields (All Files) - AUTO-MANAGED ```yaml --- # Auto-generated by backend (user never edits these manually) id: ironpad-index # Derived from filename: {folder}-{filename} type: note # Detected from file location created: 2026-02-02T01:00:00Z # Set on file creation updated: 2026-02-02T01:15:00Z # Auto-updated on every save # Optional user fields title: Ironpad Development # Optional: display title (fallback: filename) tags: [dev, rust, personal] # Optional: user-defined tags status: active # Optional: draft|active|archived|complete --- ``` **Key Change:** Users never manually write `id`, `created`, or `updated` fields. Backend handles these automatically. #### ID Generation Strategy - **Format**: `{parent-folder}-{filename-without-extension}` - **Examples**: - `projects/ironpad/index.md` → `id: ironpad-index` - `notes/ideas.md` → `id: notes-ideas` - `daily/2026-02-02.md` → `id: daily-2026-02-02` - **Rationale**: Human-readable, deterministic, no UUIDs cluttering files #### Task File Example ```yaml --- id: ironpad-tasks # Auto-generated from filename type: tasks # Auto-detected project_id: ironpad # Parent folder name created: 2026-02-01T12:00:00Z updated: 2026-02-02T01:00:00Z # Auto-updated on every save --- # Tasks: Ironpad ## Active - [ ] Set up Rust backend with Axum - [ ] Create Vue frontend with CodeMirror - [ ] Implement task parsing ## Completed - [x] Write PRD - [x] Review architecture decisions ## Backlog - [ ] Add full-text search - [ ] Implement Git auto-commit ``` --- ## 7. Functional Requirements ### 7.1 Notes Management - **Create** new notes via sidebar button - **Read** note content with markdown rendering - **Update** notes with auto-save (2s debounce after last edit) - **Delete** notes (moves to archive/ folder) - Notes stored as `.md` files in `data/notes/` - **Auto-update** `updated` timestamp on every save ### 7.2 Project Management - **Create** new projects (creates `data/projects/{id}/` folder + `assets/` subfolder) - **View** project overview (`index.md`) - **Switch** between projects via sidebar - **Archive** completed projects (moves to `archive/`) - Projects automatically get `tasks.md` file + `assets/` folder ### 7.3 Task Management - **View** tasks per project at `/projects/:id/tasks` - **Toggle** task completion (checkbox state) - **Add** new tasks via UI (appends to tasks.md) - **Organize** tasks in sections: Active, Completed, Backlog - Tasks represented as Markdown checkboxes: ```markdown - [ ] Incomplete task - [x] Completed task ``` - **No global task view in v1** - tasks belong to projects - **Concurrency handling**: If Task View is open for a file, Editor View shows "Read-Only - Open in Task View" banner ### 7.4 Search - **Full-text search** across all markdown files - Search triggered from sidebar search box - Results show: filename, matching line, context - **Implementation**: Use `ripgrep` as library (faster than naive grep) - Performance target: <100ms for 1000 notes - **Future**: Migrate to Tantivy if needed (>5000 notes) ### 7.5 Git Integration - **Auto-commit** with time-based batching (5 minutes) - **Manual commit** button available - **Commit message format**: `Auto-save: {timestamp}` or user-provided message - **Git history** viewable via external tools (GitKraken, git log, etc.) - **Remote push** optional (manual via git push or UI button) - **Lock handling**: If `.git/index.lock` exists, skip auto-commit and retry next cycle - **Conflict detection**: If `git status` shows conflicts, show warning banner in UI ### 7.6 File System Watching (NEW) - **Backend watches** `data/` directory using `notify` crate - **Detects changes** made by external editors (VS Code, Obsidian, Vim) - **Sends WebSocket message** to frontend: `{ type: "file_changed", path: "notes/ideas.md" }` - **Frontend response**: - If file is currently open in editor → Show banner: "File changed externally. Reload?" - If file is not open → Auto-refresh sidebar file list - If Task View is open for that file → Auto-reload task list - **Debouncing**: Batch file changes (100ms) to avoid spamming WebSocket ### 7.7 Concurrency Control (NEW) - **Single-file lock**: Only one view (Editor OR Task View) can edit a file at a time - **Implementation**: - Backend tracks "open files" via WebSocket connection state - When Task View opens `tasks.md`, backend marks file as "locked for task editing" - If user tries to open same file in Editor, show "Read-Only" mode - When Task View closes, file unlocked automatically - **Rationale**: Prevents race conditions between checkbox toggles and text edits --- ## 8. API Design ### REST Endpoints #### Notes ``` GET /api/notes # List all notes GET /api/notes/:id # Get note content POST /api/notes # Create new note (auto-generates frontmatter) PUT /api/notes/:id # Update note content (auto-updates 'updated' field) DELETE /api/notes/:id # Delete note (archive) ``` #### Projects ``` GET /api/projects # List all projects GET /api/projects/:id # Get project details POST /api/projects # Create new project (creates folder + assets/) PUT /api/projects/:id # Update project DELETE /api/projects/:id # Archive project ``` #### Tasks ``` GET /api/projects/:id/tasks # Get tasks for project PUT /api/projects/:id/tasks # Update tasks for project POST /api/tasks/lock/:id # Lock file for task editing POST /api/tasks/unlock/:id # Unlock file ``` #### Search ``` GET /api/search?q={query} # Search all content (ripgrep-powered) ``` #### Git ``` POST /api/git/commit # Manual commit with message GET /api/git/status # Get git status (detects conflicts) POST /api/git/push # Push to remote (if configured) GET /api/git/conflicts # Check for merge conflicts ``` #### Assets (NEW) ``` POST /api/assets/upload # Upload image/file to project assets/ GET /api/assets/:project/:file # Retrieve asset file ``` ### WebSocket Endpoints (NEW) ``` WS /ws # WebSocket connection for real-time updates # Messages from backend → frontend: { type: "file_changed", path: "notes/ideas.md", timestamp: "2026-02-02T01:00:00Z" } { type: "file_deleted", path: "notes/old.md" } { type: "file_created", path: "daily/2026-02-03.md" } { type: "git_conflict", files: ["notes/ideas.md"] } # Messages from frontend → backend: { type: "subscribe_file", path: "notes/ideas.md" } # Track active file { type: "unsubscribe_file", path: "notes/ideas.md" } ``` --- ## 9. Technical Implementation Details ### 9.1 Auto-Save Strategy - **Decision**: 2-second debounce after last edit - **Rationale**: Balance between data safety and performance - **Implementation**: Frontend debounces PUT requests - **Backend behavior**: On PUT, auto-update `updated` timestamp in frontmatter ### 9.2 Git Commit Strategy - **Decision**: Time-based batching (5 minutes) + manual commit button - **Rationale**: Clean history, reduced I/O, user control - **Implementation**: - Rust background task (tokio::spawn) runs every 5 minutes - Checks `git status --porcelain` for changes - If `.git/index.lock` exists → Skip and log warning - If changes exist → `git add .` → `git commit -m "Auto-save: {timestamp}"` - If commit fails (lock, conflict) → Show error in UI via WebSocket ### 9.3 Editor Choice - **Decision**: CodeMirror 6 with markdown mode - **Rationale**: Mature, performant, excellent UX out-of-box - **Features**: Syntax highlighting, line numbers, keyboard shortcuts ### 9.4 Search Implementation (v1) - **Decision**: Use `ripgrep` as library (via `grep` crate or direct integration) - **Rationale**: - Faster than naive grep (uses SIMD, optimized algorithms) - Handles >1000 files easily - Used by VS Code, Sublime Text - **Future**: Migrate to Tantivy if search becomes slow (>5000 notes) ### 9.5 Browser Launch - **Decision**: Use `webbrowser` crate to open default browser - **Rationale**: Cross-platform, simple, no browser bundling - **Fallback**: Print URL if browser launch fails ### 9.6 Port Conflict Handling (NEW) - **Decision**: Dynamic port selection (3000-3010 range) - **Implementation**: ```rust async fn find_available_port() -> u16 { for port in 3000..=3010 { if let Ok(listener) = TcpListener::bind(("127.0.0.1", port)).await { return port; } } panic!("No available ports in range 3000-3010"); } ``` - **User experience**: Always works, even if other dev tools running - **Logging**: Print actual port used: `🚀 Ironpad running on http://localhost:3005` ### 9.7 File System Watching (NEW) - **Decision**: Use `notify` crate with debouncing - **Implementation**: ```rust use notify::{Watcher, RecursiveMode, Event}; let (tx, rx) = channel(); let mut watcher = notify::recommended_watcher(tx)?; watcher.watch(Path::new("data/"), RecursiveMode::Recursive)?; // Debounce: Collect events for 100ms, then broadcast via WebSocket ``` - **Events tracked**: Create, Modify, Delete - **Ignored paths**: `.git/`, `node_modules/`, `.DS_Store` ### 9.8 Frontmatter Automation (NEW) - **Decision**: Backend owns all frontmatter management - **Implementation**: - On file creation: Generate frontmatter with `id`, `type`, `created`, `updated` - On file update: Parse YAML, update `updated` field, rewrite file - Use `gray_matter` or `serde_yaml` crates - **User experience**: Users never manually edit timestamps or IDs ### 9.9 Markdown Consistency (NEW) - **Decision**: Use CommonMark standard everywhere - **Backend**: `markdown-rs` (CommonMark compliant) - **Frontend**: `markdown-it` with CommonMark preset - **Rationale**: Prevents rendering mismatches between preview and backend parsing --- ## 10. Build & Distribution ### Development Workflow ```bash # Terminal 1: Frontend dev server with hot reload cd frontend npm install npm run dev # Runs on localhost:5173 # Terminal 2: Rust backend (proxies frontend) cd backend cargo run # Runs on localhost:3000-3010, opens browser ``` ### Production Build ```bash # Step 1: Build frontend cd frontend npm run build # Outputs to frontend/dist/ # Step 2: Copy frontend to Rust static folder cp -r frontend/dist/* backend/static/ # Step 3: Build Rust release binary cd backend cargo build --release # Output: backend/target/release/ironpad # Size: ~5-15 MB ``` ### Distribution - **Single executable** - `ironpad.exe` (Windows), `ironpad` (Mac/Linux) - **No installer required** - Just run the binary - **No dependencies** - Statically linked (except libc) - **User experience**: 1. User downloads `ironpad.exe` 2. User double-clicks executable 3. Browser opens automatically to available port 4. App is ready to use --- ## 11. Data Safety & Backup ### Git as Primary Backup - Every save eventually commits to local Git repo - Repo can be pushed to remote (GitHub, GitLab, self-hosted) - All history preserved indefinitely ### Recommended Setup ```bash # Initialize repo (done automatically on first run) cd data git init # Add remote (optional, manual or via UI) git remote add origin https://github.com/yourusername/ironpad-data.git # Auto-push (future feature) # UI button: "Push to Remote" ``` ### Disaster Recovery - Clone repo on new machine - Point Ironpad to cloned folder - All history and data preserved - No proprietary formats to migrate ### Conflict Handling - If `.git/index.lock` exists → Skip auto-commit, retry next cycle - If `git status` shows conflicts → Show banner in UI: "Git conflicts detected. Resolve manually." - Never auto-merge conflicts → User must resolve via git CLI or external tool --- ## 12. Success Criteria The system is successful if: - ✅ Used daily for notes and tasks - ✅ Data remains readable without the app - ✅ Adding new features does not require migrations - ✅ System feels flexible rather than restrictive - ✅ Binary size stays under 15 MB - ✅ Startup time under 500ms - ✅ Search completes in <100ms - ✅ **External edits sync instantly** (<500ms latency) - ✅ **No manual frontmatter editing required** - ✅ **Never crashes due to port conflicts** ### Usage Metrics (Personal Tracking) - Daily notes created per week (target: 5+) - Tasks completed per week (target: 10+) - Projects with active tasks (target: 2-3) - Average note length (target: 200+ words) - External edits per week (VS Code usage, target: tracked but not enforced) ### Performance Metrics - App startup time (target: <500ms) - Search response time (target: <100ms) - Save operation time (target: <50ms) - Binary size (target: <15 MB) - File change notification latency (target: <500ms) - WebSocket message latency (target: <100ms) --- ## 13. Implementation Phases ### Phase 1: MVP (Week 1-2) **Goal**: Basic note CRUD + auto-open browser + dynamic port - [ ] Rust backend with Axum - [ ] Dynamic port selection (3000-3010) - [ ] File CRUD operations (read/write .md files) - [ ] Automatic frontmatter generation (id, created, updated) - [ ] Auto-open browser on launch - [ ] Vue frontend scaffold - [ ] Basic markdown editor (textarea) - [ ] File list sidebar - [ ] Auto-save with debounce ### Phase 2: Core Features (Week 3-4) **Goal**: Usable daily driver with real-time sync - [ ] CodeMirror 6 integration - [ ] Project creation/switching (with assets/ folders) - [ ] Task parsing (checkboxes in markdown) - [ ] Task view per project - [ ] File locking (Task View vs Editor) - [ ] Git init + auto-commit with lock handling - [ ] Ripgrep-based search - [ ] **File system watching** (notify crate) - [ ] **WebSocket server** for real-time updates - [ ] External edit notifications in UI ### Phase 3: Polish (Week 5-6) **Goal**: Production-ready personal tool - [ ] Split view (editor + preview) - [ ] Manual commit with message - [ ] Git status/history viewer - [ ] Git conflict detection and UI warnings - [ ] Tag extraction and filtering - [ ] Daily note templates - [ ] UI polish and keyboard shortcuts - [ ] Asset upload for images ### Phase 4: Advanced (Future) - [ ] Global hotkey (Ctrl+Shift+Space) using `global-hotkey` crate - [ ] System tray icon (stays running in background) - [ ] Tantivy full-text search (if ripgrep becomes slow) - [ ] Backlinks between notes - [ ] Remote Git push/pull from UI - [ ] Export to PDF/HTML - [ ] Custom themes --- ## 14. Project Structure ``` ironpad/ ├── README.md ├── LICENSE │ ├── backend/ # Rust backend │ ├── Cargo.toml │ ├── Cargo.lock │ ├── src/ │ │ ├── main.rs # Server startup + router + port detection │ │ ├── websocket.rs # WebSocket handler for real-time updates │ │ ├── watcher.rs # File system watcher (notify integration) │ │ ├── routes/ │ │ │ ├── mod.rs │ │ │ ├── notes.rs # /api/notes endpoints │ │ │ ├── projects.rs # /api/projects endpoints │ │ │ ├── tasks.rs # /api/projects/:id/tasks │ │ │ ├── search.rs # /api/search (ripgrep integration) │ │ │ ├── git.rs # /api/git endpoints │ │ │ └── assets.rs # /api/assets endpoints │ │ ├── services/ │ │ │ ├── mod.rs │ │ │ ├── filesystem.rs # File read/write logic │ │ │ ├── git.rs # Git operations (git2) with lock handling │ │ │ ├── search.rs # Ripgrep search implementation │ │ │ ├── markdown.rs # Markdown parsing (CommonMark) │ │ │ ├── frontmatter.rs # Auto-manage YAML frontmatter │ │ │ └── locks.rs # File lock management for concurrency │ │ └── models/ │ │ ├── mod.rs │ │ ├── note.rs # Note struct │ │ ├── project.rs # Project struct │ │ └── task.rs # Task struct │ └── static/ # Vue build output (in production) │ └── (frontend dist files) │ ├── frontend/ # Vue 3 frontend │ ├── package.json │ ├── vite.config.ts │ ├── tsconfig.json │ ├── index.html │ ├── src/ │ │ ├── main.ts │ │ ├── App.vue │ │ ├── composables/ │ │ │ └── useWebSocket.ts # WebSocket client composable │ │ ├── components/ │ │ │ ├── Sidebar.vue │ │ │ ├── ProjectList.vue │ │ │ ├── NoteList.vue │ │ │ ├── Editor.vue │ │ │ ├── MarkdownPreview.vue │ │ │ ├── TaskList.vue │ │ │ ├── SearchBar.vue │ │ │ ├── GitStatus.vue │ │ │ ├── ExternalEditBanner.vue # Shows when file changed externally │ │ │ └── ReadOnlyBanner.vue # Shows when file locked │ │ ├── views/ │ │ │ ├── NotesView.vue # Main notes editor │ │ │ ├── TasksView.vue # Project tasks view │ │ │ └── ProjectView.vue # Project overview │ │ ├── stores/ │ │ │ ├── notes.ts # Pinia store (minimal caching) │ │ │ ├── projects.ts # Pinia store for projects │ │ │ ├── tasks.ts # Pinia store for tasks │ │ │ └── websocket.ts # WebSocket state management │ │ ├── api/ │ │ │ └── client.ts # API client (fetch wrappers) │ │ └── types/ │ │ └── index.ts # TypeScript types │ └── dist/ # Build output (gitignored) │ └── data/ # User data (separate repo) ├── .git/ ├── .gitignore ├── index.md ├── inbox.md ├── projects/ │ └── ironpad/ │ ├── index.md │ ├── tasks.md │ └── assets/ # Project-specific images ├── notes/ │ ├── ideas.md │ └── assets/ # Shared note assets ├── daily/ └── archive/ ``` --- ## 15. Dependencies ### Backend (Cargo.toml) ```toml [package] name = "ironpad" version = "0.1.0" edition = "2021" [dependencies] # Web framework axum = { version = "0.8", features = ["ws"] } # WebSocket support tokio = { version = "1", features = ["full"] } tower = "0.5" tower-http = { version = "0.6", features = ["fs", "cors"] } # Serialization serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.9" # Frontmatter parsing # Markdown parsing (CommonMark) markdown = "1.0.0-alpha.22" # Frontmatter support # Git operations git2 = "0.19" # Browser opening webbrowser = "1.0" # File system watching notify = "6.1" notify-debouncer-full = "0.3" # Debounced file events # Search (ripgrep as library) grep = "0.3" # ripgrep internals walkdir = "2.4" # Date/time chrono = { version = "0.4", features = ["serde"] } # Logging tracing = "0.1" tracing-subscriber = "0.3" ``` ### Frontend (package.json) ```json { "name": "ironpad-frontend", "version": "0.1.0", "dependencies": { "vue": "^3.5.0", "vue-router": "^4.5.0", "pinia": "^2.3.0", "codemirror": "^6.0.1", "@codemirror/lang-markdown": "^6.3.2", "markdown-it": "^14.1.0" }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.1", "vite": "^6.0.5", "typescript": "^5.7.2" } } ``` --- ## 16. Open Questions & Decisions Tracking | Question | Decision | Rationale | Date | |----------|----------|-----------|------| | Electron vs Rust backend? | **Rust backend** | Smaller binary, no bundled browser | 2026-02-02 | | Auto-save strategy? | **2s debounce** | Balance safety and performance | 2026-02-02 | | Git commit strategy? | **5min batch + manual** | Clean history, user control | 2026-02-02 | | Editor library? | **CodeMirror 6** | Mature, performant, good UX | 2026-02-02 | | Search in v1? | **Yes (ripgrep-based)** | Fast, proven, <100ms target | 2026-02-02 | | Tasks per project or global? | **Per project** | Cleaner mental model | 2026-02-02 | | Mobile access? | **Not v1 priority** | Desktop-first | 2026-02-02 | | Port conflict handling? | **Dynamic 3000-3010** | Always works, graceful fallback | 2026-02-02 | | External edit support? | **Yes (notify + WebSocket)** | True local-first philosophy | 2026-02-02 | | Frontmatter management? | **Auto-managed by backend** | Low ceremony, no manual IDs | 2026-02-02 | | Task View vs Editor conflict? | **File locking** | Prevent race conditions | 2026-02-02 | | Markdown standard? | **CommonMark** | Consistency backend/frontend | 2026-02-02 | --- ## 17. Risk Assessment | Risk | Likelihood | Impact | Mitigation | |------|-----------|--------|------------| | Rust learning curve | High | Medium | Start simple, use Axum (easier than Actix) | | Git conflicts (concurrent edits) | Low | Medium | Detect `.git/index.lock`, show UI warnings | | Search performance at scale | Low | Medium | Ripgrep handles 5000+ files easily | | Browser doesn't auto-open | Low | Low | Print URL as fallback | | File corruption | Low | High | Git versioning protects against data loss | | **WebSocket connection drops** | Medium | Medium | Auto-reconnect in frontend with exponential backoff | | **File watcher overhead** | Low | Low | Debounce events (100ms), ignore .git/ | | **Port conflicts** | Low | Low | Dynamic port selection 3000-3010 | | **Race condition (Task View + Editor)** | Medium | High | File locking prevents simultaneous edits | | **Markdown rendering mismatch** | Low | Medium | Use CommonMark everywhere | --- ## 18. Future Enhancements (Out of Scope) ### Potential v2+ Features - **Global hotkey** - Ctrl+Shift+Space to bring app to front (using `global-hotkey` crate) - **System tray icon** - Keep app running in background (using `tray-icon` crate) - **Backlinks** - Automatic link detection between notes - **Graph view** - Visual representation of note connections - **Rich editor** - WYSIWYG markdown editor - **Templates** - Note templates (daily, meeting, project) - **Plugins** - Extension system for custom functionality - **Sync** - Optional cloud sync via Git remote - **Themes** - Dark mode, custom color schemes - **Export** - PDF, HTML, DOCX export - **Mobile web UI** - Responsive design for mobile browsers - **Kanban view** - Visual task board per project - **Time tracking** - Track time spent on tasks - **Voice notes** - Audio recording integration - **OCR** - Extract text from images --- ## 19. Addressing Gemini's Feedback ### ✅ 1. Task Syncing Race Conditions **Issue**: Task View checkboxes vs Editor text edits conflict **Solution**: File locking system - Task View locks `tasks.md` when open - Editor shows "Read-Only" banner if file locked - Only one view can edit at a time ### ✅ 2. File System Watching **Issue**: External edits (VS Code, Obsidian) don't sync **Solution**: `notify` crate + WebSocket - Backend watches `data/` directory - Sends real-time updates to frontend - UI shows "File changed externally. Reload?" banner ### ✅ 3. Git Conflict Handling **Issue**: `.git/index.lock` can cause crashes **Solution**: Graceful lock detection - Check for lock file before committing - Skip auto-commit if locked, retry next cycle - Show UI warning if git conflicts detected ### ✅ 4. Frontmatter Management **Issue**: Manual timestamp/ID editing is high-friction **Solution**: Backend owns all frontmatter - Auto-generate `id` from filename - Auto-update `updated` on every save - Users never manually edit metadata ### ✅ 5. Port Conflicts **Issue**: Hardcoded :3000 breaks if port busy **Solution**: Dynamic port selection - Try ports 3000-3010 - Bind to first available - Log actual port used ### ✅ 6. Search Performance **Issue**: Naive grep slow at >500 files **Solution**: Use `ripgrep` library - Battle-tested, used by VS Code - Handles 5000+ files easily - <100ms target achieved ### ✅ 7. Markdown Consistency **Issue**: Backend parsing vs frontend rendering mismatch **Solution**: CommonMark everywhere - Backend: `markdown-rs` (CommonMark mode) - Frontend: `markdown-it` (CommonMark preset) - Guaranteed consistency ### ✅ 8. State Management **Issue**: Pinia caching vs file system truth **Solution**: Minimal caching philosophy - File system is source of truth - Pinia only caches current view - WebSocket invalidates cache on external changes ### ✅ 9. Asset Management **Issue**: No image/file storage **Solution**: `assets/` folders - Each project gets `assets/` subfolder - Global `notes/assets/` for shared files - Upload via `/api/assets/upload` --- ## 20. Conclusion **Ironpad v3.0** represents a robust, production-ready architecture: - **Local-first** with true external editor support - **Lightweight** Rust backend (no browser bundling) - **Real-time** sync via WebSocket - **Conflict-free** via file locking - **Low-ceremony** via automatic frontmatter management - **Resilient** via git lock handling and dynamic ports The system is designed to be: - ✅ Easy to use daily - ✅ Easy to understand and modify - ✅ Easy to back up and migrate - ✅ Fast and responsive - ✅ A practical Rust learning project - ✅ **Robust against real-world edge cases** **Next Step**: Begin Phase 1 implementation - Rust backend with dynamic port selection + automatic frontmatter. --- **Document Version History** - v1.0 (2026-02-01): Initial draft with general architecture - v2.0 (2026-02-02): Complete rewrite with Rust backend, browser-based frontend, detailed technical decisions - v3.0 (2026-02-02): Addressed concurrency, file watching, git conflicts, port handling, frontmatter automation, and Gemini's architectural feedback **Contact**: Internal project - personal use