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 }
93}
94
95// BrokerServerEnvelope is the closed server-to-client broker union. Exactly
96// one variant must be set per envelope.
97message BrokerServerEnvelope {
98 oneof payload {
99 Registered registered = 201;
100 SnapshotPart snapshot_part = 202;
101 OperationResult operation_result = 203;
102 StreamOpened stream_opened = 204;
103 ServerStreamData server_stream_data = 205;
104 StreamClosed stream_closed = 206;
105 Progress progress = 207;
106 BrokerErrorMessage broker_error_message = 208;
107 Shutdown shutdown = 209;
108 PreviewPublication preview_publication = 210;
109 }
110}
111
112// Register opens one client connection to the broker.
113message Register {
114 string build = 1;
115}
116
117// Subscribe asks for snapshot and lifecycle publications for one scope.
118message Subscribe {
119 BrokerScope scope = 1;
120 uint64 generation = 2;
121 // passive marks a read-only snapshot reader (vev ls, vev host list). It
122 // receives publications but never counts as observation demand, so reading
123 // state never triggers a probe.
124 bool passive = 3;
125}
126
127// Resync asks for a full snapshot publication after an epoch change or a
128// gap in the client's generation series.
129message Resync {
130 BrokerScope scope = 1;
131 uint64 generation = 2;
132}
133
134// Unsubscribe stops publications for one scope.
135message Unsubscribe {
136 BrokerScope scope = 1;
137 uint64 generation = 2;
138}
139
140// AddHost adds one configured host registration. operation_id is the
141// idempotency identity of this mutating operation. policy is mandatory:
142// the request is refused unless it carries an exact valid policy, so a
143// membership addition can never be authorized by an absent policy.
144message AddHost {
145 BrokerScope scope = 1;
146 bytes operation_id = 2; // exactly 16 bytes
147 string endpoint = 3;
148 BrokerWirePolicy policy = 4;
149}
150
151// RemoveHost removes exactly the registration carried by registration
152// (endpoint, incarnation, and generation). A request whose authority does
153// not exactly match the current registration is refused rather than
154// applied to an endpoint name.
155message RemoveHost {
156 BrokerScope scope = 1;
157 bytes operation_id = 2; // exactly 16 bytes
158 RemoteRegistration registration = 3;
159}
160
161// UpdateHostPolicy replaces the policy of exactly the registration carried
162// by registration and answers with the advanced registration authority.
163// policy is mandatory: an absent policy is refused like an invalid one.
164message UpdateHostPolicy {
165 BrokerScope scope = 1;
166 bytes operation_id = 2; // exactly 16 bytes
167 RemoteRegistration registration = 3;
168 BrokerWirePolicy policy = 4;
169}
170
171// Reconcile hands the broker one exact registration to probe.
172message Reconcile {
173 BrokerScope scope = 1;
174 RemoteRegistration registration = 2;
175}
176
177// OpenStream asks the broker to open one independently cancellable logical
178// stream to the owning daemon. env is the per-request session environment
179// and is never inherited from the broker process environment.
180//
181// admission is the closed attachment-admission taxonomy: an attachment
182// stream names exactly how the daemon must admit it, while control and
183// observation streams carry none (0). name is validated session-name
184// authority for create-named and attach-named and is empty for every other
185// variant.
186// start_mode is the explicit daemon-start authorization carried to the
187// transport: observation and daemon-stop must be existing_only, while
188// attach/creation and the explicit list/mutate controls may be
189// start_if_needed. Zero is invalid and is refused by semantic validation.
190message OpenStream {
191 BrokerStreamRef ref = 1;
192 // Closed taxonomy: 1 = attachment, 2 = control, 3 = observation.
193 uint32 purpose = 2;
194 bool local = 3;
195 string endpoint = 4;
196 RemoteRegistration registration = 5;
197 // Present only for exact attach/resume; the zero-value message is never
198 // sent for a creation admission.
199 ExactTarget target = 6;
200 repeated string env = 7;
201 BrokerWirePolicy policy = 8;
202 // Closed admission taxonomy: 0 = none (control/observation), 1 = exact
203 // attach/resume, 2 = create named, 3 = create ephemeral, 4 = attach named
204 // (the daemon resolves the name and refuses an unknown one).
205 uint32 admission = 9;
206 // Validated session name for create-named and attach-named; empty otherwise.
207 string name = 10;
208 // Closed daemon-start taxonomy (ports.BrokerDaemonStartMode): 1 =
209 // existing_only, 2 = start_if_needed. Always present; zero is refused.
210 uint32 start_mode = 11;
211}
212
213// ClientStreamData carries one opaque client-to-daemon stream frame.
214message ClientStreamData {
215 BrokerStreamRef ref = 1;
216 bytes data = 2;
217}
218
219// CloseStream closes one logical stream.
220message CloseStream {
221 BrokerStreamRef ref = 1;
222}
223
224// Registered confirms one accepted client connection and its assigned scope.
225message Registered {
226 BrokerScope scope = 1;
227 string build = 2;
228 bool retiring = 3;
229}
230
231// SnapshotPart is one fragment of a snapshot publication. Parts of one
232// generation and revision arrive in index order and carry exactly one
233// nested payload.
234message SnapshotPart {
235 BrokerScope scope = 1;
236 uint64 generation = 2;
237 uint64 revision = 3;
238 uint32 index = 4;
239 oneof part {
240 SnapshotBegin begin = 10;
241 SnapshotDaemon daemon = 15;
242 SnapshotSession session = 12;
243 SnapshotTombstone tombstone = 13;
244 SnapshotEnd end = 14;
245 }
246}
247
248// SnapshotBegin opens a snapshot publication with its element counts.
249// host_count counts every daemon part in the transfer (the local daemon plus
250// every remote host), and local_present reports whether daemon index 0 is the
251// local daemon. A publication carries at most one local daemon, always first.
252message SnapshotBegin {
253 uint32 host_count = 1;
254 uint32 session_count = 2;
255 uint32 tombstone_count = 3;
256 bool local_present = 4;
257}
258
259// SnapshotDaemon is one daemon projection (ports.BrokerDaemonObservation),
260// local or remote. Configured authority (endpoint, registration, policy,
261// display origin, rank) stays separate from observed state (identity,
262// incarnation, version, capabilities, availability, failure, freshness),
263// and observation never invents unknown values. registration is absent for
264// the local daemon; policy is always present. Sessions travel as separate
265// SnapshotSession parts.
266message SnapshotDaemon {
267 uint32 host_index = 1;
268 bool local = 2;
269 string endpoint = 3;
270 string display_origin = 4;
271 uint32 rank = 5;
272 RemoteRegistration registration = 6;
273 BrokerWirePolicy policy = 7;
274 // Authenticated daemon identity; empty until observed.
275 string daemon_identity = 8;
276 // Daemon process incarnation; exactly 16 bytes when an identity is
277 // present and absent otherwise.
278 bytes daemon_incarnation = 9;
279 // Observed daemon protocol version; 0 = unobserved.
280 uint32 protocol_version = 10;
281 // Observed capability bitmask (mirrors protocol.Capability* bits).
282 uint32 capabilities = 11;
283 // Closed availability taxonomy (domain.RemoteAvailability); 0 is
284 // invalid, an unobserved daemon carries RemoteAvailabilityUnknown.
285 uint32 availability = 12;
286 bool checking = 13;
287 BrokerTimestamp last_attempt = 14;
288 BrokerTimestamp last_success = 15;
289 BrokerTimestamp next_due = 16;
290 uint64 consecutive_failures = 17;
291 uint64 failure_episode = 18;
292 // Closed cause taxonomy (domain.RemoteFailureKind); 0 = none.
293 uint32 failure_kind = 19;
294 bool inventory_known = 20;
295 uint32 session_count = 21;
296}
297
298// SnapshotSession is one catalogue session bound to its daemon by index.
299// local marks a session of the local daemon; its host_index is then exactly
300// 0 and follows the local daemon part rather than a remote host index.
301message SnapshotSession {
302 uint32 host_index = 1;
303 uint32 session_index = 2;
304 BrokerCatalogSession session = 3;
305 bool local = 4;
306}
307
308// SnapshotTombstone fences one retired host registration so a stale probe
309// cannot revive authority for that exact incarnation.
310message SnapshotTombstone {
311 uint32 tombstone_index = 1;
312 RemoteRegistration registration = 2;
313 uint64 retired_revision = 3;
314}
315
316// SnapshotEnd closes a snapshot publication.
317message SnapshotEnd {}
318
319// OperationResult is exactly one completion for one admitted mutating
320// operation. removed and registration are presence-carrying authority:
321// an authoritative success carries the resulting registration, a removal
322// success carries none, and a failed or outcome-unknown result carries
323// neither removal nor registration, only its typed error.
324message OperationResult {
325 BrokerScope scope = 1;
326 bytes operation_id = 2; // exactly 16 bytes
327 // Closed taxonomy: 1 = ok, 2 = failed, 3 = outcome_unknown.
328 uint32 outcome = 3;
329 bool removed = 4;
330 BrokerErrorDetail error = 5;
331 RemoteRegistration registration = 6;
332}
333
334// StreamOpened confirms one logical stream is established.
335message StreamOpened {
336 BrokerStreamRef ref = 1;
337}
338
339// ServerStreamData carries one opaque daemon-to-client stream frame.
340message ServerStreamData {
341 BrokerStreamRef ref = 1;
342 bytes data = 2;
343}
344
345// StreamClosed ends one logical stream. An unset error is an orderly close;
346// a set error reports the typed failure that ended it.
347message StreamClosed {
348 BrokerStreamRef ref = 1;
349 BrokerErrorDetail error = 2;
350}
351
352// Progress reports one phase of a long-running broker operation.
353message Progress {
354 BrokerStreamRef ref = 1;
355 uint32 phase = 2;
356 string text = 3;
357}
358
359// BrokerErrorMessage reports a broker-wide typed failure for one scope.
360message BrokerErrorMessage {
361 BrokerScope scope = 1;
362 BrokerErrorDetail error = 2;
363}
364
365// Shutdown reports the broker process is stopping.
366message Shutdown {
367 BrokerScope scope = 1;
368 // Closed reason taxonomy; 0 is the zero value.
369 uint32 reason = 2;
370 string text = 3;
371}
372
373// PreviewRoute names the daemon a preview observes. The broker allocates the
374// observation stream itself; endpoint and registration are present only for a
375// remote route, and policy is always present.
376message PreviewRoute {
377 bool local = 1;
378 string endpoint = 2;
379 RemoteRegistration registration = 3;
380 BrokerWirePolicy policy = 4;
381}
382
383// StartPreview starts or replaces one connection-scoped live preview.
384// scope carries the broker epoch plus client connection; generation is the
385// connection-scoped preview authority. route names the observed daemon and
386// preview is the bounded viewport request sent to it. Repeated authority is
387// scope/generation plus the preview target identity response.
388message StartPreview {
389 // 3 was the client-allocated OpenStream route.
390 reserved 3;
391
392 BrokerScope scope = 1;
393 uint64 generation = 2;
394 RemotePreviewRequest preview = 4;
395 PreviewRoute route = 5;
396}
397
398// CancelPreview cancels one connection-scoped live preview.
399message CancelPreview {
400 BrokerScope scope = 1;
401 uint64 generation = 2;
402}
403
404// PreviewPublication is the newest result for one preview subscription.
405// Exactly one of preview/error is set via the result oneof.
406message PreviewPublication {
407 BrokerScope scope = 1;
408 uint64 generation = 2;
409 oneof result {
410 RemotePreview preview = 3;
411 BrokerErrorDetail error = 4;
412 }
413}
414
415// BrokerCatalogSession mirrors catalogue.RemoteCatalogSession exactly: the
416// lifecycle identity is a fixed 16-byte value, the state is a closed
417// taxonomy, and the tab list keeps its presence separately from its
418// length.
419message BrokerCatalogSession {
420 bytes lifecycle_id = 1; // exactly 16 bytes
421 string name = 2;
422 // Closed taxonomy: 1 = up, 2 = down, 3 = broken.
423 uint32 state = 3;
424 bool ephemeral = 4;
425 repeated BrokerCatalogTab tabs = 5;
426 bool attached = 6;
427 uint64 last_used_seq = 7;
428 string active_tab_id = 8;
429 string reason = 9;
430}
431
432// BrokerCatalogTab mirrors catalogue.RemoteCatalogTab exactly.
433message BrokerCatalogTab {
434 string id = 1;
435 uint32 index = 2;
436 string name = 3;
437 string detail = 4;
438 bool attention = 5;
439}