1// Broker wire schema: the client/server broker conversations.
2//
3// The broker is an independently versioned conversation from the session
4// protocol: client tags 101-113 and server tags 201-210 are disjoint from
5// every session tag, and each direction is its own closed oneof union.
6// Scope (broker epoch plus client connection) is carried explicitly on
7// every payload so stream IDs and events never cross a scope. Semantic
8// validation stays outside this package; conversion mirrors the port
9// types losslessly.
10syntax = "proto3";
11
12package vev.wire.v1;
13
14import "common.proto";
15import "terminal.proto";
16
17option go_package = "github.com/bnema/vev/internal/protocol/wire";
18
19// BrokerScope identifies one broker process incarnation and one client
20// connection within it (ports.BrokerEpoch / ports.BrokerConnectionID).
21// Streams and events are scoped to one scope; an epoch change forces a
22// full resynchronization.
23message BrokerScope {
24 uint64 broker_epoch = 1;
25 bytes connection_id = 2; // exactly 16 bytes
26}
27
28// BrokerStreamRef identifies one logical stream within one scope
29// (ports.BrokerStreamID). It never crosses connections or epochs.
30message BrokerStreamRef {
31 BrokerScope scope = 1;
32 uint64 stream_id = 2;
33}
34
35// BrokerWirePolicy is the exact connection policy two endpoints must share
36// before pooling one physical transport (ports.BrokerPolicy). Tokens are
37// opaque, authoritative identities: never display labels, never secrets.
38message BrokerWirePolicy {
39 uint32 protocol_version = 1;
40 uint32 catalogue_version = 2;
41 // Closed taxonomy: 0 = client-owned, 1 = daemon-owned.
42 uint32 environment_policy = 3;
43 string transport = 4;
44 string trust = 5;
45 string launch = 6;
46 string isolation = 7;
47}
48
49// BrokerErrorDetail is the typed broker failure carried to clients
50// (ports.BrokerError plus bounded display text). The underlying cause is
51// local-only diagnostic authority and never travels on this wire.
52message BrokerErrorDetail {
53 // Closed taxonomy: 1 = unavailable, 2 = incompatible, 3 = timeout,
54 // 4 = cancelled, 5 = outcome_unknown, 6 = attachment_lost,
55 // 7 = stale_epoch, 8 = conflicting_policy, 9 = explicit_exit,
56 // 10 = fatal_terminal, 11 = host_conflict, 12 = membership_immutable.
57 uint32 code = 1;
58 string text = 2;
59 // Closed admission-refusal taxonomy; 0 = none. Codes are assigned by the
60 // broker admission slice.
61 uint32 admission_code = 3;
62 // Closed cause taxonomy (domain.RemoteFailureKind): 0 = none, 1 =
63 // transport, through the last declared failure kind.
64 uint32 failure_kind = 4;
65}
66
67// BrokerTimestamp is one instant split into seconds and nanoseconds so
68// time.Time survives the wire exactly.
69message BrokerTimestamp {
70 int64 seconds = 1;
71 int32 nanos = 2;
72}
73
74// BrokerClientEnvelope is the closed client-to-server broker union. Exactly
75// one variant must be set per envelope; empty, duplicate, and
76// wrong-direction payloads are rejected before generated unmarshal.
77message BrokerClientEnvelope {
78 oneof payload {
79 Register register = 101;
80 Subscribe subscribe = 102;
81 Resync resync = 103;
82 Unsubscribe unsubscribe = 104;
83 AddHost add_host = 105;
84 RemoveHost remove_host = 106;
85 Reconcile reconcile = 107;
86 OpenStream open_stream = 108;
87 ClientStreamData client_stream_data = 109;
88 CloseStream close_stream = 110;
89 UpdateHostPolicy update_host_policy = 111;
90 StartPreview start_preview = 112;
91 CancelPreview cancel_preview = 113;
92 StreamWindowUpdate stream_window_update = 114;
93 }
94}
95
96// BrokerServerEnvelope is the closed server-to-client broker union. Exactly
97// one variant must be set per envelope.
98message BrokerServerEnvelope {
99 oneof payload {
100 Registered registered = 201;
101 SnapshotPart snapshot_part = 202;
102 OperationResult operation_result = 203;
103 StreamOpened stream_opened = 204;
104 ServerStreamData server_stream_data = 205;
105 StreamClosed stream_closed = 206;
106 Progress progress = 207;
107 BrokerErrorMessage broker_error_message = 208;
108 Shutdown shutdown = 209;
109 PreviewPublication preview_publication = 210;
110 StreamWindowUpdate stream_window_update = 211;
111 }
112}
113
114// Register opens one client connection to the broker.
115message Register {
116 string build = 1;
117}
118
119// Subscribe asks for snapshot and lifecycle publications for one scope.
120message Subscribe {
121 BrokerScope scope = 1;
122 uint64 generation = 2;
123 // passive marks a read-only snapshot reader (vev ls, vev host list). It
124 // receives publications but never counts as observation demand, so reading
125 // state never triggers a probe.
126 bool passive = 3;
127}
128
129// Resync asks for a full snapshot publication after an epoch change or a
130// gap in the client's generation series.
131message Resync {
132 BrokerScope scope = 1;
133 uint64 generation = 2;
134}
135
136// Unsubscribe stops publications for one scope.
137message Unsubscribe {
138 BrokerScope scope = 1;
139 uint64 generation = 2;
140}
141
142// AddHost adds one configured host registration. operation_id is the
143// idempotency identity of this mutating operation. policy is mandatory:
144// the request is refused unless it carries an exact valid policy, so a
145// membership addition can never be authorized by an absent policy.
146message AddHost {
147 BrokerScope scope = 1;
148 bytes operation_id = 2; // exactly 16 bytes
149 string endpoint = 3;
150 BrokerWirePolicy policy = 4;
151}
152
153// RemoveHost removes exactly the registration carried by registration
154// (endpoint, incarnation, and generation). A request whose authority does
155// not exactly match the current registration is refused rather than
156// applied to an endpoint name.
157message RemoveHost {
158 BrokerScope scope = 1;
159 bytes operation_id = 2; // exactly 16 bytes
160 RemoteRegistration registration = 3;
161}
162
163// UpdateHostPolicy replaces the policy of exactly the registration carried
164// by registration and answers with the advanced registration authority.
165// policy is mandatory: an absent policy is refused like an invalid one.
166message UpdateHostPolicy {
167 BrokerScope scope = 1;
168 bytes operation_id = 2; // exactly 16 bytes
169 RemoteRegistration registration = 3;
170 BrokerWirePolicy policy = 4;
171}
172
173// Reconcile hands the broker one exact registration to probe.
174message Reconcile {
175 BrokerScope scope = 1;
176 RemoteRegistration registration = 2;
177}
178
179// OpenStream asks the broker to open one independently cancellable logical
180// stream to the owning daemon. env is the per-request session environment
181// and is never inherited from the broker process environment.
182//
183// admission is the closed attachment-admission taxonomy: an attachment
184// stream names exactly how the daemon must admit it, while control and
185// observation streams carry none (0). name is validated session-name
186// authority for create-named and attach-named and is empty for every other
187// variant.
188// start_mode is the explicit daemon-start authorization carried to the
189// transport: observation and daemon-stop must be existing_only, while
190// attach/creation and the explicit list/mutate controls may be
191// start_if_needed. Zero is invalid and is refused by semantic validation.
192message OpenStream {
193 BrokerStreamRef ref = 1;
194 // Closed taxonomy: 1 = attachment, 2 = control, 3 = observation.
195 uint32 purpose = 2;
196 bool local = 3;
197 string endpoint = 4;
198 RemoteRegistration registration = 5;
199 // Present only for exact attach/resume; the zero-value message is never
200 // sent for a creation admission.
201 ExactTarget target = 6;
202 repeated string env = 7;
203 BrokerWirePolicy policy = 8;
204 // Closed admission taxonomy: 0 = none (control/observation), 1 = exact
205 // attach/resume, 2 = create named, 3 = create ephemeral, 4 = attach named
206 // (the daemon resolves the name and refuses an unknown one).
207 uint32 admission = 9;
208 // Validated session name for create-named and attach-named; empty otherwise.
209 string name = 10;
210 // Closed daemon-start taxonomy (ports.BrokerDaemonStartMode): 1 =
211 // existing_only, 2 = start_if_needed. Always present; zero is refused.
212 uint32 start_mode = 11;
213}
214
215// ClientStreamData carries one opaque client-to-daemon stream frame.
216message ClientStreamData {
217 BrokerStreamRef ref = 1;
218 bytes data = 2;
219}
220
221// CloseStream closes one logical stream.
222message CloseStream {
223 BrokerStreamRef ref = 1;
224}
225
226// StreamWindowUpdate grants the peer credit to send more stream data on one
227// logical stream: the receiver consumed that much of its stream window. Each
228// direction of a stream has its own window, so a client grants credit for
229// ServerStreamData and the server grants credit for ClientStreamData.
230message StreamWindowUpdate {
231 BrokerStreamRef ref = 1;
232 uint64 credit = 2;
233}
234
235// Registered confirms one accepted client connection and its assigned scope.
236message Registered {
237 BrokerScope scope = 1;
238 string build = 2;
239 bool retiring = 3;
240}
241
242// SnapshotPart is one fragment of a snapshot publication. Parts of one
243// generation and revision arrive in index order and carry exactly one
244// nested payload.
245message SnapshotPart {
246 BrokerScope scope = 1;
247 uint64 generation = 2;
248 uint64 revision = 3;
249 uint32 index = 4;
250 oneof part {
251 SnapshotBegin begin = 10;
252 SnapshotDaemon daemon = 15;
253 SnapshotSession session = 12;
254 SnapshotTombstone tombstone = 13;
255 SnapshotEnd end = 14;
256 }
257}
258
259// SnapshotBegin opens a snapshot publication with its element counts.
260// host_count counts every daemon part in the transfer (the local daemon plus
261// every remote host), and local_present reports whether daemon index 0 is the
262// local daemon. A publication carries at most one local daemon, always first.
263message SnapshotBegin {
264 uint32 host_count = 1;
265 uint32 session_count = 2;
266 uint32 tombstone_count = 3;
267 bool local_present = 4;
268}
269
270// SnapshotDaemon is one daemon projection (ports.BrokerDaemonObservation),
271// local or remote. Configured authority (endpoint, registration, policy,
272// display origin, rank) stays separate from observed state (identity,
273// incarnation, version, capabilities, availability, failure, freshness),
274// and observation never invents unknown values. registration is absent for
275// the local daemon; policy is always present. Sessions travel as separate
276// SnapshotSession parts.
277message SnapshotDaemon {
278 uint32 host_index = 1;
279 bool local = 2;
280 string endpoint = 3;
281 string display_origin = 4;
282 uint32 rank = 5;
283 RemoteRegistration registration = 6;
284 BrokerWirePolicy policy = 7;
285 // Authenticated daemon identity; empty until observed.
286 string daemon_identity = 8;
287 // Daemon process incarnation; exactly 16 bytes when an identity is
288 // present and absent otherwise.
289 bytes daemon_incarnation = 9;
290 // Observed daemon protocol version; 0 = unobserved.
291 uint32 protocol_version = 10;
292 // Observed capability bitmask (mirrors protocol.Capability* bits).
293 uint32 capabilities = 11;
294 // Closed availability taxonomy (domain.RemoteAvailability); 0 is
295 // invalid, an unobserved daemon carries RemoteAvailabilityUnknown.
296 uint32 availability = 12;
297 bool checking = 13;
298 BrokerTimestamp last_attempt = 14;
299 BrokerTimestamp last_success = 15;
300 BrokerTimestamp next_due = 16;
301 uint64 consecutive_failures = 17;
302 uint64 failure_episode = 18;
303 // Closed cause taxonomy (domain.RemoteFailureKind); 0 = none.
304 uint32 failure_kind = 19;
305 bool inventory_known = 20;
306 uint32 session_count = 21;
307}
308
309// SnapshotSession is one catalogue session bound to its daemon by index.
310// local marks a session of the local daemon; its host_index is then exactly
311// 0 and follows the local daemon part rather than a remote host index.
312message SnapshotSession {
313 uint32 host_index = 1;
314 uint32 session_index = 2;
315 BrokerCatalogSession session = 3;
316 bool local = 4;
317}
318
319// SnapshotTombstone fences one retired host registration so a stale probe
320// cannot revive authority for that exact incarnation.
321message SnapshotTombstone {
322 uint32 tombstone_index = 1;
323 RemoteRegistration registration = 2;
324 uint64 retired_revision = 3;
325}
326
327// SnapshotEnd closes a snapshot publication.
328message SnapshotEnd {}
329
330// OperationResult is exactly one completion for one admitted mutating
331// operation. removed and registration are presence-carrying authority:
332// an authoritative success carries the resulting registration, a removal
333// success carries none, and a failed or outcome-unknown result carries
334// neither removal nor registration, only its typed error.
335message OperationResult {
336 BrokerScope scope = 1;
337 bytes operation_id = 2; // exactly 16 bytes
338 // Closed taxonomy: 1 = ok, 2 = failed, 3 = outcome_unknown.
339 uint32 outcome = 3;
340 bool removed = 4;
341 BrokerErrorDetail error = 5;
342 RemoteRegistration registration = 6;
343}
344
345// StreamOpened confirms one logical stream is established.
346message StreamOpened {
347 BrokerStreamRef ref = 1;
348}
349
350// ServerStreamData carries one opaque daemon-to-client stream frame.
351message ServerStreamData {
352 BrokerStreamRef ref = 1;
353 bytes data = 2;
354}
355
356// StreamClosed ends one logical stream. An unset error is an orderly close;
357// a set error reports the typed failure that ended it.
358message StreamClosed {
359 BrokerStreamRef ref = 1;
360 BrokerErrorDetail error = 2;
361}
362
363// Progress reports one phase of a long-running broker operation.
364message Progress {
365 BrokerStreamRef ref = 1;
366 uint32 phase = 2;
367 string text = 3;
368}
369
370// BrokerErrorMessage reports a broker-wide typed failure for one scope.
371message BrokerErrorMessage {
372 BrokerScope scope = 1;
373 BrokerErrorDetail error = 2;
374}
375
376// Shutdown reports the broker process is stopping.
377message Shutdown {
378 BrokerScope scope = 1;
379 // Closed reason taxonomy; 0 is the zero value.
380 uint32 reason = 2;
381 string text = 3;
382}
383
384// PreviewRoute names the daemon a preview observes. The broker allocates the
385// observation stream itself; endpoint and registration are present only for a
386// remote route, and policy is always present.
387message PreviewRoute {
388 bool local = 1;
389 string endpoint = 2;
390 RemoteRegistration registration = 3;
391 BrokerWirePolicy policy = 4;
392}
393
394// StartPreview starts or replaces one connection-scoped live preview.
395// scope carries the broker epoch plus client connection; generation is the
396// connection-scoped preview authority. route names the observed daemon and
397// preview is the bounded viewport request sent to it. Repeated authority is
398// scope/generation plus the preview target identity response.
399message StartPreview {
400 // 3 was the client-allocated OpenStream route.
401 reserved 3;
402
403 BrokerScope scope = 1;
404 uint64 generation = 2;
405 RemotePreviewRequest preview = 4;
406 PreviewRoute route = 5;
407}
408
409// CancelPreview cancels one connection-scoped live preview.
410message CancelPreview {
411 BrokerScope scope = 1;
412 uint64 generation = 2;
413}
414
415// PreviewPublication is the newest result for one preview subscription.
416// Exactly one of preview/error is set via the result oneof.
417message PreviewPublication {
418 BrokerScope scope = 1;
419 uint64 generation = 2;
420 oneof result {
421 RemotePreview preview = 3;
422 BrokerErrorDetail error = 4;
423 }
424}
425
426// BrokerCatalogSession mirrors catalogue.RemoteCatalogSession exactly: the
427// lifecycle identity is a fixed 16-byte value, the state is a closed
428// taxonomy, and the tab list keeps its presence separately from its
429// length.
430message BrokerCatalogSession {
431 bytes lifecycle_id = 1; // exactly 16 bytes
432 string name = 2;
433 // Closed taxonomy: 1 = up, 2 = down, 3 = broken.
434 uint32 state = 3;
435 bool ephemeral = 4;
436 repeated BrokerCatalogTab tabs = 5;
437 bool attached = 6;
438 uint64 last_used_seq = 7;
439 string active_tab_id = 8;
440 string reason = 9;
441}
442
443// BrokerCatalogTab mirrors catalogue.RemoteCatalogTab exactly.
444message BrokerCatalogTab {
445 string id = 1;
446 uint32 index = 2;
447 string name = 3;
448 string detail = 4;
449 bool attention = 5;
450}