---
name: configuration-secrets
description: "Configure security.secrets plugins and secret lookup values for users, credentials, and crypto. Use for static/AWS manager wiring and lookup failures; runtime field support belongs to runtime resolution."
---

# Configuration Secrets

## Purpose

Use this skill to configure `secrets <plugin_name> <secret_id>` blocks and
secret-backed Caddyfile values. The parser is `caddyfile_secrets.go`; runtime
replacement is in `caddyfile_resolve.go`.

Secrets managers are external Caddy modules under the `security.secrets`
namespace. The `security` app loads them through `SecretsManagerConfigs`; each
module must expose `GetConfig(ctx)["id"]`, `GetSecret(ctx)`, and
`GetSecretByKey(ctx, key)`.

Authcrunch does not resolve `secrets:*:*` values itself. Caddy-security replaces
them during app provisioning, before the authcrunch config is validated and used
to build the server. A secret lookup must therefore resolve to the final string
that authcrunch expects, such as a bcrypt password hash, API key hash, OAuth
client secret, SMTP password, or crypto shared secret.

## Shape

```caddyfile
{
	security {
		secrets static_secrets_manager access_token {
			shared_secret {env.JWT_SHARED_KEY}
		}

		authentication portal myportal {
			crypto key sign-verify "secrets:access_token:shared_secret"
		}
	}
}
```

The block form is:

```caddyfile
secrets <secrets_plugin_name> <secret_id> {
	...
}
```

The third token is the manager ID, also called the secret ID in plugin logs and
docs. It must match the middle segment of every lookup:
`secrets:<secret_id>:<key>`. The inner block is owned by the external secrets
manager module; do not invent manager-specific fields.

## Static Secrets Manager

Use `static_secrets_manager` only when the Caddy binary is built with
`github.com/greenpau/caddy-security-secrets-static-secrets-manager`.

```caddyfile
{
	security {
		secrets static_secrets_manager users/jsmith {
			name "John Smith"
			email "jsmith@localhost.localdomain"
			password "bcrypt:10:$2a$10$iqq53VjdCwknBSBrnyLd9OH1Mfh6kqPezMMy6h6F41iLdVDkj13I6"
			api_key "bcrypt:10:$2a$10$TEQ7ZG9cAdWwhQK36orCGOlokqQA55ddE0WEsl00oLZh567okdcZ6"
		}
	}
}
```

The static plugin treats each inner line as one key with exactly one value and
stores an inline key-value map. Lookups read keys from that map:

```caddyfile
name "secrets:users/jsmith:name"
password "secrets:users/jsmith:password" overwrite
```

## AWS Secrets Manager

Use `aws_secrets_manager` only when the Caddy binary is built with
`github.com/greenpau/caddy-security-secrets-aws-secrets-manager`.

```caddyfile
{
	security {
		secrets aws_secrets_manager access_token {
			region us-east-1
			path authcrunch/caddy/access_token
		}
	}
}
```

The AWS plugin accepts only `region` and `path` in the block. Both are required,
and the ID is still the Caddyfile block ID (`access_token` above), not the AWS
path. The AWS secret value must be a JSON object; lookup keys read fields from
that object. For example, if the object contains `{"value":"..."}`, use:

```caddyfile
crypto key sign-verify "secrets:access_token:value"
```

## Secret Lookup Values

Resolved values use:

```text
secrets:<secret_id>:<key>
```

Examples:

```caddyfile
password "secrets:users/jsmith:password" overwrite
api key XnxJ5W0AAcDb2FO1nefd35fT "secrets:users/jsmith:api_key"
crypto key sign-verify "secrets:access_token:shared_secret"
```

Resolution is strict:

- The lookup must split into exactly three colon-separated fields; use slashes
  in IDs such as `users/jsmith`, not colons.
- `<secret_id>` must match `GetConfig(ctx)["id"]` from a loaded secrets manager.
- `<key>` is passed to `GetSecretByKey(ctx, key)`.
- The returned value must be a string. Non-string values cause provisioning to
  fail with `secret value is not a string`.

Only a whole value matching that three-part form is recognized as a lookup.
Malformed forms such as `secrets:smtp:password:extra` are ordinary strings to
the current resolver; do not assume they are rejected as missing secrets.
Check lookup spelling explicitly. A well-formed lookup with a missing manager
or key fails provisioning rather than falling back to the literal reference.

