POLICY_TROUBLESHOOTING.md raw

Policy System Troubleshooting Guide

This guide helps you configure and troubleshoot the ORLY relay policy system based on the requirements from Issue #5.

Definition of Done Requirements

The policy system must support:

  1. Configure relay to accept only certain kind events
  2. Scenario A: Only certain users should be allowed to write events ✅
  3. Scenario B: Only certain users should be allowed to read events ✅
  4. Scenario C: Only users involved in events should be able to read the events (privileged) ✅
  5. Scenario D: Scripting option for complex validation ✅

All requirements are implemented and tested (see pkg/policy/comprehensive_test.go).

Policy Evaluation Order (CRITICAL FOR CORRECT CONFIGURATION)

The policy system evaluates rules in a specific order. Understanding this order is crucial for correct configuration:

Overall Evaluation Flow:

  1. Global Rules (age, size) - Universal constraints applied first
  2. Kind Whitelist/Blacklist - Absolute gatekeepers for event types
  3. Script Execution (if configured and enabled)
  4. Rule-based Filtering (see detailed order below)

Rule-based Filtering Order (within checkRulePolicy):

  1. Universal Constraints - Size limits, required tags, timestamps
  2. Explicit Denials (deny lists) - Highest priority blacklist
  3. Privileged Access Check - Parties involved override allow lists
  4. Exclusive Allow Lists - ONLY listed users get access
  5. Privileged Final Check - Non-involved users denied for privileged events
  6. Default Behavior - Fallback when no specific rules apply

Key Concepts:

Common Misunderstandings:

  1. "Allow lists should be inclusive" - NO! Allow lists are exclusive. If you want some users to have guaranteed access while others follow default policy, use privileged events or scripting.
  1. "Default policy should apply when not in allow list" - NO! When an allow list exists, it completely overrides default policy for that kind.
  1. "Privileged should be checked last" - NO! Privileged access is checked early to override allow lists for parties involved.

Quick Start

Step 1: Enable Policy System

Set the environment variable:

export ORLY_POLICY_ENABLED=true

Or add to your service file:

Environment="ORLY_POLICY_ENABLED=true"

Step 2: Create Policy Configuration File

The policy configuration file must be located at:

$HOME/.config/ORLY/policy.json

Or if using a custom app name:

$HOME/.config/<YOUR_APP_NAME>/policy.json

Step 3: Configure Your Policy

Create ~/.config/ORLY/policy.json with your desired rules. See examples below.

Step 4: Restart Relay

sudo systemctl restart orly

Step 5: Verify Policy is Loaded

Check the logs:

sudo journalctl -u orly -f | grep -i policy

You should see:

loaded policy configuration from /home/user/.config/ORLY/policy.json

Configuration Examples

Example 1: Kind Whitelist (Requirement 1)

Only accept kinds 1, 3, 4, and 7:

{
  "kind": {
    "whitelist": [1, 3, 4, 7]
  },
  "default_policy": "allow"
}

How it works:

Example 2: Per-Kind Write Access (Scenario A)

Only specific users can write kind 10 events:

{
  "rules": {
    "10": {
      "description": "Only Alice can write kind 10",
      "write_allow": ["ALICE_PUBKEY_HEX"]
    }
  },
  "default_policy": "allow"
}

How it works:

Example 3: Per-Kind Read Access (Scenario B)

Only specific users can read kind 20 events:

{
  "rules": {
    "20": {
      "description": "Only Bob can read kind 20",
      "read_allow": ["BOB_PUBKEY_HEX"]
    }
  },
  "default_policy": "allow"
}

How it works:

Example 4: Privileged Events (Scenario C)

Only users involved in the event can read it:

{
  "rules": {
    "4": {
      "description": "Encrypted DMs - only parties involved",
      "privileged": true
    },
    "14": {
      "description": "Direct Messages - only parties involved",
      "privileged": true
    }
  },
  "default_policy": "allow"
}

How it works:

1. The author of the event (ev.pubkey == user.pubkey), OR 2. Mentioned in a p tag (["p", "user_pubkey_hex"])

Example 5: Script-Based Validation (Scenario D)

Use a custom script for complex validation:

{
  "rules": {
    "30078": {
      "description": "Custom validation via script",
      "script": "/home/user/.config/ORLY/validate-30078.sh"
    }
  },
  "default_policy": "allow"
}

Script Requirements:

  1. Must be executable (chmod +x script.sh)
  2. Reads JSONL (one event per line) from stdin
  3. Writes JSONL responses to stdout
  4. Each response must have: {"id":"event_id","action":"accept|reject|shadowReject","msg":"reason"}

Example script:

