Concepts

The three types you interact with most: the ref parsed from a tag, the Value a provider returns, and the secret wrappers that keep sensitive data out of your logs.

Refs and the tag grammar

A ref is parsed from the source tag. The grammar is:

<scheme>://<path>[#<key>][?<opt>=<v>&...]

Opaque schemes such as env: and exec: take everything after the colon as the path (no //):

type Config struct {
	// whole secret string
	APIKey     secret.String `source:"aws-sm://prod/api-key"`
	// one key of a JSON secret
	DBPassword secret.String `source:"aws-sm://prod/db#password"`
	// provider option
	Leased     secret.String `source:"vault://kv/data/api#key?renew=true"`
	// opaque scheme
	LogLevel   string        `source:"env:LOG_LEVEL"`
	// absolute file path
	Cert       []byte        `source:"file:///etc/tls/tls.crt"`
}

ParseRef produces a Ref{Scheme, Path, Key, Opts, Raw}. #key selects one field from a structured (JSON) payload; ?opts are provider-specific plus a few core-recognized options.

Supplementary tags

Tag Meaning
default:"..." Value used when the ref resolves to not-found (not on error).
validate:"..." Field validation (go-playground/validator syntax), evaluated on every update. See the Validation page for the available rules.
flatten:"json|yaml|env" Decode a single provider payload into a nested struct.
optional:"true" Not-found is tolerated with no default (field keeps its zero value).
?debounce=<dur> Per-field coalescing window override, e.g. ?debounce=0 for certs.

Nested structs compose. A struct field with a source and flatten decodes one payload into the sub-struct; a struct field with no source is a container mamori recurses into:

type Config struct {
	Redis RedisConfig `source:"aws-sm://prod/redis" flatten:"json"`
}

type RedisConfig struct {
	Addr     string        `mapstructure:"addr"`
	Password secret.String `mapstructure:"password"`
	DB       int           `mapstructure:"db"`
}

The Value type

Providers return a Value, not raw bytes. This is the keystone for rotation and hygiene:

type Value struct {
	Bytes     []byte
	Version   string            // provider revision: SM VersionId, Vault version, file mtime hash
	Sensitive bool              // drives redaction downstream
	NotAfter  time.Time         // zero if unknown; e.g. a Vault lease expiry schedules a refresh
	Metadata  map[string]string
}

Version gives cheap change detection (no byte comparison when the provider supplies a revision). NotAfter lets lease-based providers request a refresh before expiry rather than waiting for the next poll tick.

Secret types

Import github.com/xavidop/mamori/secret. secret.String and secret.Bytes redact themselves everywhere a value is normally rendered:

s := secret.NewString("hunter2")

fmt.Println(s)              // [REDACTED]
fmt.Sprintf("%v", s)        // [REDACTED]
json.Marshal(s)            // "[REDACTED]"
slog.Info("login", "pw", s) // pw=[REDACTED]

s.Reveal()                  // "hunter2"  <- the only way to read it
s.Zero()                    // best-effort wipe of the backing bytes

Reveal() is deliberately the single, greppable access point, so secret reads are easy to audit. Zero() is best-effort: Go’s GC may already have copied the value, and we document that honestly rather than promise memory safety we cannot deliver. The reconcilevet analyzer flags a secret-bearing ref stored in a plain string.