KatieHunt

← Back to Projects
Case Study

CodeVault — Secure Code Repository Management System

I designed and built CodeVault, a local-first desktop and web-style application for developers who want a single, private vault for code snippets, project files, a repo-style file browser, encrypted credentials (API keys and passwords), notes, uploads, and global search — without sending vault contents to a third-party cloud.

Role Product Designer & Front-End Engineer
Scope 12 primary surfaces · React SPA · Electron desktop
Stack React 18 · IndexedDB · Web Crypto API · Electron
Output Shipped v1 · Windows portable build
CodeVault Dashboard — vault entry point overview

Dashboard — the central entry point: vault areas, quick-launch tiles, and status at a glance.

What was shipped

Deliverables

Product & IA

Single-vault model with a consistent app shell: dashboard entry, sidebar navigation, and global search entry points. Clear separation between "build" surfaces (snippets, projects, code repo) and "secrets" surfaces (API keys, passwords).

Interaction & UI

Lock screen and session lifecycle; dashboard overview; dense-but-readable developer tool layouts for lists, trees, and editors; settings and backup flows with explicit confirmations for sensitive actions.

Engineering

React 18 + React Router (HashRouter for packaged builds); IndexedDB storage layer; Electron main process with single-instance lock and hardened IPC sender checks; optional LibreOffice-backed document conversion.

Security UX

No vault content visible without a valid session. Auto-lock and quick-lock affordances. Client-side encryption for protected categories via Web Crypto API (AES-GCM, PBKDF2 key derivation).

How I worked

Design Process

01 Frame requirements Define the vault model — what stays local, what must be encrypted, what ships in v1.
02 Map IA & flows Lock → dashboard → feature areas; global search; backup and restore paths.
03 Look & feel Calibrated for a developer tool: information density, contrast, and scanning — not marketing polish.
04 Build in React Route structure, protected routes, lists/trees/viewers, and settings surface.
05 Storage & crypto IndexedDB helpers, encrypted buckets, session manager, and migration hooks.
06 Harden & ship Auto-lock, Electron constraints, IPC boundaries, build targets — then ship the desktop artifact.
Starting point

Foundation & Constraints

I started from a blank product definition: unify snippets, files, notes, and secrets in one local app, with no backend dependency for the core vault. The tech baseline was browser storage and React, later extended with Electron for a single-window desktop experience.

I had full control over navigation model, feature grouping, visual system, storage schema, encryption approach, and Electron packaging strategy. The binding constraints were the local-only trust model (device compromise is explicitly out of scope for the v1 threat model) and shipping without a design team.

CodeVault Lock Screen — single-task focus

Lock Screen — the first and only surface visible before authentication. Single-task, minimal chrome.

Visual direction

Establishing Look & Feel

Brand intent: "Calm vault, sharp tool." The app should feel trustworthy and quiet on lock and secrets screens, and efficient wherever developers work (lists, search, file tree). No playful illustration overload — readability and hierarchy come first.

Chosen direction: "Dense Neutral"

High density for catalogs (snippets, projects, search results). Neutral chrome so content — code, keys, filenames — stays the hero. Explicit states for lock, loading, empty, and destructive backup actions.

Snippets folder — hierarchical list view
Option explored

Wider cards with more breathing room between items. Friendlier first impression, but slower to scan during repeat-use workflows.

Code snippet editor — dense neutral layout
Chosen: Dense Neutral

Tighter layouts with strong section headers. Steeper first impression, faster repeat use for daily snippet management and search — which are the primary tasks.

Why chosen: Snippets, search, and repo navigation are repeat tasks performed quickly. Optimizing for scanning and muscle memory beat onboarding comfort for the target user (developers storing and retrieving their own material).

Feature drilled end-to-end

Mini Case Study: Vault Unlock → Session → Protected Content

The session lifecycle was the highest-stakes design and engineering problem: get it wrong and vault content leaks, or the friction makes the app unusable. Here's how I worked through it.

Requirements

  • After launch, no vault content (snippets, secrets, repo, notes) is reachable without authentication.
  • Session must expire on idle (auto-lock) and be refreshable on activity.
  • Navigation must fail closed: an invalid session always routes to lock — no content flash, no stale UI.

