Skip to content

onllm-dev/onvault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

onvault

Seamless file encryption & per-process access control for macOS.

Protect ~/.ssh, ~/.aws, ~/.kube, and other sensitive directories from supply chain attacks, malicious packages, and unauthorized processes. Files are encrypted at rest and only decrypted for verified, code-signed binaries.

Links: Website | Buy Me a Coffee

Trust & Quality

Stars License: GPL-3.0 Platform Language

Zero telemetry. All data stays on your machine.

Powered by onllm.dev


The Problem

Supply chain attacks like the litellm credential stealer (March 2026) demonstrate a critical gap: any process running as your user can read your SSH keys, AWS credentials, and Kubernetes configs. Standard Unix permissions offer no protection against same-user attacks.

A malicious PyPI/npm package runs as you. It reads ~/.ssh/id_rsa, ~/.aws/credentials, ~/.kube/config — and exfiltrates everything. FileVault doesn't help (it's full-disk, not per-process). No existing macOS tool combines file encryption with per-process access control.

How onvault Solves This

onvault uses a two-layer defense:

Layer 1 — Encryption at Rest (macFUSE): Your sensitive directories are encrypted on disk. Files are only decrypted through a FUSE mount when onvault is running and you've authenticated. Kill the daemon? Files stay encrypted. Attacker reads disk? Ciphertext only.

Layer 2 — Per-Process Access Control (Endpoint Security Framework, when available): On builds/runtimes with the required ESF entitlement and permissions, only verified processes can read mounted plaintext. /usr/bin/ssh can read ~/.ssh/id_rsa. python3 cannot. Process identity is verified by Apple's code signing (cdHash + Team ID), not just the binary path.

Malicious package runs → tries to read ~/.ssh/id_rsa
  → Layer 2: python3 not in allowlist → DENIED
  → Even without Layer 2: file on disk is AES-256-XTS ciphertext

You run `ssh user@host`:
  → Layer 2: /usr/bin/ssh is in allowlist, Apple-signed → ALLOWED
  → Layer 1: FUSE decrypts on-the-fly → SSH reads the key

Quick Start

Prerequisites

# macFUSE (one-time, requires reboot)
brew install --cask macfuse
# Approve the system extension in System Settings → Privacy & Security
# Reboot

Build from Source

git clone https://github.com/onllm-dev/onvault.git
cd onvault
make        # Development build
make test   # Run test suite (172 tests across 19 suites)
make dist   # Distribution build (macFUSE still required at runtime)

Usage

# 1. First-time setup — sets passphrase, generates a recovery key display
onvault init

# 2. Start the daemon (shows menu bar icon + web UI)
onvault start                # with menu bar
onvault start --no-gui       # headless (servers, CI)

# 3. Unlock — authenticates, loads the master key, and mounts configured vaults
onvault unlock

# 4. Protect directories — encrypts files, creates symlink
onvault vault add ~/.ssh --smart  # encrypt + auto-populate allowlist
onvault vault add ~/.aws --smart  # encrypt AWS credentials

# 5. Status — see what's protected
onvault status

# 6. Lock — unmount vaults, wipe keys from memory (passphrase required)
onvault lock

Recovery keys are displayed during onvault init. If you lose your passphrase, use onvault recover with the recovery key to set a new passphrase. Touch ID unlock is available after the first passphrase unlock.

Menu Bar & Web UI

The daemon shows a menu bar icon and serves an interactive web UI. Click the lock icon or open http://127.0.0.1:<port>/menubar in any browser. The web UI unlock flow returns a localhost-only bearer token; subsequent API calls use that token rather than unauthenticated localhost access.

Everything can be done from the menu bar — no CLI needed:

  • Unlock/Lock — Touch ID first, passphrase fallback
  • Add Vault — type a path or use quick-add buttons (~/.ssh, ~/.aws, etc.)
  • Allow/Deny Process — inline input per vault with UID/GID options
  • Global Rules — rules that apply across all vaults
  • View Rules — per-vault and global rule management
  • Watch Mode — start/stop directory observation from the Watch tab
  • Audit Log — filterable log viewer (All / Denied)
  • Settings — per-vault verify mode, key rotation, recovery key, log forwarding
  • Recent Denials — denied access attempts with quick-allow button
  • Auto-refresh — vault status updates every 5 seconds