#!/bin/bash
while IFS= read -r line; do
  # Parse event JSON and apply custom logic
  if echo "$line" | jq -e '.kind == 30078 and (.content | length) < 1000' > /dev/null; then
    echo "{\"id\":\"$(echo "$line" | jq -r .id)\",\"action\":\"accept\",\"msg\":\"ok\"}"
  else
    echo "{\"id\":\"$(echo "$line" | jq -r .id)\",\"action\":\"reject\",\"msg\":\"content too long\"}"
  fi
done

Example 6: Combined Policy

All features together:

{
  "kind": {
    "whitelist": [1, 3, 4, 10, 20, 30]
  },
  "rules": {
    "10": {
      "description": "Only Alice can write",
      "write_allow": ["ALICE_PUBKEY_HEX"]
    },
    "20": {
      "description": "Only Bob can read",
      "read_allow": ["BOB_PUBKEY_HEX"]
    },
    "4": {
      "description": "Encrypted DMs - privileged",
      "privileged": true
    },
    "30": {
      "description": "Custom validation",
      "script": "/home/user/.config/ORLY/validate.sh",
      "write_allow": ["ALICE_PUBKEY_HEX"]
    }
  },
  "global": {
    "description": "Global rules for all events",
    "max_age_of_event": 31536000,
    "max_age_event_in_future": 3600
  },
  "default_policy": "allow"
}

Common Issues and Solutions

Issue 1: Events Outside Whitelist Are Accepted

Symptoms:

Solution: Check that policy is enabled:

# Check if policy is enabled
echo $ORLY_POLICY_ENABLED

# Check if config file exists
ls -l ~/.config/ORLY/policy.json

# Check logs for policy loading
sudo journalctl -u orly | grep -i policy

If policy is not loading:

  1. Verify ORLY_POLICY_ENABLED=true is set
  2. Verify config file is in correct location
  3. Verify JSON is valid (use jq . < ~/.config/ORLY/policy.json)
  4. Restart the relay

Issue 2: Read Restrictions Not Enforced

Symptoms:

Solution:

  1. Check authentication: Users MUST be authenticated via NIP-42 AUTH

- Set ORLY_AUTH_REQUIRED=true to force authentication - Or use ACL mode: ORLY_ACL_MODE=managed or ORLY_ACL_MODE=follows

  1. Check policy configuration:

`bash cat ~/.config/ORLY/policy.json | jq '.rules["YOURKIND"].readallow' `

  1. Check relay logs when a REQ is made:

`bash sudo journalctl -u orly -f | grep -E "(policy|CheckPolicy|read)" `

  1. Verify pubkey format: Use hex (64 chars), not npub

Example to convert npub to hex:

# Using nak (nostr army knife)
nak decode npub1...

# Or use your client's developer tools

Issue 3: Kind Whitelist Not Working

Symptoms:

Possible Causes:

  1. Policy not enabled

`bash # Check environment variable systemctl show orly | grep ORLYPOLICYENABLED `

  1. Config file not loaded

- Check file path: ~/.config/ORLY/policy.json - Check file permissions: chmod 644 ~/.config/ORLY/policy.json - Check JSON syntax: jq . < ~/.config/ORLY/policy.json

  1. Default policy overriding

- If default_policy is not set correctly - Kind whitelist is checked BEFORE default policy

Issue 4: Privileged Events Visible to Everyone

Symptoms:

Solution:

  1. Check authentication: Users MUST authenticate via NIP-42

`bash # Force authentication export ORLYAUTHREQUIRED=true `

  1. Check event has p-tags: For users to be "involved", they must be:

- The author (ev.pubkey), OR - In a p-tag: ["p", "user_pubkey_hex"]

  1. Verify policy configuration:

`json { "rules": { "4": { "privileged": true } } } `

  1. Check logs:

`bash sudo journalctl -u orly -f | grep -E "(privileged|IsPartyInvolved)" `

Issue 5: Script Not Running

Symptoms:

Solution:

  1. Check script exists and is executable:

`bash ls -l ~/.config/ORLY/policy.sh chmod +x ~/.config/ORLY/policy.sh `

  1. Check policy manager is enabled:

`bash echo $ORLYPOLICYENABLED # Must be "true" `

  1. Test script manually:

`bash echo '{"id":"test","pubkey":"abc","created_at":1234567890,"kind":1,"content":"test","tags":[],"sig":"def"}' | ~/.config/ORLY/policy.sh `

  1. Check script output format: Must output JSONL:

`json {"id":"event_id","action":"accept","msg":"ok"} `

  1. Check relay logs:

`bash sudo journalctl -u orly -f | grep -E "(policy script|script)" `

Testing Your Policy Configuration

