Introduction
mamori (守り, “protection”) loads typed, validated configuration and secrets into a Go struct from a broad provider ecosystem (environment, files, AWS, Vault, GCP, Azure, Kubernetes, Consul, and more), then keeps that struct reconciled while your program runs. Reach for it when you want one struct to describe every value your service needs, revalidated and hot-swapped whenever a source changes, without a restart.
Install
The core module ships the env: and file:// providers and has zero cloud-SDK dependencies:
go get github.com/xavidop/mamori
Requires Go 1.26 or newer. Each cloud or backend provider is a separate module, so its SDK only enters your build if you actually use it:
go get github.com/xavidop/mamori/providers/aws # aws-sm:// aws-ps://
go get github.com/xavidop/mamori/providers/vault # vault://
go get github.com/xavidop/mamori/providers/k8s # k8s-secret:// k8s-cm://
See the Providers overview for the full list of schemes.
Quick start
Tag a struct with source: refs, then call Load. A blank import registers a provider (the database/sql pattern):
package main
import (
"context"
"log"
"github.com/xavidop/mamori"
"github.com/xavidop/mamori/secret"
_ "github.com/xavidop/mamori/providers/aws" // registers aws-sm:// and aws-ps://
)
type Config struct {
DBPassword secret.String `source:"aws-sm://prod/db#password"`
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"`
TLSCert []byte `source:"file:///etc/tls/tls.crt"`
}
func main() {
cfg, err := mamori.Load[Config](context.Background())
if err != nil {
log.Fatal(err)
}
log.Printf("workers=%d level=%s password=%s", cfg.Workers, cfg.LogLevel, cfg.DBPassword)
// password prints as [REDACTED]; cfg.DBPassword.Reveal() returns the value.
}
To react to changes at runtime instead of loading once, see Loading & watching.
How it works
The model has three moving parts:
- Refs. Each struct field carries a
sourcetag: a small URL-ish reference to a value in some provider (aws-sm://prod/db#password,env:LOG_LEVEL,file:///etc/tls/tls.crt). - Providers. A provider resolves a scheme (
aws-sm,vault,env, …) into aValue. Providers register with thedatabase/sqlpattern, so the core module keeps zero cloud-SDK dependencies. - The reconciler.
Watchresolves everything once (fail-fast), then watches each source: natively where the backend can push, by polling with jitter otherwise. On a change it re-validates the whole struct and, only if the result is valid, atomically swaps it in and fires your callback.
flowchart LR Struct["Config struct with source: tags"] --> R["Reconciler (Load / Watch)"] R --> P["Providers, one per scheme"] P --> B["env, files, AWS, Vault, GCP, ..."] B -->|value or change| R R -->|"validate, then atomic swap"| Struct R -.->|on change| CB["your OnChange callback"]
The payoff: rotate a database password in Secrets Manager and your connection pool rotates with it, without a restart and without a half-applied config ever being observed.
Where to go next
- Concepts: refs, providers, the reconciler, and the full
source/default/validatetag grammar. - Loading & watching: one-shot
Loadversus a liveWatch, change events, source chains, and snapshot pinning. - Validation: the
validate:rules applied on load and on every update. - Providers overview: every scheme, its watch strategy, and how to authenticate it.
- Config server: the opt-in module that serves resolved values to other callers behind mandatory auth.
- CLI:
explain,schema, andpolicyread your source statically;doctorandstatusprobe a running process. - Observability:
Status,Health, the pre-deployDoctorcheck, and the admin HTTP endpoint. - Security: secret hygiene, the two HTTP surfaces, and what
mamoriis deliberately not (it is not a secrets store).