Skip to main content
A policy is a JSON file that decides one kind of action. Its rules read the request’s signals; the first rule that matches decides approve or reject. A policy authorizes nothing until one person proposes it and a different person approves it.

The shape of a policy

The real customer-refund 1.2.0, shortened:
The full file is 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 is approve 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:
The consequence for design: the agent’s own facts can refuse, only a person can approve. Put the facts the agent declares in unboundSignalReasons and use them in reject rules and as extra conditions next to the approval.

Check a policy before proposing it

It loads every file under 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-approvals job 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-scan runs the same comparison.
What still needs a deploy: binding a built in action to a different policy name. A new action for your own system needs neither: register it as an external connector, with its policy, through maker checker (Chapter 6).

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.
These checks are always on in production. In 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-refund 1.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.