The web UI port is written to ~/.onvault/http.port for scripting and testing.

What Happens When You Add a Vault

onvault vault add ~/.ssh --smart
  1. Files in ~/.ssh/ are encrypted (AES-256-XTS) and moved to ~/.onvault/vaults/ssh/
  2. A nonce is stored in each file's xattr for key derivation
  3. ~/.ssh is replaced with a symlink → ~/.onvault/mnt/ssh/
  4. Smart defaults auto-populate 7 allow rules (ssh, scp, sftp, ssh-add, ssh-agent, ssh-keygen, git)
  5. When unlocked: FUSE mount decrypts on-the-fly. ssh, git, etc. work normally.
  6. When locked or daemon stops: FUSE unmounts. ~/.ssh symlink points to nothing. Files are ciphertext.

To undo: onvault vault remove ssh decrypts everything back to the original location (passphrase required).

Auth-Gated Operations

Destructive operations require passphrase verification via challenge-response. A malicious script running as your user cannot disable onvault without knowing your passphrase:

Operation Auth Required
onvault lock Challenge-response passphrase proof
onvault vault remove Challenge-response passphrase proof
onvault allow/deny Challenge-response passphrase proof
onvault policy import Challenge-response passphrase proof
onvault rotate-keys Challenge-response passphrase proof
onvault configure Local passphrase verification
onvault unlock Passphrase (Argon2id) or Touch ID
Menu bar Lock/Unlock Passphrase dialog or Touch ID
onvault vault add Session (must be unlocked)
onvault status/rules/log Requires unlocked daemon

The daemon uses short-lived single-use nonces for challenge-response — proofs cannot be replayed.

Smart Defaults

Pass --smart when adding a vault for a known directory to auto-populate an allowlist of verified binaries. Smart defaults are opt-in; without --smart, the vault starts with default-deny rules until you add explicit allow entries.

Path Auto-allowed
~/.ssh ssh, scp, sftp, ssh-add, ssh-agent, ssh-keygen, git
~/.aws aws, terraform, pulumi
~/.kube kubectl, helm, k9s
~/.gnupg gpg, gpg2, gpg-agent, git
~/.docker docker, Docker.app

Only binaries that exist on your system are added. Each binary is hash-verified.

Managing Access Policies

Only processes in the allowlist can read your encrypted files. Everything else is denied by default.

# Allow a specific binary to access a vault
onvault allow /usr/bin/vim ssh

# Allow with glob pattern (matches ssh, ssh-agent, sshd, etc.)
onvault allow "/usr/bin/ssh*" ssh

# Restrict to a specific user (UID) or group (GID)
onvault allow /usr/bin/vim ssh --uid 501
onvault allow /opt/homebrew/bin/aws aws --gid 20

# Require process ancestry verification
onvault allow /usr/bin/ssh ssh --chain-verify

# Global rules (apply across all vaults as fallback)
onvault allow /usr/bin/vim --global
onvault rules --global

# Deny a specific binary
onvault deny /usr/bin/python3 ssh

# View rules for a vault
onvault rules ssh

# Export/import policies as YAML (version-controllable)
onvault policy export > policies.yaml
onvault policy import policies.yaml

# See what processes access a path (learning mode — observe for 24h)
onvault vault watch ~/.ssh
onvault vault suggest ssh

# View audit log (all events or denied only)
onvault log
onvault log --denied

# Forward logs to external systems
onvault log --add-sink syslog
onvault log --add-sink json /var/log/onvault.json

# Compliance reports
onvault compliance --pci-dss
onvault compliance --hipaa

# Key management
onvault rotate-keys              # Zero-downtime key rotation
onvault key-info                 # Key lifecycle metadata
onvault configure --rotation-interval 90d  # Scheduled rotation

