README.md raw

next.orly.dev

orly.dev

Version v0.62.0 Documentation

Can Youse Paradigm?

Every hour you don’t zap, a donkey eats another cabbage. You can stop this. 🫏

Support this project

mleku Β· npub1fjqqy4a93z5zsjwsfxqhc2764kvykfdyttvldkkkdera8dr78vhsmmleku

Architecture Overview

ORLY supports a modular IPC architecture where core functionality runs as independent gRPC services:

orly launcher (process supervisor)
β”œβ”€β”€ orly db              (gRPC :50051) - Event storage & queries
β”œβ”€β”€ orly acl             (gRPC :50052) - Access control
β”œβ”€β”€ orly bridge          (SMTP :2525)  - Marmot email bridge (DM ↔ SMTP)
β”œβ”€β”€ orly sync distributed (gRPC :50061) - Peer-to-peer sync
β”œβ”€β”€ orly sync cluster    (gRPC :50062) - Cluster replication
β”œβ”€β”€ orly sync relaygroup (gRPC :50063) - Relay group config (Kind 39105)
β”œβ”€β”€ orly sync negentropy (gRPC :50064) - NIP-77 set reconciliation
└── orly                 (WebSocket/HTTP) - Main relay

Benefits:

See docs/IPC_SYNC_SERVICES.md for detailed API documentation.

Table of Contents

- Prerequisites - Basic Build - Building with Web UI

- Web UI - Sprocket Event Processing - Policy System

- Automated Deployment - Network Options - systemd Service Management - Remote Deployment - Configuration - Firewall Configuration - Monitoring

- Follows ACL - Curation ACL - Cluster Replication

- Client Setup (White Noise, etc.)

⚠️ Bug Reports & Feature Requests

Bug reports and feature requests that do not follow the protocol will not be accepted.

Before submitting any issue, you must read and follow BUG_REPORTS_AND_FEATURE_REQUEST_PROTOCOL.md.

Requirements:

Issues missing required information will be closed without review.

⚠️ System Requirements

IMPORTANT: ORLY requires a minimum of 500MB of free memory to operate. The relay uses adaptive PID-controlled rate limiting to manage memory pressure. By default, it will: - Auto-detect available system memory at startup - Target 66% of available memory, capped at 1.5GB for optimal performance - Fail to start if less than 500MB is available You can override the memory target with ORLY_RATE_LIMIT_TARGET_MB (e.g., ORLY_RATE_LIMIT_TARGET_MB=2000 for 2GB). To disable rate limiting (not recommended): ORLY_RATE_LIMIT_ENABLED=false

About

ORLY is a nostr relay written from the ground up to be performant, low latency, and built with a number of features designed to make it well suited for:

Performance & Cryptography

ORLY leverages high-performance libraries and custom optimizations for exceptional speed:

The encoders achieve 24% faster JSON marshaling, 16% faster canonical encoding, and 54-91% reduction in memory allocations through custom buffer pre-allocation and zero-allocation optimization techniques.

ORLY uses a fast embedded badger database with a database designed for high performance querying and event storage.

Building

ORLY is a standard Go application that can be built using the Go toolchain.

Prerequisites

Basic Build

To build the unified binary (relay + all subcommands):

git clone <repository-url>
cd next.orly.dev
go build -o orly ./cmd/orly

To build the relay-only binary (no subcommands):

go build -o orly .

Building with Web UI

To build with the embedded web interface:

# Build the Svelte web application
cd app/web
bun install
bun run build

# Build the Go binary from project root
cd ../../
go build -o orly ./cmd/orly

The recommended way to build and embed the web UI is using the provided script:

./scripts/update-embedded-web.sh

This script will:

For manual builds, you can also use:

#!/bin/bash
# build.sh
echo "Building Svelte app..."
cd app/web
bun install
bun run build

echo "Building Go binary..."
cd ../../
go build -o orly ./cmd/orly

