4. Policy Language

August 1, 2026 · View on GitHub

rulegate.yaml is the human-readable source of authorization policy. RuleGate loads the complete document, validates it, compiles it into typed policy definitions, and activates it only when the entire candidate is valid.

Document structure

schemaVersion: 1

application:
  id: document-service
  name: Document Service

policies:
  - id: document-read
    resourceType: document
    action: read
    requirement:
      permission: DOC.READ
MemberMeaning
schemaVersionExact manifest schema; currently 1
applicationStable application metadata
policiesNon-empty collection of policy definitions
idUnique policy identifier used by tools and projections
resourceType + actionUnique route selected by the engine
requirementOne built-in or logical requirement tree

Every requirement may have an optional id. Add IDs to security-significant leaves so tests and redacted diagnostics can identify them without exposing values.

Permission and role requirements

requirement:
  all:
    - id: approve-capability
      permission: DOC.APPROVE
    - id: approver-responsibility
      role: DOCUMENT.APPROVER

Matching is exact and case-sensitive. Empty or duplicate values in the runtime subject are normalized safely, but no wildcard or implicit hierarchy exists.

Logical requirements

requirement:
  all:
    - any:
        - permission: DOC.READ
        - role: DOCUMENT.READER
    - not:
        role: DOCUMENT.BLOCKED
OperatorMeaningSecurity behavior
allEvery child must be satisfiedOne denied or indeterminate child denies
anyAt least one child must be satisfiedAllows only after a satisfied child; indeterminate input cannot become allow by itself
notChild must be conclusively not satisfiedIndeterminate stays indeterminate; missing data is not inverted into access

Keep trees shallow enough to review. Manifest and runtime depth limits protect the application from unbounded input.

Literal attribute requirements

Read an attribute from subject, resource, or context and compare it to a typed literal:

requirement:
  attribute:
    source: resource
    name: status
    operator: in
    valueType: stringCollection
    value: [draft, returned]

Operators

FamilyOperatorsTypical use
Equalityequal, notEqualstatus, organization, boolean flags
OrderinggreaterThan, greaterThanOrEqual, lessThan, lessThanOrEquallimits, classification, dates
Stringcontains, startsWith, endsWithnormalized domains or prefixes
Collectioncontains, containsAny, containsAll, intersectsgroups, labels, regions
Membershipin, notInscalar in an approved/blocked set
Presenceexists, notExistswhether a key was supplied
NullisNull, isNotNullpresent explicit null state
EmptyisEmpty, isNotEmptypresent collection state

Value types

TokenRuntime type
stringstring
booleanbool
numberinteger or invariant decimal normalized as a number
dateTimeOffsetISO 8601 value with Z or a numeric offset
nullValueexplicit null literal
stringCollectionhomogeneous strings
booleanCollectionhomogeneous booleans
numberCollectionhomogeneous numbers
dateTimeOffsetCollectionhomogeneous date/time values

Not every operator accepts every type. For example, boolean ordering is invalid, and collection operations require compatible element kinds. The CLI rejects incompatible combinations.

String comparison

String matching is ordinal and case-sensitive by default:

attribute:
  source: subject
  name: department
  operator: startsWith
  stringComparison: ordinalIgnoreCase
  valueType: string
  value: operations

Use ordinalIgnoreCase only when the business identifier is intentionally case-insensitive. Do not use culture-sensitive display text as a security identifier.

Missing, null, and empty are different

Runtime stateexistsnotExistsisNullisNotNullisEmpty
Key absentnoyesnonono
Key present with nullyesnoyesnono
Empty collectionyesnonoyesyes
Non-empty valueyesnonoyesdepends on kind

Missing data never becomes implicit null. Use the operator that represents the domain state you actually intend.

Attribute-to-attribute comparison

Compare trusted values from two sources:

requirement:
  all:
    - attributeComparison:
        left:
          source: subject
          name: organizationId
        operator: equal
        right:
          source: resource
          name: organizationId
    - attributeComparison:
        left:
          source: subject
          name: clearanceLevel
        operator: greaterThanOrEqual
        right:
          source: resource
          name: classificationLevel

