vesper
Ephemeral, capability-based secret delivery for local processes and CI jobs.
Vesper keeps secret values out of child-process environment variables, command arguments and temporary files. A small local broker stores each value in protected memory and gives the target process only an opaque 256-bit capability. The application exchanges that capability for the secret over a private Unix domain socket.
The repository is deliberately polyglot, but not as a demonstration gimmick: one C broker serves native clients for C, Ruby, PHP, Kotlin/JVM, Swift and POSIX shell through the same documented wire protocol.
Why this exists
Section titled “Why this exists”Traditional secret injection often looks like this:
API_TOKEN=super-secret ./applicationThat value can be inherited by subprocesses, copied into crash diagnostics, dumped by debug tooling, exposed by accidental logging or kept alive much longer than necessary. Vesper changes the contract:
VESPER_SOCKET=/tmp/vesper.../broker.sockVESPER_CAP_API_TOKEN=5ee8...f93aThe environment contains only a short-lived opaque capability. The real value remains in the broker until a permitted process requests it.
Properties
Section titled “Properties”- 256-bit cryptographically random capability tokens.
- One-time retrieval by default; configurable use count.
- Monotonic TTL expiration.
- Per-UID authorization from kernel peer credentials.
- Linux process-group binding for
vesper run, including descendants. - Unix socket and containing directory restricted to the current user.
- Secret pages use
mlockwhen allowed,MADV_DONTDUMP, and LinuxMADV_DONTFORK. - Memory is explicitly overwritten before release.
- Optional JSON Lines audit log containing metadata, never values or tokens.
- Binary-safe values up to 1 MiB.
- No external dependency in the broker or C client.
Requirements: C11 compiler, CMake 3.20+, a POSIX system with Unix domain sockets.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Releasecmake --build build --parallelctest --test-dir build --output-on-failureOr:
make testInstall:
sudo cmake --install buildThis installs vesper, libvesper, the C header, pkg-config metadata, a CMake package and the man page.
Quick start
Section titled “Quick start”Read interactively without echo and launch an application:
build/vesper run \ --secret API_KEY=prompt \ --secret DATABASE_PASSWORD=file:$HOME/.config/myapp/db-password \ -- ./my-applicationMigrate an existing environment variable while removing it from Vesper’s own environment before launch:
build/vesper run --secret API_KEY=env:API_KEY -- ./my-applicationA shell command can retrieve a capability because Vesper binds the whole launched process group on Linux:
build/vesper run --secret API_KEY=prompt --uses 2 -- sh -c ' first=$(vesper get API_KEY) second=$(vesper get API_KEY) printf "received %s and %s bytes\n" "${#first}" "${#second}"'literal:value exists for tests and controlled automation, but should not be used on shared machines because shell history and the launcher command line can expose it.
Secret sources
Section titled “Secret sources”| Source | Meaning |
|---|---|
prompt | One line from /dev/tty, terminal echo disabled |
stdin | Bytes from standard input; trailing CR/LF removed |
file:/path | Exact file bytes |
env:VARIABLE | Copies and erases the variable in the Vesper launcher |
literal:value | Inline value; intentionally discouraged |
Defaults are a 300-second TTL and one successful retrieval. Configure them with --ttl and --uses.
#include <vesper/client.h>
vesper_secret secret = {0};char error[256];if (vesper_get("API_KEY", &secret, error, sizeof(error)) == VESPER_STATUS_OK) { use_bytes(secret.data, secret.length); vesper_secret_free(&secret);}CMake:
find_package(Vesper CONFIG REQUIRED)target_link_libraries(my_app PRIVATE Vesper::Client)require "vesper"secret = Vesper.get("API_KEY")The gem source is in sdk/ruby and is ready for gem build vesper.gemspec.
use Localzet\Vesper\Vesper;$secret = Vesper::get('API_KEY');The Composer package is in sdk/php.
Kotlin/JVM
Section titled “Kotlin/JVM”val secret: ByteArray = Vesper.get("API_KEY")secret.fill(0)The Gradle project is in sdk/kotlin and uses the JDK’s native Unix-domain socket API.
let secret: Data = try Vesper.get("API_KEY")The Swift Package is in sdk/swift and supports Linux and macOS.
POSIX shell
Section titled “POSIX shell”. sdk/shell/vesper.shsecret=$(vesper_get API_KEY)Shell variables are neither NUL-safe nor strongly erasable; prefer a native SDK for binary, long-lived or high-value secrets.
Standalone broker mode
Section titled “Standalone broker mode”vesper run is the safest default because it creates a private temporary socket, provisions capabilities, waits for the application and destroys the broker. The individual commands are available for supervisors and CI systems:
vesper serve --socket "$XDG_RUNTIME_DIR/vesper.sock" --audit ./vesper-audit.jsonl &TOKEN=$(printf %s "$SECRET" | vesper put \ --socket "$XDG_RUNTIME_DIR/vesper.sock" \ --name API_KEY --source stdin --ttl 30 --uses 1)VESPER_SOCKET="$XDG_RUNTIME_DIR/vesper.sock" \VESPER_CAP_API_KEY="$TOKEN" ./applicationInspect broker occupancy without exposing identifiers:
vesper stats --socket "$XDG_RUNTIME_DIR/vesper.sock"# {"entries":1,"limit":4096}Architecture
Section titled “Architecture”secret source -> vesper launcher -> protected broker memory ^ | VSP1 over private Unix socketopaque capability -> target process -+The broker is intentionally single-threaded. Secret retrieval is tiny, deterministic work, and a serial event loop avoids locks and secret-lifetime races. The default limit is 4,096 live entries.
See:
docs/PROTOCOL.md— complete VSP1 wire protocol.docs/THREAT_MODEL.md— guarantees and explicit non-goals.docs/ARCHITECTURE.md— process and memory lifecycle.SECURITY.md— vulnerability reporting.
Platform behavior
Section titled “Platform behavior”Linux receives the strongest mode: the broker verifies the requester’s UID and process group using SO_PEERCRED. macOS provides UID verification through getpeereid; vesper run therefore uses UID-scoped capabilities there. Root, a ptrace-capable debugger, a compromised kernel and code already executing inside the target’s trust boundary are outside the threat model.
Repository layout
Section titled “Repository layout”src/ C broker, CLI and protocol implementationinclude/vesper/ public C APIsdk/ Ruby, PHP, Kotlin, Swift and shell clientsexamples/ runnable examples for every clienttests/ unit and end-to-end testsdocs/ protocol, architecture and threat modelLicense
Section titled “License”MIT © 2026 localzet
Source · MIT