POLICY_CONFIGURATION_REFERENCE.md raw

ORLY Policy Configuration Reference

This document provides a definitive reference for all policy configuration options and when each rule applies. Use this as the authoritative source for understanding policy behavior.

Quick Reference: Read vs Write Applicability

Rule FieldWrite (EVENT)Read (REQ)Notes
size_limitValidates incoming events only
content_limitValidates incoming events only
max_age_of_eventPrevents replay attacks
max_age_event_in_futurePrevents future-dated events
max_expiry_durationRequires expiration tag
must_have_tagsValidates required tags
protected_requiredRequires NIP-70 "-" tag
identifier_regexValidates "d" tag format
tag_validationValidates tag values with regex
write_allowPubkey whitelist for writing
write_denyPubkey blacklist for writing
read_allowPubkey whitelist for reading
read_denyPubkey blacklist for reading
privilegedParty-involved access control
write_allow_followsGrants both read AND write
follows_whitelist_adminsGrants both read AND write
scriptScripts only run for writes

Core Principle: Validation vs Filtering

The policy system has two distinct modes of operation:

Write Operations (EVENT messages)

Read Operations (REQ messages)

Configuration Structure

{
  "default_policy": "allow|deny",
  "kind": {
    "whitelist": [1, 3, 7],
    "blacklist": [4, 42]
  },
  "owners": ["hex_pubkey_64_chars"],
  "policy_admins": ["hex_pubkey_64_chars"],
  "policy_follow_whitelist_enabled": true,
  "global": { /* Rule object */ },
  "rules": {
    "1": { /* Rule object for kind 1 */ },
    "30023": { /* Rule object for kind 30023 */ }
  }
}

Top-Level Configuration Fields

default_policy

Type: string Values: "allow" (default) or "deny" Applies to: Both read and write

The fallback behavior when no specific rule makes a decision.

{
  "default_policy": "deny"
}

kind.whitelist and kind.blacklist

Type: []int Applies to: Both read and write

Controls which event kinds are processed at all.

{
  "kind": {
    "whitelist": [0, 1, 3, 7, 30023]
  }
}

owners

Type: []string (64-character hex pubkeys) Applies to: Policy administration

Relay owners with full control. Merged with ORLY_OWNERS environment variable.

{
  "owners": ["4a93c5ac0c6f49d2c7e7a5b8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8"]
}

policy_admins

Type: []string (64-character hex pubkeys) Applies to: Policy administration

Pubkeys that can update policy via kind 12345 events (with restrictions).

policy_follow_whitelist_enabled

Type: boolean Default: false Applies to: Both read and write (when write_allow_follows is true)

When enabled, allows write_allow_follows rules to grant access to policy admin follows.

Rule Object Fields

Rules can be defined in global (applies to all events) or rules[kind] (applies to specific kind).

Access Control Fields

write_allow

Type: []string (hex pubkeys) Applies to: Write only Behavior: Exclusive whitelist

When present with entries, ONLY these pubkeys can write events of this kind. All others are denied.

{
  "rules": {
    "1": {
      "write_allow": ["pubkey1_hex", "pubkey2_hex"]
    }
  }
}

Special case: Empty array [] explicitly allows all writers.

write_deny

Type: []string (hex pubkeys) Applies to: Write only Behavior: Blacklist (highest priority)

These pubkeys cannot write events of this kind. Checked before allow lists.

{
  "rules": {
    "1": {
      "write_deny": ["banned_pubkey_hex"]
    }
  }
}

read_allow

Type: []string (hex pubkeys) Applies to: Read only Behavior: Exclusive whitelist (with OR logic for privileged)

When present with entries:

{
  "rules": {
    "4": {
      "read_allow": ["trusted_pubkey_hex"],
      "privileged": true
    }
  }
}

read_deny

Type: []string (hex pubkeys) Applies to: Read only Behavior: Blacklist (highest priority)

These pubkeys cannot read events of this kind. Checked before allow lists.

privileged

Type: boolean Default: false Applies to: Read only

When true, events are only readable by "parties involved":

Interaction with `read_allow`:

{
  "rules": {
    "4": {
      "description": "DMs - only sender and recipient can read",
      "privileged": true
    }
  }
}

write_allow_follows

Type: boolean Default: false Applies to: Both read AND write Requires: policy_follow_whitelist_enabled: true at top level

Grants both read and write access to pubkeys followed by policy admins.

Important: Despite the name, this grants BOTH read and write access.
{
  "policy_follow_whitelist_enabled": true,
  "rules": {
    "1": {
      "write_allow_follows": true
    }
  }
}

follows_whitelist_admins

Type: []string (hex pubkeys) Applies to: Both read AND write

Alternative to write_allow_follows that specifies which admin pubkeys' follows are whitelisted for this specific rule.

{
  "rules": {
    "30023": {
      "follows_whitelist_admins": ["curator_pubkey_hex"]
    }
  }
}

Validation Fields (Write-Only)

These fields validate incoming events and are completely ignored for read operations.

size_limit

Type: int64 (bytes) Applies to: Write only

Maximum total serialized event size.

{
  "global": {
    "size_limit": 100000
  }
}

content_limit

Type: int64 (bytes) Applies to: Write only

Maximum size of the content field.

{
  "rules": {
    "1": {
      "content_limit": 10000
    }
  }
}

max_age_of_event

Type: int64 (seconds) Applies to: Write only

