Skip to content

AutomataNexus/NexusVault

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NexusVault Logo

NexusVault

Zero-dependency secrets management for Rust applications

License: Apache-2.0 Rust 1.75+ Version 0.2.3 Tests: 63 LOC: 2475


Overview

NexusVault is a lightweight, Rust-native encrypted secrets manager designed to be embedded directly into application infrastructure or deployed as a standalone service. Unlike HashiCorp Vault, which operates as a heavyweight external dependency requiring its own cluster management, operator workflows, and enterprise licensing, NexusVault compiles into a single binary with zero runtime dependencies beyond the operating system.

NexusVault provides the core primitives that production applications actually need: encrypted key-value secret storage with versioning, a transit encryption engine for encryption-as-a-service, passphrase-based seal/unseal lifecycle, component-scoped access control, and a bounded in-memory audit log. All cryptographic operations use AES-256-GCM with PBKDF2-HMAC-SHA256 key derivation, implemented via the aes-gcm and ring crates -- both widely audited Rust cryptography libraries.

The project originated as the integrated vault module (aegis-vault) within the Aegis-DB multi-paradigm database platform. NexusVault extracts that module into a standalone server with an HTTP REST API, making it usable by any application regardless of whether it uses Aegis-DB. The vault can run in-memory for development or persist encrypted data to disk for production. Auto-unseal is supported for development convenience, while production deployments can start sealed and require an explicit unseal operation with the correct passphrase.


Architecture

                        +-----------------------+
                        |     REST API Layer    |
                        |   (Axum HTTP Server)  |
                        +----------+------------+
                                   |
                    +--------------+--------------+
                    |                             |
          +---------+---------+       +-----------+-----------+
          |    AegisVault     |       |    Transit Engine     |
          |  (Main Facade)    |       | (Encryption-as-a-Svc)|
          +---------+---------+       +-----------+-----------+
                    |                             |
          +---------+---------+       +-----------+-----------+
          |    VaultStore     |       |   Named AES-256-GCM   |
          | (Encrypted K/V)   |       |   Keys (in-memory)    |
          +---------+---------+       +-----------------------+
                    |
       +------------+------------+
       |            |            |
+------+------+ +---+----+ +----+-------+
| SealManager | | Access | | Audit Log  |
| (Master Key | |Control | | (Bounded   |
|  Lifecycle) | |        | |  VecDeque) |
+------+------+ +--------+ +------------+
       |
+------+------+
|    Disk     |
| Persistence |
| (vault.dat  |
|  vault.key) |
+-------------+

Data flow for a SET operation:

  1. HTTP request arrives at the REST API layer.
  2. AegisVault::set() is called, which delegates to VaultStore::set().
  3. VaultStore checks that the vault is not sealed via SealManager::is_sealed().
  4. AccessController::check_access() validates the requesting component has write permission for the key prefix.
  5. SealManager::encrypt() encrypts the plaintext value using AES-256-GCM with the master key.
  6. The encrypted blob is stored as a new SecretVersion in the in-memory HashMap.
  7. If data_dir is configured, VaultStore::save_to_disk() serializes the entire secrets map, encrypts it as a single blob, and writes it atomically (temp file + rename) to vault.dat.
  8. VaultAuditLog::record_success() logs the operation.

Features

Encrypted Secret Storage

All secret values are encrypted at rest using AES-256-GCM with the vault's master key. Secrets are organized as key-value pairs with hierarchical path-style keys (e.g., db/password, api/stripe/key). Each secret maintains a version history, allowing rollback and audit of changes over time. Old versions are automatically pruned when the configurable max_versions limit is exceeded.

Versioned Secrets

Every write to a secret key creates a new version rather than overwriting the previous value. Versions are numbered sequentially starting at 1. The get operation returns the current (latest) version by default, while get_version can retrieve any specific historical version. Each version records the creation timestamp and the component that created it.

Seal/Unseal Lifecycle

