ORLY supports splitting sync functionality into independent gRPC IPC services for improved modularity, resource isolation, and horizontal scaling.
The sync subsystem can run in two modes:
┌─────────────────────────────────────────────────────────────────────────────┐
│ orly-launcher │
│ (Process supervisor - manages lifecycle of all services) │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ orly-db │ │ orly-acl │ │ Sync Services │
│ (gRPC :50051) │ │ (gRPC :50052) │ │ (gRPC :50061-64) │
│ │ │ │ │ │
│ - Event storage │ │ - Access control │ │ - Distributed sync │
│ - Query execution │ │ - Follow lists │ │ - Cluster sync │
│ - Index management │ │ - Permissions │ │ - Relay groups │
└─────────────────────┘ └─────────────────────┘ │ - Negentropy/NIP-77 │
│ │ └─────────────────────┘
│ │ │
└──────────────────────────┼──────────────────────────┘
│
▼
┌─────────────────────┐
│ orly │
│ (Main relay) │
│ │
│ - WebSocket server │
│ - HTTP endpoints │
│ - NIP handling │
└─────────────────────┘
Serial-based peer-to-peer synchronization between relay instances.
Purpose: Keeps multiple relay instances in sync by exchanging events based on serial numbers (Lamport clocks).
Key Features:
gRPC API:
service DistributedSyncService {
rpc Ready(Empty) returns (ReadyResponse);
rpc GetInfo(Empty) returns (SyncInfo);
rpc GetCurrentSerial(CurrentRequest) returns (CurrentResponse);
rpc GetEventIDs(EventIDsRequest) returns (EventIDsResponse);
rpc HandleCurrentRequest(HTTPRequest) returns (HTTPResponse);
rpc HandleEventIDsRequest(HTTPRequest) returns (HTTPResponse);
rpc GetPeers(Empty) returns (PeersResponse);
rpc UpdatePeers(UpdatePeersRequest) returns (Empty);
rpc GetSyncStatus(Empty) returns (SyncStatusResponse);
rpc GetPeerPubkey(PeerPubkeyRequest) returns (PeerPubkeyResponse);
rpc IsAuthorizedPeer(AuthorizedPeerRequest) returns (AuthorizedPeerResponse);
rpc NotifyNewEvent(NewEventNotification) returns (Empty);
}
Environment Variables:
| Variable | Default | Description |
|---|---|---|
ORLY_SYNC_DISTRIBUTED_LISTEN | 127.0.0.1:50061 | gRPC listen address |
ORLY_SYNC_DISTRIBUTED_DB_TYPE | grpc | Database backend |
ORLY_SYNC_DISTRIBUTED_DB_SERVER | 127.0.0.1:50051 | Database server address |
ORLY_SYNC_DISTRIBUTED_PEERS | Comma-separated peer URLs | |
ORLY_SYNC_DISTRIBUTED_INTERVAL | 30s | Sync polling interval |
Cluster replication with persistent state for high-availability deployments.
Purpose: Enables multiple relay instances to form a cluster with consistent event replication.
Key Features:
gRPC API:
service ClusterSyncService {
rpc Ready(Empty) returns (ReadyResponse);
rpc Start(Empty) returns (Empty);
rpc Stop(Empty) returns (Empty);
rpc HandleLatestSerial(HTTPRequest) returns (HTTPResponse);
rpc HandleEventsRange(HTTPRequest) returns (HTTPResponse);
rpc GetMembers(Empty) returns (MembersResponse);
rpc UpdateMembership(UpdateMembershipRequest) returns (Empty);
rpc HandleMembershipEvent(MembershipEventRequest) returns (Empty);
rpc GetClusterStatus(Empty) returns (ClusterStatusResponse);
rpc GetMemberStatus(MemberStatusRequest) returns (MemberStatusResponse);
rpc GetLatestSerial(Empty) returns (LatestSerialResponse);
rpc GetEventsInRange(EventsRangeRequest) returns (EventsRangeResponse);
}
Environment Variables:
| Variable | Default | Description |
|---|---|---|
ORLY_SYNC_CLUSTER_LISTEN | 127.0.0.1:50062 | gRPC listen address |
ORLY_SYNC_CLUSTER_DB_TYPE | grpc | Database backend |
ORLY_SYNC_CLUSTER_DB_SERVER | 127.0.0.1:50051 | Database server address |
ORLY_CLUSTER_ADMINS | Authorized cluster admin pubkeys | |
ORLY_CLUSTER_PROPAGATE_PRIVILEGED_EVENTS | true | Replicate DMs/gift wraps |
Relay group configuration discovery using Kind 39105 events.
Purpose: Manages relay group configurations that define which relays should sync together.
Key Features:
gRPC API:
service RelayGroupService {
rpc Ready(Empty) returns (ReadyResponse);
rpc FindAuthoritativeConfig(Empty) returns (RelayGroupConfigResponse);
rpc GetRelays(Empty) returns (RelaysResponse);
rpc IsAuthorizedPublisher(AuthorizedPublisherRequest) returns (AuthorizedPublisherResponse);
rpc GetAuthorizedPubkeys(Empty) returns (AuthorizedPubkeysResponse);
rpc ValidateRelayGroupEvent(ValidateEventRequest) returns (ValidateEventResponse);
rpc HandleRelayGroupEvent(HandleEventRequest) returns (Empty);
}
Environment Variables:
| Variable | Default | Description |
|---|---|---|
ORLY_SYNC_RELAYGROUP_LISTEN | 127.0.0.1:50063 | gRPC listen address |
ORLY_SYNC_RELAYGROUP_DB_TYPE | grpc | Database backend |
ORLY_SYNC_RELAYGROUP_DB_SERVER | 127.0.0.1:50051 | Database server address |
ORLY_RELAY_GROUP_ADMINS | Authorized relay group config publishers |
NIP-77 negentropy-based efficient set reconciliation.
Purpose: Provides efficient set reconciliation for both relay-to-relay sync and client-facing NIP-77 WebSocket protocol.
Key Features:
gRPC API:
service NegentropyService {
rpc Ready(Empty) returns (ReadyResponse);
rpc Start(Empty) returns (Empty);
rpc Stop(Empty) returns (Empty);
// Client-facing NIP-77
rpc HandleNegOpen(NegOpenRequest) returns (NegOpenResponse);
rpc HandleNegMsg(NegMsgRequest) returns (NegMsgResponse);
rpc HandleNegClose(NegCloseRequest) returns (Empty);
// Relay-to-relay sync
rpc SyncWithPeer(SyncPeerRequest) returns (stream SyncProgress);
rpc GetSyncStatus(Empty) returns (SyncStatusResponse);
rpc GetPeers(Empty) returns (PeersResponse);
rpc AddPeer(AddPeerRequest) returns (Empty);
rpc RemovePeer(RemovePeerRequest) returns (Empty);
rpc TriggerSync(TriggerSyncRequest) returns (Empty);
rpc GetPeerSyncState(PeerSyncStateRequest) returns (PeerSyncStateResponse);
// Session management
rpc ListSessions(Empty) returns (ListSessionsResponse);
rpc CloseSession(CloseSessionRequest) returns (Empty);
}
Environment Variables:
| Variable | Default | Description |
|---|---|---|
ORLY_SYNC_NEGENTROPY_LISTEN | 127.0.0.1:50064 | gRPC listen address |
ORLY_SYNC_NEGENTROPY_DB_TYPE | grpc | Database backend |
ORLY_SYNC_NEGENTROPY_DB_SERVER | 127.0.0.1:50051 | Database server address |
ORLY_SYNC_NEGENTROPY_PEERS | Comma-separated peer WebSocket URLs | |
ORLY_SYNC_NEGENTROPY_INTERVAL | 60s | Background sync interval |
ORLY_SYNC_NEGENTROPY_FRAME_SIZE | 4096 | Negentropy frame size |
ORLY_SYNC_NEGENTROPY_ID_SIZE | 16 | ID truncation size |
ORLY_SYNC_NEGENTROPY_SESSION_TIMEOUT | 5m | Client session timeout |
The orly-launcher manages all services. Enable sync services with these environment variables:
| Variable | Default | Description |
|---|---|---|
ORLY_LAUNCHER_SYNC_DISTRIBUTED_ENABLED | false | Enable distributed sync |
ORLY_LAUNCHER_SYNC_DISTRIBUTED_BINARY | orly-sync-distributed | Binary path |
ORLY_LAUNCHER_SYNC_DISTRIBUTED_LISTEN | 127.0.0.1:50061 | Listen address |
ORLY_LAUNCHER_SYNC_CLUSTER_ENABLED | false | Enable cluster sync |
ORLY_LAUNCHER_SYNC_CLUSTER_BINARY | orly-sync-cluster | Binary path |
ORLY_LAUNCHER_SYNC_CLUSTER_LISTEN | 127.0.0.1:50062 | Listen address |
ORLY_LAUNCHER_SYNC_RELAYGROUP_ENABLED | false | Enable relay group |
ORLY_LAUNCHER_SYNC_RELAYGROUP_BINARY | orly-sync-relaygroup | Binary path |
ORLY_LAUNCHER_SYNC_RELAYGROUP_LISTEN | 127.0.0.1:50063 | Listen address |
ORLY_LAUNCHER_SYNC_NEGENTROPY_ENABLED | false | Enable negentropy |
ORLY_LAUNCHER_SYNC_NEGENTROPY_BINARY | orly-sync-negentropy | Binary path |
ORLY_LAUNCHER_SYNC_NEGENTROPY_LISTEN | 127.0.0.1:50064 | Listen address |
ORLY_LAUNCHER_SYNC_READY_TIMEOUT | 30s | Sync service startup timeout |
When sync services are enabled, configure the relay to connect:
| Variable | Default | Description |
|---|---|---|
ORLY_SYNC_TYPE | local | Sync backend: local or grpc |
ORLY_GRPC_SYNC_DISTRIBUTED | Distributed sync server address | |
ORLY_GRPC_SYNC_CLUSTER | Cluster sync server address | |
ORLY_GRPC_SYNC_RELAYGROUP | Relay group server address | |
ORLY_GRPC_SYNC_NEGENTROPY | Negentropy server address | |
ORLY_GRPC_SYNC_TIMEOUT | 10s | gRPC connection timeout |
ORLY_NEGENTROPY_ENABLED | false | Enable NIP-77 WebSocket support |
The launcher starts services in dependency order:
Shutdown happens in reverse order.
To enable NIP-77 negentropy support on an existing deployment:
# In systemd service or environment
Environment=ORLY_LAUNCHER_SYNC_NEGENTROPY_ENABLED=true
Environment=ORLY_LAUNCHER_SYNC_NEGENTROPY_BINARY=/path/to/orly-sync-negentropy
Environment=ORLY_NEGENTROPY_ENABLED=true
Build the binary:
CGO_ENABLED=0 go build -o orly-sync-negentropy ./cmd/orly-sync-negentropy
Proto files are located in:
proto/orlysync/common/v1/types.proto - Shared typesproto/orlysync/distributed/v1/service.proto - Distributed syncproto/orlysync/cluster/v1/service.proto - Cluster syncproto/orlysync/relaygroup/v1/service.proto - Relay groupproto/orlysync/negentropy/v1/service.proto - NegentropyGenerate Go code with:
buf generate
The negentropy service implements NIP-77 for efficient client synchronization:
["NEG-OPEN", subscription_id, filter, initial_message?] - Start reconciliation["NEG-MSG", subscription_id, message] - Continue reconciliation["NEG-CLOSE", subscription_id] - End session["NEG-MSG", subscription_id, message] - Reconciliation response["NEG-ERR", subscription_id, reason] - Error responseThe relay automatically routes these messages to the negentropy service when ORLY_NEGENTROPY_ENABLED=true.