Maximum age of events. Events with created_at older than now - max_age_of_event are rejected.

{
  "global": {
    "max_age_of_event": 86400
  }
}

max_age_event_in_future

Type: int64 (seconds) Applies to: Write only

Maximum time events can be dated in the future. Events with created_at later than now + max_age_event_in_future are rejected.

{
  "global": {
    "max_age_event_in_future": 300
  }
}

max_expiry_duration

Type: string (ISO-8601 duration) Applies to: Write only

Maximum allowed expiry time from event creation. Events must have an expiration tag when this is set.

Format: P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S

Examples:

{
  "rules": {
    "20": {
      "description": "Ephemeral events must expire within 24 hours",
      "max_expiry_duration": "P1D"
    }
  }
}

must_have_tags

Type: []string (tag names) Applies to: Write only

Required tags that must be present on the event.

{
  "rules": {
    "1": {
      "must_have_tags": ["p", "e"]
    }
  }
}

protected_required

Type: boolean Default: false Applies to: Write only

Requires events to have a - tag (NIP-70 protected events).

{
  "rules": {
    "4": {
      "protected_required": true
    }
  }
}

identifier_regex

Type: string (regex pattern) Applies to: Write only

Regex pattern that d tag values must match. Events must have a d tag when this is set.

{
  "rules": {
    "30023": {
      "identifier_regex": "^[a-z0-9-]{1,64}$"
    }
  }
}

tag_validation

Type: map[string]string (tag name → regex pattern) Applies to: Write only

Regex patterns for validating specific tag values. Only validates tags that are present on the event.

Note: To require a tag to exist, use must_have_tags. tag_validation only validates format.
{
  "rules": {
    "30023": {
      "tag_validation": {
        "t": "^[a-z0-9-]{1,32}$",
        "d": "^[a-z0-9-]+$"
      }
    }
  }
}

Script Configuration

script

Type: string (file path) Applies to: Write only

Path to a custom validation script. Scripts are NOT executed for read operations.

{
  "rules": {
    "1": {
      "script": "/etc/orly/scripts/spam-filter.py"
    }
  }
}

Policy Evaluation Order

For Write Operations

1. Global Rule Check (all fields apply)
   ├─ Universal constraints (size, tags, age, etc.)
   ├─ write_deny check
   ├─ write_allow_follows / follows_whitelist_admins check
   └─ write_allow check

2. Kind Filtering (whitelist/blacklist)

3. Kind-Specific Rule Check (same as global)
   ├─ Universal constraints
   ├─ write_deny check
   ├─ write_allow_follows / follows_whitelist_admins check
   ├─ write_allow check
   └─ Script execution (if configured)

4. Default Policy (if no rules matched)

For Read Operations

1. Global Rule Check (access control only)
   ├─ read_deny check
   ├─ write_allow_follows / follows_whitelist_admins check
   ├─ read_allow check
   └─ privileged check (party involved)

2. Kind Filtering (whitelist/blacklist)

3. Kind-Specific Rule Check (access control only)
   ├─ read_deny check
   ├─ write_allow_follows / follows_whitelist_admins check
   ├─ read_allow + privileged (OR logic)
   └─ privileged-only check

4. Default Policy (if no rules matched)

NOTE: Scripts are NOT executed for read operations

Common Configuration Patterns

Private Relay (Whitelist Only)

{
  "default_policy": "deny",
  "global": {
    "write_allow": ["trusted_pubkey_1", "trusted_pubkey_2"],
    "read_allow": ["trusted_pubkey_1", "trusted_pubkey_2"]
  }
}

Open Relay with Spam Protection

{
  "default_policy": "allow",
  "global": {
    "size_limit": 100000,
    "max_age_of_event": 86400,
    "max_age_event_in_future": 300
  },
  "rules": {
    "1": {
      "script": "/etc/orly/scripts/spam-filter.sh"
    }
  }
}

Community Relay (Follows-Based)

{
  "default_policy": "deny",
  "policy_admins": ["community_admin_pubkey"],
  "policy_follow_whitelist_enabled": true,
  "global": {
    "write_allow_follows": true
  }
}

Encrypted DMs (Privileged Access)

{
  "rules": {
    "4": {
      "description": "Encrypted DMs - only sender/recipient",
      "privileged": true,
      "protected_required": true
    }
  }
}

Long-Form Content with Validation

{
  "rules": {
    "30023": {
      "description": "Long-form articles",
      "size_limit": 100000,
      "content_limit": 50000,
      "max_expiry_duration": "P30D",
      "identifier_regex": "^[a-z0-9-]{1,64}$",
      "tag_validation": {
        "t": "^[a-z0-9-]{1,32}$"
      }
    }
  }
}

Important Behaviors

Whitelist vs Blacklist Precedence

  1. Deny lists (write_deny, read_deny) are checked first and have highest priority
  2. Allow lists are exclusive when populated - ONLY listed pubkeys are allowed
  3. Deny-only configuration: If only deny list exists (no allow list), all non-denied pubkeys are allowed

Empty Arrays vs Null

Global Rules Are Additive

Global rules are always evaluated in addition to kind-specific rules. They cannot be overridden at the kind level.

Implicit Kind Whitelist

When rules are defined but no explicit kind.whitelist:

Debugging Policy Issues

Enable debug logging to see policy decisions:

export ORLY_LOG_LEVEL=debug

Log messages include:


Source Code Reference