Date: June 29, 2026
Auditor: VariableThe
App Version: 0.5.8 (Tauri, custom arithmetic evaluator)
PaperCache is built on Tauri v2, Rust, and React. After migrating from Electron (v0.5.0-beta), we've continued shrinking the footprint through dependency removal, custom evaluators, and lazy-loading.
| Metric | V0.5.0-beta | V0.5.8 | Delta |
|---|---|---|---|
| App Installer Size (DMG) | ~7.3 MB | ~6.8 MB | -7% |
| Idle RAM Usage | ~40 MB | ~38 MB | -5% |
| Idle CPU Usage | 0.0% | 0.0% | Maintained |
| Bundle Dependencies | ~600 packages | ~590 packages | Smaller |
- Hardware: MacBook Air M4, 16GB RAM
- OS: macOS 15.7.5
- Dataset: Workspace containing 500 markdown notes, averaging 2KB each.
- Tooling: Activity Monitor,
ls -lh,du -sh
Replaced expr-eval (~15KB, high-severity prototype pollution vulnerability, no fix available) with a custom recursive-descent parser at src/lib/evaluator.ts (~2KB).
- Zero external dependencies — no
eval, noFunctionconstructors. - ~150 lines of TypeScript with 26 unit tests.
- Not susceptible to prototype pollution.
- Impact: Eliminated a security vulnerability with a smaller, faster evaluator.
Removed 4 unused npm packages: @tauri-apps/plugin-fs, @tauri-apps/plugin-shell, @emnapi/core, @emnapi/runtime.
- Impact: Reduced package.json weight and CI install times.
Replaced D3.js Canvas 2D graph with react-force-graph-3d (Three.js/WebGL), dynamically imported via React.lazy() + Suspense.
- The Three.js bundle (~1.3 MB) loads only when the graph is first opened.
- Impact: Graph rendering is offloaded to the GPU. No startup penalty. No UI freeze on large graphs.
Created dslPlugin.ts — createRegexPlugin() scans only view.visibleRanges per update tick.
- O(visible lines) complexity instead of O(document length).
- Impact: Lag-free typing at any document size, even with many active regex rules. Eliminated the previous main-thread bottleneck for custom DSL parsing.
Replaced setInterval with chained setTimeout loops in the timer panel.
- Impact: No timer drift accumulation. Accurate countdowns regardless of browser event loop pressure.
500ms debounce on saveNote IPC calls.
- Disk I/O reduced from ~3 writes/sec to ~1 write/sec during continuous typing.
Enabled strict: true in tsconfig, catching null/type issues at compile time rather than runtime.
- Impact: Zero new type errors at enablement — the codebase was already compatible.
Added minimum coverage guardrails to vitest config (statements 65%, branches 50%, functions 55%, lines 65%).
- Impact: Prevents silent coverage regression in CI.
- Native API Key Storage: Uses the OS keychain (macOS Keychain, Linux Secret Service, Windows Credential Manager) via the Rust
keyringcrate. No user-visible memory or latency cost. - Global Shortcuts: Registered via
tauri-plugin-global-shortcut— zero ongoing CPU overhead, fires events only on keypress. - Auto-Updates: Uses
tauri-plugin-updater— checks version metadata on startup (~1 HTTP HEAD request). No persistent overhead. - Window State Persistence: Reads/writes a small JSON file (~200 bytes) on exit/startup. No runtime cost.
-
Full-Text Search: Currently searches are done client-side via JavaScript string matching on loaded notes. For workspaces exceeding 5,000+ notes, this could introduce noticeable search latency. A future optimization could implement a persistent search index in Rust (e.g., via
tantivyorskim). -
Graph Layout on Very Large Datasets: The WebGL graph renders smoothly for typical workspaces (up to ~500 nodes). With 2,000+ nodes, force simulation convergence time and interaction framerate may degrade. Future optimization: implement LOD (level-of-detail) rendering or cluster-collapse for large folders.
-
Startup Time (Cold): Initial load requires parsing note directory via Rust's
walkdir(~5ms for 500 notes) plus React hydration. The JS bundle is ~200KB gzipped. Cold launch is typically sub-second but varies by filesystem speed and note count.