# Rego Syntax Guide \[Supported Rego syntax, builtins, and Newton extensions (crypto, identity) for policy development]

## Modules & Files

**A module is one policy file.**

* Must start with a package
* Optional imports
* Then one or more **rules**

```rego
package app.auth

import data.roles
import data.helpers as H

default allow = false
```

***

## Rules

## Value Rules (with if/else bodies)

```rego
allow if {
  input.user == "admin"
} else if {
  input.action == "read"
}
```

* Branching with else
* Each block is a **query** of literals

## Default Rules

```rego
default allow = false
```

* Sets a fallback value if no other rule branch applies

## Function Rules

```rego
allow(u, a) := true if {
  u == "admin"
} else if {
  a == "read"
}
```

* Rules can take arguments, like functions

## Set & Object Rules

```rego
# Set membership
my_ids[id] if {
  id := input.items[_].id
  id > 1000
}

# Object construction
user_roles[uid] := role if {
  role := data.roles[uid]
}
```

## Comprehension Rules

```rego
allowed_names := [n | u := input.users[_]; u.allowed; n := u.name]
project_ids   := {p | p := input.projects[_].id}
role_by_user  := { u.id: u.role | u := input.users[_] }
```

***

## Expressions & Queries

## Literals

* Expressions, not exprs
* `some` existential, `every` universal

```rego
some i
not blocked[i]

every u in input.users { u.active }
```

## Assignments & Comparisons

```rego
x := input.value
x in [1,2,3]
count(input.items) >= 5
```

## Arithmetic & Boolean

```rego
total := price * quantity + tax
ok if input.age >= 18 and not input.banned
```

## References & Calls

```rego
v1 := input.user.name
v2 := data.groups["admins"].members
r  := helper.add(2,3)
```

## With Modifiers

```rego
allow with data.now as "2025-06-01T00:00:00Z"
```

***

## Collections & Scalars

## Arrays, Objects, Sets

```rego
a := [1,2,3]
o := {"x":1, "y":2}
s := {1,2,3}
```

## Scalars & Variables

```rego
n := 42
s := "hello"
t := true
z := null
```

***

## Quantifiers

* `some` / `every`

```rego
some i in input.items
input.items[i].active

every u in input.users {
  u.age >= 18
}
```

***

## Negation

* `not`

```rego
deny if {
  not input.authenticated
}
```

***

## Membership

* `in`

```rego
"admin" in input.user.roles
```

***

## Builtins (Supported Categories)

## Aggregates

```rego
n := count(input.items)
sum_ok := sum([1,2,3]) == 6
min_val := min([4,9,1])
```

## Arrays

```rego
array.slice([1,2,3,4], 1, 3)  # [2,3]
```

## Sets

```rego
u := union({{1,2},{2,3}})
```

## Objects

```rego
ks := object.keys({"a":1,"b":2})
```

## Strings

```rego
ok := contains("hello world", "world")
```

## Numbers

```rego
cl := ceil(3.2)  # 4
```

## Time

```rego
ts := time.parse_rfc3339_ns("2024-01-01T00:00:00Z")
```

## Conversions & Encoding

```rego
j := json.marshal({"x":1})
```

## Regex

```rego
regex.match(`^\\d+$`, "12345")
```

## Semver

```rego
semver.is_valid("1.2.3")
```

***

## Newton Crypto Extensions

Newton extends the standard Rego runtime with custom cryptographic builtins for signature recovery. These are available in the Newton Regorus engine used by operators and the `newton-cli regorus eval` command.

### `newton.crypto.ecdsa_recover_signer`

Recovers the signer address from a raw message hash and ECDSA signature.

```rego
signer := newton.crypto.ecdsa_recover_signer(signature, message_hash)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `signature` | `string` | Hex-encoded ECDSA signature (65 bytes with recovery id) |
| `message_hash` | `string` | Hex-encoded 32-byte message hash |

**Returns:** Hex-encoded Ethereum address of the signer.

### `newton.crypto.ecdsa_recover_signer_personal`

Recovers the signer address from a personal message and ECDSA signature. Applies the EIP-191 `\x19Ethereum Signed Message:\n` prefix before recovery.

```rego
signer := newton.crypto.ecdsa_recover_signer_personal(signature, message)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `signature` | `string` | Hex-encoded ECDSA signature (65 bytes with recovery id) |
| `message` | `string` | The original message string (prefix is applied automatically) |

**Returns:** Hex-encoded Ethereum address of the signer.

### Example

```rego
package auth

default allow = false

allow if {
    signer := newton.crypto.ecdsa_recover_signer(input.intent_signature, input.intent_hash)
    signer == input.from
}
```

***

## Newton Identity Extensions

Newton extends the Rego runtime with identity verification built-ins for checking user identity data within policies. Identity data is domain-namespaced — the `identity_domain` (bytes32) stored on-chain determines which schema and built-ins apply. Domain is always required.

Two APIs are available:

