Composable secrets, feature flags, and runtime config for Go.
Vault is a Go library that unifies three capabilities behind a single API surface: encrypted secrets, rule-based feature flags, and hot-reloadable runtime configuration. It is a library, not a service: you import it, bring your own database and encryption key, and control the process lifecycle. Vault provides the plumbing: encryption, versioning, tenant-scoped evaluation, rotation, audit, and a plugin system for extending every subsystem.
Vault is part of the Forge ecosystem and integrates cleanly with Forge extensions, Grove ORM stores, and Confy config sources, but none of them are required to use it standalone.
Secrets
- AES-256-GCM encryption at rest, transparent decryption on read
- Auto-versioning: every
Setarchives the previous value; fetch any historical version by number - Environment-variable key provider with hex/base64 auto-detection
- Optional metadata (expiration, custom fields)
Feature flags
- Five value types:
bool,string,int,float,json - Six targeting rule types:
WhenTenant,WhenTenantTag,WhenUser,Rollout(deterministic %),Schedule(time window),Custom(plugin-evaluated) - Priority-ordered evaluation with per-tenant overrides
- Type-safe accessors (
Bool,String,Int,Float,JSON) that return a default on error - LRU evaluation cache with configurable TTL (default 30s)
Runtime configuration
- Typed entries (
string,bool,int,float,json) withDurationparsing - Per-tenant overrides resolved ahead of the app-level value
- Version tracking and
Watchcallbacks fired on mutation - Composable sources: memory, env, database, priority chain
Cross-cutting
- Tenant isolation by design:
scope.WithTenantID(ctx, ...)propagates to every service viacontext.Context; cross-tenant reads are structurally impossible - Secret rotation: background manager with per-key
Rotatorcallbacks, automatic versioning, and record-keeping - Audit logging: append-only trail of action/resource/outcome with full scope context; optional hook for forwarding to external systems
- Plugin system: register plugins that implement any subset of eight capability interfaces (
OnInit,OnShutdown,SourceProvider,EncryptionProvider,FlagEvaluator,OnSecretAccess,OnConfigChange,RotationStrategy) - TypeID identifiers: every entity uses type-prefixed, K-sortable UUIDv7 IDs (
sec_,flag_,cfg_, …); passing the wrong prefix fails at parse time
go get github.com/xraph/vaultpackage main
import (
"context"
"fmt"
"log"
"github.com/xraph/vault/config"
"github.com/xraph/vault/crypto"
"github.com/xraph/vault/flag"
"github.com/xraph/vault/scope"
"github.com/xraph/vault/secret"
"github.com/xraph/vault/store/memory"
)
func main() {
ctx := scope.WithScope(context.Background(), "myapp", "tenant-1", "user-42", "10.0.0.1")
store := memory.New()
enc, err := crypto.NewEncryptor([]byte("my-32-byte-secret-key-for-vault!"))
if err != nil {
log.Fatal(err)
}
// Secrets: encrypted, auto-versioned
secrets := secret.NewService(store, enc, secret.WithAppID("myapp"))
meta, _ := secrets.Set(ctx, "openai-api-key", []byte("sk-abc123"), "myapp")
sec, _ := secrets.Get(ctx, "openai-api-key", "myapp")
fmt.Printf("secret v%d = %s\n", meta.Version, sec.Value)
// Feature flags: evaluated against context scope
flags := flag.NewService(flag.NewEngine(store), flag.WithAppID("myapp"))
if flags.Bool(ctx, "new-dashboard", false) {
fmt.Println("new dashboard enabled")
}
// Runtime config: typed accessors with defaults
cfg := config.NewService(store, config.WithAppID("myapp"))
_ = cfg.Set(ctx, "rate-limit", 100, "myapp")
fmt.Printf("rate limit = %d\n", cfg.Int(ctx, "rate-limit", 50))
}See docs/content/docs/getting-started.mdx for a step-by-step walkthrough covering rule-based flags, tenant overrides, rotation, and swapping in a production store.
Every backend satisfies the same composite store.Store interface (secrets, flags, config, overrides, rotation and audit), so switching stores is a single type change. All expose Migrate, Ping, and Close.
| Backend | Import | Use case |
|---|---|---|
| Memory | github.com/xraph/vault/store/memory |
Tests, development, single-process demos |
| PostgreSQL | github.com/xraph/vault/store/postgres |
Production, direct pgx connection pool |
| SQLite | github.com/xraph/vault/store/sqlite |
Embedded deployments, local-first apps (via Grove) |
| MongoDB | github.com/xraph/vault/store/mongo |
Document-oriented workloads (mongo-driver/v2) |
| Grove | github.com/xraph/vault/store/grovestore |
Apps already using the Grove ORM |
Vault is organised as a root package with shared types, six service packages implementing domain logic, supporting packages for encryption/scoping/sources, and a set of store backends implementing one composite interface.
- Services: secret/, flag/, config/, override/, rotation/, audit/
- Supporting: crypto/ (AES-256-GCM), id/ (TypeID), scope/ (context keys), source/ (memory/env/database/chain), plugin/, audit_hook/, metrics/
- Stores: store/memory/, store/postgres/, store/sqlite/, store/mongo/, store/grovestore/
- Integrations: extension/ (Forge), confy/ (Confy), extension/contract/ (dashboard contract)
Every operation follows the same three-phase flow: scope the context → resolve the value (tenant override → rule match → default) → respond with a coerced type or the caller's default on error. See docs/content/docs/architecture.mdx for the full package diagram and request-flow breakdown.
Forge extension. Mount Vault as a Forge extension to auto-wire the store, encryption and the dashboard contract into your Forge app (audit is always on). The React dashboard plugin, @forge-go/dashboard-plugin-vault, reads that contract (see MIGRATION.md for what moved from the old templ pages). See extension/ and docs/content/docs/guides/forge-extension.mdx.
Confy source. Adapt Vault as a Confy ConfigSource and SecretProvider so a single confy.Load call pulls typed config and encrypted secrets through Vault alongside your other sources. See confy/ and docs/content/docs/guides/confy-integration.mdx.
The root Config struct controls instance-wide behaviour. All fields have sensible defaults via DefaultConfig().
| Field | Purpose | Default |
|---|---|---|
AppID |
Application identifier used to scope secrets, flags, and config | none |
EncryptionKey |
32-byte AES-256-GCM master key | none |
EncryptionKeyEnv |
Fallback environment variable holding the key (hex or base64) | none |
FlagCacheTTL |
TTL for the flag evaluation cache | 30s |
SourcePollInterval |
Interval for polling database-backed config sources | 30s |
Matching functional options live in options.go: WithAppID, WithEncryptionKey, WithEncryptionKeyEnv, WithLogger, WithConfig.
The Makefile exposes short aliases for the common workflows:
make test # go test ./...
make test-race # go test -race ./...
make coverage # coverage.out + function-level summary
make coverage-html
make lint # golangci-lint run ./...
make fmt # gofmt + goimports
make check # fmt + vet + lint
make docs # serve the Next.js docs site at http://localhost:3000
make deps # install goimports, air, golangci-lintRequires Go 1.25+. See go.mod for the full dependency set.
A full documentation site lives under docs/ and can be served locally with make docs. Key pages:
- Getting started: eight-step walkthrough from install to Postgres
- Architecture: package diagram, request flow, plugin system, package index
- Concepts: entities, multi-tenancy, identity, errors
- Subsystems: secrets, feature flags, runtime config, overrides, rotation, audit, encryption, sources, plugins, observability
- Stores: memory, postgres, sqlite, mongo, grove
- Guides: full example, custom store, Forge extension, Confy integration, multi-tenant patterns
Actively developed. Core service APIs (secret, flag, config) are stable; storage backends and the Forge/Confy integrations continue to evolve. See recent commits on main.
See the repository root for licensing.