BengarTrust infrastructure

Docs · Guides

Policy

Deterministic rules that decide ALLOW, DENY or REQUIRE_APPROVAL — versioned, simulated, then activated.

The document

A policy is a JSON document with a spec version and an ordered set of rules. Each rule names an effect and, optionally, a condition; a rule with no condition always matches.

{
  "spec_version": "bengar-policy/1",
  "rules": [
    {
      "id": "office-supplies-under-5k",
      "effect": "ALLOW",
      "when": {
        "op": "and",
        "args": [
          { "op": "matches_resource", "input": "resource",
            "value": "resource://company/procurement/*" },
          { "op": "lte", "input": "amount.minor", "value": 500000, "currency": "TRY" }
        ]
      }
    },
    {
      "id": "large-spend-needs-a-person",
      "effect": "REQUIRE_APPROVAL",
      "when": { "op": "gt", "input": "amount.minor", "value": 500000, "currency": "TRY" }
    }
  ]
}

Effects, and how they compose

Three effects: ALLOW, DENY and REQUIRE_APPROVAL. When several rules match, the result is their meet — the most restrictive wins, and DENY is absorbing. The identity is ALLOW, so a document where nothing matched restricts nothing; the Gateway treats “no rules matched” as its own reason code rather than as an allow.

REQUIRE_APPROVAL is not an allow. It is its own value, never an ALLOW carrying a flag, so a caller that treats “not DENY” as go has to make that mistake deliberately.

Inputs a rule may read

InputType
actionstring
resourceresource
agentdid
amount.minormoney — minor units, compared against a currency
amount.currencystring
request_timetimestamp
delegation_depthinteger
permit_commitmentstring
backend_finalitystring

An input that no milestone produces yet — risk_score today — is refused at validation with the milestone named, rather than accepted and silently unavailable. A policy referencing a value nobody supplies would deny everything, and its author would have no way to see why.

Operators

  • eq, neq — equality
  • lt, lte, gt, gte — comparison, and for money the currency must match
  • between — a closed range
  • in, not_in — membership
  • matches_resource — resource patterns
  • within_time_window — from and until
  • and, or, not — composition

Money comparisons are in minor units, as integers. A limit a rounding error can cross is not a limit, so nothing here parses a decimal.

Determinism

The same inputs against the same document always produce the same decision, and the evaluation reports which rules matched and how many steps it took. Nothing here is a model output, and there is no scoring that varies between runs.

Versioning, simulation and activation

  1. Save a version with PUT /v1/projects/:id/policy. Saving does not activate it.
  2. Simulate against it — the simulation authorizes nothing, spends no counter and writes no audit event.
  3. Activate with POST .../policy/activate. That is the moment it starts deciding, and it is audited whether it succeeds or is refused.

AVAILABLE Publish a policy version/projects/:id/policies

Where: Policies → Author a policy

First: POLICY_AUTHOR, which an administrator can now grant.

Requires: POLICY_AUTHOR

AVAILABLE Simulate a decision against the policy/projects/:id/policies

Where: Policies → Simulate

First: An active policy version and at least one registered agent.

Requires: ADMIN, POLICY_AUTHOR, AUDITOR

AVAILABLE Activate a policy version/projects/:id/policies

Where: Policies → Activate

First: An inactive version to activate.

Requires: POLICY_AUTHOR

Separation of duties

policy.publish and policy.activate are POLICY_AUTHOR. ADMIN does not acquire them by being ADMIN — an administrator who could author the rules and also approve under them would defeat the arrangement the rules exist to create.

Related