* **Domain-namespaced built-ins** (primary): `newton.identity.kyc.age_gte(21)`. Type-safe with input validation and specific error messages.
* **Generic field accessor** (escape hatch): `newton.identity.get("field_name")`. Returns the raw field value from the current domain's data. Useful for rapid prototyping with new domains before dedicated built-ins exist.

:::info
Identity data is injected by Newton operators at evaluation time from the on-chain IdentityRegistry. Policy authors do not have direct access to personally identifying information — only the built-in check results (booleans) are exposed to the policy.
:::

### KYC Domain (`newton.identity.kyc.*`)

The KYC domain provides 8 built-ins for verifying Know Your Customer identity data.

#### KYC Data Fields

| Field | Type | Description |
|-------|------|-------------|
| `reference_date` | `YYYY-MM-DD` | Timestamp of "now" for time-based checks |
| `status` | `string` | KYC status: `approved`, `pending`, `completed`, `failed`, `expired`, `declined` |
| `selected_country_code` | ISO 3166-1 alpha-2 | Country selected during KYC process |
| `address_country_code` | ISO 3166-1 alpha-2 | Country from document address |
| `address_subdivision` | `string` | State/province from document address |
| `birthdate` | `YYYY-MM-DD` | Date of birth |
| `expiration_date` | `YYYY-MM-DD` | Document expiration date |
| `issue_date` | `YYYY-MM-DD` | Document issuance date |
| `issuing_authority` | `string` | Issuing country or state |

#### `newton.identity.kyc.check_approved`

Returns `true` if the identity status is `"approved"`.

```rego
result := newton.identity.kyc.check_approved()
```

No parameters. Returns `bool`.

#### `newton.identity.kyc.address_in_countries`

Checks if the document's address country code is in the provided list.

```rego
result := newton.identity.kyc.address_in_countries(["US", "CA", "DE"])
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `countries` | `string[]` | Array of ISO 3166-1 alpha-2 country codes |

Returns `bool`. Errors if the array is empty or the stored country code is empty.

#### `newton.identity.kyc.address_in_subdivision`

Checks if the document's address subdivision is in the provided list.

```rego
result := newton.identity.kyc.address_in_subdivision(["US-CA", "US-OR", "US-WA"])
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `subdivisions` | `string[]` | Array of ISO subdivision codes (`CC-SS` format, e.g., `US-CA`) |

Returns `bool`. The check concatenates `address_country_code` + `-` + `address_subdivision` and matches against the list. Errors if the array is empty or country/subdivision fields are empty.

#### `newton.identity.kyc.address_not_in_subdivision`

Inverse of `address_in_subdivision`. Returns `true` if the address is NOT in the list.

```rego
result := newton.identity.kyc.address_not_in_subdivision(["US-NY", "US-NC"])
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `subdivisions` | `string[]` | Array of ISO subdivision codes to exclude |

Returns `bool`. Useful when combined with `address_in_countries` to include a country but exclude specific states.

#### `newton.identity.kyc.age_gte`

Checks if the person's age (calculated from `birthdate` to `reference_date`) is at least the specified years.

```rego
result := newton.identity.kyc.age_gte(21)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `min_age` | `number` | Minimum age in years (must be positive) |

Returns `bool`. Errors if `min_age` is not positive or dates cannot be parsed.

#### `newton.identity.kyc.not_expired`

Returns `true` if the document expiration date has not passed relative to `reference_date`.

```rego
result := newton.identity.kyc.not_expired()
```

No parameters. Returns `bool`.

#### `newton.identity.kyc.valid_for`

Checks if the document will remain valid for at least the specified number of days.

```rego
result := newton.identity.kyc.valid_for(365)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `min_days` | `number` | Minimum days of remaining validity (must be positive) |

Returns `bool`. Compares `(expiration_date - reference_date)` against the provided day count.

#### `newton.identity.kyc.issued_since`

Checks if the document was issued at least the specified number of days ago.

```rego
result := newton.identity.kyc.issued_since(90)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `min_days` | `number` | Minimum days since issuance (must be positive) |

Returns `bool`. Compares `(reference_date - issue_date)` against the provided day count.

### Generic Field Accessor (`newton.identity.get`)

Works across any identity domain. Returns the raw field value by name from the current domain's data. Returns `undefined` if the field does not exist, allowing Rego `default` patterns.

```rego
result := newton.identity.get("field_name")
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `field_name` | `string` | Field name to look up |

Returns the field value (any type), or `undefined` if not found.

When multiple identity domains are registered, all domains' fields are accessible through this single accessor.

### KYC Policy Example

```rego
package kyc_compliance

default allow = false

# Allow transactions from approved US/CA users over 21 with valid documents
allow if {
    newton.identity.kyc.check_approved()
    newton.identity.kyc.age_gte(21)
    newton.identity.kyc.address_in_countries(["US", "CA"])
    newton.identity.kyc.address_not_in_subdivision(["US-NY", "US-NC"])
    newton.identity.kyc.not_expired()
    newton.identity.kyc.valid_for(90)
    newton.identity.kyc.issued_since(30)
}
```

### Mixed Domain and Generic Accessor Example

```rego
package mixed_check