An operand can be an attribute or a literal. This example caps an amount:

attributeComparison:
  left:
    source: resource
    name: totalAmount
  operator: lessThanOrEqual
  right:
    valueType: number
    value: 50000

Both values must have compatible types. Missing or incompatible values deny.

Canonical context requirements

Canonical context properties give common request facts stable names:

requirement:
  all:
    - context:
        property: networkZone
        operator: in
        valueType: stringCollection
        value: [internal, vpn]
    - context:
        property: requestChannel
        operator: equal
        valueType: string
        value: web
    - context:
        property: trustedDevice
        operator: equal
        valueType: boolean
        value: true

Canonical properties include authentication method, request channel, network zone, tenant ID, organization ID, trusted device, and identity type. The application must still provide trustworthy values.

Use a normal attribute with source: context for application-specific facts such as a validated risk score or correlation category.

Authentication and MFA age

requirement:
  all:
    - contextAge:
        timestamp: authentication
        maximumAge: '08:00:00'
    - contextAge:
        timestamp: mfa
        maximumAge: '00:15:00'

authentication reads the canonical authentication timestamp. mfa reads the multi-factor timestamp. A missing, future, malformed, or too-old timestamp does not satisfy the requirement.

Recurring time windows

timeWindow:
  days: [monday, tuesday, wednesday, thursday, friday]
  start: '08:00'
  end: '18:00'
  timeZone: Europe/Istanbul

RuleGate converts the trusted evaluation time into the named time zone. Use exact HH:mm values and lowercase day tokens. Overnight windows are supported by the defined time semantics; test boundary instants, daylight-saving transitions, and the host's available time-zone database.

An organization-specific schedule should not be hard-coded into a shared policy when every organization differs. A context provider can resolve the current organization's schedule into trusted attributes, or applications can maintain separate policy routes/snapshots when that is the clearer model.

Bounded date-time windows

dateTimeWindow:
  startsAt: '2026-09-01T00:00:00Z'
  endsAt: '2026-10-01T00:00:00Z'

Use this for a release, campaign, emergency exception, or migration interval with fixed absolute boundaries. Always include a UTC marker or numeric offset.

Complete approval policy

schemaVersion: 1

application:
  id: document-approval
  name: Document Approval

policies:
  - id: document-approve
    resourceType: document
    action: approve
    requirement:
      id: complete-approval-rule
      all:
        - permission: DOC.APPROVE
        - role: DOCUMENT.APPROVER
        - attribute:
            source: resource
            name: status
            operator: equal
            valueType: string
            value: submitted
        - attributeComparison:
            left: { source: subject, name: organizationId }
            operator: equal
            right: { source: resource, name: organizationId }
        - attributeComparison:
            left: { source: resource, name: totalAmount }
            operator: lessThanOrEqual
            right: { source: subject, name: approvalLimit }
        - not:
            attributeComparison:
              left: { source: subject, name: userId }
              operator: equal
              right: { source: resource, name: ownerId }
        - context:
            property: networkZone
            operator: in
            valueType: stringCollection
            value: [internal, vpn]
        - context:
            property: trustedDevice
            operator: equal
            valueType: boolean
            value: true
        - contextAge:
            timestamp: mfa
            maximumAge: '00:15:00'
        - timeWindow:
            days: [monday, tuesday, wednesday, thursday, friday]
            start: '08:00'
            end: '18:00'
            timeZone: Europe/Istanbul

The manifest states the rule. The host is responsible for supplying every referenced value from the correct trusted source.

Validate and test every change

rulegate validate rulegate.yaml
rulegate lint rulegate.yaml
rulegate test authorization.tests.yaml

Validation proves structural correctness. Linting finds maintainability risks. Policy tests prove behavior for explicit subjects, resources, context, and times. None of these replaces endpoint integration tests.

Further reference


Previous: First protected API · Next: ASP.NET Core integration