The vault implements a seal/unseal lifecycle inspired by HashiCorp Vault. When sealed, the master encryption key is zeroized from memory and all operations that require decryption fail with a VaultError::Sealed error. Unsealing requires the correct passphrase, which is used to derive the master key via PBKDF2-HMAC-SHA256 (100,000 iterations) and then decrypt the stored master key blob. This ensures that even if the vault process is memory-dumped while sealed, the master key cannot be recovered.

Transit Encryption Engine

The transit engine provides encryption-as-a-service: applications can create named encryption keys and use them to encrypt/decrypt arbitrary data without ever having access to the raw key material. Each transit key is an independent AES-256-GCM key generated from a cryptographically secure random source. This pattern is useful for encrypting data at the application layer (e.g., encrypting user PII before storing it in a database) without requiring the application to manage encryption keys directly.

Component-Based Access Control

Access to secrets is governed by policies that restrict which components can perform which operations on which key prefixes. The AccessController supports both default-allow (for backwards compatibility and development) and default-deny modes. Policies specify allowed components, allowed key prefixes, and permitted operations (read, write, delete, list).

Bounded Audit Log

Every vault operation -- reads, writes, deletes, seal/unseal events, transit operations -- is recorded in an in-memory audit log implemented as a bounded VecDeque. When the log reaches its configured maximum capacity, the oldest entries are evicted. Each entry records the timestamp, operation type, key (if applicable), requesting component, success/failure status, and optional detail message. When data_dir is configured, audit entries are also appended to an audit.log file as JSON lines for durable persistence across restarts.

Disk Persistence

When a data_dir is configured, the vault persists its encrypted data to disk after every mutation. The secrets map is serialized to JSON, encrypted as a single AES-256-GCM blob using the master key, and written atomically via a temp-file-and-rename pattern. The master key itself is stored separately in vault.key, encrypted with the passphrase-derived key. Transit encryption keys are persisted to transit.dat, also encrypted with the master key. On startup, the vault loads all persisted files and auto-unseals if configured. All vault files are created with 0600 permissions (owner-only read/write).

Auto-Unseal

For development and testing, the vault supports auto-unseal on startup. When auto_unseal is enabled and a passphrase is provided, the vault derives the master key from the passphrase and unseals automatically. If no passphrase is provided, a random one is generated and logged as a warning. Production deployments should disable auto-unseal and manage the unseal process explicitly.

Atomic Writes

Disk persistence uses an atomic write strategy: data is first written to a .tmp file, then renamed to the final path. This prevents corruption if the process crashes mid-write. The rename operation is atomic on all major filesystems (ext4, XFS, APFS, NTFS).

Key Zeroization

The master key type (MasterKey) implements the Zeroize trait via the zeroize crate. When the vault is sealed or the MasterKey is dropped, the key material is overwritten with zeros in memory. This mitigates the risk of key recovery from process memory dumps or core files.

REST API

NexusVault exposes all vault operations over a JSON REST API built on Axum. The API supports secret CRUD, seal/unseal, transit encryption/decryption, and audit log retrieval. Responses use standard HTTP status codes (503 for sealed, 404 for not found, 403 for access denied, 409 for conflicts).


Quick Start

Running the Server

# Build from source
cargo build --release

# Run with in-memory storage (development, no auth)
./target/release/nexusvault --port 8200 --no-auth --passphrase "dev-passphrase"

# Run with disk persistence and authentication (production)
./target/release/nexusvault \
  --port 8200 \
  --data-dir /var/lib/nexusvault \
  --passphrase "your-secure-passphrase" \
  --api-key "your-api-key"

# Run with TLS enabled (production)
./target/release/nexusvault \
  --port 8200 \
  --data-dir /var/lib/nexusvault \
  --passphrase "your-secure-passphrase" \
  --api-key "your-api-key" \
  --tls-cert /path/to/cert.pem \
  --tls-key /path/to/key.pem

