hyperion_vault 0.1.0

This Release
hyperion_vault 0.1.0
Date
Status
Unstable
Latest Unstable
hyperion_vault 0.3.0 —
Other Releases
Abstract
Encrypted secrets vault for PostgreSQL (KMS envelope encryption, REST API, automatic rotation)
Description
hyperion_vault stores secrets encrypted at rest in PostgreSQL using envelope encryption: a per-version data key is wrapped by AWS KMS and the secret is sealed with XChaCha20-Poly1305, so only ciphertext, the wrapped key, and a nonce are ever written to disk or WAL. A co-located REST API creates, reads, updates, deletes, verifies, and automatically rotates secrets, with old versions kept valid for a configurable grace period. Designed to run on every member of a pg_replica cluster: reads are served locally on any node, writes are routed to the current primary, and the schema replicates byte-for-byte. Access to read secrets is restricted to an IPv4 allowlist; management operations require admin tokens.
Released By
nordlet
License
GPL 3
Resources
Special Files
Tags

Extensions

hyperion_vault 0.1.0
Encrypted secrets vault for PostgreSQL

Documentation

SECURITY
Security
THREAT_MODEL
Threat model
DECISIONS
Design decisions
ARCHITECTURE
Architecture
API
REST API
index
hyperion-vault — apt repository

README

HyperionDB Vault (hyperion-vault)

A PostgreSQL secrets vault for HyperionDB clusters: a CREATE EXTENSION that stores secrets encrypted at rest plus a co-located REST API to create, read, update, delete, and automatically rotate them.

Secrets are sealed with envelope encryption — a per-secret data key (DEK) protected by AWS KMS, with the secret bytes sealed under XChaCha20-Poly1305. PostgreSQL only ever stores ciphertext, the wrapped DEK, and a nonce; plaintext never touches disk or WAL.

Built to run on every member of a hyperiondb cluster: reads are served locally from any node, while writes are transparently routed to the current primary and replicated byte-for-byte to the rest.

Comparable to supabase/vault (in-database secrets) and OpenBao (KMS-backed envelope encryption + API + rotation) — this project combines both models for a replicated Postgres cluster.

Status: in progress


Features

  • Encryption at rest, KMS-backed. Envelope encryption: AWS KMS wraps a 256-bit data key (via GenerateDataKey/Decrypt); the secret is sealed with XChaCha20-Poly1305. Postgres stores only {wrapped_dek, nonce, ciphertext}.
  • Two secret kinds.
    • manual — set and changed explicitly through the API.
    • automatic — rotated on a schedule by a background worker. Old versions stay valid for a configurable grace period, so dependent services can keep authenticating with the previous secret during cutover.
  • REST API. POST/GET/PUT/DELETE /v1/secrets, plus /rotate and a constant-time /verify endpoint for grace-window validation.
  • Cluster-native (works with hyperiondb). The API runs on every node. Reads (GET, verify) are served from the local node; writes (create/update/delete/rotate) are routed to the current primary via a multi-host libpq pool with target_session_attrs=read-write, so they work from any member and follow failover automatically.
  • IP allowlist for reads. Secret reads are permitted only from an IPv4/CIDR allowlist supplied via VAULT_ALLOWED_IPS. Fail-closed: an empty allowlist denies all reads.
  • Admin token auth for management. create/update/delete/rotate require an admin bearer token. Tokens are stored only as SHA-256 fingerprints and checked in constant time.
  • Automatic rotation. An in-database background worker (running only on the primary) enqueues due rotations and NOTIFYs; the API’s rotation worker performs the re-encryption and expires superseded versions after grace.
  • KMS-outage resilience. Unwrapped data keys are cached in memory for VAULT_DEK_CACHE_TTL_SECS, so reads of previously-read secrets keep working even if AWS KMS is briefly unavailable, and KMS calls are retried with exponential backoff up to VAULT_KMS_MAX_RETRIES to ride out rate-limits.
  • Audit log. Every operation is recorded (actor, client IP, action, outcome) in vault.audit_log.
  • Defense in depth. Row-level security on every table, a dedicated service role, version-bound AEAD associated data (a ciphertext can’t be replayed under another secret name or version), and zeroized key material in memory.
  • Extensive security tests. The hyperion-vault-core crate carries a property-style security suite (tamper detection, nonce uniqueness, AAD binding, fail-closed allowlist, constant-time tokens, grace-window correctness); a Docker-based end-to-end suite covers the API and cluster.

Workspace layout

