# Policy System Troubleshooting Guide This guide helps you configure and troubleshoot the ORLY relay policy system based on the requirements from [Issue #5](https://git.nostrdev.com/mleku/next.orly.dev/issues/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: - **Allow lists are EXCLUSIVE**: When `write_allow` or `read_allow` is specified, **ONLY** those users can access. Others are denied regardless of default policy. - **Deny lists have highest priority**: Users in deny lists are **always denied**, even if they're in allow lists or involved in privileged events. - **Allow lists override privileged access**: When BOTH `privileged: true` AND allow lists are specified, the allow list is **authoritative** - even parties involved must be in the allow list. - **Privileged without allow lists**: If `privileged: true` but no allow lists, parties involved get automatic access. - **Default policy rarely applies**: Only used when no specific rules exist for a kind. ### 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. 2. **"Default policy should apply when not in allow list"** - NO! When an allow list exists, it completely overrides default policy for that kind. 3. **"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: ```bash export ORLY_POLICY_ENABLED=true ``` Or add to your service file: ```ini 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//policy.json ``` ### Step 3: Configure Your Policy Create `~/.config/ORLY/policy.json` with your desired rules. See examples below. ### Step 4: Restart Relay ```bash sudo systemctl restart orly ``` ### Step 5: Verify Policy is Loaded Check the logs: ```bash 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: ```json { "kind": { "whitelist": [1, 3, 4, 7] }, "default_policy": "allow" } ``` **How it works:** - Events with kinds 1, 3, 4, or 7 are allowed - Events with any other kind are **automatically rejected** - This is checked BEFORE any rule-specific policies ### Example 2: Per-Kind Write Access (Scenario A) Only specific users can write kind 10 events: ```json { "rules": { "10": { "description": "Only Alice can write kind 10", "write_allow": ["ALICE_PUBKEY_HEX"] } }, "default_policy": "allow" } ``` **How it works:** - Only the pubkey in `write_allow` can publish kind 10 events - All other users are denied - The pubkey in the event MUST match one in `write_allow` ### Example 3: Per-Kind Read Access (Scenario B) Only specific users can read kind 20 events: ```json { "rules": { "20": { "description": "Only Bob can read kind 20", "read_allow": ["BOB_PUBKEY_HEX"] } }, "default_policy": "allow" } ``` **How it works:** - Only users authenticated as the pubkey in `read_allow` can see kind 20 events in REQ responses - Unauthenticated users cannot see these events - Users authenticated as different pubkeys cannot see these events ### Example 4: Privileged Events (Scenario C) Only users involved in the event can read it: ```json { "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:** - A user can read a privileged event ONLY if they are: 1. The author of the event (`ev.pubkey == user.pubkey`), OR 2. Mentioned in a `p` tag (`["p", "user_pubkey_hex"]`) - Unauthenticated users cannot see privileged events - Third parties cannot see privileged events ### Example 5: Script-Based Validation (Scenario D) Use a custom script for complex validation: ```json { "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: ```bash #!/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: ```json { "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:** - You configured a kind whitelist - Events with kinds NOT in the whitelist are still accepted **Solution:** Check that policy is enabled: ```bash # 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:** - You configured `read_allow` for a kind - Unauthorized users can still see those events **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` 2. **Check policy configuration**: ```bash cat ~/.config/ORLY/policy.json | jq '.rules["YOUR_KIND"].read_allow' ``` 3. **Check relay logs** when a REQ is made: ```bash sudo journalctl -u orly -f | grep -E "(policy|CheckPolicy|read)" ``` 4. **Verify pubkey format**: Use hex (64 chars), not npub Example to convert npub to hex: ```bash # Using nak (nostr army knife) nak decode npub1... # Or use your client's developer tools ``` ### Issue 3: Kind Whitelist Not Working **Symptoms:** - You have `"whitelist": [1,3,4]` - Events with kind 5 are still accepted **Possible Causes:** 1. **Policy not enabled** ```bash # Check environment variable systemctl show orly | grep ORLY_POLICY_ENABLED ``` 2. **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` 3. **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:** - You set `"privileged": true` for a kind - Users can see events they're not involved in **Solution:** 1. **Check authentication**: Users MUST authenticate via NIP-42 ```bash # Force authentication export ORLY_AUTH_REQUIRED=true ``` 2. **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"]` 3. **Verify policy configuration**: ```json { "rules": { "4": { "privileged": true } } } ``` 4. **Check logs**: ```bash sudo journalctl -u orly -f | grep -E "(privileged|IsPartyInvolved)" ``` ### Issue 5: Script Not Running **Symptoms:** - You configured a script path - Script is not being executed **Solution:** 1. **Check script exists and is executable**: ```bash ls -l ~/.config/ORLY/policy.sh chmod +x ~/.config/ORLY/policy.sh ``` 2. **Check policy manager is enabled**: ```bash echo $ORLY_POLICY_ENABLED # Must be "true" ``` 3. **Test script manually**: ```bash echo '{"id":"test","pubkey":"abc","created_at":1234567890,"kind":1,"content":"test","tags":[],"sig":"def"}' | ~/.config/ORLY/policy.sh ``` 4. **Check script output format**: Must output JSONL: ```json {"id":"event_id","action":"accept","msg":"ok"} ``` 5. **Check relay logs**: ```bash sudo journalctl -u orly -f | grep -E "(policy script|script)" ``` ## Testing Your Policy Configuration ### Test 1: Kind Whitelist ```bash # 1. Configure whitelist for kinds 1,3 cat > ~/.config/ORLY/policy.json < ~/.config/ORLY/policy.json < ~/.config/ORLY/policy.json < ~/.config/ORLY/policy.json <