# Start sealed (requires explicit unseal)
./target/release/nexusvault \
  --port 8200 \
  --data-dir /var/lib/nexusvault \
  --api-key "your-api-key" \
  --start-sealed

Basic Usage with curl

# Check health (no auth required)
curl http://localhost:8200/health

# All /v1/* endpoints require Bearer token authentication:
API_KEY="your-api-key"

# Store a secret
curl -X PUT http://localhost:8200/v1/secrets/db/password \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "s3cret!", "component": "myapp"}'

# Retrieve a secret
curl -H "Authorization: Bearer $API_KEY" \
  http://localhost:8200/v1/secrets/db/password?component=myapp

# List secrets by prefix
curl -H "Authorization: Bearer $API_KEY" \
  "http://localhost:8200/v1/secrets?prefix=db/&component=myapp"

# Delete a secret
curl -X DELETE -H "Authorization: Bearer $API_KEY" \
  "http://localhost:8200/v1/secrets/db/password?component=myapp"

# Seal the vault
curl -X POST -H "Authorization: Bearer $API_KEY" \
  http://localhost:8200/v1/seal

# Unseal the vault (rate limited: 5 attempts per 60 seconds)
curl -X POST -H "Authorization: Bearer $API_KEY" \
  http://localhost:8200/v1/unseal \
  -H "Content-Type: application/json" \
  -d '{"passphrase": "your-secure-passphrase"}'

Using as a Library

use aegis_vault::{AegisVault, VaultConfig};
use std::path::PathBuf;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = VaultConfig {
        data_dir: Some(PathBuf::from("/var/lib/myapp/vault")),
        auto_unseal: true,
        passphrase: Some("my-passphrase".to_string()),
        max_versions: 10,
        rotation_check_interval_secs: 3600,
        audit_log_max_entries: 10_000,
    };

    let vault = AegisVault::init(config).await?;

    // Store secrets
    vault.set("db/password", "hunter2", "myapp")?;
    vault.set("api/stripe_key", "sk_live_...", "myapp")?;

    // Retrieve secrets
    let password = vault.get("db/password", "myapp")?;
    println!("DB password: {}", password);

    // Transit encryption
    vault.transit_create_key("user-data")?;
    let ciphertext = vault.transit_encrypt("user-data", b"sensitive PII")?;
    let plaintext = vault.transit_decrypt("user-data", &ciphertext)?;
    assert_eq!(plaintext, b"sensitive PII");

    // Audit trail
    let entries = vault.audit_entries(10);
    for entry in &entries {
        println!("{}: {:?} on {:?}", entry.timestamp, entry.operation, entry.key);
    }

    Ok(())
}

API Reference

AegisVault

The main vault facade. Combines secret storage, transit encryption, seal management, access control, and audit logging into a single interface.

AegisVault::init(config: VaultConfig) -> Result<Self, VaultError>

Initialize the vault with the given configuration. Performs the full startup sequence:

  1. Creates the SealManager.
  2. If data_dir exists and contains a vault.key file, loads the encrypted master key blob.
  3. If auto_unseal is true, derives the master key from the passphrase (or generates a new key on first run).
  4. Creates VaultStore, TransitEngine, AccessController, and VaultAuditLog.
  5. Loads persisted secrets from vault.dat if the vault is unsealed and the file exists.

Errors: VaultError::Encryption if the passphrase is wrong on an existing vault. VaultError::Io if disk operations fail.


AegisVault::new_auto(data_dir: Option<PathBuf>) -> Self

Synchronous constructor that creates a vault with auto-unseal enabled and no passphrase requirement. Generates a random master key. Intended for use in contexts where async initialization is not available (e.g., building AppState in a synchronous constructor).

Parameters:

  • data_dir -- Optional path for disk persistence. If None, vault operates in-memory only.

Returns: An unsealed AegisVault instance. If auto-unseal fails, returns a sealed vault.