Use quotes around secret lookup strings when they contain characters that could
be parsed unexpectedly.

## Generated Secret Values

Use `authdbctl` when secret-backed local users need password or API key hashes.
The source guide is `../go-authcrunch/cmd/authdbctl/README.md`.

For local user passwords, generate a bcrypt value:

```bash
authdbctl generate password hash
authdbctl generate password hash --db-path assets/config/users.json
authdbctl generate password hash --cost 10 --password SomeFunkyPassword
```

Prefer the prompt form for real secrets; the CLI marks `--password` as
insecure. When `--db-path` points at the local users database, authdbctl checks
that database's password policy before printing the hash. Store only the value
inside the emitted `password "..."` directive:

```caddyfile
secrets static_secrets_manager users/jsmith {
	password "bcrypt:10:$2a$10$K9KksvjRCdjT1sYbecGCCu.Y33xpii94itQPgGVS6vShuEUB0On1q"
}

local identity store localdb {
	realm local
	path assets/config/users.json
	user jsmith {
		email jsmith@localhost.localdomain
		password "secrets:users/jsmith:password" overwrite
	}
}
```

For local user API keys, generate both the client secret and server-side bcrypt
payload:

```bash
authdbctl generate api key
```

The output includes `secret: <full-secret>` for the API client and
`api key <24-char-prefix> "<bcrypt-payload>"` for the Caddyfile. Do not store
the plaintext `secret:` value in the server config. If the payload is
secret-backed, keep the 24-character prefix in the Caddyfile and store only the
bcrypt payload in the secrets manager. This example adds a key to an existing
local user; keep its username and email consistent with that record:

```caddyfile
secrets static_secrets_manager users/jsmith {
	api_key "bcrypt:10:$2a$10$2QKmYR9Q5wvl8UUNkICUoOf5KMVixTEhbUor5Y3oUfQsrz5iiG.K6"
}

local identity store localdb {
	realm local
	path assets/config/users.json
	user jsmith {
		email jsmith@localhost.localdomain
		api key XnxJ5W0AAcDb2FO1nefd35fT "secrets:users/jsmith:api_key"
	}
}
```

## Validation Notes

The fixture test binary may not register external secrets manager modules. The
`testcase_security_with_secrets` fixture intentionally expects a
`module not registered: security.secrets.static_secrets_manager` error even
though the Caddyfile shape is intentional. `TestIdentityStoreSecretsFixture`
parses its local-user block separately so a missing plugin cannot hide obsolete
user syntax. `api_key` is a key inside the external manager; the local user
consumes it as `api key <24-character-prefix> secrets:<id>:api_key`, without
an `overwrite` suffix. The challenge E2E verifies static API-key provisioning
and login before checking explicit challenge-policy rejection.

The AWS plugin validates by fetching and caching the configured AWS secret during
plugin validation. The static plugin serves the configured inline map locally.
In both cases, caddy-security only consumes the common `SecretsManager`
interface after Caddy loads the module.

External plugin behavior must be checked against the module included in the
actual Caddy binary; these plugins are not selected by this repository's
`go.mod`. In static manager v1.0.1, `{env.*}` expansion happens in
the plugin's Caddyfile unmarshaler, so adapted JSON already contains the value.
An unresolved placeholder becomes `CADDY_REPLACEMENT_FAILED` there. This differs
from caddy-security's strict provisioning-time lookup. Do not use successful
adaptation as proof that a plugin's environment reference resolved correctly,
or publish adapted output containing real secrets.

The [runtime resolution contract](../configuration-runtime-resolution/SKILL.md)
identifies which fields substitute `{env.*}` and `secrets:*:*` after adaptation. Runtime replacement also
revalidates affected authcrunch config sections after substitution, so examples
must resolve to values acceptable to go-authcrunch parsers.

## Fixtures

Use these references:

- `testdata/caddyfile_adapt/testcase_security_with_secrets.Caddyfile` for
  static manager block and lookup shape.
- `caddyfile_resolve_test.go`.

`TestResolveRuntimeAppConfigEncodedInstructions` uses an in-process synthetic
manager to verify exact string replacement and missing-key failure; the Caddy
lifecycle E2E checks rejected reload recovery. Neither loads the static/AWS
plugins or contacts AWS. Qualifying an external manager requires the intended
module version and isolated synthetic values, including an unavailable key and
non-string result; AWS service behavior remains outside the normal test gate.
