Modules & Files
A module is one policy file.
- Must start with a package
- Optional imports
- Then one or more rules
package app.auth
import data.roles
import data.helpers as H
default allow = false
Rules
Value Rules (with if/else bodies)
allow if {
input.user == "admin"
} else if {
input.action == "read"
}
- Branching with else
- Each block is a query of literals
Default Rules
- Sets a fallback value if no other rule branch applies
Function Rules
allow(u, a) := true if {
u == "admin"
} else if {
a == "read"
}
- Rules can take arguments, like functions
Set & Object Rules
# 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
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
some i
not blocked[i]
every u in input.users { u.active }
Assignments & Comparisons
x := input.value
x in [1,2,3]
count(input.items) >= 5
Arithmetic & Boolean
total := price * quantity + tax
ok if input.age >= 18 and not input.banned
References & Calls
v1 := input.user.name
v2 := data.groups["admins"].members
r := helper.add(2,3)
With Modifiers
allow with data.now as "2025-06-01T00:00:00Z"
Collections & Scalars
Arrays, Objects, Sets
a := [1,2,3]
o := {"x":1, "y":2}
s := {1,2,3}
Scalars & Variables
n := 42
s := "hello"
t := true
z := null
Quantifiers
some i in input.items
input.items[i].active
every u in input.users {
u.age >= 18
}
Negation
deny if {
not input.authenticated
}
Membership
"admin" in input.user.roles
Builtins (Supported Categories)
Aggregates
n := count(input.items)
sum_ok := sum([1,2,3]) == 6
min_val := min([4,9,1])
Arrays
array.slice([1,2,3,4], 1, 3) # [2,3]
Sets
u := union({{1,2},{2,3}})
Objects
ks := object.keys({"a":1,"b":2})
Strings
ok := contains("hello world", "world")
Numbers
Time
ts := time.parse_rfc3339_ns("2024-01-01T00:00:00Z")
Conversions & Encoding
j := json.marshal({"x":1})
Regex
regex.match(`^\\d+$`, "12345")
Semver
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.
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.
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
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.
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".
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.
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.
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.
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.
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.
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.
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.
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.
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
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
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
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
result := newton.privacy.blacklist.count()
Returns the number of addresses in the blacklist.
Allowlist Domain (newton.privacy.allowlist.*)
newton.privacy.allowlist.contains
result := newton.privacy.allowlist.contains(address)
Returns true if address is in the provider’s allowlist.
newton.privacy.allowlist.count
result := newton.privacy.allowlist.count()
Returns the number of addresses in the allowlist.
Generic Field Accessor (newton.privacy.get)
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)
package sanctions_check
default allow := false
allow if {
not newton.privacy.blacklist.contains(input.from)
}
Allowlist check (only allow approved addresses)
package allowlist_only
default allow := false
allow if {
newton.privacy.allowlist.contains(input.from)
}
Combined identity + privacy check
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
result := newton.time.days_between(date_a, date_b)
Returns the absolute number of days between two dates.
newton.time.days_since
result := newton.time.days_since(past_date, reference_date)
Returns positive days if past_date is before reference_date.
newton.time.is_within_days
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
result := newton.time.is_before(date_a, date_b)
result := newton.time.is_after(date_a, date_b)
newton.time.age_years
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
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