Files
next.orly.dev/docs/POLICY_CONFIGURATION_REFERENCE.md
mleku c5db0abf73 Add policy configuration reference documentation
Introduce a comprehensive reference guide for ORLY policy configuration. This document outlines policy options, validation rules, access control, and debugging methods, serving as the authoritative resource for policy-related behavior.
2025-12-02 18:12:11 +00:00

14 KiB

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 Field Write (EVENT) Read (REQ) Notes
size_limit Validates incoming events only
content_limit Validates incoming events only
max_age_of_event Prevents replay attacks
max_age_event_in_future Prevents future-dated events
max_expiry_duration Requires expiration tag
must_have_tags Validates required tags
protected_required Requires NIP-70 "-" tag
identifier_regex Validates "d" tag format
tag_validation Validates tag values with regex
write_allow Pubkey whitelist for writing
write_deny Pubkey blacklist for writing
read_allow Pubkey whitelist for reading
read_deny Pubkey blacklist for reading
privileged Party-involved access control
write_allow_follows Grants both read AND write
follows_whitelist_admins Grants both read AND write
script Scripts only run for writes

Core Principle: Validation vs Filtering

The policy system has two distinct modes of operation:

Write Operations (EVENT messages)

  • Purpose: Validate and accept/reject incoming events
  • All rules apply except read_allow, read_deny, and privileged
  • Events are checked before storage
  • Rejected events are never stored

Read Operations (REQ messages)

  • Purpose: Filter which stored events a user can retrieve
  • Only access control rules apply: read_allow, read_deny, privileged, write_allow_follows, follows_whitelist_admins
  • Validation rules (size, age, tags) do NOT apply
  • Scripts are NOT executed for reads
  • Filtering happens after database query

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.

  • Whitelist takes precedence: If present, ONLY whitelisted kinds are allowed
  • Blacklist: If no whitelist, these kinds are denied
  • Neither: Behavior depends on default_policy and whether rules exist
{
  "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:

  • If privileged: false: ONLY these pubkeys can read
  • If privileged: true: These pubkeys OR parties involved can read
{
  "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":

  • The event author (event.pubkey)
  • Users mentioned in p tags

Interaction with read_allow:

  • read_allow present + privileged: true = OR logic (in list OR party involved)
  • read_allow empty + privileged: true = Only parties involved
  • privileged: true alone = Only parties involved
{
  "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:

  • P7D = 7 days
  • PT1H = 1 hour
  • P1DT12H = 1 day 12 hours
  • PT30M = 30 minutes
{
  "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

  • [] (empty array explicitly set) = Allow all
  • null or field omitted = No list configured, use other rules

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:

  • If default_policy: "allow": All kinds allowed
  • If default_policy: "deny" or unset: Only kinds with rules allowed

Debugging Policy Issues

Enable debug logging to see policy decisions:

export ORLY_LOG_LEVEL=debug

Log messages include:

  • Policy evaluation steps
  • Rule matching
  • Access decisions with reasons

Source Code Reference

  • Policy struct definition: pkg/policy/policy.go:75-144 (Rule struct)
  • Policy struct definition: pkg/policy/policy.go:380-412 (P struct)
  • Check evaluation: pkg/policy/policy.go:1260-1595 (checkRulePolicy)
  • Write handler: app/handle-event.go:114-138
  • Read handler: app/handle-req.go:420-438