ORLY implements a modular Inter-Process Communication (IPC) architecture where core functionality runs as independent gRPC services. This design provides resource isolation, fault tolerance, and flexible deployment options.
┌─────────────────────────────────────────────────────────────────────────────────┐
│ orly-launcher │
│ (Process Supervisor & Lifecycle Manager) │
│ │
│ Responsibilities: │
│ - Start services in dependency order │
│ - Monitor process health and restart on failure │
│ - Graceful shutdown in reverse order │
│ - Pass environment configuration to child processes │
└─────────────────────────────────────────────────────────────────────────────────┘
│
┌───────────────────────────────┼───────────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────────────┐
│ orly-db │ │ orly-acl │ │ Sync Services │
│ (gRPC :50051) │ │ (gRPC :50052) │ │ (gRPC :50061-50064) │
│ │ │ │ │ │
│ Core Database: │ │ Access Control: │ │ orly-sync-distributed │
│ ├─ Event storage │ │ ├─ Permission │ │ ├─ Peer-to-peer sync │
│ ├─ Query engine │ │ │ checks │ │ └─ Serial-based polling │
│ ├─ Index mgmt │ │ ├─ Follow lists │ │ │
│ ├─ Serial counter │ │ ├─ Admin/owner │ │ orly-sync-cluster │
│ └─ Export/import │ │ │ management │ │ ├─ HA replication │
│ │ │ └─ Policy hooks │ │ └─ Membership events │
│ Backends: │ │ │ │ │
│ ├─ Badger (SSD) │ │ Modes: │ │ orly-sync-relaygroup │
│ ├─ Neo4j (graph) │ │ ├─ none (open) │ │ ├─ Kind 39105 config │
│ └─ WasmDB (wasm) │ │ ├─ follows │ │ └─ Dynamic peer lists │
│ │ │ ├─ managed │ │ │
│ │ │ └─ curating │ │ orly-sync-negentropy │
│ │ │ │ │ ├─ NIP-77 WebSocket │
│ │ │ │ │ └─ Set reconciliation │
└───────────────────┘ └───────────────────┘ └───────────────────────────┘
│ │ │
│ │ │
└───────────────────────────┼───────────────────────────────┘
│
▼
┌───────────────────────┐
│ orly │
│ (Main Relay) │
│ │
│ Client-Facing: │
│ ├─ WebSocket server │
│ ├─ NIP handling │
│ ├─ REQ/EVENT/AUTH │
│ ├─ NEG-* (NIP-77) │
│ └─ HTTP API │
│ │
│ Internal: │
│ ├─ gRPC DB client │
│ ├─ gRPC ACL client │
│ └─ gRPC sync clients │
└───────────────────────┘
│
▼
┌───────────────┐
│ Caddy │
│ (TLS Proxy) │
└───────────────┘
│
▼
Internet
| Service | Default Port | Description |
|---|---|---|
| orly-db | 50051 | Database server (Badger/Neo4j/WasmDB) |
| orly-acl | 50052 | Access control server |
| orly-sync-distributed | 50061 | Peer-to-peer sync |
| orly-sync-cluster | 50062 | Cluster replication |
| orly-sync-relaygroup | 50063 | Relay group config |
| orly-sync-negentropy | 50064 | NIP-77 negentropy |
| orly | 3334 | Main relay (WebSocket/HTTP) |
The launcher starts services in strict dependency order:
1. orly-db ──────► Wait for gRPC ready
│
2. orly-acl ──────► Wait for gRPC ready (depends on DB)
│
3. Sync Services ──────► Start in parallel, wait for all ready
├─ orly-sync-distributed (depends on DB)
├─ orly-sync-cluster (depends on DB)
├─ orly-sync-relaygroup (depends on DB)
└─ orly-sync-negentropy (depends on DB)
│
4. orly ──────► Connects to all services via gRPC
Shutdown happens in reverse order: relay → sync services → ACL → database.
The database service handles all event storage, indexing, and querying.
orly-db-badger - Badger LSM-tree database (SSD optimized)orly-db-neo4j - Neo4j graph database (social graph queries)service DatabaseService {
// Readiness
rpc Ready(Empty) returns (ReadyResponse);
// Event operations
rpc SaveEvent(SaveEventRequest) returns (SaveEventResponse);
rpc QueryEvents(QueryEventsRequest) returns (QueryEventsResponse);
rpc CountEvents(CountEventsRequest) returns (CountEventsResponse);
rpc DeleteEvent(DeleteEventRequest) returns (DeleteEventResponse);
rpc QueryAllVersions(QueryEventsRequest) returns (QueryEventsResponse);
// Special queries
rpc GetSerials(GetSerialsRequest) returns (GetSerialsResponse);
rpc GetLastSerial(Empty) returns (GetLastSerialResponse);
rpc CheckForDeleted(CheckDeletedRequest) returns (CheckDeletedResponse);
// Admin operations
rpc Export(ExportRequest, stream ExportResponse);
rpc Import(stream ImportRequest) returns (ImportResponse);
}
| Variable | Default | Description |
|---|---|---|
ORLY_DB_LISTEN | 127.0.0.1:50051 | gRPC listen address |
ORLY_DATA_DIR | ~/.local/share/ORLY | Database storage path |
ORLY_DB_LOG_LEVEL | info | Logging level |
ORLY_DB_BLOCK_CACHE_MB | 1024 | Badger block cache |
ORLY_DB_INDEX_CACHE_MB | 512 | Badger index cache |
ORLY_DB_ZSTD_LEVEL | 3 | Compression level (0-9) |
The ACL service handles access control decisions for all relay operations.
orly-acl-none - Open relay (no restrictions)orly-acl-follows - Follow-list based accessorly-acl-managed - NIP-86 managed accessorly-acl-curating - Curator-based accessservice ACLService {
// Readiness
rpc Ready(Empty) returns (ReadyResponse);
// Access checks
rpc CheckAccess(CheckAccessRequest) returns (CheckAccessResponse);
rpc GetAccessLevel(GetAccessLevelRequest) returns (GetAccessLevelResponse);
rpc CheckPolicy(CheckPolicyRequest) returns (CheckPolicyResponse);
// Admin operations
rpc Configure(ConfigureRequest) returns (ConfigureResponse);
rpc GetType(Empty) returns (TypeResponse);
}
| Variable | Default | Description |
|---|---|---|
ORLY_ACL_LISTEN | 127.0.0.1:50052 | gRPC listen address |
ORLY_ACL_MODE | follows | ACL mode |
ORLY_ACL_DB_TYPE | grpc | Database backend |
ORLY_ACL_GRPC_DB_SERVER | 127.0.0.1:50051 | Database server |
ORLY_ACL_LOG_LEVEL | info | Logging level |
The main relay handles all client-facing operations.
Local mode (default):
ORLY_DB_TYPE=badger
ORLY_ACL_TYPE=local
gRPC mode (IPC):
ORLY_DB_TYPE=grpc
ORLY_GRPC_SERVER=127.0.0.1:50051
ORLY_ACL_TYPE=grpc
ORLY_GRPC_ACL_SERVER=127.0.0.1:50052
ORLY_NEGENTROPY_ENABLED=true
ORLY_GRPC_SYNC_NEGENTROPY=127.0.0.1:50064
See IPC_SYNC_SERVICES.md for detailed sync service documentation.
| Service | Port | Purpose |
|---|---|---|
| orly-sync-distributed | 50061 | Serial-based peer sync |
| orly-sync-cluster | 50062 | HA cluster replication |
| orly-sync-relaygroup | 50063 | Kind 39105 relay groups |
| orly-sync-negentropy | 50064 | NIP-77 set reconciliation |
# Start database
ORLY_DB_LISTEN=127.0.0.1:50051 ./orly-db-badger &
# Start relay with gRPC database
ORLY_DB_TYPE=grpc \
ORLY_GRPC_SERVER=127.0.0.1:50051 \
./orly
# Use launcher for automatic management
ORLY_LAUNCHER_DB_BACKEND=badger \
ORLY_LAUNCHER_ACL_ENABLED=true \
ORLY_ACL_MODE=follows \
./orly-launcher
# Enable NIP-77 support
ORLY_LAUNCHER_DB_BACKEND=badger \
ORLY_LAUNCHER_ACL_ENABLED=true \
ORLY_LAUNCHER_SYNC_NEGENTROPY_ENABLED=true \
ORLY_NEGENTROPY_ENABLED=true \
./orly-launcher
See the example at /etc/systemd/system/orly.service on relay.orly.dev for a production configuration.
# Build all IPC binaries
make all-acl # Builds orly-db-*, orly-acl-*, orly-launcher
# Build sync services
go build -o orly-sync-distributed ./cmd/orly-sync-distributed
go build -o orly-sync-cluster ./cmd/orly-sync-cluster
go build -o orly-sync-relaygroup ./cmd/orly-sync-relaygroup
go build -o orly-sync-negentropy ./cmd/orly-sync-negentropy
# Cross-compile for ARM64
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o orly-sync-negentropy ./cmd/orly-sync-negentropy
Each gRPC service implements a Ready RPC that returns when the service is accepting requests.
All services log to stdout/stderr. Use journalctl for systemd deployments:
# Follow all ORLY logs
journalctl -u orly -f
# Filter by process
journalctl -u orly | grep "orly-db"
journalctl -u orly | grep "orly-sync-negentropy"
Enable HTTP pprof for profiling:
ORLY_PPROF_HTTP=true ./orly-launcher
# Access at http://localhost:6060/debug/pprof/
ss -tlnp | grep 5005journalctl -u orly -n 100ORLY_LAUNCHER_DB_READY_TIMEOUT (default 30s)ORLY_NEGENTROPY_ENABLED=true in relay configps aux | grep negentropyORLY_GRPC_SYNC_NEGENTROPY=127.0.0.1:50064