Initial Exploration & Problems Found

Treating the lock screen as "just another route" introduced a class of problems that only surfaced under real use:

  • Content flash: If session state updates asynchronously, a protected route could render briefly before the redirect fires.
  • Forgotten sessions: Users staying unlocked all day created a false sense of security with no ambient awareness that the vault was open.
  • Slow lock: Power users needed a way to lock without digging through settings menus — especially when leaving their desk.

Design Iteration

Each problem drove a specific design and engineering decision:

  • Wrapped sensitive routes in a protected route pattern that checks session validity before render, not after mount — eliminating the content flash entirely.
  • Added an app-wide auto-lock hook that resets on navigation activity and fires after a configurable idle threshold (set in Settings).
  • Built a quick-lock shortcut accessible from any screen so locking is a two-keystroke action, not a menu dig.
  • Session checks on route entry clear session state and replace history to /lock so the back button cannot re-expose prior views.
Final lock screen design

Final lock screen — single task, no distractions.

Dashboard after unlock

Dashboard loads immediately after valid authentication.

Design Rationale & Validation

Rather than formal multi-participant testing, I evaluated the flow using a structured heuristic pass against three tasks: cold-start unlock, navigation to a secrets surface, and quick-lock from an active session. Findings shaped the final decisions above.

Task Problem identified Fix applied
Cold-start unlock Password field label ambiguous on first visit Clarified label + error copy; added placeholder hint
Navigate to secrets No visual confirmation user is in a protected area Added lock icon badge to secrets nav items
Quick-lock Shortcut undiscoverable; locking took 4+ clicks Built quick-lock shortcut; surfaced it in Settings tooltip

Settings — Configuration of the Session

All session behavior (idle timeout, auto-lock threshold, backup schedule) is controlled from a dedicated Settings surface with no hidden state. Destructive actions (lock now, reset vault) use an explicit confirmation step.

All screens

High-Fidelity Gallery

All screens use synthetic data only. Dark mode is the default; light mode available via theme toggle in-app.

Component library

Design System

While building screens I simultaneously built out the component library in an atomic structure, so each new surface could compose from existing parts rather than inventing new ones.

Atoms
Buttons Inputs Toggles Language badges Status dots Icons
Molecules
Sidebar nav item Stat/summary card List row (title + meta + actions) Empty state block Masked field row
Organisms
App shell (sidebar + top bar + content) File tree + viewer split Settings sections Search overlay
Templates
Dashboard List + detail layouts Secrets forms (masked) Settings page

Theme and density are centralized via CSS variables, making dark/light switching a single attribute toggle — the same pattern used on this portfolio.

Result

Outcome

CodeVault shipped as a complete v1: a single portable Electron app that runs offline, keeps all vault data on-device in IndexedDB, and enforces client-side encryption for secrets categories. The hardest integration wins were aligning session management, encryption, IndexedDB durability, and Electron packaging into one coherent, testable product.

The project demonstrates the full design-to-delivery arc: product thinking (what belongs in a local vault), UX decisions (session lifecycle, information architecture), visual design (developer tool density and hierarchy), and front-end engineering (React, crypto, Electron).

Technical Appendix — for design-engineer readers

Cryptography

Web Crypto API throughout: AES-GCM (256-bit keys) for encryption; PBKDF2 for key derivation (100,000 iterations, SHA-256); per-encryption 96-bit GCM nonce (IV) so the same key never reuses a nonce.

Storage

IndexedDB for all vault records, with structured schemas per category. Settings and scheduler state written separately. Migration hooks handle schema evolution.

Desktop packaging

Electron single-instance lock prevents duplicate vault windows. IPC channels are guarded by trusted-sender URL checks so renderer cannot call privileged main-process APIs from injected content. Optional LibreOffice path enables office-to-PDF conversion in packaged builds.

Routing

HashRouter instead of BrowserRouter for compatibility with both file:// (Electron) and HTTP contexts. Protected routes check session state synchronously before render; invalid session replaces history to /lock so Back cannot re-enter protected views.