OmniVault is a unified Go library for secret management across multiple providers. It provides a single interface for accessing secrets from password managers, cloud secret managers, enterprise vaults, and local storage.
- Unified Interface: Single API for all secret storage backends
- Extensible Architecture: Add custom providers as separate Go modules without modifying the core library
- URI-Based Resolution: Reference secrets using URIs like
op://vault/item/fieldoraws-sm://secret-name - Built-in Providers: Environment variables, file-based, and in-memory storage included
- Zero External Dependencies: Core library has no external dependencies beyond the standard library
- CLI Tool: Command-line interface with encrypted local storage and daemon architecture
- Secure Local Storage: AES-256-GCM encryption with Argon2id key derivation
go get github.com/agentplexus/omnivaultgo install github.com/agentplexus/omnivault/cmd/omnivault@latestpackage main
import (
"context"
"fmt"
"log"
"github.com/agentplexus/omnivault"
)
func main() {
ctx := context.Background()
// Create client with environment variable provider
client, err := omnivault.NewClient(omnivault.Config{
Provider: omnivault.ProviderEnv,
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Get a secret
secret, err := client.Get(ctx, "API_KEY")
if err != nil {
log.Fatal(err)
}
fmt.Println("API Key:", secret.Value)
}package main
import (
"context"
"fmt"
"github.com/agentplexus/omnivault"
"github.com/agentplexus/omnivault/providers/env"
"github.com/agentplexus/omnivault/providers/memory"
)
func main() {
ctx := context.Background()
// Create resolver with multiple providers
resolver := omnivault.NewResolver()
resolver.Register("env", env.New())
resolver.Register("memory", memory.NewWithSecrets(map[string]string{
"database/password": "secret123",
}))
// Resolve secrets from different providers using URIs
apiKey, _ := resolver.Resolve(ctx, "env://API_KEY")
dbPass, _ := resolver.Resolve(ctx, "memory://database/password")
fmt.Println("API Key:", apiKey)
fmt.Println("DB Password:", dbPass)
}package main
import (
"context"
"fmt"
"github.com/agentplexus/omnivault"
aws "github.com/agentplexus/omnivault-aws"
"github.com/agentplexus/omnivault-keyring"
)
func main() {
ctx := context.Background()
// Create providers
awsProvider, _ := aws.NewSecretsManager(aws.Config{Region: "us-east-1"})
keyringProvider := keyring.New(keyring.Config{ServiceName: "myapp"})
// Multi-provider resolver
resolver := omnivault.NewResolver()
resolver.Register("aws-sm", awsProvider)
resolver.Register("keyring", keyringProvider)
// Resolve from AWS Secrets Manager
dbCreds, _ := resolver.Resolve(ctx, "aws-sm://prod/database")
// Resolve from OS keyring
localToken, _ := resolver.Resolve(ctx, "keyring://dev/api-token")
fmt.Println("DB Credentials:", dbCreds)
fmt.Println("Local Token:", localToken)
}| Provider | Scheme | Description |
|---|---|---|
| Environment Variables | env:// |
Read from os.Getenv() |
| File | file:// |
File-based storage |
| Memory | memory:// |
In-memory storage (for testing) |
First-party provider modules maintained alongside OmniVault:
| Module | Providers | Schemes |
|---|---|---|
| omnivault-aws | AWS Secrets Manager, AWS Parameter Store | aws-sm://, aws-ssm:// |
| omnivault-keyring | macOS Keychain, Windows Credential Manager, Linux Secret Service | keyring:// |
# Install official provider modules
go get github.com/agentplexus/omnivault-aws
go get github.com/agentplexus/omnivault-keyringExternal providers can be developed as separate Go modules and injected via Config.CustomVault:
| Category | Providers |
|---|---|
| Password Managers | 1Password, Bitwarden, LastPass, KeePass, pass/gopass |
| Cloud Secret Managers | GCP Secret Manager, Azure Key Vault |
| Enterprise Vaults | HashiCorp Vault, CyberArk Conjur, Akeyless, Doppler |
Custom providers can be developed as separate Go modules. Import only the vault package to avoid pulling in unnecessary dependencies:
package myprovider
import (
"context"
"github.com/agentplexus/omnivault/vault"
)
type Provider struct {
// Your provider fields
}
// New creates a new provider instance.
func New(apiKey string) vault.Vault {
return &Provider{/* ... */}
}
// Implement vault.Vault interface
func (p *Provider) Get(ctx context.Context, path string) (*vault.Secret, error) {
// Your implementation
}
func (p *Provider) Set(ctx context.Context, path string, secret *vault.Secret) error {
// Your implementation
}
func (p *Provider) Delete(ctx context.Context, path string) error {
// Your implementation
}
func (p *Provider) Exists(ctx context.Context, path string) (bool, error) {
// Your implementation
}
func (p *Provider) List(ctx context.Context, prefix string) ([]string, error) {
// Your implementation
}
func (p *Provider) Name() string {
return "myprovider"
}
func (p *Provider) Capabilities() vault.Capabilities {
return vault.Capabilities{
Read: true,
Write: true,
// ...
}
}
func (p *Provider) Close() error {
return nil
}Then use it with OmniVault:
import (
"github.com/agentplexus/omnivault"
"github.com/yourorg/omnivault-myprovider"
)
client, _ := omnivault.NewClient(omnivault.Config{
CustomVault: myprovider.New("api-key"),
})scheme://path[#field]
# Examples:
env://API_KEY # Environment variable
file:///path/to/secret # File
memory://database/password # In-memory
# External providers (when installed):
op://vault/item/field # 1Password
keychain://service/account # macOS Keychain
aws-sm://secret-name#key # AWS Secrets Manager
gcp-sm://project/secret # GCP Secret Manager
azure-kv://vault/secret # Azure Key Vault
vault://secret/path#field # HashiCorp Vault
// Create a new client
client, err := omnivault.NewClient(omnivault.Config{
Provider: omnivault.ProviderEnv, // Built-in provider
CustomVault: myVault, // OR custom provider
})
// Basic operations
secret, err := client.Get(ctx, "path")
err := client.Set(ctx, "path", &omnivault.Secret{Value: "secret"})
err := client.Delete(ctx, "path")
exists, err := client.Exists(ctx, "path")
paths, err := client.List(ctx, "prefix")
// Convenience methods
value, err := client.GetValue(ctx, "path") // Returns just the value
value, err := client.GetField(ctx, "path", "field") // Returns a specific field
err := client.SetValue(ctx, "path", "value") // Set a simple string value
// Must variants (panic on error)
secret := client.MustGet(ctx, "path")
value := client.MustGetValue(ctx, "path")// Create resolver
resolver := omnivault.NewResolver()
// Register providers
resolver.Register("env", envVault)
resolver.Register("op", onePasswordVault)
// Resolve secrets
value, err := resolver.Resolve(ctx, "env://API_KEY")
secret, err := resolver.ResolveSecret(ctx, "op://vault/item")
// Resolve if it's a secret reference, otherwise return as-is
value, err := resolver.ResolveString(ctx, maybeSecretRef)
// Resolve all values in a map
resolved, err := resolver.ResolveMap(ctx, configMap)// Create secrets
secret := &omnivault.Secret{
Value: "my-secret-value",
Fields: map[string]string{
"username": "admin",
"password": "secret",
},
Metadata: omnivault.Metadata{
Tags: map[string]string{"env": "prod"},
},
}
// Access values
value := secret.String() // Primary value
value := secret.GetField("username") // Specific field
bytes := secret.Bytes() // As bytesomnivault/
├── vault/ # Core interface (import this for custom providers)
│ ├── interface.go # Vault interface definition
│ ├── types.go # Secret, Metadata, SecretRef types
│ └── errors.go # Standard errors
├── providers/ # Built-in providers
│ ├── env/ # Environment variables
│ ├── file/ # File-based storage
│ └── memory/ # In-memory storage
├── client.go # Main client
├── resolver.go # URI-based resolution
├── providers.go # Provider factory
├── constants.go # Provider names
├── errors.go # Client errors
└── types.go # Type aliases
To create an external provider module:
- Create a new Go module (e.g.,
github.com/yourorg/omnivault-onepassword) - Import only
github.com/agentplexus/omnivault/vault - Implement the
vault.Vaultinterface - Export a constructor function returning
vault.Vault
This architecture ensures:
- External providers don't bloat the core library with dependencies
- Providers can be versioned independently
- Users only install the providers they need
The omnivault CLI provides secure local secret management with a daemon architecture.
# Start the daemon
omnivault daemon start
# Initialize a new vault with a master password
omnivault init
# Store a secret
omnivault set database/password
# Retrieve a secret
omnivault get database/password
# List all secrets
omnivault list
# Lock the vault
omnivault lock
# Unlock the vault
omnivault unlock
# Check status
omnivault status| Command | Description |
|---|---|
omnivault init |
Initialize a new vault with a master password |
omnivault unlock |
Unlock the vault with master password |
omnivault lock |
Lock the vault |
omnivault status |
Show vault and daemon status |
| Command | Description |
|---|---|
omnivault get <path> |
Get a secret value |
omnivault set <path> [value] |
Set a secret (prompts for value if not provided) |
omnivault list [prefix] |
List secrets, optionally filtered by prefix |
omnivault delete <path> |
Delete a secret (with confirmation) |
| Command | Description |
|---|---|
omnivault daemon start |
Start the daemon in background |
omnivault daemon stop |
Stop the daemon |
omnivault daemon status |
Show daemon status |
omnivault daemon run |
Run daemon in foreground (for debugging) |
The CLI uses a daemon (background service) architecture for secure secret access:
- Cross-Platform IPC: Unix socket on macOS/Linux, TCP localhost on Windows
- Session-Based Unlock: Vault stays unlocked until locked or timeout
- Auto-Lock: Configurable inactivity timeout (default: 15 minutes)
- Graceful Shutdown: Vault is locked on daemon shutdown
| Platform | IPC Method | Socket/Address |
|---|---|---|
| macOS | Unix Socket | ~/.omnivault/omnivaultd.sock |
| Linux | Unix Socket | ~/.omnivault/omnivaultd.sock |
| Windows | TCP | 127.0.0.1:19839 |
- Algorithm: AES-256-GCM (authenticated encryption)
- Key Derivation: Argon2id (memory-hard, resistant to GPU attacks)
- 3 iterations
- 64 MB memory
- 4 parallel threads
- Salt: Random 32 bytes per vault
- Nonce: Random 12 bytes per secret
macOS / Linux:
~/.omnivault/
├── vault.enc # Encrypted secrets (AES-256-GCM)
├── vault.meta # Unencrypted metadata (salt, Argon2 params)
├── omnivaultd.sock # Unix socket (runtime)
└── omnivaultd.pid # Daemon PID file (runtime)
Windows:
%LOCALAPPDATA%\OmniVault\
├── vault.enc # Encrypted secrets (AES-256-GCM)
├── vault.meta # Unencrypted metadata (salt, Argon2 params)
└── omnivaultd.pid # Daemon PID file (runtime)
- Never stored on disk
- Used only to derive the encryption key
- Minimum 8 characters enforced
- Session-based unlock with configurable timeout
Contributions are welcome! Please submit pull requests or create issues for bugs and feature requests.
MIT License - see LICENSE for details.