AegisVault::get(key: &str, component: &str) -> Result<String, VaultError>

Retrieve the current (latest) version of a secret by key.

Parameters:

  • key -- The secret key path (e.g., "db/password").
  • component -- The requesting component name for access control and audit logging.

Returns: The decrypted secret value as a String.

Errors:

  • VaultError::Sealed -- Vault is sealed.
  • VaultError::SecretNotFound -- No secret exists with the given key.
  • VaultError::AccessDenied -- Component does not have read access to this key.
  • VaultError::Encryption -- Decryption failed (corrupted data).

AegisVault::set(key: &str, value: &str, component: &str) -> Result<(), VaultError>

Store a secret value. If the key already exists, a new version is created. The value is encrypted with AES-256-GCM before storage. If data_dir is configured, the vault data is persisted to disk after the write.

Parameters:

  • key -- The secret key path.
  • value -- The plaintext secret value to encrypt and store.
  • component -- The requesting component name.

Errors:

  • VaultError::Sealed -- Vault is sealed.
  • VaultError::AccessDenied -- Component does not have write access.
  • VaultError::Encryption -- Encryption failed.
  • VaultError::Io -- Disk persistence failed.

AegisVault::delete(key: &str, component: &str) -> Result<(), VaultError>

Delete a secret and all its versions. If data_dir is configured, the vault data is persisted to disk after deletion.

Parameters:

  • key -- The secret key path to delete.
  • component -- The requesting component name.

Errors:

  • VaultError::Sealed -- Vault is sealed.
  • VaultError::SecretNotFound -- No secret exists with the given key.
  • VaultError::AccessDenied -- Component does not have delete access.

AegisVault::list(prefix: &str, component: &str) -> Result<Vec<String>, VaultError>

List all secret keys matching a prefix. Pass an empty string to list all keys.

Parameters:

  • prefix -- Key prefix to filter by (e.g., "db/" to list all database secrets).
  • component -- The requesting component name.

Returns: A Vec<String> of matching key names.

Errors:

  • VaultError::Sealed -- Vault is sealed.
  • VaultError::AccessDenied -- Component does not have list access.

AegisVault::seal() -> Result<(), VaultError>

Seal the vault. The master key is zeroized from memory. All subsequent operations requiring encryption or decryption will fail with VaultError::Sealed until unseal() is called.


AegisVault::unseal(passphrase: &str) -> Result<(), VaultError>

Unseal the vault with a passphrase. Derives the master key via PBKDF2-HMAC-SHA256 and decrypts the stored master key blob. Reloads persisted secrets from disk if available.

Parameters:

  • passphrase -- The passphrase used to derive the master key.

Errors:

  • VaultError::InvalidPassphrase -- The passphrase is incorrect.
  • VaultError::Other -- No encrypted key blob is available (vault was never initialized with a passphrase).

AegisVault::is_sealed() -> bool

Check if the vault is currently sealed. Returns true if sealed, false if unsealed.


AegisVault::status() -> VaultStatus

Get vault status information including sealed state, secret count, transit key count, and uptime since last unseal.

Returns: A VaultStatus struct:

pub struct VaultStatus {
    pub sealed: bool,
    pub secret_count: usize,
    pub transit_key_count: usize,
    pub uptime_secs: Option<u64>,
}

AegisVault::transit_encrypt(key_name: &str, plaintext: &[u8]) -> Result<Vec<u8>, VaultError>

Encrypt data using a named transit key. The raw key material is never exposed to the caller.

Parameters:

  • key_name -- Name of the transit key to use.
  • plaintext -- Data to encrypt.

Returns: Encrypted bytes (12-byte nonce + AES-256-GCM ciphertext).

Errors:

  • VaultError::SecretNotFound -- Transit key does not exist.
  • VaultError::Encryption -- Encryption failed.

AegisVault::transit_decrypt(key_name: &str, ciphertext: &[u8]) -> Result<Vec<u8>, VaultError>