Test 1: Kind Whitelist

# 1. Configure whitelist for kinds 1,3
cat > ~/.config/ORLY/policy.json <<EOF
{
  "kind": {
    "whitelist": [1, 3]
  },
  "default_policy": "allow"
}
EOF

# 2. Restart relay
sudo systemctl restart orly

# 3. Try to publish kind 1 (should succeed)
# 4. Try to publish kind 5 (should fail)

Test 2: Write Access Control

# 1. Get your pubkey
YOUR_PUBKEY="$(nak key public)"

# 2. Configure write access
cat > ~/.config/ORLY/policy.json <<EOF
{
  "rules": {
    "10": {
      "write_allow": ["$YOUR_PUBKEY"]
    }
  },
  "default_policy": "allow"
}
EOF

# 3. Restart relay
sudo systemctl restart orly

# 4. Publish kind 10 with your key (should succeed)
# 5. Publish kind 10 with different key (should fail)

Test 3: Read Access Control

# 1. Configure read access
cat > ~/.config/ORLY/policy.json <<EOF
{
  "rules": {
    "20": {
      "read_allow": ["$YOUR_PUBKEY"]
    }
  },
  "default_policy": "allow"
}
EOF

# 2. Enable authentication
export ORLY_AUTH_REQUIRED=true

# 3. Restart relay
sudo systemctl restart orly

# 4. Authenticate with your key and query kind 20 (should see events)
# 5. Query without auth or with different key (should not see events)

Test 4: Privileged Events

# 1. Configure privileged
cat > ~/.config/ORLY/policy.json <<EOF
{
  "rules": {
    "4": {
      "privileged": true
    }
  },
  "default_policy": "allow"
}
EOF

# 2. Restart relay
sudo systemctl restart orly

# 3. Publish kind 4 with p-tag to Bob
# 4. Query as Bob (authenticated) - should see event
# 5. Query as Alice (authenticated) - should NOT see event

Policy Evaluation Order

The policy system evaluates in this order:

  1. Global Rules - Applied to ALL events first
  2. Kind Whitelist/Blacklist - Checked before specific rules
  3. Specific Kind Rules - Rule for the event's kind
  4. Script Validation (if configured) - Custom script logic
  5. Default Policy - Applied if no rule denies
Event Arrives
     ↓
Global Rules (max_age, size_limit, etc.)
     ↓ (if passes)
Kind Whitelist/Blacklist
     ↓ (if passes)
Specific Rule for Kind
     ├─ Script (if configured)
     ├─ write_allow/write_deny
     ├─ read_allow/read_deny
     ├─ privileged
     └─ Other rule criteria
     ↓ (if no rule found or passes)
Default Policy (allow or deny)

Getting Your Pubkey in Hex Format

From npub:

# Using nak
nak decode npub1abc...

# Using Python
python3 -c "from nostr_sdk import PublicKey; print(PublicKey.from_bech32('npub1abc...').to_hex())"

From nsec:

# Using nak
nak key public nsec1abc...

# Using Python
python3 -c "from nostr_sdk import Keys; print(Keys.from_sk_str('nsec1abc...').public_key().to_hex())"

Additional Configuration

Combine with ACL System

Policy and ACL work together:

# Enable managed ACL + Policy
export ORLY_ACL_MODE=managed
export ORLY_POLICY_ENABLED=true
export ORLY_AUTH_REQUIRED=true

Query Cache with Policy

Policy filtering happens BEFORE cache, so cached results respect policy:

export ORLY_QUERY_CACHE_SIZE_MB=512
export ORLY_QUERY_CACHE_MAX_AGE=5m

Debugging Tips

Enable Debug Logging

export ORLY_LOG_LEVEL=debug
sudo systemctl restart orly
sudo journalctl -u orly -f

Test Policy in Isolation

Use the comprehensive test:

cd /home/mleku/src/next.orly.dev
CGO_ENABLED=0 go test -v ./pkg/policy -run TestPolicyDefinitionOfDone

Check Policy Manager Status

Look for these log messages:

✅ "loaded policy configuration from ..."
✅ "policy script started: ..."
❌ "failed to load policy configuration: ..."
❌ "policy script does not exist at ..."

Support

If you're still experiencing issues:

  1. Check logs: sudo journalctl -u orly -f | grep -i policy
  2. Verify configuration: cat ~/.config/ORLY/policy.json | jq .
  3. Run tests: go test -v ./pkg/policy
  4. File an issue: https://git.nostrdev.com/mleku/next.orly.dev/issues

Summary

All requirements are implemented and workingComprehensive tests verify all scenariosConfiguration examples providedTroubleshooting guide available

The policy system is fully functional. Most issues are due to: