Typed loading
One struct, tag-driven, many sources. A generics API - Load[T] and Watch[T] - decodes and validates into your own types. No stringly-typed lookups.
mamori loads configuration and secrets from anywhere into typed, validated Go structs - then watches every source and reconciles changes at runtime. Rotate a database password upstream; your pool rotates too. No restart.
go get github.com/xavidop/mamoriPoint your coding agent at mamori - install the skill, or hand it the whole documentation as one file. Works with Claude Code, Cursor, Copilot, and more.
Add the agent skill
npx skills add xavidop/mamoriOr paste this prompt
Add mamori to my Go project. Docs: https://mamorigo.dev/llms.txtaws-sm://prod/db#passwordv1v2type Config {
DBPassword secret.String✓
}pool.Rotate(cfg.DBPassword.Reveal())Each does one thing, through a well-defined interface, testable on its own.
One struct, tag-driven, many sources. A generics API - Load[T] and Watch[T] - decodes and validates into your own types. No stringly-typed lookups.
Native watch where the backend supports it - Kubernetes informers, Consul blocking queries, fsnotify - polling with jitter everywhere else, and lease-aware refresh for Vault. Values you assemble yourself keep up too: WithDerive rebuilds a DSN from its parts on every update, so a rotated password never leaves a stale connection string behind.
A bad update never goes live. Validation rejects it, and you can add your own check on top: try the new credential, and say no if it fails. Until then your app keeps serving the last good config.
secret.String redacts itself in logs, fmt, and JSON. Only Reveal() exposes the value - and a shipped go vet analyzer flags sensitive refs stored in plain strings.
A restart while the backend is down still starts. WithBootstrapCache keeps an encrypted snapshot of the last known-good values on disk and boots from it when a cold start cannot reach the backend, then returns to live values as soon as it can. Opt-in, bounded by a max age you set, and reported in Status so a stale start is never silent.
Tag a struct with where each value lives. mamori resolves, validates, and - withWatch - keeps it reconciled, handing you a diff-aware callback.
// One struct, many sources.
type Config struct {
DBPassword secret.String `source:"aws-sm://prod/db#password"`
LogLevel string `source:"env:LOG_LEVEL" default:"info"`
Workers int `source:"env:WORKERS" validate:"gte=1,lte=256"`
TLSCert []byte `source:"file:///etc/tls/tls.crt"`
}// Watch: reconcile at runtime, react without restarting.
w, _ := mamori.Watch[Config](ctx,
mamori.OnChange(func(ev mamori.Change[Config]) {
if ev.Changed("DBPassword") {
pool.Rotate(ev.New.DBPassword.Reveal())
}
}),
)
defer w.Close()
cfg := w.Get() // lock-free; always the last valid config51 sources across 6 categories, all behind one interface. Providers register with the database/sql pattern, the core has zero cloud-SDK dependencies, and every provider passes the providertest conformance kit.
env · dotenv · file · exec · Viper
AWS · Vault · HCP Vault Secrets · GCP · Azure · Scaleway · Bitwarden · Doppler · Infisical · 1Password · SOPS · Supabase
LaunchDarkly · Unleash · Flagsmith · ConfigCat · Split · GrowthBook · Flipt · GO Feature Flag · OpenFeature · PostHog
Postgres · MySQL · SQLite · MongoDB · Redis · DynamoDB · Cosmos
Consul · etcd · Nacos · AWS AppConfig · Azure AppConfig · Vercel Global Config · Cloudflare Workers KV · Heroku Config Vars · Generic HTTPS · Kubernetes Secret · ConfigMap
S3 · GCS · Azure Blob · Firestore · Firebase RTDB · Remote Config
Because every provider is a Provider, decorators nest freely. Cache to cut API cost, fail over to a replica, rate-limit a twitchy backend, audit every resolve, or rewrite refs per tenant.
mamori.WithProvider(
middleware.Cache(5*time.Minute,
middleware.Audit(logger,
middleware.Failover(
primarySM,
replicaSM,
),
),
),
)Serve config to other services, inspect it from the CLI, probe its health in CI and at runtime, and hand it to your coding agent.
A read-through fan-out: one process holds the credentials and watches each upstream once, while every other service reads values by name over a Unix socket or TLS, behind mandatory policy and audit.
Config server →explain, schema, and policy read your source statically; doctor and status probe a running process with distinct exit codes, so a script can tell a broken config from one it cannot reach.
CLI →See every field's health right now, or run the same check in CI before you deploy. Logs, metrics and traces cover what happened over time. The OpenTelemetry and Prometheus bridges are separate modules, so neither lands in your build unless you ask.
Health & telemetry →An installable agent skill and an llms.txt, so your coding agent can wire mamori and load the whole documentation from a single URL.
Agent skill →Every secret passes through your config code. That code also ends up in logs, errors, and bug reports. So these are on by default.
Secrets have their own type. Print one and you get [REDACTED]. That holds in logs, in JSON, in errors, in a panic. Getting the real value takes an explicit call, so it is one word to grep for.
mamori ships a go vet analyzer. Put a secret in a plain string and CI fails. Nobody has to catch it in review.
Running commands is off by default. A secret mamori resolved can never build the next lookup. Neither can your environment. So a leaked secret cannot point mamori somewhere new.
Wiping a secret from memory is best effort. Go's runtime cannot promise more, and we do not pretend otherwise. What we do promise is tested: no secret value ever reaches a log.
Typed. Watched. Reconciled. Runs in a Lambda, a systemd unit, or a Pod.
mamori uses cookies only for anonymous analytics, to see which docs are useful. No ads, no cross-site tracking. You can change this any time from the footer.