Architecture Overview
Comprehensive technical documentation of the haex-vault architecture, components, and how they work together.
System Overview
haex-vault is a Tauri-based desktop and mobile application that serves as a secure host for extensions (haextensions). The architecture is designed around three core principles: offline-first operation, end-to-end encryption, and extensibility.
A Vue 3 frontend talks to a Rust backend through Tauri's IPC layer. All user data is stored in an encrypted SQLite database (SQLCipher), with CRDT-based replication to haex-sync-server.
Technology Stack
haex-vault uses a modern tech stack optimized for security, performance, and developer experience.
Frontend
- Vue 3 + TypeScript
- Pinia State Management
- Drizzle ORM
- shadcn-vue / Tailwind
Backend (Rust)
- Tauri 2 (Rust)
- SQLite + SQLCipher
- CRDT Engine (UHLC)
- Extension Runtime
Sync Server
- haex-sync-server (HTTP + WebSocket)
- DID-based auth
- Column-Level HLC
- E2E Encryption (AES-256-GCM)
Extensions
- Iframe / Tauri WebviewWindow
- @haex-space/vault-sdk
- Permission System
- Hot Reload (Dev)
Repository Structure
The haex ecosystem consists of multiple repositories, each with a specific responsibility.
Main application - Desktop / Mobile client (Tauri 2 + Vue 3)
haex-sync-server: encrypted column-level change log over HTTP + WebSocket
Extension marketplace and distribution
@haex-space/vault-sdk - SDK + CLI for extension developers
haex.space website and documentation
Project Structure
The main haex-vault repository follows a standard Tauri project structure with additional organization for the CRDT sync system and extension runtime.
haex-vault/
├── src/ # Vue Frontend
│ ├── components/ # Vue Components (shadcn-vue)
│ ├── stores/ # Pinia Stores
│ │ ├── vault/ # Vault Management
│ │ ├── sync/ # Sync Engine & Orchestrator
│ │ │ ├── orchestrator/ # Push/Pull/Realtime Logic
│ │ │ ├── engine.ts # Key Management
│ │ │ ├── backends.ts # Backend Configuration
│ │ │ └── syncEvents.ts # Event Bus for Updates
│ │ └── extensions/ # Extension Store
│ ├── composables/ # IPC Handlers & Utilities
│ │ └── handlers/ # Tauri Command Wrappers
│ ├── database/ # Drizzle Schemas
│ └── utils/crypto/ # Encryption Utilities
├── src-tauri/ # Rust Backend
│ ├── src/
│ │ ├── crdt/ # CRDT Implementation
│ │ │ ├── commands.rs # Tauri Commands
│ │ │ ├── trigger.rs # SQL Trigger Generation
│ │ │ └── hlc.rs # Hybrid Logical Clock
│ │ ├── extension/ # Extension Runtime
│ │ │ ├── core/ # Manager, Manifest, Protocol
│ │ │ ├── database/ # SQL Execution & Validation
│ │ │ ├── permissions/ # Permission Enforcement
│ │ │ └── webview/ # Multi-Window Management
│ │ ├── database/ # SQLite Connection & Migrations
│ │ └── lib.rs # Tauri Command Registration
│ └── migrations/ # SQL Migrations
└── .claude/ # Knowledge DatabaseData Flow
Data flows through the system in a predictable pattern, from user interaction through the sync layer.
User Interaction
User interacts with Vue components. Changes are dispatched to Pinia stores.
IPC Communication
Stores call Tauri commands through composable handlers. Data is serialized for IPC.
Rust Processing
Rust backend validates, encrypts, and stores data in SQLite. CRDT triggers fire.
Sync Orchestration
Sync orchestrator detects dirty tables and pushes to haex-sync-server. Other devices receive a WebSocket nudge that triggers a debounced pull.
Security Architecture
Security is built into every layer of the haex-vault architecture.
Encrypted Storage
All user data is stored in an SQLCipher-encrypted SQLite database. The encryption key is derived from the user's vault password.
DID-Based Authentication
haex-vault uses a local cryptographic identity (DID) for server authentication and a separate vault password for local encryption:
- Vault Password: Used to derive the SQLCipher key and the vault encryption key. Never leaves the device.
- DID Identity: Each device holds a local key pair. Requests to haex-sync-server are signed with the private key.
End-to-End Encryption
Every column value is encrypted with the vault key (AES-256-GCM) before it leaves the device. The sync server never sees plaintext.