Zero-dependency secrets management for Rust applications
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.
+-----------------------+
| 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:
- HTTP request arrives at the REST API layer.
AegisVault::set()is called, which delegates toVaultStore::set().VaultStorechecks that the vault is not sealed viaSealManager::is_sealed().AccessController::check_access()validates the requesting component has write permission for the key prefix.SealManager::encrypt()encrypts the plaintext value using AES-256-GCM with the master key.- The encrypted blob is stored as a new
SecretVersionin the in-memoryHashMap. - If
data_diris configured,VaultStore::save_to_disk()serializes the entire secrets map, encrypts it as a single blob, and writes it atomically (temp file + rename) tovault.dat. VaultAuditLog::record_success()logs the operation.
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.
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.
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.
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.
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).
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.
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).
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.
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).
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.
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).
# 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# 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"}'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(())
}The main vault facade. Combines secret storage, transit encryption, seal management, access control, and audit logging into a single interface.
Initialize the vault with the given configuration. Performs the full startup sequence:
- Creates the
SealManager. - If
data_direxists and contains avault.keyfile, loads the encrypted master key blob. - If
auto_unsealis true, derives the master key from the passphrase (or generates a new key on first run). - Creates
VaultStore,TransitEngine,AccessController, andVaultAuditLog. - Loads persisted secrets from
vault.datif 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.
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. IfNone, vault operates in-memory only.
Returns: An unsealed AegisVault instance. If auto-unseal fails, returns a sealed vault.
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).
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.
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.
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.
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.
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).
Check if the vault is currently sealed. Returns true if sealed, false if unsealed.
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>,
}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.
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 bytransit_encrypt).
Returns: Decrypted plaintext bytes.
Errors:
VaultError::SecretNotFound-- Transit key does not exist.VaultError::Encryption-- Decryption failed (wrong key or corrupted data).
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.
List all transit key names. Returns an empty vector if no transit keys have been created.
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>,
}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":[...]} |
| 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) |
All errors return a JSON body with an error field:
{
"error": "vault is sealed"
}| 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. |
| 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 |
| 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 |
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)
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.
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.
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.
The seal/unseal mechanism provides defense-in-depth:
- Sealed state: The master key is
Nonein memory. All encrypt/decrypt operations returnVaultError::Sealed. TheMasterKeystruct's memory was zeroized when it was dropped. - 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.
- Seal operation: The master key is set to
Noneand 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.
- Zeroization: The
MasterKeytype implementsDropto zeroize the key bytes when the key is released. Thezeroizecrate is used, which prevents the compiler from optimizing away the zeroing write. - No unsafe code: The vault implementation uses no
unsafeblocks. - Concurrency: All shared state is protected by
parking_lot::RwLock, which provides reader-writer locking with no poisoning.
| 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 |
| 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.
- Rust 1.75 or later
- Cargo (included with Rust)
# 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 ./dataIf 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" }# 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-darwinAPI_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"]}# 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"}# 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
# },
# ...
# ]
# }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.