Decrypt data using a named transit key.

Parameters:

  • key_name -- Name of the transit key to use.
  • ciphertext -- Data to decrypt (nonce + ciphertext as produced by transit_encrypt).

Returns: Decrypted plaintext bytes.

Errors:

  • VaultError::SecretNotFound -- Transit key does not exist.
  • VaultError::Encryption -- Decryption failed (wrong key or corrupted data).

AegisVault::transit_create_key(name: &str) -> Result<(), VaultError>

Create a new named transit encryption key. The key is a randomly generated 256-bit AES key.

Parameters:

  • name -- Name for the new transit key.

Errors:

  • VaultError::AlreadyExists -- A transit key with this name already exists.

AegisVault::transit_list_keys() -> Vec<String>

List all transit key names. Returns an empty vector if no transit keys have been created.


AegisVault::audit_entries(limit: usize) -> Vec<VaultAuditEntry>

Get the most recent audit log entries, ordered from newest to oldest.

Parameters:

  • limit -- Maximum number of entries to return.

Returns: A Vec<VaultAuditEntry> where each entry contains:

pub struct VaultAuditEntry {
    pub timestamp: u64,
    pub operation: VaultOperation,  // Get, Set, Delete, List, Seal, Unseal, Rotate, TransitEncrypt, TransitDecrypt
    pub key: Option<String>,
    pub component: Option<String>,
    pub success: bool,
    pub detail: Option<String>,
}

REST API Endpoints