CLI Reference

onvault init                              First-time setup
onvault unlock [--touchid]                Authenticate and mount vaults
onvault lock                              Unmount vaults, wipe keys (passphrase)
onvault status                            Show daemon and vault status
onvault health                            Machine-readable health check (JSON)

onvault vault add <path> [--smart]        Encrypt and protect a directory
  [--exclude "*.log"]                       Skip files matching pattern
onvault vault remove <vault_id>           Decrypt and unprotect (passphrase)
onvault vault list                        List all vaults
onvault vault watch <path>                Learning mode (24h observation)
onvault vault suggest <vault_id>          Show watch suggestions

onvault allow <proc> <vault> [flags]      Allow a process
  [--uid <uid>] [--gid <gid>]              Restrict to specific user/group
  [--chain-verify]                          Verify process ancestry
  [--global]                                Apply as global rule (no vault)
onvault deny <proc> <vault> [flags]       Deny a process (same flags)
onvault rules <vault_id>                  Show rules for a vault
onvault rules --global                    Show global rules

onvault policy show                       Show all policies
onvault policy export                     Export policies as YAML
onvault policy import <file>              Import policies from YAML

onvault rotate-keys                       Rotate encryption keys (zero downtime)
onvault key-info                          Key lifecycle metadata
onvault export-recovery                   Recovery key info
onvault recover                           Unlock with recovery key + new passphrase
onvault compliance --pci-dss|--hipaa|--sox  Compliance report (JSON)

onvault log [--denied]                    View audit log
onvault log --sinks                       List active log sinks
onvault log --add-sink syslog|json|socket Add log forwarding sink
onvault log --remove-sink <type>          Remove log forwarding sink
onvault configure <vault> --verify-mode   Set per-vault verification mode
onvault configure --rotation-interval 90d Set scheduled key rotation
onvault --version                         Show version

Architecture

┌──────────────────────────────────────────────────────┐
│                    onvault CLI (C)                     │
│  init, unlock, lock, vault, allow, deny, configure    │
├──────────────────────────────────────────────────────┤
│      Menu Bar (WKWebView + HTML/CSS/JS popover)       │
│  Vault mgmt, allow/deny, rules, denials, lock/unlock  │
├──────────────────────────────────────────────────────┤
│               Daemon — onvaultd (C)                    │
│  IPC Server │ HTTP Server │ Policy │ Auth │ Audit Log  │
│  Integrity Monitor │ Rotation Scheduler │ Health API  │
├──────────────────────────────────────────────────────┤
│                                                        │
│  Layer 1: Encryption at Rest (macFUSE)                │
│  AES-256-XTS (data) + AES-256-GCM (config/metadata)  │
│  Per-file keys via HKDF-SHA512 + nonce in xattr       │
│  No daemon = no mount = ciphertext only                │
│                                                        │
│  Layer 2: Per-Process Access Control (ESF, when       │
│  entitlement + permission are available)              │
│  AUTH_OPEN + AUTH_RENAME + AUTH_CREATE + more          │
│  cdHash + Team ID + Signing ID + binary hash verify    │
│  UID/GID rules, glob patterns, time-based access       │
│  Process chain verification, binary tamper detection   │
│  Global + per-vault policy hierarchy, default deny     │
│                                                        │
├──────────────────────────────────────────────────────┤
│  KMS Provider Abstraction                              │
│  Secure Enclave + Keychain (default, ECDH-wrapped)    │
│  Pluggable: file-based KMS (dev), extensible to       │
│  AWS KMS / Azure Key Vault / KMIP                     │
└──────────────────────────────────────────────────────┘

Key Hierarchy

User Passphrase
  → [Argon2id, 64 MiB, 3 iter, 4 threads] → Master Key (AES-256)
    → Stored in Secure Enclave (ECDH-wrapped, non-exportable)
    → [HKDF-SHA512] → Config Key (policy/recovery/log encryption)
    → [HKDF-SHA512] → Per-Vault Key (one per protected directory)
      → [HKDF-SHA512 + nonce] → Per-File Key (unique per file)

