Guides

Protected actions and immutable policies

A protected action is one consequential operation in your application. The stable key identifies it; an immutable policy version defines how a specific request is validated and decided.

Action identity

An action records:

Security-relevant values are snapshotted into each policy version:

Changing those values requires a new version. Every version carries a content digest. Authorization fails closed if stored content no longer matches it. Activation retires the previous version in the same database transaction.

Modes

Mode Policy evaluated Proof issued Customer behavior
test Yes No Wiring and schema validation only
shadow Yes No Hypothetical decision recorded; GrayPass does not enforce it
enforce in sandbox Yes On allow Integration test may verify and execute a mock action
enforce in live Blocked No Real-human validation and calibration release are not registered

The live enforcement gate is in the action service and authorization path. Changing a database mode value directly does not bypass it.

Decision order

  1. Registered action and intact active policy.
  2. Resource type, context schema, caller, and audience.
  3. Fresh request time, stable idempotency key, and durable nonce uniqueness.
  4. Deny conditions.
  5. Hold conditions, including positive experimental detector signals.
  6. Coverage, freshness, and continuity sufficiency.
  7. Per-claim requirements and explicit action approval.
  8. Exactly one final decision: allow, challenge, hold, or deny.

An active operator_change.detected or automation.detected hold is checked before missing evidence. Completing an unrelated challenge cannot erase it.

Insufficient evidence

insufficient_evidence remains in the Assurance Profile and reason codes. A policy must choose one of:

It can never map directly to allow.

Policy creation

JSON
{
  "requirements": {
    "claims": {
      "operator_continuity": "uncertain"
    },
    "coverage_min": "partial",
    "freshness_max_ms": 60000,
    "insufficient_evidence": "challenge",
    "challenge_methods": ["passkey"],
    "explicit_approval_required": true,
    "challenge_compensates": [
      "operator_continuity",
      "coverage",
      "freshness"
    ],
    "hold_on": [
      "operator_change.detected",
      "automation.detected"
    ],
    "deny_on": []
  },
  "allowed_audiences": ["payments-app"],
  "allowed_callers": ["key_customer_backend"],
  "proof_ttl_seconds": 300,
  "context_schema": {
    "type": "object",
    "required": ["destination_fingerprint"],
    "properties": {
      "destination_fingerprint": {
        "type": "string",
        "minLength": 3,
        "maxLength": 64
      }
    },
    "additionalProperties": false
  }
}

The supported context-schema subset rejects unknown keywords, malformed keyword values, floats, undeclared required properties, excessive nesting, and additional properties when disabled.

Generated from the API contract in contracts/ and the source in docs/. The build fails if this page disagrees with the running API.