default allow = false

# Combine domain-specific built-ins with generic field access
allow if {
    newton.identity.kyc.check_approved()
    newton.identity.kyc.age_gte(18)
    newton.identity.get("address_country_code") == "US"
}
```

### Adding New Identity Domains

The identity extension system is designed for new domains beyond KYC. Each domain (social, credit, professional, etc.) defines its own data struct, Rego built-ins, and field accessors. New domains register under `newton.identity.<domain>.*` and merge their fields into the shared `newton.identity.get` accessor.

Until domain-specific built-ins are available for a new domain, policy authors can use `newton.identity.get("field_name")` to access any field from the domain's data.

***

## Privacy Extensions (`newton.privacy.*`)

Provider-managed confidential data (blacklists, allowlists, sanctions lists) uploaded via the `ConfidentialDataRegistry`. Operators fetch and decrypt this data at task time based on the `confidential_domain` in `policyParams`.

Two access patterns:

* **Domain-namespaced builtins** (primary): `newton.privacy.blacklist.contains(addr)`. Type-safe with address normalization.
* **Generic field accessor** (escape hatch): `newton.privacy.get("field_name")`. Returns raw field values across all registered privacy domains.

### Blacklist Domain (`newton.privacy.blacklist.*`)

#### `newton.privacy.blacklist.contains`

```rego
result := newton.privacy.blacklist.contains(address)
```

Returns `true` if `address` is in the provider's blacklist. Addresses are normalized to lowercase hex before comparison.

#### `newton.privacy.blacklist.count`

```rego
result := newton.privacy.blacklist.count()
```

Returns the number of addresses in the blacklist.

### Allowlist Domain (`newton.privacy.allowlist.*`)

#### `newton.privacy.allowlist.contains`

```rego
result := newton.privacy.allowlist.contains(address)
```

Returns `true` if `address` is in the provider's allowlist.

#### `newton.privacy.allowlist.count`

```rego
result := newton.privacy.allowlist.count()
```

Returns the number of addresses in the allowlist.

### Generic Field Accessor (`newton.privacy.get`)

```rego
value := newton.privacy.get("field_name")
```

Returns `undefined` if the field does not exist, which allows `default` rules to apply.

### Privacy Policy Examples

#### Blacklist check (deny blacklisted senders)

```rego
package sanctions_check

default allow := false

allow if {
    not newton.privacy.blacklist.contains(input.from)
}
```

#### Allowlist check (only allow approved addresses)

```rego
package allowlist_only

default allow := false

allow if {
    newton.privacy.allowlist.contains(input.from)
}
```

#### Combined identity + privacy check

```rego
package compliant_transfer

default allow := false

allow if {
    # User must pass KYC
    newton.identity.kyc.check_approved()
    newton.identity.kyc.age_gte(18)
    # Sender must not be blacklisted
    not newton.privacy.blacklist.contains(input.from)
    # Recipient must be on allowlist
    newton.privacy.allowlist.contains(input.to)
}
```

***

## Time Extensions (`newton.time.*`)

Date arithmetic builtins for time-based policy checks. All dates use `YYYY-MM-DD` format strings.

### `newton.time.days_between`

```rego
result := newton.time.days_between(date_a, date_b)
```

Returns the absolute number of days between two dates.

### `newton.time.days_since`

```rego
result := newton.time.days_since(past_date, reference_date)
```

Returns positive days if `past_date` is before `reference_date`.

### `newton.time.is_within_days`

```rego
result := newton.time.is_within_days(date, reference_date, max_days)
```

Returns `true` if the absolute difference between `date` and `reference_date` is at most `max_days`.

### `newton.time.is_before` / `newton.time.is_after`

```rego
result := newton.time.is_before(date_a, date_b)
result := newton.time.is_after(date_a, date_b)
```

### `newton.time.age_years`

```rego
result := newton.time.age_years(birthdate, reference_date)
```

Returns the number of complete years between `birthdate` and `reference_date`. Useful for age verification without the KYC identity domain.

### Time Policy Example

```rego
package time_check

default allow := false

# Allow if document was issued within the last 90 days
allow if {
    newton.time.is_within_days(data.issued_date, data.reference_date, 90)
}
```

***

## Not Yet Supported

* **Standard Crypto / Tokens / JWT**: `crypto.*`, `jwtverify*`, `jwtencode*` — use Newton crypto extensions instead
* **HTTP**: `http.send` — not implemented (use PolicyData WASM oracles for external data)
* **GraphQL**: `graphql.*` — not implemented
* **Glob matching**: `regex.globs_match` — not implemented
* **JSON Patch**: `json.patch` — not implemented
* **Networking**: `net.*` — not implemented
* **AWS Providers**: `providers.aws.*` — not implemented
* **Rego Meta**: `rego.metadata.*`, `rego.parse_module` — not implemented
* **Template rendering**: `strings.render_template` — not implemented