Crate / dir Kind Purpose
crates/hyperion-vault-core lib Pure-Rust security core: AEAD envelope, IP allowlist, token auth, rotation policy. No DB, no network — fully unit-testable.
crates/hyperion-vault-api bin The REST API service (axum): handlers, IP/token guards, dual DB pools, KMS, rotation worker.
crates/hyperion-vault lib Umbrella crate re-exporting the security core as a single dependency.
extension/ (hyperion_vault) cdylib The pgrx PostgreSQL extension: vault schema, RLS, helper functions, rotation supervisor background worker.
docs/ Architecture, decisions, threat model, API and security docs.
docker/ Node image (built on the pg_replica image) + 3-node cluster compose + e2e overlay.
scripts/ test.sh, test-security.sh, and the e2e/ cluster suite.
packaging/ .deb build + signed apt-repo assembly (mirrors pg_replica).

The encryption algorithm choice (XChaCha20-Poly1305), the application-layer (vs in-database) encryption decision, and the primary-routing strategy are documented in docs/DECISIONS.md.


How it fits with pg_replica

pg_replica (HyperionDB) provides physical streaming replication with Raft-based automatic failover. Standbys are byte-for-byte copies and are read-only; only the primary accepts writes. Roles, DDL, and table data all replicate.

Vault leverages this directly:

  • CREATE EXTENSION hyperion_vault on the primary creates the vault schema; the DDL replicates to every node automatically.
  • Encrypted secret rows replicate like any other table → any node can serve reads (and decrypt locally via KMS).
  • The API’s writer pool uses target_session_attrs=read-write, so create/update/delete/rotate from any node land on the current primary and follow failover with no client changes.

Quick start (local dev, no AWS)

# 1. Build + test the security core (no Postgres needed)
cargo test -p hyperion-vault-core

# 2. Build the pg_replica cluster image first (sibling repo)
cd ../pg_replica && docker compose -f docker/docker-compose.yml build

# 3. Bring up a 3-node cluster with vault + an API sidecar on each node
cd ../vault/docker && cp .env.example .env && docker compose up --build

# 4. Run the end-to-end suite (CRUD, auth, replicated reads, rotation)
bash scripts/e2e.sh        # from the repo root

APIs listen on localhost:8200 (node1), :8201 (node2), :8202 (node3). See docs/API.md for full request/response examples and docker/ for the cluster topology.

# Create a manual secret (admin token required)
curl -sS -X POST localhost:8200/v1/secrets \
  -H "Authorization: Bearer $VAULT_ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"name":"db/password","kind":"manual","value":"s3cr3t"}'

# Read it back (must come from an allowlisted IP)
curl -sS localhost:8200/v1/secrets/db/password

# Create an auto-rotating secret with a 24h interval and 1h grace
curl -sS -X POST localhost:8200/v1/secrets \
  -H "Authorization: Bearer $VAULT_ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"name":"svc/api-key","kind":"automatic","rotation_interval_secs":86400,"grace_period_secs":3600}'

Configuration (environment)

Variable Default Description
VAULT_API_LISTEN 0.0.0.0:8200 API bind address.
VAULT_ALLOWED_IPS (empty → deny all reads) Comma-separated IPv4 / CIDR allowed to read secrets.
VAULT_TRUST_PROXY false Trust X-Forwarded-For for the client IP (only behind a trusted proxy).
VAULT_PG_HOSTS 127.0.0.1 Comma-separated cluster hosts (multi-host libpq).
VAULT_PG_PORT 5432 Postgres port.
VAULT_PG_USER / VAULT_PG_PASSWORD / VAULT_PG_DBNAME vault_service / — / postgres Service-role connection.
VAULT_KMS_MODE aws aws (production) or local (dev only).
VAULT_KMS_KEY_ID AWS KMS key id/ARN (required for aws).
VAULT_LOCAL_MASTER_KEY base64 32-byte master key for local mode.
VAULT_ROTATION_POLL_SECS 15 How often the API worker claims rotation jobs.
VAULT_DEK_CACHE_TTL_SECS 300 TTL of the in-memory decrypted-DEK cache; lets previously-read secrets survive a KMS outage. 0 disables.
VAULT_KMS_MAX_RETRIES 5 Retry KMS calls (writes always; reads on a cache miss) up to N times with exponential backoff, to ride out rate-limits/brief outages. 0 disables.

The extension is configured via GUCs: hyperion_vault.rotation_enabled, hyperion_vault.scan_interval_secs, hyperion_vault.database.


Install (Debian/Ubuntu)

A [cd]-tagged commit (or a manual workflow run) builds a signed apt repo on GitHub Pages via .github/workflows/packages.yml. The .deb ships the extension and the hyperion-vault-api binary (/usr/bin/hyperion-vault-api):

curl -fsSL https://hyperiondb.github.io/hyperion-vault/install.sh | sudo bash
sudo apt-get install -y postgresql-18-hyperion-vault

Security

This is security-critical software. Read docs/SECURITY.md and docs/THREAT_MODEL.md before deploying. TLS to Postgres is not enabled by default in this scaffold and must be configured for production.

License

GPL-3.0-or-later. See LICENCE.