- name
- configuration-state
- description
- Configure durable AuthCrunch runtime state, exclusive storage ownership, stop/start persistence, reload rejection, and recovery. Use for root state blocks and process-level restart guarantees.
# Persistent Runtime State
Use this skill for `security { state { ... } }`, native `config.state` JSON,
restart persistence, and its lifecycle boundary. OAuth client registrations and
identity databases retain their existing owners. Keep changes and test output
in this repository; sibling AuthCrunch source is a read-only reference under
the [repository scope](../coding-directives/SKILL.md#repository-scope).
## Configuration
Add one optional block inside the global security app:
```caddyfile
{
security {
state {
directory /var/lib/authcrunch/runtime
}
# Existing providers, portals and/or authorization policies follow.
}
}
```
This is a fragment: a usable security app also needs a portal or policy.
The JSON field is `apps.security.config.state`, with the library's shape:
`"state":{"directory":"/var/lib/authcrunch/runtime"}`.
Paths must be absolute, non-root and private. Quote spaces. Omission retains
volatile behavior; an empty block, duplicate block/setting, extra arguments,
unknown settings and nested blocks are errors. Closing braces end their lines.
Use `{$VARIABLE}` for adaptation-time substitution. `{env.VARIABLE}` and whole
`secrets:manager:key` references remain declarative until provisioning; an empty
or invalid resolved directory fails instead of disabling persistence.
`caddyfile_state.go` owns traversal and `cfgutil.EncodeArgs` encoding;
`go-authcrunch/pkg/state/parser.NewStateConfigFromDirectives` owns grammar and
normalization. Deferred values use a validation-only stand-in, retain their
original token in `Config.State`, and are resolved/revalidated in the private
configuration copy. Never open the stand-in, choose a process-specific directory,
implement another directory grammar, or copy the library's records, encryption,
session DTOs, replay history or locks into Caddy.
## Lifecycle and operation
Pending cross-device interactions are always volatile. Reload or complete
stop/start discards them even when completed sessions use persistent state.
The existing rejection of overlapping persistent reload still applies. See
[cross-device lifecycle](../configuration-authentication-cross-device/SKILL.md#identity-cancellation-and-lifecycle).
Read [operator guidance](references/operations.md) when enabling persistence,
planning deployment/recovery, or explaining its guarantees. Persistent runtimes
are constructed by `App.Start`, not `Provision`, so adaptation and validation
do not initialize keys or state files. Route provisioning validates declared
names; admission stays closed until `NewServer` succeeds.
Caddy provisions and starts a replacement before retiring the old app.
There is no atomic drain/construct/rollback facility. A candidate persistent
app checks Caddy's active app during provisioning and rejects replacement of a
live persistent runtime before candidate HTTP routes start. This deliberately
also rejects changing directories by reload. Use a complete stop/start.
The library independently enforces exclusive directory ownership across
processes. Do not add a host storage lock or shared runtime/snapshot map.
`App.Cleanup` closes admission, drains every admitted portal/gatekeeper call
(including callbacks, token and profile APIs), then closes the root. A failed
candidate never closes the serving root. Failed construction unwinds its own
resources. Close neither flushes nor deletes committed data. Preserve handled
503/protocol failures and never issue fallback credentials or retry rotations.
Policy-only OAuth needs no placeholder portal. Volatile use needs no declared
local database, but retaining completed sessions across restart also requires
the provider to `attach to local identity store NAME` with a file-backed local
store. Runtime state preserves that store's proof epoch; the provider's implicit
private-memory attachment loses its identities at restart and cannot validate
old session evidence.
The [direct OAuth policy contract](../configuration-authorization/SKILL.md#direct-oauth-without-a-portal)
owns those settings; mount callback/logout paths through the same policy.
## Named refresh storage
A portal selecting [named refresh storage](../configuration-authentication/references/refresh-storage.md)
must enable this root state block. The SQLite database supplies transactional
families and spent history; root state supplies the stable namespace, changing
configuration generation and other completed session/identity authority. Retain
both through a complete stop/start. Selecting SQLite alone does not retain
implicit provider-memory identities or downstream OP grants.
Provisioning freezes declarations and checks known storage/key-reader namespaces;
Start repeats those metadata checks before opening runtime handles. The root
activates selected views only after complete configuration normalization and
loaded trust admission. Same-generation restart preserves deadlines and replay
history; changed generations retire that namespace across its portals and reclaim
capacity. Complete feature removal/reintroduction also changes the durable epoch.
A failed later activation cannot roll back an earlier database retirement: cold
startup is not a transaction across all files. Failed candidates publish no root,
close their app-owned handles after root disposal and require corrected fresh
construction. Root/portals never close borrowed views or snapshot their families.
Keep database files outside managed state/client-registration directories and
separate from key readers and other backend files, including aliases/sidecars.
Candidate removal does not discard the active graph's ownership until drain.
See [operations](references/operations.md#named-refresh-database) for loss,
uncertainty and backup boundaries.
## Validation
Check the selected module's public state and managed refresh APIs before
changing this contract. Library tests alone do not certify this host.
- `caddyfile_state_test.go`, `caddyfile_authz_oauth_test.go` and the
`testcase_security_state` adaptation fixture cover grammar, exact paths,
JSON, placeholders, redacted failures and policy-only configuration.
- `app_state_test.go` covers no-I/O provisioning, single ownership, failed
initialization, retry, 503 route admission before startup/after cleanup and
drain before storage release.
- `TestAuthzResponseContract` tests the actual three-outcome route handler.
- `TestCaddyRuntimeStateE2E` builds an actual Caddy command from
`testdata/runtime_state_caddy`, with standard/production modules and only an
isolated test CA pool added to the main program. TLS verification remains
enabled; no machine trust is changed. It uses SIGKILL and the same origin,
directory and config across fresh processes. It covers direct OAuth, ACLs,
lost pending callbacks, sessions/JWKS/signatures, browser/native refresh,
OIDC consent/code/access/refresh and replay, logout, password/DB rollback,
configuration transitions, competing processes, corrupt/lost storage,
permissions, write failures, real snapshot capacity and overlapping reload
under admitted callbacks and application traffic. Deferred state-directory
resolution preserves its placeholder in autosave. Omitted-state behavior and
failed persistence activation retaining the volatile deployment's routes and
sessions remain separate phases. The snapshot-capacity fixture writes tens
of MiB.
Selected refresh acceptance adds `TestCaddySQLiteRefreshPersistenceE2E` for real
process continuity, replay/logout, capacity-one generation retirement, loaded
trust and complete omission/reintroduction. `TestCaddySQLiteRefreshUncertaintyE2E`
checks real attempted-COMMIT failure and browser/native recovery after reopen;
local/root readiness and request-drain units remain separate. Use the
[managed refresh test map](../testing-and-ci/references/test-surfaces.md#selected-refresh-storage)
for exact fixture scope and limits.
When changing the state parser or runtime implementation, run focused
unit/adaptation tests and
`go test -mod=readonly -race -count=1 -timeout=8m -run 'TestCaddyRuntimeStateE2E|TestPersistentApp' .`.
Retain the existing OAuth, lifecycle, composition and browser refresh coverage.
Ordinary configuration review uses the relevant grammar, path/ownership and
deployment checks; skill-only changes need metadata/link/source validation.
Full regression/report runs follow [testing-and-ci](../testing-and-ci/SKILL.md).
GitHubで見る