IPC_SYNC_SERVICES.md raw

IPC Sync Services Architecture

ORLY supports splitting sync functionality into independent gRPC IPC services for improved modularity, resource isolation, and horizontal scaling.

Overview

The sync subsystem can run in two modes:

  1. Local mode (default): All sync functionality runs in-process with the main relay
  2. gRPC mode: Sync services run as separate processes communicating via gRPC

Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                              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      │
                          └─────────────────────┘

Sync Services

1. orly-sync-distributed (Port 50061)

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:

VariableDefaultDescription
ORLY_SYNC_DISTRIBUTED_LISTEN127.0.0.1:50061gRPC listen address
ORLY_SYNC_DISTRIBUTED_DB_TYPEgrpcDatabase backend
ORLY_SYNC_DISTRIBUTED_DB_SERVER127.0.0.1:50051Database server address
ORLY_SYNC_DISTRIBUTED_PEERSComma-separated peer URLs
ORLY_SYNC_DISTRIBUTED_INTERVAL30sSync polling interval

2. orly-sync-cluster (Port 50062)

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:

VariableDefaultDescription
ORLY_SYNC_CLUSTER_LISTEN127.0.0.1:50062gRPC listen address
ORLY_SYNC_CLUSTER_DB_TYPEgrpcDatabase backend
ORLY_SYNC_CLUSTER_DB_SERVER127.0.0.1:50051Database server address
ORLY_CLUSTER_ADMINSAuthorized cluster admin pubkeys
ORLY_CLUSTER_PROPAGATE_PRIVILEGED_EVENTStrueReplicate DMs/gift wraps

3. orly-sync-relaygroup (Port 50063)

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:

VariableDefaultDescription
ORLY_SYNC_RELAYGROUP_LISTEN127.0.0.1:50063gRPC listen address
ORLY_SYNC_RELAYGROUP_DB_TYPEgrpcDatabase backend
ORLY_SYNC_RELAYGROUP_DB_SERVER127.0.0.1:50051Database server address
ORLY_RELAY_GROUP_ADMINSAuthorized relay group config publishers

4. orly-sync-negentropy (Port 50064)

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:

VariableDefaultDescription
ORLY_SYNC_NEGENTROPY_LISTEN127.0.0.1:50064gRPC listen address
ORLY_SYNC_NEGENTROPY_DB_TYPEgrpcDatabase backend
ORLY_SYNC_NEGENTROPY_DB_SERVER127.0.0.1:50051Database server address
ORLY_SYNC_NEGENTROPY_PEERSComma-separated peer WebSocket URLs
ORLY_SYNC_NEGENTROPY_INTERVAL60sBackground sync interval
ORLY_SYNC_NEGENTROPY_FRAME_SIZE4096Negentropy frame size
ORLY_SYNC_NEGENTROPY_ID_SIZE16ID truncation size
ORLY_SYNC_NEGENTROPY_SESSION_TIMEOUT5mClient session timeout

Launcher Configuration

The orly-launcher manages all services. Enable sync services with these environment variables:

VariableDefaultDescription
ORLY_LAUNCHER_SYNC_DISTRIBUTED_ENABLEDfalseEnable distributed sync
ORLY_LAUNCHER_SYNC_DISTRIBUTED_BINARYorly-sync-distributedBinary path
ORLY_LAUNCHER_SYNC_DISTRIBUTED_LISTEN127.0.0.1:50061Listen address
ORLY_LAUNCHER_SYNC_CLUSTER_ENABLEDfalseEnable cluster sync
ORLY_LAUNCHER_SYNC_CLUSTER_BINARYorly-sync-clusterBinary path
ORLY_LAUNCHER_SYNC_CLUSTER_LISTEN127.0.0.1:50062Listen address
ORLY_LAUNCHER_SYNC_RELAYGROUP_ENABLEDfalseEnable relay group
ORLY_LAUNCHER_SYNC_RELAYGROUP_BINARYorly-sync-relaygroupBinary path
ORLY_LAUNCHER_SYNC_RELAYGROUP_LISTEN127.0.0.1:50063Listen address
ORLY_LAUNCHER_SYNC_NEGENTROPY_ENABLEDfalseEnable negentropy
ORLY_LAUNCHER_SYNC_NEGENTROPY_BINARYorly-sync-negentropyBinary path
ORLY_LAUNCHER_SYNC_NEGENTROPY_LISTEN127.0.0.1:50064Listen address
ORLY_LAUNCHER_SYNC_READY_TIMEOUT30sSync service startup timeout

Relay Configuration

When sync services are enabled, configure the relay to connect:

VariableDefaultDescription
ORLY_SYNC_TYPElocalSync backend: local or grpc
ORLY_GRPC_SYNC_DISTRIBUTEDDistributed sync server address
ORLY_GRPC_SYNC_CLUSTERCluster sync server address
ORLY_GRPC_SYNC_RELAYGROUPRelay group server address
ORLY_GRPC_SYNC_NEGENTROPYNegentropy server address
ORLY_GRPC_SYNC_TIMEOUT10sgRPC connection timeout
ORLY_NEGENTROPY_ENABLEDfalseEnable NIP-77 WebSocket support

Startup Order

The launcher starts services in dependency order:

  1. Database (orly-db) - Must be ready first
  2. ACL (orly-acl) - Depends on database
  3. Sync Services (parallel) - All depend on database only
  4. Relay (orly) - Depends on all above

Shutdown happens in reverse order.

Example: Enabling Negentropy

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 Definitions

Proto files are located in:

Generate Go code with:

buf generate

NIP-77 Client Protocol

The negentropy service implements NIP-77 for efficient client synchronization:

Client Messages

Server Responses

The relay automatically routes these messages to the negentropy service when ORLY_NEGENTROPY_ENABLED=true.