# Secrets and recovery material ## Storage contract - `sops.operationalSecretsPath` defaults to `/operational-secrets.sops.yaml`. It is a SOPS-encrypted input containing operational secrets, keyed by logical OpenBao paths. It is never plaintext Git content. - `sops.ageKeyPath` defaults to `/.age/key.txt`. This Flux SOPS identity is private local material; do not commit, print, or copy it. - `sops.recoveryIdentityPath` defaults to `/.age/recovery-key.txt`. This separate OpenBao-recovery identity is private local material; do not commit, print, or copy it. - `sops.recoveryBundlePath` defaults to `/.recovery/openbao-recovery.age`. This bundle is encrypted to `sops.recoveryRecipient`; it contains OpenBao recovery material and is never plaintext Git content. - During generated Flux repository reconciliation, recovery material is rendered only into `base/openbao/unseal.sops.yaml`, encrypted with the Flux SOPS identity. This is the narrowly scoped encrypted recovery path for the `openbao-unseal` controller; no plaintext recovery material belongs in a workload, config, log, or command argument. ## Bootstrap gates `bootstrap` resolves and validates the configuration before reconciliation. Any bootstrap requires a readable SOPS age identity. `talos.autoBootstrapFlux: true` additionally requires an existing encrypted operational-secrets file and a nonempty recovery recipient. A non-webhook bootstrap additionally requires configured, existing recovery-identity and recovery-bundle files. The webhook-only path requires a complete delivery contract, an approved configuration, and a readable SOPS age identity. It must reseed OpenBao and observe the refreshed `forgejo-webhook` target Secret plus the EventListener and Pipeline before Forgejo is changed. The registry-token, delivery-status-token, and webhook-authorization flows also require an explicit configuration; they are live credential operations and are not offline-safe. ## Rules - Credential values are accepted only at an approved secure input boundary and stored only in encrypted operational or recovery material. - Never pass credential values in CLI arguments, URLs, logs, Git commits, generated config, tickets, or evidence. - Do not revoke a previous credential until OpenBao, External Secrets, and every listed consumer have passed validation. - Use the sanitized procedure in [runbooks/credential-rotation.md](runbooks/credential-rotation.md) for any live rotation. - Application, artifact, and shared-secret access is documented in [secret-grants.md](secret-grants.md). Secret values remain outside that declaration. ## Automated app-secret identities Use `bootstrap --provision-app-secret-identities --e2e-app ` to create a short-lived non-root `admin` identity and an exact-path `e2e-probe` identity. Bootstrap reads root recovery material only through its encrypted recovery bundle, writes the generated tokens only into encrypted operational state, and never prints either value. `app secret --identity admin` and `app secret --identity e2e:` create a temporary local token file only for the command lifetime. Root tokens, recovery bundles, and unseal shares are not valid app-secret identities.