# Secrets

Read named credentials from the environment or Vault, separate from layered config.

`secrets` returns one named credential on demand, without it sitting in a YAML file or a log line. Use it for API keys, webhook signing secrets, and provider tokens, separate from layered `config`.

## Quick start

```sh
SECRETS_ADAPTER=env
SECRETS_PREFIX=ZEVER
```

```sh
SECRETS_PREFIX=ZEVER
ZEVER_STRIPE_KEY=sk_live_...
ZEVER_WEBHOOK_SECRET=whsec_...
```

`prefix` is required (default `ZEVER`) and must have no `/`, `..`, or spaces; `DEMO_KEY` then reads `ZEVER_DEMO_KEY` exactly ([reference](https://zenta-dev.github.io/zever/reference/config/#secrets)).

## Read a secret

```go title="main.go" check
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/zenta-dev/zever/config"
	"github.com/zenta-dev/zever/container"
)

func main() {
	os.Setenv("ZEVER_DEMO_KEY", "s3cr3t")
	c := container.New(config.Default())
	ctx := context.Background()
	defer c.Close(ctx)

	s, err := c.Secrets()
	if err != nil {
		fmt.Println("resolve error:", err)
		return
	}
	val, _ := s.Get(ctx, "DEMO_KEY")
	fmt.Println("bytes:", len(val))
	names, _ := s.List(ctx)
	fmt.Println("count:", len(names))
}
```

```text
bytes: 6
count: 1
```

Assert on value length, never the value. `Secrets()` resolves lazily; its `Close(ctx)` takes a context, unlike most services.

## Use Vault for rotation and payments

`env` is read-only, so `Set`/`Delete` return `secrets.ErrNotSupported` and rotation means restarting with new env values. For payment credentials use `vault` (KV v2): `Get`, `Set`, `Delete`, `List` over HTTP, 5s timeout, values never in errors.

```sh
SECRETS_ADAPTER=vault
SECRETS_ADDR=https://vault.internal:8200
SECRETS_MOUNT=secret
SECRETS_TOKEN_FILE=/run/secrets/vault-token
```

`addr` (scheme + host, `https` only unless `allow_insecure: true` for test setups), `mount` (no spaces, `..`, empty or dot segments; each `/`-separated segment is URL-escaped, so nested mounts like `team/secret` work and reserved characters cannot shift path segments), and one of `token`/`token_file` are required; `namespace` is optional. `token_file` must be readable by its owner only (`0600` or stricter) unless `allow_insecure_token_file_perms: true` explicitly permits broader access. Register with `vault.Register()` (`adapters/secrets/vault`). Adapters: `secrets/env` (default, read-only) and `secrets/vault`; names must match `[A-Za-z0-9._-]+` (`ValidateName`).

## Production checklist

- Prefer `secrets` over config env for anything that signs money or auth: `payment` `webhook_secret`, the `auth` JWT secret, provider keys.
- Values never appear in errors, only service and field names (`cfg.RedactedServices()`).

## Troubleshooting

<details>
<summary>`Secrets()` fails at startup with a prefix error</summary>

`prefix` is empty or contains `/`, `..`, or a space. Set `SECRETS_PREFIX` to a plain namespace such as `ZEVER`.
</details>

<details>
<summary>`Get` reports "not found" for an exported variable</summary>

The lookup is `_` exactly, case included; with `ZEVER`, name `DEMO_KEY` needs `ZEVER_DEMO_KEY`.
</details>

<details>
<summary>`Set` always fails</summary>

That is the `env` contract: read-only by design. Store the value in the environment or process manager, or switch to `vault`.
</details>

## Related

[Payments](https://zenta-dev.github.io/zever/guides/integrations/payment/) · [Configuration](https://zenta-dev.github.io/zever/guides/operate/configuration/) · [Container](https://zenta-dev.github.io/zever/concepts/container/)
