Strict startup: varlatch run --strict
varlatch run --strict works out the exact environment your command would
receive, checks it against the active Contract, and starts the command only
if nothing is wrong. On any problem it starts nothing, prints every problem
at once, and exits with status 78.
varlatch run --strict [--allow-inherited NAME]... -- <command> [args...]A default varlatch run is unchanged: it never injects defaults and never
fails because of the Contract. --strict combines with --redact, which
masks the delivered Secrets in the command’s output; see
Output redaction.
What it checks
Section titled “What it checks”Strict startup makes one request to the server, which returns every value this identity may receive, the active Contract, and what was captured, all from one consistent snapshot of the server’s state. It needs:
- a server running Varlatch 0.11.0 or later. On an older server it fails and never falls back to a default run;
- an active Contract;
contract.readon the project, so the Contract can be checked;- a Contract semantics version this CLI implements.
If the request fails on the server, strict startup retries once and then fails.
Where each Contract item’s value comes from
Section titled “Where each Contract item’s value comes from”For each Contract item, the first rule that applies decides:
| What the server did | Your shell | Result |
|---|---|---|
| Delivered the value, references expanded | anything | The delivered value, replacing any value in your shell |
Delivered a value with a ${NAME} reference left literal |
anything | Violation: unresolved-reference |
| Withheld the value from this identity | sets it, and --allow-inherited NAME names it |
Your shell’s value |
| Withheld the value | sets it, not allowed | Violation: withheld |
| Withheld the value | does not set it | Violation withheld if the item is required in this environment; otherwise absent |
| Has no stored value | sets it, and --allow-inherited NAME names it |
Your shell’s value |
| Has no stored value | sets it, not allowed | Violation: inherited |
| Has no stored value | does not set it, and the Contract has a default | The Contract default |
| Has no stored value | does not set it, no default | Violation missing if required in this environment; otherwise absent |
- Every value the command will receive is validated exactly as it will receive it, whether it was delivered, inherited, or a default, using the Contract’s semantics version. An invalid value is a violation.
- A Contract default never replaces a withheld value. The real value exists; a default could silently change behaviour.
--allow-inherited NAMElets one named Contract item take its value from your shell when Varlatch delivered none (not stored, or withheld). It never overrides a delivered value. It takes exact names, can be repeated, and a name that is not in the Contract is an error.--omit NAMEleaves an item out before any rule of the table applies: the command does not get it, whatever the server delivered and whatever your shell sets, and no Contract default replaces it. Leaving out an item the Contract requires in this environment is a violation (omitted). The run context records the item asabsent, with the server’s status as it was. Strict startup reads every value in one request, so the omitted value is part of that response (and its audit event); it never reaches the command. The name must be stored in the environment or be in the Contract; another name exits with status 64.- Items outside the Contract that Varlatch delivers are passed to the
command as usual and counted. Other variables in your shell (
PATH,HOME, and so on) pass through unchanged.
Output
Section titled “Output”Violations name the item, the kind, and the reason. They never print a value.
varlatch: strict startup found 3 violation(s); the command was not started: HOST: inherited: not stored in Varlatch and set only in the parent environment; accept it with --allow-inherited HOST LEVEL: invalid: the delivered value must be one of: debug, info NEEDED: missing: required in this environment and not storedThe run context
Section titled “The run context”A strict run gives the command one extra variable, VARLATCH_RUN_CONTEXT.
It is JSON that records, for each Contract item, what the server did and how
the item reached the command. It holds names and identifiers only, never
values:
{"v":1,"mode":"strict","contractRevisionId":"rev_…","contractHash":"sha256:…", "semanticsVersion":2,"environment":{"rootId":"env_…","tier":"production"}, "items":{"API_KEY":{"server":"withheld","delivery":"inherited"}, "PORT":{"server":"notStored","delivery":"default"}, "DATABASE_URL":{"server":"delivered","delivery":"varlatch"}}}modeisstrict, orexportedfor a default run started with--export-context.serverisdelivered,withheld, ornotStored.deliveryisvarlatch,inherited,default, orabsent.
The name is reserved: no Contract item or stored value may use it, and every
varlatch run removes a VARLATCH_RUN_CONTEXT inherited from an outer run,
so it never describes the wrong Environment. A default run gives the command
a context of its own only with --export-context; see Type
generation.
The Typed Accessor that varlatch types generates reads the context to check
requiredness for the Environment and to tell withheld items from absent ones.
Agent-safe strict startup
Section titled “Agent-safe strict startup”With --agent-safe, the Agent never receives Secret plaintext: each stored
Secret reaches it as a Placeholder that the local Broker substitutes on the
way out, only at the targets you give with --target, or is left out with
--omit. See Agent-safe runs. --strict --agent-safe adds the strict checks without the operator
ever seeing a Secret either:
- The operator’s preflight. One request returns the non-sensitive
values and, when the operator holds
secret.reveal, a verdict for each Secret, never its value. The server decrypts each Secret only to validate it, after an audit event that records the attempt and its purpose. - The Broker’s issuance. The Capability is issued against the state
the preflight saw. If the configuration changed in between, issuance
refuses and names only what kind of thing changed (the Environment, the
Contract, item versions, or a rotation window); both requests are
retried once. On a match, issuance reports for each Secret whether it is
stored and whether the Agent holds
secret.usehere. It decrypts nothing.
The Agent starts only if nothing is wrong. In addition to the ordinary strict checks, these are violations:
- the operator lacks
secret.reveal, so the Secrets cannot be validated (not-evaluated); - a Secret’s stored value is invalid, or one of its references stays literal for the operator;
- the Agent lacks
secret.use, or a Requirement on it is not met (agent-unauthorized); - a Contract Secret is set only in your shell: it would reach the Agent as plaintext;
- a Secret required here is left out with
--omit(omitted).
--allow-inherited may not name a Secret in an agent-safe run.
What this does not show. Matching state means the same configuration, not the same permissions: the operator and the Agent may hold different Grants. A Secret whose references the operator can expand may reference something the Agent may not read. Every request the Agent makes through the Broker is authorized again, and resolves every reference or is denied.
Limits
Section titled “Limits”Strict startup checks what the command receives when it starts. Values the command reads later, or changes itself, are outside it. It is a correctness check, not a confidentiality control: once a value is in the command’s environment, the command can do anything with it. In an agent-safe run, rotation, revocation, and Grant changes still apply at every exercise, and a later exercise may use a newer version than the one the preflight checked.

