service.proto raw
1 syntax = "proto3";
2 package orlysync.negentropy.v1;
3 option go_package = "next.orly.dev/pkg/proto/orlysync/negentropy/v1;negentropyv1";
4
5 import "orlysync/common/v1/types.proto";
6
7 // NegentropyService provides NIP-77 negentropy-based set reconciliation
8 // for both relay-to-relay sync and client-facing WebSocket operations
9 service NegentropyService {
10 // === Lifecycle Methods ===
11
12 // Ready returns whether the service is ready to serve requests
13 rpc Ready(orlysync.common.v1.Empty) returns (orlysync.common.v1.ReadyResponse);
14
15 // Start starts the background relay-to-relay sync
16 rpc Start(orlysync.common.v1.Empty) returns (orlysync.common.v1.Empty);
17
18 // Stop stops the background sync
19 rpc Stop(orlysync.common.v1.Empty) returns (orlysync.common.v1.Empty);
20
21 // === Client-Facing NIP-77 (WebSocket Message Handling) ===
22 // These handle NEG-OPEN, NEG-MSG, NEG-CLOSE from WebSocket clients
23
24 // HandleNegOpen processes a NEG-OPEN message from a client
25 rpc HandleNegOpen(NegOpenRequest) returns (NegOpenResponse);
26
27 // HandleNegMsg processes a NEG-MSG message from a client
28 rpc HandleNegMsg(NegMsgRequest) returns (NegMsgResponse);
29
30 // HandleNegClose processes a NEG-CLOSE message from a client
31 rpc HandleNegClose(NegCloseRequest) returns (orlysync.common.v1.Empty);
32
33 // === Relay-to-Relay Sync ===
34
35 // SyncWithPeer initiates negentropy sync with a specific peer relay
36 rpc SyncWithPeer(SyncPeerRequest) returns (stream SyncProgress);
37
38 // GetSyncStatus returns the current sync status
39 rpc GetSyncStatus(orlysync.common.v1.Empty) returns (SyncStatusResponse);
40
41 // === Peer Management ===
42
43 // GetPeers returns the list of negentropy sync peers
44 rpc GetPeers(orlysync.common.v1.Empty) returns (PeersResponse);
45
46 // AddPeer adds a peer for negentropy sync
47 rpc AddPeer(AddPeerRequest) returns (orlysync.common.v1.Empty);
48
49 // RemovePeer removes a peer from negentropy sync
50 rpc RemovePeer(RemovePeerRequest) returns (orlysync.common.v1.Empty);
51
52 // === Sync Control ===
53
54 // TriggerSync manually triggers sync with a specific peer or all peers
55 rpc TriggerSync(TriggerSyncRequest) returns (orlysync.common.v1.Empty);
56
57 // GetPeerSyncState returns sync state for a specific peer
58 rpc GetPeerSyncState(PeerSyncStateRequest) returns (PeerSyncStateResponse);
59
60 // === Session Management ===
61
62 // ListSessions returns active client negentropy sessions
63 rpc ListSessions(orlysync.common.v1.Empty) returns (ListSessionsResponse);
64
65 // CloseSession forcefully closes a client session
66 rpc CloseSession(CloseSessionRequest) returns (orlysync.common.v1.Empty);
67 }
68
69 // === Client-Facing NIP-77 Messages ===
70
71 // NegOpenRequest processes a NEG-OPEN from client
72 // NEG-OPEN format: ["NEG-OPEN", subscription_id, filter, initial_message?]
73 message NegOpenRequest {
74 string subscription_id = 1; // Client's subscription ID
75 orlysync.common.v1.Filter filter = 2; // Nostr filter for reconciliation
76 bytes initial_message = 3; // Optional initial negentropy message
77 string connection_id = 4; // Connection ID for session tracking
78 }
79
80 // NegOpenResponse returns the initial negentropy response
81 message NegOpenResponse {
82 bytes message = 1; // Negentropy protocol message to send back
83 repeated bytes have_ids = 2; // Event IDs we have that client needs (if initial reconciliation completes)
84 repeated bytes need_ids = 3; // Event IDs we need from client (if initial reconciliation completes)
85 bool complete = 4; // True if reconciliation completed in first round
86 string error = 5; // Error message if failed
87 }
88
89 // NegMsgRequest processes a NEG-MSG from client
90 // NEG-MSG format: ["NEG-MSG", subscription_id, message]
91 message NegMsgRequest {
92 string subscription_id = 1;
93 bytes message = 2; // Negentropy protocol message
94 string connection_id = 3;
95 }
96
97 // NegMsgResponse returns reconciliation results
98 message NegMsgResponse {
99 bytes message = 1; // Negentropy protocol message to send back
100 repeated bytes have_ids = 2; // Event IDs we have that client needs
101 repeated bytes need_ids = 3; // Event IDs we need from client
102 bool complete = 4; // True if reconciliation is complete
103 string error = 5; // Error message if failed
104 }
105
106 // NegCloseRequest processes a NEG-CLOSE from client
107 // NEG-CLOSE format: ["NEG-CLOSE", subscription_id]
108 message NegCloseRequest {
109 string subscription_id = 1;
110 string connection_id = 2;
111 }
112
113 // === Relay-to-Relay Sync Messages ===
114
115 // SyncPeerRequest initiates sync with a peer
116 message SyncPeerRequest {
117 string peer_url = 1; // WebSocket URL of peer relay
118 orlysync.common.v1.Filter filter = 2; // Optional filter to limit sync scope
119 int64 since = 3; // Optional: only sync events since timestamp
120 }
121
122 // SyncProgress streams sync progress updates
123 message SyncProgress {
124 string peer_url = 1;
125 int32 round = 2; // Reconciliation round number
126 int64 have_count = 3; // Events we have that peer needs
127 int64 need_count = 4; // Events we need from peer
128 int64 fetched_count = 5; // Events fetched so far
129 int64 sent_count = 6; // Events sent so far
130 bool complete = 7; // True when sync is complete
131 string error = 8; // Error message if failed
132 }
133
134 // SyncStatusResponse contains overall sync status
135 message SyncStatusResponse {
136 bool active = 1; // Whether background sync is running
137 int64 last_sync = 2; // Timestamp of last sync
138 int32 peer_count = 3;
139 repeated PeerSyncState peer_states = 4;
140 }
141
142 // === Peer Management Messages ===
143
144 // PeersResponse contains the list of peers
145 message PeersResponse {
146 repeated string peers = 1; // List of peer WebSocket URLs
147 }
148
149 // AddPeerRequest adds a peer
150 message AddPeerRequest {
151 string peer_url = 1;
152 }
153
154 // RemovePeerRequest removes a peer
155 message RemovePeerRequest {
156 string peer_url = 1;
157 }
158
159 // TriggerSyncRequest triggers manual sync
160 message TriggerSyncRequest {
161 string peer_url = 1; // Optional: specific peer (empty = all)
162 orlysync.common.v1.Filter filter = 2; // Optional: filter for sync scope
163 }
164
165 // PeerSyncStateRequest requests state for a peer
166 message PeerSyncStateRequest {
167 string peer_url = 1;
168 }
169
170 // PeerSyncStateResponse contains peer sync state
171 message PeerSyncStateResponse {
172 PeerSyncState state = 1;
173 bool found = 2;
174 }
175
176 // PeerSyncState represents sync state for a peer
177 message PeerSyncState {
178 string peer_url = 1;
179 int64 last_sync = 2; // Timestamp of last successful sync
180 int64 events_synced = 3; // Total events synced from this peer
181 string status = 4; // "idle", "syncing", "error"
182 string last_error = 5; // Last error message
183 int32 consecutive_failures = 6;
184 }
185
186 // === Session Management Messages ===
187
188 // ClientSession represents an active client negentropy session
189 message ClientSession {
190 string subscription_id = 1;
191 string connection_id = 2;
192 int64 created_at = 3;
193 int64 last_activity = 4;
194 int32 round_count = 5; // Number of reconciliation rounds
195 }
196
197 // ListSessionsResponse contains active sessions
198 message ListSessionsResponse {
199 repeated ClientSession sessions = 1;
200 }
201
202 // CloseSessionRequest closes a session
203 message CloseSessionRequest {
204 string subscription_id = 1;
205 string connection_id = 2; // Optional: if empty, close all with this sub_id
206 }
207