The problem: lazy failure
Most software reads its configuration as it needs it. A missing key file is discovered when the first request tries to sign something. A missing database is discovered when the first write happens. Until then, everything looks fine. For an authorization layer that is backwards. The whole point of Parmana is that an agent’s action runs only after checks that leave evidence behind. If the thing that stores the evidence is missing, the right moment to find out is before any request is accepted, not halfway through one. There is a second, quieter version of the same problem: the silent fallback. A setting is wrong, so the code picks something that works. It feels helpful. In a security system it is the worst choice available, because the operator believes they have one protection and actually have a weaker one.The idea: fail closed, at startup, by name
The rule I follow is simple. If a setting the server cannot work safely without is missing or wrong, the server refuses to start, and the error names the variable. No degraded mode, no default that looks like the real thing. The environment variable reference ends with a table of these startup errors and their fixes, because they are meant to be read by the person deploying, at the moment they deploy.How Parmana does it
These are the checks the claims register backs (sections 2.17 and 2.18):- No caller keys, no server. If
PARMANA_API_KEYSis empty or unset, the server will not start. The only way around it is settingPARMANA_AUTH_DISABLED=trueexplicitly, which is for local development, logs a warning on every boot, and shows up asauthDisabledon the readiness check so monitoring can alert on it. - No policy directory, no server. An unset or blank
PARMANA_POLICY_DIRstops startup with a named error, instead of an obscure crash on the first request. - No database, no server. Outside tests, the nonce store and the caller audit trail always live in Postgres. If
DATABASE_URLis missing, startup fails. There is a test that the server never silently falls back to the in-memory versions. - No signing keys, no server. If the
defaultkey pair is not present, as files or throughPARMANA_KEY_MATERIAL_JSON, startup stops. - No pretend key custody.
KEY_PROVIDERaccepts some values that have no implementation:azure-key-vault,gcp-kmsandhsm. Until 2026-09-10, setting one of them quietly gave you private keys on disk. Now the server refuses to start and says the value is not implemented. That fix is the clearest example of why this matters: an operator expecting a key vault got files, with no error at any point. - No odd key ids. The file key provider rejects any key id with characters outside letters, digits, dot, underscore and hyphen, so a crafted id such as one containing
../cannot point outside the key directory.
What it does not do
- Not every setting fails at startup. An unimplemented
HASH_PROVIDERvalue passes validation and fails the first time the server hashes. An unrecognizedLOG_LEVELfalls back toinfo. I would rather say so than claim a guarantee I have not built. - Some checks are fail open on purpose. The check that live policies still match their signed approvals runs after the server is up and logs a mismatch; it does not stop traffic. The execution path enforces policy approval separately.
- It checks presence, not wisdom. A valid but wrong database URL, or a caller key given too many capabilities, passes. Startup validation catches missing and malformed settings, not bad decisions.
- Development modes are not covered.
NODE_ENV=testturns off the durable stores, anddevelopmentrelaxes three safety gates. A deployment that sets them has removed protections on purpose, and the threat model says so.