All /v1/* endpoints require a valid Authorization: Bearer <api-key> header (unless --no-auth is used). The /health endpoint is always unauthenticated. The base URL is http://<host>:<port> (or https:// when TLS is enabled).

Method Path Description Request Body Response
GET /health Health check (no auth) -- {"status":"ok","version":"0.2.3","sealed":false}
GET /v1/status Vault status -- {"sealed":false,"secret_count":5,"transit_key_count":2,"uptime_secs":3600}
GET /v1/secrets/{key} Get a secret Query: ?component=myapp {"key":"db/password","value":"s3cret!"}
PUT /v1/secrets/{key} Store a secret {"value":"s3cret!","component":"myapp"} {"message":"secret stored","key":"db/password"}
DELETE /v1/secrets/{key} Delete a secret Query: ?component=myapp {"message":"secret deleted","key":"db/password"}
GET /v1/secrets List secrets Query: ?prefix=db/&component=myapp {"keys":["db/password","db/username"]}
POST /v1/seal Seal the vault -- {"sealed":true,"message":"vault sealed"}
POST /v1/unseal Unseal the vault {"passphrase":"my-secret"} {"sealed":false,"message":"vault unsealed"}
POST /v1/transit/keys Create transit key {"name":"my-key"} {"message":"transit key created","name":"my-key"}
GET /v1/transit/keys List transit keys -- {"keys":["my-key","other-key"]}
POST /v1/transit/encrypt Encrypt data {"key_name":"my-key","plaintext":"hello"} {"ciphertext":"vault:v1:base64..."}
POST /v1/transit/decrypt Decrypt data {"key_name":"my-key","ciphertext":"vault:v1:base64..."} {"plaintext":"hello"}
GET /v1/audit Get audit log Query: ?limit=50 {"entries":[...]}

HTTP Status Codes

Code Meaning
200 Success
201 Created (secret stored, transit key created)
401 Unauthorized (invalid passphrase on unseal, or missing/invalid API key)
403 Forbidden (access control denied)
404 Not found (secret or transit key does not exist)
409 Conflict (transit key already exists)
429 Too many requests (unseal rate limit exceeded)
503 Service unavailable (vault is sealed)

Error Response Format

All errors return a JSON body with an error field:

{
  "error": "vault is sealed"
}

Configuration Reference

VaultConfig Fields

Field Type Default Description
data_dir Option<PathBuf> None Directory for vault persistence. If None, vault operates in-memory only. Files created: vault.dat (encrypted secrets), vault.key (encrypted master key).
auto_unseal bool true Whether to automatically unseal on startup. If true and a passphrase is available, the vault unseals during initialization.
passphrase Option<String> $AEGIS_VAULT_PASSPHRASE Passphrase for seal/unseal operations. Falls back to the AEGIS_VAULT_PASSPHRASE environment variable. If neither is set and auto-unseal is enabled, a random passphrase is generated (logged as a warning).
max_versions u32 10 Maximum number of versions to retain per secret key. Older versions are pruned when this limit is exceeded.
rotation_check_interval_secs u64 3600 Interval in seconds between automatic rotation TTL checks. Secrets with a rotation_ttl_secs metadata field that have exceeded their TTL will generate log warnings.
audit_log_max_entries usize 10000 Maximum number of audit log entries to keep in memory. Oldest entries are evicted when this limit is reached.

CLI Arguments

Argument Short Default Description
--port -p 8200 Port for the HTTP server
--host 127.0.0.1 Host to bind to
--data-dir -d None Data directory for persistence
--passphrase None Vault passphrase (or set NEXUSVAULT_PASSPHRASE env var)
--api-key None API key for bearer token authentication (or set NEXUSVAULT_API_KEY env var). Required unless --no-auth is used.
--no-auth false Disable API key authentication (NOT recommended for production)
--component-name api Component name bound to the API key for access control
--tls-cert None Path to TLS certificate file (PEM). Enables HTTPS when used with --tls-key.
--tls-key None Path to TLS private key file (PEM). Enables HTTPS when used with --tls-cert.
--max-versions 10 Maximum secret versions per key
--audit-log-max 10000 Maximum in-memory audit log entries
--start-sealed false Start the vault in sealed state

Environment Variables

Variable Description
NEXUSVAULT_PASSPHRASE Vault passphrase (alternative to --passphrase flag)
NEXUSVAULT_API_KEY API key for authentication (alternative to --api-key flag)
AEGIS_VAULT_PASSPHRASE Vault passphrase (used by the aegis-vault library directly)
RUST_LOG Log level filter (default: info). Examples: debug, nexusvault=debug,aegis_vault=trace

Security Model

Key Derivation

The master encryption key is derived from the user-supplied passphrase using PBKDF2-HMAC-SHA256 with 100,000 iterations and a 16-byte cryptographically random salt. The derived key is 256 bits (32 bytes). The salt is stored alongside the encrypted master key blob, ensuring that the same passphrase produces different derived keys for different vault instances.

The derivation chain is:

passphrase (user input)
    |
    v
PBKDF2-HMAC-SHA256 (100,000 iterations, 16-byte random salt)
    |
    v
Derived Key (256-bit)
    |
    v
AES-256-GCM encrypt(Master Key)  -->  Encrypted Master Key Blob
    |                                   (stored in vault.key)
    v
Master Key (256-bit, random)
    |
    v
AES-256-GCM encrypt(secret values)  -->  Encrypted Secrets
                                          (stored in vault.dat)

Encryption

All encryption uses AES-256-GCM (Galois/Counter Mode), which provides both confidentiality and authenticity. Each encryption operation generates a fresh 12-byte (96-bit) nonce from the OS cryptographic random number generator (OsRng). The ciphertext format is:

  • Master key blob: salt (16 bytes) || nonce (12 bytes) || ciphertext + tag
  • Secret values: nonce (12 bytes) || ciphertext + tag
  • Transit encryption: nonce (12 bytes) || ciphertext + tag

AES-256-GCM provides a 128-bit authentication tag, ensuring that any tampering with the ciphertext is detected during decryption.

Disk Persistence

When persistence is enabled, the following files are written to the data_dir:

  • vault.key -- The master key encrypted with the passphrase-derived key. Format: salt (16) + nonce (12) + AES-256-GCM(master_key).
  • vault.dat -- The entire secrets map serialized to JSON, then encrypted with the master key. Format: nonce (12) + AES-256-GCM(json_blob).
  • transit.dat -- Transit encryption keys serialized to JSON, encrypted with the master key.
  • audit.log -- Append-only JSON lines audit log for durable persistence across restarts.

All files are created with 0600 permissions (owner-only read/write on Unix). Encrypted files are written atomically using a temp-file-and-rename strategy to prevent corruption on crash.

Access Control

The AccessController supports two modes:

  • Default-allow (default): All operations are permitted unless explicitly denied by a policy. This is the default for backwards compatibility and development use.
  • Default-deny: All operations are blocked unless a matching policy grants access. Recommended for production.

Policies specify:

  • Allowed components: Which component names can use this policy (empty = all).
  • Allowed prefixes: Which key prefixes this policy covers (empty = all).
  • Operations: Read, write, delete permissions as booleans.

Seal Lifecycle

The seal/unseal mechanism provides defense-in-depth:

  1. Sealed state: The master key is None in memory. All encrypt/decrypt operations return VaultError::Sealed. The MasterKey struct's memory was zeroized when it was dropped.
  2. Unseal operation: The passphrase is used to derive a key via PBKDF2, which decrypts the master key blob. The master key is loaded into memory and the vault transitions to unsealed state.
  3. Seal operation: The master key is set to None and zeroized on drop. No passphrase is needed to seal.

The seal state does not affect the encrypted data on disk -- it only controls whether the master key is available in process memory.

Memory Safety

  • Zeroization: The MasterKey type implements Drop to zeroize the key bytes when the key is released. The zeroize crate is used, which prevents the compiler from optimizing away the zeroing write.
  • No unsafe code: The vault implementation uses no unsafe blocks.
  • Concurrency: All shared state is protected by parking_lot::RwLock, which provides reader-writer locking with no poisoning.

Module Reference

Module File Lines Description
lib lib.rs 485 Main AegisVault facade, VaultStatus, initialization logic
master_key master_key.rs 388 SealManager, MasterKey, PBKDF2 key derivation, AES-256-GCM encrypt/decrypt
store store.rs 444 VaultStore encrypted key-value storage, disk persistence, versioned get/set
transit transit.rs 201 TransitEngine encryption-as-a-service with named keys
access access.rs 240 AccessController, AccessPolicy, component-based access control
config config.rs 102 VaultConfig with defaults, file path helpers, test config
secret secret.rs 151 Secret, SecretVersion, SecretMetadata, version pruning
audit audit.rs 195 VaultAuditLog, VaultAuditEntry, VaultOperation, bounded log
error error.rs 69 VaultError enum with thiserror derives
provider provider.rs 102 AegisVaultProvider simplified component-scoped wrapper
rotation rotation.rs 109 Rotation TTL checking, async rotation loop

Comparison: NexusVault vs HashiCorp Vault

Feature NexusVault HashiCorp Vault
Language Rust Go
Deployment Single binary, embeddable Separate server process, requires cluster
Dependencies Zero runtime dependencies Requires Consul or integrated storage
Secret Storage AES-256-GCM encrypted KV Multiple backends (KV, Consul, etc.)
Transit Encryption Named AES-256-GCM keys Named keys with multiple algorithms
Seal/Unseal Single passphrase Shamir's Secret Sharing (multiple keys)
Key Derivation PBKDF2-HMAC-SHA256 (100K iterations) Shamir + AES-256-GCM
Access Control Component + prefix policies Full ACL with paths, capabilities, tokens
Audit Log In-memory + persistent JSON lines file File, syslog, socket backends
Secret Versioning Built-in, configurable retention KV v2 engine
Dynamic Secrets Not supported Database, AWS, PKI, SSH, etc.
Authentication Bearer token API key + component binding Tokens, AppRole, LDAP, OIDC, Kubernetes
Replication Not built-in (use with Aegis-DB) Integrated HA with Raft
License Apache-2.0 BSL 1.1 (source-available, not open-source)
Resource Usage ~5 MB memory, instant startup ~100 MB+ memory, slower startup
Configuration Rust struct or CLI args HCL files
API REST/JSON REST/JSON + CLI
Namespaces Key prefixes Enterprise-only feature
Rotation TTL warnings + manual Automated with lease management
PKI/SSH Not supported Full PKI and SSH certificate authority

When to use NexusVault:

  • You need a lightweight secrets manager embedded in your Rust application.
  • You want a single binary with no external dependencies.
  • Your use case is key-value secrets and transit encryption.
  • You are already using Aegis-DB and want integrated secrets management.
  • You need Apache-2.0 licensed software.

When to use HashiCorp Vault:

  • You need dynamic secrets (database credentials, cloud IAM, PKI).
  • You need multi-party unseal (Shamir's Secret Sharing).
  • You need enterprise features (namespaces, sentinel policies, replication).
  • You operate a large-scale multi-service infrastructure.
  • You need integrated identity and authentication providers.

Building from Source

Prerequisites

  • Rust 1.75 or later
  • Cargo (included with Rust)

Build

# Clone the repository
git clone https://github.com/automatanexus/NexusVault.git
cd NexusVault

# Build in debug mode
cargo build

# Build in release mode (optimized)
cargo build --release

# Run tests
cargo test

# Run the server
cargo run -- --port 8200 --data-dir ./data

Build the aegis-vault library separately

If you want to use NexusVault as a library in your own Rust project, add the aegis-vault crate as a dependency:

[dependencies]
aegis-vault = { git = "https://github.com/automatanexus/Aegis-DB.git", version = "0.2.3" }

Cross-compilation

# Linux x86_64 (musl for static linking)
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl

# macOS ARM
rustup target add aarch64-apple-darwin
cargo build --release --target aarch64-apple-darwin

Examples

Storing Database Credentials

API_KEY="your-api-key"

# Store credentials
curl -X PUT http://localhost:8200/v1/secrets/db/postgres/host \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "db.example.com", "component": "webapp"}'

curl -X PUT http://localhost:8200/v1/secrets/db/postgres/port \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "5432", "component": "webapp"}'

curl -X PUT http://localhost:8200/v1/secrets/db/postgres/password \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "super-secret-password", "component": "webapp"}'

# Retrieve all database secrets
curl -H "Authorization: Bearer $API_KEY" \
  "http://localhost:8200/v1/secrets?prefix=db/postgres/&component=webapp"
# {"keys":["db/postgres/host","db/postgres/port","db/postgres/password"]}

Transit Encryption for User Data

# Create an encryption key for user PII
curl -X POST http://localhost:8200/v1/transit/keys \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "user-pii"}'

# Encrypt a social security number
curl -X POST http://localhost:8200/v1/transit/encrypt \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key_name": "user-pii", "plaintext": "123-45-6789"}'
# {"ciphertext":"vault:v1:BASE64_ENCODED_CIPHERTEXT"}

# Decrypt it back
curl -X POST http://localhost:8200/v1/transit/decrypt \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key_name": "user-pii", "ciphertext": "vault:v1:BASE64_ENCODED_CIPHERTEXT"}'
# {"plaintext":"123-45-6789"}

Monitoring with the Audit Log

# Get the last 20 audit entries
curl -H "Authorization: Bearer $API_KEY" \
  "http://localhost:8200/v1/audit?limit=20"
# {
#   "entries": [
#     {
#       "timestamp": 1711234567,
#       "operation": "Set",
#       "key": "db/password",
#       "component": "webapp",
#       "success": true,
#       "detail": null
#     },
#     ...
#   ]
# }

License

Licensed under the Apache License, Version 2.0. See LICENSE for the full text.

Copyright 2024-2026 AutomataNexus Development Team

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

About

Zero-dependency secrets management for Rust applications. Built into Aegis-DB.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages