The shape of a policy
The realcustomer-refund 1.2.0, shortened:
policies/customer-refund/1.2.0/policy.json. A policy lives at
policies/<policyId>/<policyVersion>/policy.json.
Rules and conditions
Rules run in order and the first match decides. If none matches, the request is refused (no_rule_matched). End
every policy with an always rule that rejects with a reason the agent can act on, as above.
An outcome is { "action": "approve" | "reject", "reason": "..." }. The reason is returned to the agent and stored
in the record, so write it for the person who reads it.
A condition is one of:
The operators (
packages/policy/src/OperatorEvaluator.ts):
Every signal must be accountable
A signal is a claim. The policy says how each claim the rules read is held to account, or it does not load:
An approval signal’s
resourceId is "target" or a path into parameters; its optional value is a path into
parameters to a number the approval must cover. With more than one approval signal, give each its own artifact
(the signal that carries its approval; the default is approvalArtifact).
The rule the validator enforces: no approval without a person
Every rule whose outcome isapprove must require an approval signal with is_true, either as its whole condition or
directly inside its top level all. A nested any does not count, because another branch could match. A policy that
breaks this does not load, and every request under it is refused with:
unboundSignalReasons and use them in reject rules and as extra conditions next to the approval.
Check a policy before proposing it
policies/ through PolicyValidator, the same check the server runs. Common refusals:
From a file to the policy in effect
A policy takes effect when a second person approves it. No deploy.
Maker and checker each need an API key registered as a human (
credentialHolderType USER); the checker also needs a
registered step up public key. The exact commands, in bash and PowerShell, with every answer and error, are in
Policy lifecycle and approvals. The SDKs sign the step up authorization too:
signPolicyChangeStepUp() and sign_policy_change_step_up().
The same flow is in the SDKs as proposePolicyChange, policyChanges, approvePolicyChange and
rejectPolicyChange (TypeScript), and propose_policy_change, policy_changes, approve_policy_change and
reject_policy_change (Python) (SDK reference).
Change and roll back
- Change. Copy the folder to a new version, edit, check, propose, approve. Agents that read the policy in effect (Chapter 3) follow at once. A request naming the old version is refused before any rule runs, with a message naming the version in effect.
- Roll back. Propose and approve the older version again. The most recent approval decides the version in effect.
- Send the file unchanged. The approval record stores a hash of the approved content. CI’s
verify-policy-approvalsjob and the server at startup compare it with the file in Git; one changed character fails the check.npx tsx scripts/verify-policy-changes-approved.ts --full-scanruns the same comparison.
What is checked on every request
- The policy named is the one bound to the action, at the version in effect.
- The policy has an approval record, the record’s signature verifies, and its content hash equals the live content. A policy edited in the database outside this flow authorizes nothing.
- The gateway checks the approval record again just before release.
NODE_ENV test or development they are off unless
POLICY_EXECUTION_VERIFICATION_ENFORCED=true, which is why the local server in
Chapter 2 accepts unapproved example policies.
See it run
- Tutorial 119: the real
customer-refund1.2.0, each rule in turn. examples/tutorials/14-custom-policy: a custom policy, approved and refused.- Write your first policy: a policy from scratch, step by step.