echo "Build complete!"

Make it executable with chmod +x build.sh and run with ./build.sh.

Core Features

Web UI

ORLY includes a modern web-based user interface built with Svelte for relay management and monitoring.

The web UI is embedded in the relay binary and accessible at the relay's root path.

Web UI Development

For development with hot-reloading, ORLY can proxy web requests to a local dev server while still handling WebSocket relay connections and API requests.

Environment Variables:

Setup:

  1. Start the dev server (in one terminal):
cd app/web
bun install
bun run dev

Note the port sirv is listening on (e.g., http://localhost:8080).

  1. Start the relay with dev proxy enabled (in another terminal):
export ORLY_WEB_DISABLE=true
export ORLY_WEB_DEV_PROXY_URL=localhost:8080
./orly

The relay will:

With a reverse proxy/tunnel:

If you're running behind a reverse proxy or tunnel (e.g., Caddy, nginx, Cloudflare Tunnel), the setup is the same. The relay listens locally and your reverse proxy forwards traffic to it:

Browser οΏ½ Reverse Proxy οΏ½ ORLY (port 3334) οΏ½ Dev Server (port 8080)
                              οΏ½
                         WebSocket/API

Example with the relay on port 3334 and sirv on port 8080:

# Terminal 1: Dev server
cd app/web && bun run dev
# Output: Your application is ready~!
#         Local: http://localhost:8080

# Terminal 2: Relay
export ORLY_WEB_DISABLE=true
export ORLY_WEB_DEV_PROXY_URL=localhost:8080
export ORLY_PORT=3334
./orly

Disabling the web UI without a proxy:

If you only want to disable the embedded web UI (without proxying to a dev server), just set ORLY_WEB_DISABLE=true without setting ORLY_WEB_DEV_PROXY_URL. The relay will return 404 for web UI requests while still handling WebSocket and API requests.

Sprocket Event Processing

ORLY includes a powerful sprocket system for external event processing scripts. Sprocket scripts enable custom filtering, validation, and processing logic for Nostr events before storage.

export ORLY_SPROCKET_ENABLED=true
export ORLY_APP_NAME="ORLY"
# Place script at ~/.config/ORLY/sprocket.sh

For detailed configuration and examples, see the sprocket documentation.

Policy System

ORLY includes a comprehensive policy system for fine-grained control over event storage and retrieval. Configure custom validation rules, access controls, size limits, and age restrictions.

export ORLY_POLICY_ENABLED=true
# Default policy file: ~/.config/ORLY/policy.json

# OPTIONAL: Use a custom policy file location
# WARNING: ORLY_POLICY_PATH MUST be an ABSOLUTE path (starting with /)
# Relative paths will be REJECTED and the relay will fail to start
export ORLY_POLICY_PATH=/etc/orly/policy.json

For detailed configuration and examples, see the Policy Usage Guide.

Deployment

ORLY includes an automated deployment script that handles Go installation, dependency setup, building, and systemd service configuration.

Automated Deployment

The deployment script (scripts/deploy.sh) provides a complete setup solution:

# Clone the repository
git clone <repository-url>
cd next.orly.dev

# Run the deployment script
./scripts/deploy.sh

The script will:

  1. Install Go 1.25.3 if not present (in ~/.local/go)
  2. Configure environment by creating ~/.goenv and updating ~/.bashrc
  3. Build the relay with embedded web UI using update-embedded-web.sh
  4. Set capabilities for port 443 binding (requires sudo)
  5. Install binary to ~/.local/bin/orly
  6. Create systemd service and enable it

After deployment, reload your shell environment:

source ~/.bashrc

Network Options

ORLY can handle TLS itself (direct mode) or sit behind a reverse proxy. Choose one.

Option A: Reverse Proxy (Recommended)

Run ORLY on localhost and let Caddy, nginx, or another proxy handle TLS termination, WebSocket upgrades, and certificate renewal. This is the production setup used at relay.orly.dev.

Internet (wss://relay.example.com)
    β†’ Caddy/nginx (:443, TLS termination)
        β†’ ORLY (127.0.0.1:3334, plain HTTP/WebSocket)

1. Configure ORLY to listen on localhost only:

export ORLY_LISTEN=127.0.0.1
export ORLY_PORT=3334

Do NOT set ORLY_TLS_DOMAINS β€” the reverse proxy handles TLS.

2. Install and configure the reverse proxy.

Caddy (recommended β€” automatic HTTPS, minimal config):

# Install Caddy (Ubuntu/Debian)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Create /etc/caddy/Caddyfile:

relay.example.com {
    reverse_proxy 127.0.0.1:3334
}

That's it. Caddy handles TLS certificates, HTTPS, and WebSocket upgrades automatically. No additional configuration needed for WebSocket β€” Caddy proxies upgrade requests by default.

Reload Caddy:

sudo systemctl reload caddy

nginx alternative:

server {
    listen 443 ssl http2;
    server_name relay.example.com;

    ssl_certificate /etc/letsencrypt/live/relay.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/relay.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3334;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

server {
    listen 80;
    server_name relay.example.com;
    return 301 https://$host$request_uri;
}

With nginx you must obtain certificates separately (e.g., certbot --nginx).

3. Open firewall ports:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

4. Verify:

# Check ORLY is listening on localhost
ss -tlnp | grep 3334

# Check proxy is listening externally
ss -tlnp | grep -E ':(80|443)'

# Test WebSocket connection from outside
wscat -c wss://relay.example.com

Option B: Direct Listening (Built-in TLS)

ORLY handles TLS itself using Let's Encrypt ACME. No reverse proxy needed. ORLY binds directly to ports 80 and 443, which requires setcap since it runs as a non-root user.

Internet (wss://relay.example.com)
    β†’ ORLY (:443 HTTPS/WSS + :80 ACME challenges)

1. Set capabilities on the binary:

# Allow binding to privileged ports without root
sudo setcap 'cap_net_bind_service=+ep' ~/.local/bin/orly

Note: setcap must be re-applied after every binary update.

2. Configure TLS domains:

export ORLY_TLS_DOMAINS=relay.example.com

When ORLY_TLS_DOMAINS is set, ORLY ignores ORLY_PORT and listens on :443 (HTTPS/WSS) and :80 (ACME challenges) instead.

3. Optional: custom certificates:

# Load certificates from files instead of (or in addition to) ACME
export ORLY_CERTS=/path/to/cert1,/path/to/cert2

Certificate files should be named with .pem and .key extensions:

4. Open firewall ports:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

5. Verify:

# ORLY should be listening on 80 and 443
ss -tlnp | grep orly

# Test WebSocket
wscat -c wss://relay.example.com

Which option to choose

Reverse ProxyDirect Listening
TLS managementCaddy/nginx handles itORLY's built-in ACME
Multiple services on same IPYes (proxy routes by domain)No (ORLY owns port 443)
WebSocket configAutomatic (Caddy) or manual (nginx)Built-in
Binary updatesJust restart ORLYRestart + re-run setcap
Additional softwareCaddy or nginxNone
Used at relay.orly.devYes (Caddy)No

systemd Service Management

The deployment script creates a systemd service for easy management:

# Start the service
sudo systemctl start orly

# Stop the service
sudo systemctl stop orly

# Restart the service
sudo systemctl restart orly

# Enable service to start on boot
sudo systemctl enable orly --now

# Disable service from starting on boot
sudo systemctl disable orly --now

# Check service status
sudo systemctl status orly

# View service logs
sudo journalctl -u orly -f

# View recent logs
sudo journalctl -u orly --since "1 hour ago"

Remote Deployment

You can deploy ORLY on a remote server using SSH:

# Deploy to a VPS with SSH key authentication
ssh user@your-server.com << 'EOF'
  # Clone and deploy
  git clone <repository-url>
  cd next.orly.dev
  ./scripts/deploy.sh

  # Configure your relay
  echo 'export ORLY_TLS_DOMAINS=relay.example.com' >> ~/.bashrc
  echo 'export ORLY_ADMINS=npub1your_admin_key_here' >> ~/.bashrc

  # Start the service
  sudo systemctl start orly --now
EOF

# Check deployment status
ssh user@your-server.com 'sudo systemctl status orly'

Configuration

After deployment, configure your relay by setting environment variables in your shell profile:

# Add to ~/.bashrc or ~/.profile
export ORLY_TLS_DOMAINS=relay.example.com
export ORLY_ADMINS=npub1your_admin_key
export ORLY_ACL_MODE=follows
export ORLY_APP_NAME="MyRelay"

Then restart the service:

source ~/.bashrc
sudo systemctl restart orly

Firewall Configuration

Ensure your firewall allows the necessary ports:

# For TLS-enabled relays
sudo ufw allow 80/tcp   # HTTP (ACME challenges)
sudo ufw allow 443/tcp  # HTTPS/WSS

# For non-TLS relays
sudo ufw allow 3334/tcp # Default ORLY port

# Enable firewall if not already enabled
sudo ufw enable

Monitoring

Monitor your relay using systemd and standard Linux tools:

# Service status and logs
sudo systemctl status orly
sudo journalctl -u orly -f

# Resource usage
htop
sudo ss -tulpn | grep orly

# Disk usage (database grows over time)
du -sh ~/.local/share/ORLY/

# Check TLS certificates (if using Let's Encrypt)
ls -la ~/.local/share/ORLY/autocert/

Testing

ORLY includes comprehensive testing tools for protocol validation and performance testing.

For detailed testing instructions, multi-relay testing scenarios, and advanced usage, see the Relay Testing Guide.

The benchmark suite provides comprehensive performance testing and comparison across multiple relay implementations, including throughput, latency, and memory usage metrics.

Command-Line Tools

ORLY includes several command-line utilities in the cmd/ directory for testing, debugging, and administration.

relay-tester

Nostr protocol compliance testing tool. Validates that a relay correctly implements the Nostr protocol specification.

# Run all protocol compliance tests
go run ./cmd/relay-tester -url ws://localhost:3334

# List available tests
go run ./cmd/relay-tester -list

# Run specific test
go run ./cmd/relay-tester -url ws://localhost:3334 -test "Basic Event"

# Output results as JSON
go run ./cmd/relay-tester -url ws://localhost:3334 -json

benchmark

Comprehensive relay performance benchmarking tool. Tests event storage, queries, and subscription performance with detailed latency metrics (P90, P95, P99).

# Run benchmarks against local database
go run ./cmd/benchmark -data-dir /tmp/bench-db -events 10000 -workers 4

# Run benchmarks against a running relay
go run ./cmd/benchmark -relay ws://localhost:3334 -events 5000

# Use different database backends
go run ./cmd/benchmark -dgraph -events 10000
go run ./cmd/benchmark -neo4j -events 10000

The cmd/benchmark/ directory also includes Docker Compose configurations for comparative benchmarks across multiple relay implementations (strfry, nostr-rs-relay, khatru, etc.).

stresstest

Load testing tool for evaluating relay performance under sustained high-traffic conditions. Generates events with random content and tags to simulate realistic workloads.

# Run stress test with 10 concurrent workers
go run ./cmd/stresstest -url ws://localhost:3334 -workers 10 -duration 60s

# Generate events with random p-tags (up to 100 per event)
go run ./cmd/stresstest -url ws://localhost:3334 -workers 5

blossomtest

Tests the Blossom blob storage protocol (BUD-01/BUD-02) implementation. Validates upload, download, and authentication flows.

# Test with generated key
go run ./cmd/blossomtest -url http://localhost:3334 -size 1024

# Test with specific nsec
go run ./cmd/blossomtest -url http://localhost:3334 -nsec nsec1...

# Test anonymous uploads (no authentication)
go run ./cmd/blossomtest -url http://localhost:3334 -no-auth

aggregator

Event aggregation utility that fetches events from multiple relays using bloom filters for deduplication. Useful for syncing events across relays with memory-efficient duplicate detection.

go run ./cmd/aggregator -relays wss://relay1.com,wss://relay2.com -output events.jsonl

convert

Key format conversion utility. Converts between hex and bech32 (npub/nsec) formats for Nostr keys.

# Convert npub to hex
go run ./cmd/convert npub1abc...

# Convert hex to npub
go run ./cmd/convert 0123456789abcdef...

# Convert secret key (nsec or hex) - outputs both nsec and derived npub
go run ./cmd/convert --secret nsec1xyz...

FIND

Free Internet Name Daemon - CLI tool for the distributed naming system. Manages name registration, transfers, and certificate issuance.

# Validate a name format
go run ./cmd/FIND verify-name example.nostr

# Generate a new key pair
go run ./cmd/FIND generate-key

# Create a registration proposal
go run ./cmd/FIND register myname.nostr

# Transfer a name to a new owner
go run ./cmd/FIND transfer myname.nostr npub1newowner...

policytest

Tests the policy system for event write control. Validates that policy rules correctly allow or reject events based on kind, pubkey, and other criteria.

go run ./cmd/policytest -url ws://localhost:3334 -type event -kind 4678
go run ./cmd/policytest -url ws://localhost:3334 -type req -kind 1
go run ./cmd/policytest -url ws://localhost:3334 -type publish-and-query -count 5

policyfiltertest

Tests policy-based filtering with authorized and unauthorized pubkeys. Validates access control rules for specific users.

go run ./cmd/policyfiltertest -url ws://localhost:3334 \
  -allowed-pubkey <hex> -allowed-sec <hex> \
  -unauthorized-pubkey <hex> -unauthorized-sec <hex>

subscription-test

Tests WebSocket subscription stability over extended periods. Monitors for dropped subscriptions and connection issues.

# Run subscription stability test for 60 seconds
go run ./cmd/subscription-test -url ws://localhost:3334 -duration 60 -kind 1

# With verbose output
go run ./cmd/subscription-test -url ws://localhost:3334 -duration 120 -v

subscription-test-simple

Simplified subscription stability test that verifies subscriptions remain active without dropping over the test duration.

go run ./cmd/subscription-test-simple -url ws://localhost:3334 -duration 120

Access Control

ORLY provides four ACL (Access Control List) modes to control who can publish events to your relay:

ModeDescriptionBest For
noneOpen relay, anyone can writePublic relays
followsWrite access based on admin follow listsPersonal/community relays
managedExplicit allow/deny lists via NIP-86 APIPrivate relays
curatingThree-tier classification with rate limitingCurated community relays
export ORLY_ACL_MODE=follows  # or: none, managed, curating

Follows ACL

The follows ACL system provides flexible relay access control based on social relationships in the Nostr network.

export ORLY_ACL_MODE=follows
export ORLY_ADMINS=npub1fjqqy4a93z5zsjwsfxqhc2764kvykfdyttvldkkkdera8dr78vhsmmleku
./orly

The system grants write access to users followed by designated admins, with read-only access for others. Follow lists update dynamically as admins modify their relationships.

Curation ACL

The curation ACL mode provides sophisticated content curation with a three-tier publisher classification system:

Key features:

export ORLY_ACL_MODE=curating
export ORLY_OWNERS=npub1your_owner_key
./orly

After starting, publish a configuration event (kind 30078) to enable the relay. The web UI at /#curation provides a complete management interface.

For detailed configuration and API documentation, see the Curation Mode Guide.

Cluster Replication

ORLY supports distributed relay clusters using active replication. When configured with peer relays, ORLY will automatically synchronize events between cluster members using efficient HTTP polling.

export ORLY_RELAY_PEERS=https://peer1.example.com,https://peer2.example.com
export ORLY_CLUSTER_ADMINS=npub1cluster_admin_key

Privacy Considerations: By default, ORLY propagates all events including privileged events (DMs, gift wraps, etc.) to cluster peers for complete synchronization. This ensures no data loss but may expose private communications to other relay operators in your cluster.

To enhance privacy, you can disable propagation of privileged events:

export ORLY_CLUSTER_PROPAGATE_PRIVILEGED_EVENTS=false

Important: When disabled, privileged events will not be replicated to peer relays. This provides better privacy but means these events will only be available on the originating relay. Users should be aware that accessing their privileged events may require connecting directly to the relay where they were originally published.

Marmot Email Bridge

ORLY includes a bidirectional Nostr DM to SMTP email bridge. Users DM the bridge's Nostr pubkey to subscribe (via Lightning payment), send outbound email, and receive inbound email as encrypted DMs.

Features

Quick Start

# 1. Build the unified binary
go build -o orly ./cmd/orly

# 2. Set environment
export ORLY_BRIDGE_ENABLED=true
export ORLY_BRIDGE_DOMAIN=yourdomain.com
export ORLY_BRIDGE_SMTP_PORT=2525
export ORLY_BRIDGE_NWC_URI="nostr+walletconnect://..."

# 3. Start (bridge runs alongside the relay)
./orly

Or run standalone against any Nostr relay:

export ORLY_BRIDGE_RELAY_URL=wss://relay.example.com
./orly bridge

Bridge Profile

The bridge publishes a kind 0 (profile metadata) event on startup so Nostr clients can discover it. Create a profile.txt in the bridge data directory (or set ORLY_BRIDGE_PROFILE):

name: Marmot Bridge
about: Nostr-Email bridge at yourdomain.com. DM 'subscribe' to get started.
picture: https://yourdomain.com/avatar.png
nip05: bridge@yourdomain.com
lud16: tips@yourdomain.com
website: https://yourdomain.com

See `profile.example.txt` for a template.

Client Setup (White Noise, etc.)

NIP-17 messaging clients like White Noise need the bridge to have a kind 10002 relay list event (NIP-65) to know where to send DMs. Without it, the client sees the bridge profile but reports the bridge "isn't on White Noise yet."

Until the bridge publishes kind 10002 automatically, publish one manually using nak:

export NOSTR_SECRET_KEY=nsec1...  # Bridge identity
nak event --kind 10002 \
  --tag r='wss://your-relay.com/' \
  --tag r='wss://relay.damus.io/;read' \
  --tag r='wss://nos.lol/;read' \
  wss://your-relay.com wss://relay.damus.io wss://nos.lol

See Bridge Deployment Guide β€” Client Setup: White Noise for full instructions.

Documentation

Docker Deployment

ORLY ships a single Docker image that serves both the relay and the email bridge. The default entrypoint runs the relay; pass bridge as the command to run the email bridge.

Build the Image

docker build -t orly .

Run as Relay (default)

docker run --rm -p 3334:3334 -v orly-data:/data orly

Run as Email Bridge

docker run --rm -p 2525:2525 --env-file .env.bridge orly bridge

Docker Compose (Bridge)

A ready-made compose file is provided for bridge deployment:

cp .env.bridge.example .env.bridge   # edit with your values
docker compose -f docker-compose.bridge.yml up --build

See `.env.bridge.example` for all available configuration variables.

Negentropy Sync (NIP-77)

ORLY supports NIP-77 negentropy-based set reconciliation for efficient relay synchronization.

Quick Start

Enable negentropy client support:

export ORLY_NEGENTROPY_ENABLED=true
./orly

Enable peer relay synchronization:

export ORLY_NEGENTROPY_ENABLED=true
export ORLY_SYNC_NEGENTROPY_PEERS=wss://relay.orly.dev,wss://other-relay.com
./orly

Split IPC Mode

For production deployments, run negentropy as a separate service:

# Build binaries
CGO_ENABLED=0 go build -o orly-sync-negentropy ./cmd/orly-sync-negentropy

# Configure launcher
export ORLY_LAUNCHER_SYNC_NEGENTROPY_ENABLED=true
export ORLY_LAUNCHER_SYNC_NEGENTROPY_BINARY=/path/to/orly-sync-negentropy
export ORLY_SYNC_NEGENTROPY_PEERS=wss://peer-relay.com

strfry Compatibility

ORLY's negentropy implementation is compatible with strfry:

# Pull events from ORLY using strfry
strfry sync wss://your-orly-relay.com --filter '{"kinds": [0, 1, 3]}' --dir down

For detailed configuration including Docker deployments, filtering options, and troubleshooting, see the Negentropy Sync Guide.

Documentation

Deployment & Operations

DocumentDescription
Bridge Deployment GuideDNS, DKIM, NWC, SMTP for Marmot email bridge
Deployment TestingDeployment verification procedures
Build PlatformsMulti-platform build guide (Linux, macOS, Windows, Android)
Purego Build SystemCGO-free build system with runtime library loading
WASM/Mobile BuildsWebAssembly and mobile build targets

Configuration & Access Control

DocumentDescription
Policy Usage GuideEvent filtering and validation rules
Policy Configuration ReferenceComplete policy JSON schema
Policy TroubleshootingDiagnosing policy issues
Curation Mode GuideThree-tier publisher classification ACL
HTTP GuardBot detection and HTTP rate limiting
Branding GuideRelay name, icon, NIP-11 customization

Architecture & Internals

DocumentDescription
IPC ArchitecturegRPC split-process design
IPC Sync ServicesSync service API reference
Negentropy Sync GuideNIP-77 set reconciliation setup
Sync Client ModeClient-mode relay synchronization
Neo4j BackendNeo4j database driver setup and tuning
NIP-77 AnalysisNIP-77 implementation details

Protocol Extensions

DocumentDescription
FIND Names SpecFree Internet Name Daemon protocol
FIND ImplementationFIND integration architecture and status
NIP-XX Graph QueriesREQ filter extension for graph traversals
NIP-XX Cluster ReplicationHTTP polling-based cluster replication
NIP-XX Responsive ImagesImage variant protocol extension
NIP CurationCuration-mode protocol spec
NIP NRCNostr Relay Connection protocol

Development & Reference

DocumentDescription
GlossaryORLY terminology and domain concepts
Relay Testing GuideProtocol compliance testing
Web UI Event TemplatesEvent kind templates for the web UI
Applesauce ReferenceApplesauce library integration
Graph Implementation PhasesGraph query feature tracker
Graph Queries RemainingOutstanding graph query work
Neo4j WoT SpecWeb-of-Trust graph schema
Neo4j Schema ChangesGuide for modifying the Neo4j schema

Developer Notes

Binary-Optimized Tag Storage

The nostr library (git.mleku.dev/mleku/nostr/encoders/tag) uses binary optimization for e and p tags to reduce memory usage and improve comparison performance.

When events are unmarshaled from JSON, 64-character hex values in e/p tags are converted to 33-byte binary format (32 bytes hash + null terminator).

Important: When working with e/p tag values in code:

// CORRECT: Use ValueHex() for hex decoding
pt, err := hex.Dec(string(pTag.ValueHex()))

// WRONG: Value() may return binary bytes, not hex
pt, err := hex.Dec(string(pTag.Value()))  // Will fail for binary-encoded tags!

Release Process

The /release command pushes to the origin remote with tags:

git push origin main --tags