Mamori
Typed, watchable config and secrets for Go: load from 30+ providers into validated structs, reconciled at runtime without a restart.
Install / Use
npx skills add xavidop/mamoriInstalls into whichever agent you are using.
README
守り mamori
Typed, watchable config & secrets for Go
Load configuration and secrets from anywhere into validated Go structs - and keep them reconciled at runtime, without a restart.
</div>mamori (守り - Japanese for protection / safeguard) is an embedded Go library that loads application configuration and secrets from heterogeneous sources - environment, files, AWS Secrets Manager, Vault, GCP, Azure, Kubernetes, Consul, and more - into typed, validated structs, and keeps them reconciled at runtime. When a source value changes, mamori detects it, re-validates the whole configuration, and - only if the new snapshot is valid - atomically swaps it in and hands your application a diff-aware callback so it can react (rotate a DB pool, rebuild a client) without restarting.
Think: External Secrets Operator's provider model, one layer down - as a library inside your process instead of an operator inside your cluster.
Why
The primitives exist, but nobody composed them. runtimevar watches one variable but has no struct composition or validation. Viper/koanf do multi-source config but treat secrets and rotation as afterthoughts. The AWS caching client and Vault's LifetimeWatcher refresh one provider each, in silos. So every production Go service hand-rolls a ConfigManager with a ticker, a mutex, and a prayer. mamori is that glue, done once, with a provider ecosystem and a conformance kit.
Install
go get github.com/xavidop/mamori
env: and file:// work out of the box. Cloud providers are separate modules so the core has zero cloud-SDK dependencies:
go get github.com/xavidop/mamori/providers/aws # aws-sm:// aws-ps:// aws-appconfig://
go get github.com/xavidop/mamori/providers/vault # vault://
go get github.com/xavidop/mamori/providers/k8s # k8s-secret:// k8s-cm://
go get github.com/xavidop/mamori/providers/vercel-gc # vercel-gc://
go get github.com/xavidop/mamori/providers/cloudflare-kv # cloudflare-kv://
go get github.com/xavidop/mamori/providers/heroku # heroku://
go get github.com/xavidop/mamori/providers/https # https://
go get github.com/xavidop/mamori/providers/hcp-vault-secrets # hcp-vs://
go get github.com/xavidop/mamori/providers/infisical # infisical://
go get github.com/xavidop/mamori/providers/scaleway-sm # scaleway-sm://
go get github.com/xavidop/mamori/providers/supabase # supabase://
go get github.com/xavidop/mamori/providers/bitwarden # bitwarden-sm://
# ... gcp, azure, consul, doppler, nacos, onepassword, sops
Quick start
type Config struct {
// A secret from AWS Secrets Manager, redacted in logs by default.
// ${ENV} expands from WithRefVars below, never from the ambient environment.
DBPassword secret.String `source:"aws-sm://${ENV}/db#password"`
// Plain config, with a default and validation
LogLevel string `source:"env:LOG_LEVEL" default:"info" validate:"oneof=debug info warn error"`
Workers int `source:"env:WORKERS" default:"4" validate:"gte=1,lte=256"`
// A precedence chain: env wins if set, else Parameter Store, else the default
Port string `source:"env:PORT,aws-ps://svc/port" default:"8080"`
// A nested value, selected with an RFC 6901 JSON Pointer fragment
DBUser string `source:"aws-sm://prod/db#/credentials/user"`
// A file, hot-reloaded via fsnotify
TLSCert []byte `source:"file:///etc/tls/tls.crt"`
// ?decode= runs a stdlib decode pipeline before the field is populated
TLSKey []byte `source:"aws-sm://prod/tls#key?decode=base64"`
// A nested struct decoded from one JSON secret
Redis RedisConfig `source:"aws-sm://prod/redis" flatten:"json"`
}
// One-shot load
cfg, err := mamori.Load[Config](ctx)
// Or: watch and reconcile at runtime
w, err := mamori.Watch[Config](ctx,
mamori.WithRefVars(map[string]string{"ENV": "prod"}),
// Prove a rotated password actually works before it becomes what Get() serves
mamori.PreApply(func(ctx context.Context, ev mamori.Change[Config]) error {
if !ev.Changed("DBPassword") {
return nil
}
return pool.Ping(ctx, ev.New.DBPassword.Reveal())
}),
mamori.OnChange(func(ev mamori.Change[Config]) {
if ev.Changed("DBPassword") {
pool.Rotate(ev.New.DBPassword.Reveal())
}
}),
mamori.OnError(func(err error) { metrics.Inc("config_error") }),
)
defer w.Close()
cfg := w.Get() // lock-free snapshot; always the last *valid* config
What makes it different
- Typed & tag-driven - one struct, many sources, generics API (
Load[T]/Watch[T]). - Atomic & validated - an update that fails validation is rejected;
Get()keeps serving the last good config. - Rotation-safe -
PreApplyproves a rotated credential actually works before it goes live, at startup and on every rotation. - Derived fields -
WithDeriverebuilds a value assembled from several fields, like a DSN from a host, a user, and a password, on every applied update, so it never goes stale after just one input rotates. - Precedence chains -
source:"env:PORT,aws-ps://svc/port"tries sources in order, and every position stays watched. - Rich ref grammar - RFC 6901 JSON Pointer selection,
?decode=pipelines, and${VAR}interpolation from an explicit, non-ambient source. - Reconciled at runtime - native watch where the backend supports it, polling with jitter everywhere else, lease-aware refresh for Vault.
- Boots through an outage -
WithBootstrapCachekeeps an encrypted, on-disk snapshot of the last known-good values and starts from it when a restart cannot reach the backend, instead of failing to start. - Secret hygiene by default -
secret.String/secret.Bytesredact infmt, JSON, andslog; only the greppableReveal()exposes a value, andmamori vetflags the ones you missed. - Observable - live per-field health, a readiness probe, a pre-deploy
Doctorcheck, structured logs, and metrics. - Pluggable - providers register with the
database/sqlpattern, and aprovidertestconformance kit keeps them behaving identically. - Testable - a scriptable in-memory provider plus deterministic wait helpers, so
OnChangeand error paths are testable without a real backend.
Providers
| Module | Schemes | Watch | Errors classified beyond not-found |
|---|---|---|---|
| core (built-in) | env: · dotenv:// · file:// · exec: (opt-in) | fsnotify (file/dotenv) · poll (env/exec) | ✅ |
| providers/aws | aws-sm:// · aws-ps:// · aws-appconfig:// | poll | ✅ |
| providers/gcp | gcp-sm:// | poll | ✅ |
| providers/azure | azure-kv:// · azure-appconfig:// | poll | ✅ |
| providers/vault | vault:// | lease-aware poll (NotAfter) | ✅ |
| providers/k8s | k8s-secret:// · k8s-cm:// | native (watch API) | ✅ |
| providers/consul | consul:// | native (blocking queries) | ✅ |
| providers/doppler | doppler:// | poll | ✅ |
| providers/infisical | infisical:// | poll | ✅ |
| providers/hcp-vault-secrets | hcp-vs:// | poll | ✅ |
| providers/scaleway-sm | scaleway-sm:// | poll | ✅ |
| providers/bitwarden | bitwarden-sm:// | poll | ✅ |
| providers/onepassword | op:// | poll | ✅ |
| providers/sops | sops:// | fsnotify | ✅ |
| providers/supabase | supabase:// | poll | ✅ |
| providers/postgres | postgres:// | native (LISTEN/NOTIFY) | ✅ |
| providers/mysql | mysql:// | poll | ✅ |
| providers/sqlite | sqlite:// | fsnotify | ✅ |
| providers/mongodb | mongodb:// | native (change streams) | ✅ |
| providers/dynamodb | dynamodb:// | poll | ✅ |
| providers/redis | redis:// | native (keyspace notifications) | ✅ |
| providers/etcd | etcd:// | native (watch API) | ✅ |
| providers/nacos | nacos:// | native (long-poll listener) | ✅ |
| providers/vercel-gc | vercel-gc:// | poll (digest) | ✅ |
| providers/cloudflare-kv | cloudflare-kv:// | poll | ✅ |
| providers/heroku | heroku:// | poll | ✅ |
| providers/https | https:// | poll | ✅ |
| providers/firestore | firestore:// | native (snapshot listeners) | ✅ |
| providers/firebase-rc | firebase-rc:// | poll | ✅ |
| providers/firebase-rtdb | firebase-rtdb:// | native (streaming) | no (chain preserved) |
| providers/s3 | s3:// | poll (ETag) | ✅ |
| providers/gcs | gcs:// | poll (generation) | ✅ |
| providers/azblob | azblob:// | poll (ETag) | ✅ |
| providers/cosmos | cosmos:// | poll (ETag) | ✅ |
| providers/launchdarkly | launchdarkly:// | native (streaming) | ✅ |
| providers/unleash | unleash:// | poll | n/a (no error surface) |
| providers/flagsmith | flagsmith:// | poll | no (chain preserved) |
| providers/configcat | configcat:// | poll | n/a (no error surface) |
| providers/split | split:// | poll | n/a (no error surface) |
| providers/growthbook | growthbook:// | poll | no (chain
Related Skills
node-connect
385.5kDiagnose OpenClaw Android, iOS, or macOS node pairing, QR/setup code, route, auth, and connection failures.
ankra-cli
40.5kAnkra CLI rules and best practices for managing Kubernetes clusters via the Ankra platform
blender-python-addon
40.5kBlender Python add-on rules for operators, panels, properties, registration, testing, and API-safe scripting
flutter-development-guidelines-cursorrules-prompt-file
40.5kCursor rules for Flutter development with MVVM architecture, Riverpod state management, Material widgets, and Dart style guidelines.
