An ephemeral secret storage library and command line utility with binary verification.
burnafter is a Go utility and library that provides
time-limited, in-memory secret storage with strong binary authentication and
persistence in the system keyring.
It's designed for scenarios where you need to store sensitive data temporarily (like API keys, tokens, or passwords) that survives across command invocations and want to ensure only a specific application binary can access it.
- Minimal dependencies: Almost no expternal dependencies
- Ephemeral Storage: Secrets stored in the system keyring or in memory, never written to disk (unless the fallback mechanism kicks in)
- Binary Verification: Only the exact binary that stored a secret can retrieve it (via SHA256 hash verification)
- Time-Limited: Secrets expire based on inactivity timeout or at an absolute deadline
- Process Isolation: Uses server session IDs to prevent replaying older keys on server restarts
- On-Demand Key Derivation: Encryption keys derived when needed, never stored in memory
- Encrypted at Rest: Secrets encrypted in memory using AES-256-GCM
- Automatic Cleanup: Server shuts down after inactivity period or secret expiration
- No Persistency: Client binary updates invalidate access (by design)
For a complete overview of the security model, see the Security and Architecture document.
Applications wishing to store secrets simply need to use the burnafter client library. When secrets are stored, burnafter launches a small daemon that handles client authentication and secret encryption and in-memory storage:
graph TD
Client["Client Binary<br/>(your app)"]
subgraph daemon["🔥 burnafter daemon"]
Auth(Client binary authorization)
end
subgraph keyring["🐧system keyring"]
Secrets["Secrets<br/>(Encrypted)"]
end
Client -->|Unix Socket + gRPC| daemon
daemon --> keyring
style Client fill:#e1f5ff,stroke:#01579b,stroke-width:2px
style Secrets font-size:0.9em,fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
-
Client Authentication:
- Client binary hash (SHA256)
- Client nonce (constant defined or embedded in client binary)
- Server verifies client via Unix socket peer credentials (using
SO_PEERCRED)
-
Key Derivation (HKDF-SHA256):
key = HKDF( client_binary_hash + compile_time_nonce + server_session_id + secret_name, random_salt )
-
Storage:
- Secrets are encrypted with a derived key
- Keys are never stored (derived on-demand)
- Server session ID lost on restart (makes secrets unrecoverable)
- Secrets are kept in the system keyring, or in memory only if keyring not avilable (see fallback mechanism)
-
Access Control:
- Server verifies client binary hash matches the expected client digest
- Server checks client-defined nonce matches
- Secrets expire based on TTL or on inactivity threshold
Designed for defense in depth scenarios, burnafter is not intended to be 100% impregnable but rather to help keep secrets safer and to be used together with other protection mechhanisms.
- Accidental secret leakage to logs/files
- Other programs accessing secrets
- Long lived secrets persisting in the filesystem
- Key exposure
- Debuggers attached to server process
- Root/admin users attaching to server process
burnafter is designed to work as a library, but it also offers a command line utility that can be used to store secrets from shell scripts, etc.
# Store a secret (with 2-hour TTL)
burnafter store api-key "my-secret-token" 7200
# Retrieve a secret
burnafter get api-key
# Check if the server is running
burnafter ping
Flags in the cli utility are simple for now, you can enable debugging and control where the server unix socket is written.
-socket string Unix socket path (default: auto-generated based on binary hash)
-debug Enable debug output
Here are some usage examples, see the QUICKSTART.md for a more in depth guide.
# Store for 4 hours (default)
burnafter store github-token "ghp_xxxxxxxxxxxx"
# Store for 1 hour (3600 seconds)
burnafter store github-token "ghp_xxxxxxxxxxxx" 3600For scripting, use the burnafter binary. It is limited to simple I/O but feel free to suggest features to improve it.
#!/bin/bash
# Secrets can be stored on first run:
burnafter store db-password "$(pass show prod/db)"
# Retrieve secret in a script
DB_PASS=$(burnafter get db-password)
mysql -u user -p"$DB_PASS" mydatabaseFor Go applications, use burnafter as a library for programmatic access:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/carabiner-dev/burnafter"
"github.com/carabiner-dev/burnafter/options"
)
func main() {
ctx := context.Background()
// Create a new client with default options
client := burnafter.NewClient(options.DefaultClient)
// Connect to the server (auto-starts if not running)
if err := client.Connect(ctx); err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer client.Close()
// Store a secret with 2-hour TTL
err := client.Store(ctx, "api-key", "your-secret-token",
options.WithTTL(7200))
if err != nil {
log.Fatalf("Failed to store secret: %v", err)
}
fmt.Println("Secret stored successfully")
// Retrieve the secret
secret, err := client.Get(ctx, "api-key")
if err != nil {
log.Fatalf("Failed to get secret: %v", err)
}
fmt.Printf("Retrieved secret: %s\n", secret)
// Use the secret in your application
// ... your API calls, database connections, etc.
}Advanced Configuration:
// Custom client options
client := burnafter.NewClient(&options.Client{
Nonce: "my-app-v1.0", // Optional: version-specific nonce
Common: options.Common{
DefaultTTL: 2 * time.Hour,
Debug: true,
MaxSecrets: 200,
MaxSecretSize: 2 * 1024 * 1024, // 2 MB
},
})
// Store with absolute expiration time
expiry := time.Now().Add(24 * time.Hour).Unix()
err := client.Store(ctx, "temp-token", "secret",
options.WithAbsoluteExpiration(expiry))Burnafter enforces limits to prevent resource exhaustion:
- Maximum Secrets: 100 secrets per server instance (configurable via
options.MaxSecrets) - Maximum Secret Size: 1 MB per secret (configurable via
options.MaxSecretSize)
Notes:
- Updating an existing secret does not count against the maximum secret limit
- Limits can be customized when using burnafter as a library by modifying
options.MaxSecretsandoptions.MaxSecretSize - Expired secrets are automatically cleaned up, freeing space for new secrets
- Auto-Start: Client automatically starts server if it's not running
- Daemon Mode: Server detaches and runs in background
- Automatic Shutdown: Server shuts down automatically when:
- No activity is detected for 10 minutes (inactivity timeout), OR
- All secrets have expired or have been deleted (no secrets remaining)
- Cleanup: Expired secrets removed every minute
- Unix Socket: Listens on auto-generated hash-based path or custom path
- Go 1.24.11+
- protoc (for regenerating proto files)
- Linux/MacOs (for
SO_PEERCREDsupport)
go build -o burnafter ./cmd/burnafter./generate.shContributions are always welcome! Please open an issue or feel free to submit PR with feature enhacements, bug fixes, etc.
This project was writen with 🧡 by Carabiner Systems, Inc and is released under the Apache-2.0 license.