Key rotation (online, zero-downtime): generates new master key, re-encrypts
all vault files with atomic temp+rename, swaps FUSE keys via rwlock.
Scheduled rotation available (e.g., every 90 days for compliance).

Process Verification Modes

Mode Behavior
codesign_preferred (default) Trust Apple code signing (cdHash + Team ID) for signed binaries. SHA-256 hash for unsigned. Survives brew updates.
hash_only Always verify by SHA-256 binary hash. Every update requires re-approval. Maximum paranoia.
codesign_required Only allow code-signed binaries. Reject all unsigned.

Security Model

Protected Against

Threat How
Supply chain attacks (litellm, malicious npm/pip packages) Malicious code reads only ciphertext. Not in allowlist → DENIED.
Unauthorized daemon shutdown onvault lock requires passphrase via challenge-response. IPC socket is owner-only.
Daemon killed / not running FUSE auto-unmounts. Files remain AES-256 encrypted on disk.
Physical disk theft Encrypted at rest. Master key in Secure Enclave (hardware-bound).
Root / su impersonation Detected via audit_token — real UID vs effective UID comparison.
Binary swapping Process identity verified by cdHash (Apple's content directory hash), not just path.
Config tampering All policies and config encrypted with master key derivative.
Memory snooping Keys mlock()'d (never swapped to disk), securely wiped via volatile memset + compiler barrier after use.
IPC replay attacks Challenge-response with per-client single-use nonces for lock, vault remove, allow, deny, and policy import.
Policy enumeration Read-only IPC commands require unlocked daemon.
Multiple daemon instances PID lock file at ~/.onvault/onvaultd.pid.
Binary tampering Runtime hash cache with mtime+inode+size invalidation. Modified binaries denied on next access.
Untrusted process launchers Process chain verification walks ppid ancestry via sysctl. Trusted binary launched by malware → DENIED.
Daemon tampering kqueue-based integrity monitor detects binary/config modifications, re-hashes after atomic rename.
Compliance PCI-DSS, HIPAA, SOX reports verify encryption, key rotation, audit logging, access controls.
Key material on disk Never plaintext. Secure Enclave wrapped (production) or debug-only file KMS (guarded by #ifndef NDEBUG).

Not Protected Against

  • Kernel-level compromise (ring-0 attacker with code execution)
  • Hardware side-channel attacks (Spectre, cold boot on DRAM)
  • Compromise of the Secure Enclave hardware itself
  • DTrace/dtrace probing of the daemon process memory (requires SIP disabled)
  • Attacks via debugger attachment (mitigated by PT_DENY_ATTACH but not bulletproof)

Known Limitations

Limitation Detail Workaround
macOS only No Linux/Windows support. ESF and Secure Enclave are Apple-only. Use platform-native tools on other OSes.
No filename encryption File contents are encrypted (AES-256-XTS) but filenames remain visible in the vault directory (~/.onvault/vaults/). Use generic vault IDs and directory names to avoid leaking sensitive context through filenames.
ESF requires entitlement Layer 2 (per-process access control) requires the com.apple.developer.endpoint-security.client entitlement, which Apple grants only to approved developers. Without it, Layer 1 (encryption at rest) still works fully. Apply for the entitlement via Apple Developer Program, or rely on Layer 1 encryption alone.
macFUSE dependency Runtime dependency on macFUSE (kernel extension). Apple may restrict kexts in future macOS versions. Monitor Apple's filesystem extension plans. macFUSE actively maintains compatibility.
Container detection is a stub on macOS Docker Desktop on macOS runs containers inside a LinuxKit VM, so native PID-based container detection cannot identify containerized processes. The --container flag exists but always returns "not containerized" on macOS. Full container support planned for Linux builds. On macOS, use process path or signing identity rules instead.
Time-based rules use local time Time window rules (e.g., "allow 9am-5pm") use the system's local timezone via localtime_r(). Timezone changes or DST transitions may cause brief windows of incorrect enforcement. Use UTC-aligned windows for critical policies, or avoid sub-hour precision near DST boundaries.
No centralized management All configuration is local to each machine. No fleet-wide policy management server. Use onvault policy export/import with configuration management tools (Ansible, Chef, etc.) to manage policies across machines.
Recovery key shown only at init The 24-character recovery key is displayed once during onvault init and cannot be retrieved later. If lost, and the passphrase is also lost, data is unrecoverable. Write down the recovery key immediately and store it securely (e.g., password manager, physical safe).
Single-user design onvault protects one user's directories. It does not support multi-user shared vaults or cross-user policy delegation. Each user runs their own onvault instance with separate keys and policies.
HTTP API is localhost-only The web UI / HTTP API binds to 127.0.0.1. Passphrase is sent in plaintext over the HTTP body. While not network-accessible, any local process can connect. The IPC socket (Unix domain, owner-only) provides stronger isolation for sensitive operations. Use CLI for highest security.
Hash cache TOCTOU Binary tamper detection uses mtime+inode+size to decide whether to re-hash. A sophisticated attacker could theoretically modify a binary and restore the original mtime within the same second. Enable codesign_required mode for maximum protection. Code signing verification is not subject to this race.
Symlinks in vaults Only relative, non-traversing symlinks within a vault are preserved during encryption. Absolute symlinks and symlinks containing .. are silently skipped to prevent vault boundary escape. Convert absolute symlinks to relative before adding to vault.
Vault add on symlinks Cannot add a symlink as a vault source — the path must be a real directory. onvault replaces the source with its own symlink, so a symlink source would create confusion. Resolve the symlink first: onvault vault add $(readlink -f ~/.ssh)

Cryptography

Component Algorithm Standard
File data encryption AES-256-XTS NIST SP 800-38E
Config/metadata encryption AES-256-GCM NIST SP 800-38D
Key wrapping AES-256-GCM NIST SP 800-38D
Passphrase KDF Argon2id (64 MiB, 3 iter, 4 threads) RFC 9106, OWASP 2025
Key derivation HKDF-SHA512 RFC 5869
Process hashing SHA-256 FIPS 180-4
Master key storage Secure Enclave (ECDH P-256) Apple CryptoKit
Auth proof SHA-256(key || nonce) Challenge-response

How It Works on Disk

~/.onvault/
├── salt                    # Argon2id salt (16 bytes)
├── auth.enc                # Passphrase hash (encrypted with config key)
├── recovery.enc            # Recovery key hash (encrypted)
├── key_meta.enc            # Key lifecycle metadata (encrypted)
├── rotation.journal        # Key rotation crash-recovery journal (temporary)
├── onvaultd.pid            # PID lock (prevents multiple daemons)
├── onvault.sock            # IPC socket (CLI ↔ daemon, owner-only)
├── http.port               # HTTP server port for web UI
├── session                 # Session token + HMAC (15 min TTL)
├── policies.enc            # Per-vault encrypted policy state
├── global_policy.enc       # Global policy rules (cross-vault fallback)
├── logs/                   # Encrypted audit logs (daily rotation)
├── watch/                  # Learning mode discovery data (encrypted)
├── vaults/
│   ├── ssh/                # Ciphertext for ~/.ssh
│   │   ├── .onvault_source # Original path metadata
│   │   └── .onvault_exclusions # Excluded file patterns
│   └── aws/                # Ciphertext for ~/.aws
└── mnt/
    ├── ssh/                # FUSE mount → symlinked from ~/.ssh
    └── aws/                # FUSE mount → symlinked from ~/.aws

Requirements

  • macOS 15 Sequoia or later (Apple Silicon)
  • macFUSE 5.1+ (brew install --cask macfuse)
  • Dist builds still require macFUSE at runtime

Build Requirements (developers only)

  • Xcode Command Line Tools (xcode-select --install)
  • OpenSSL 3 (brew install openssl)
  • libargon2 (brew install argon2)
  • macFUSE (brew install --cask macfuse)

Project Structure

onvault/
├── src/
│   ├── common/         # Crypto, hashing, IPC, config, logging, types,
│   │                   # compliance reports, policy YAML import/export
│   ├── fuse/           # Layer 1: macFUSE encrypted filesystem,
│   │                   # vault lifecycle, multi-threaded encrypt pool
│   ├── esf/            # Layer 2: Endpoint Security per-process control,
│   │                   # policy engine, integrity monitor, process chain,
│   │                   # container detection
│   ├── keystore/       # Secure Enclave + Keychain, KMS provider abstraction
│   ├── daemon/         # onvaultd: IPC server, HTTP server, web UI (menubar.html)
│   ├── cli/            # onvault CLI + interactive configure
│   ├── menubar/        # macOS menu bar: WKWebView popover + notifications
│   ├── auth/           # Passphrase, sessions, Touch ID, recovery key,
│   │                   # key rotation (journal-based crash-safe), key lifecycle
│   └── watch/          # Learning/discovery mode (ESF NOTIFY observer)
├── tests/              # 172 tests across 19 suites (keystore_stub for CI)
├── defaults/           # Smart default allowlists (ssh, aws, kube, gnupg, docker)
├── install/            # launchd plist, entitlements
├── Makefile
└── LICENSE             # GPL-3.0

Enterprise Features

onvault includes production-grade features expected of enterprise file encryption solutions:

Feature Description
Glob pattern rules Wildcard process matching (/usr/bin/ssh*, /opt/*/bin/python3)
UID/GID access control Restrict rules to specific users or groups
Global policy hierarchy Global rules as fallback when no vault-specific rule matches
Policy-as-code Export/import policies as YAML for version control
Multi-threaded encryption Parallel file encryption via thread pool for large vaults
File exclusion patterns Skip *.log, .cache/* etc. during vault creation
Online key rotation Zero-downtime re-encryption with atomic key swap (rwlock)
Scheduled rotation Automatic rotation on configurable cadence (e.g., 90 days)
Crash-safe rotation Journal-based recovery from interrupted key rotations
Syslog/SIEM integration Forward logs to syslog, JSON files, or Unix sockets
Compliance reports PCI-DSS, HIPAA, SOX reports (JSON) verifying encryption, rotation, logging
Key lifecycle tracking Fingerprint, creation date, rotation count, last rotation timestamp
Agent self-protection kqueue-based monitoring of daemon binary and config integrity
Process chain verification Verify process ancestry (detect trusted binaries launched by malware)
Binary tamper detection Runtime hash cache with mtime+inode+size invalidation
KMS provider abstraction Pluggable key management (Secure Enclave default, extensible to cloud KMS)
Time-based access rules Allow access only during specific hours/days
Backup mode Authorized backup tools get plaintext access with strong identity verification
Health API Machine-readable JSON endpoint for monitoring (/api/health)
Touch ID Biometric unlock (after first passphrase unlock stores key in Keychain)
Recovery key 24-character key for passphrase reset (displayed at init)

Competitive Landscape

No existing macOS product combines file encryption with per-process access control:

Product Encryption Per-Process Control Policy-as-Code Compliance Open Source
onvault AES-256-XTS (FUSE) ESF + code signing + UID/GID YAML export/import PCI-DSS, HIPAA, SOX GPL-3.0
FileVault Full-disk (APFS) None No No No
Santa (NorthPoleSec) None ESF-based Yes No Yes
Cryptomator AES-256-GCM (FUSE) None No No Yes
CrowdStrike Falcon None Partial (behavioral) Proprietary Partial No

Contributing

Contributions welcome. Please open an issue before submitting large PRs.

# Build and test
make clean && make && make test

# 172 tests across 19 suites — crypto, vault, auth, FUSE ops, config,
# policy, logging, IPC, HTTP, key rotation, glob matching, UID/GID,
# log sinks, online rotation, encrypt pool, security, compliance, KMS, audit
make test

License

GNU General Public License v3.0


Powered by onllm.dev

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors