1// Daemon multiplexer wire schema: the broker-to-daemon conversation that
2// multiplexes independently cancellable logical streams over one pooled
3// physical connection.
4//
5// daemonmux is a third conversation, separate from the session protocol
6// (tags 1-32) and the broker protocol (client 101-111, server 201-209):
7// client tags 301-305 and server tags 401-406 are disjoint from every other
8// tag, and each direction is its own closed oneof union. The physical
9// preamble is a separate adapter message pair, not an envelope variant:
10// roles 5/6 negotiate the per-connection ceilings, the authenticated daemon
11// identity, and the accepted policy once, before any application message.
12//
13// MuxData, MuxClose, MuxReset, and MuxWindowUpdate are shared payloads carried in both
14// directions; MuxOpen carries the full stream identity, and every later
15// frame is routed by physical_stream_id alone.
16syntax = "proto3";
17
18package vev.wire.v1;
19
20import "broker.proto";
21import "common.proto";
22import "envelope.proto";
23
24option go_package = "github.com/bnema/vev/internal/protocol/wire";
25
26// MuxStreamRef identifies one logical stream multiplexed over one physical
27// daemon connection: the mux-local physical_stream_id asserted by the
28// opening side, the broker epoch and client connection that own the stream,
29// and the broker client_stream_id it carries. physical_stream_id never
30// crosses a physical connection; client_stream_id never crosses a broker
31// connection or epoch. Every numeric value is nonzero and connection_id is
32// exactly 16 bytes.
33message MuxStreamRef {
34 uint64 physical_stream_id = 1;
35 uint64 broker_epoch = 2;
36 bytes connection_id = 3; // exactly 16 bytes
37 uint64 client_stream_id = 4;
38}
39
40// MuxOpen asks the daemon to open one independently cancellable logical
41// stream over the physical connection. env is the per-request session
42// environment and is never inherited from the broker process environment.
43// The daemon revalidates the exact session identity before attachment.
44// start_mode carries the broker's explicit daemon-start authorization so a
45// transport can never widen it into a spawn.
46message MuxOpen {
47 MuxStreamRef ref = 1;
48 // Closed taxonomy: 1 = attachment, 2 = control, 3 = observation.
49 uint32 purpose = 2;
50 bool local = 3;
51 string endpoint = 4;
52 RemoteRegistration registration = 5;
53 ExactTarget target = 6;
54 repeated string env = 7;
55 BrokerWirePolicy policy = 8;
56 // Closed attachment-admission taxonomy (ports.BrokerStreamAdmission):
57 // 0 = none (control/observation), 1 = exact attach/resume carrying
58 // target, 2 = create named carrying name, 3 = create ephemeral, 4 = attach
59 // named carrying name.
60 uint32 admission = 9;
61 string name = 10;
62 // Closed daemon-start taxonomy (ports.BrokerDaemonStartMode): 1 =
63 // existing_only, 2 = start_if_needed. Always present; zero is refused.
64 uint32 start_mode = 11;
65}
66
67// MuxOpened confirms one logical stream is established.
68message MuxOpened {
69 MuxStreamRef ref = 1;
70}
71
72// MuxRefused refuses one logical stream open. The typed error is
73// mandatory: a refusal without a reason could not be classified.
74message MuxRefused {
75 MuxStreamRef ref = 1;
76 BrokerErrorDetail error = 2;
77}
78
79// MuxData carries one opaque stream frame in either direction. Once a
80// stream is opened it is routed by physical_stream_id alone and never
81// repeats its scope.
82message MuxData {
83 uint64 physical_stream_id = 1;
84 bytes data = 2;
85}
86
87// MuxClose ends one logical stream in either direction.
88message MuxClose {
89 uint64 physical_stream_id = 1;
90}
91
92// MuxReset aborts one logical stream in either direction. An unset error is
93// an abort without a typed reason; a set error carries the failure.
94message MuxReset {
95 uint64 physical_stream_id = 1;
96 BrokerErrorDetail error = 2;
97}
98
99// MuxWindowUpdate returns receive credit for one logical stream in either
100// direction: the receiver consumed MuxData frames worth `credit_bytes` credit
101// bytes, so the sender may spend that many more. One MuxData frame costs its
102// payload length plus 64 credit bytes. Each stream starts with the same window
103// on both sides, derived from the negotiated ceilings; a sender never has more
104// credit in flight than its window, so a slow receiver slows the sender
105// instead of overflowing its queue.
106message MuxWindowUpdate {
107 uint64 physical_stream_id = 1;
108 uint64 credit_bytes = 2;
109}
110
111// MuxClientEnvelope is the closed broker-to-daemon mux union. Exactly one
112// variant must be set per envelope; empty, duplicate, and wrong-direction
113// payloads are rejected before generated unmarshal.
114message MuxClientEnvelope {
115 oneof payload {
116 MuxOpen open = 301;
117 MuxData data = 302;
118 MuxClose close = 303;
119 MuxReset reset = 304;
120 MuxWindowUpdate window_update = 305;
121 }
122}
123
124// MuxServerEnvelope is the closed daemon-to-broker mux union. Exactly one
125// variant must be set per envelope.
126message MuxServerEnvelope {
127 oneof payload {
128 MuxOpened opened = 401;
129 MuxRefused refused = 402;
130 MuxData data = 403;
131 MuxClose close = 404;
132 MuxReset reset = 405;
133 MuxWindowUpdate window_update = 406;
134 }
135}
136
137// MuxPreambleRequest is the daemonmux client's first frame on one physical
138// connection: the requested per-connection ceilings and the broker policy
139// the client asks the daemon to accept. The shared field numbers mirror
140// MuxPreambleResponse, exactly as the session preamble pair does. Each
141// ceiling is advertised independently per direction: max_aggregate_bytes is
142// the buffered-bytes budget the client will enforce in the direction it
143// accepts, and never shared with the opposite direction.
144message MuxPreambleRequest {
145 uint32 magic = 1; // must equal the shared preamble magic
146 uint32 epoch = 2; // must equal the shared protocol epoch
147 uint32 version = 3; // exact session protocol version
148 PreambleRole role = 4; // must be 5 (daemonmux client)
149 uint64 max_receive_envelope_bytes = 5; // requested 1 MiB..16 MiB
150 uint64 stream_chunk_limit = 6; // requested 1..64 KiB
151 uint64 max_streams = 7; // requested 1..128
152 uint64 max_aggregate_bytes = 8; // requested 1 max chunk + overhead..64 MiB, per direction
153 uint32 capability_bits = 9; // must be zero
154 BrokerWirePolicy policy = 10; // requested connection policy
155}
156
157// MuxPreambleResponse is the daemon's first frame: acceptance carrying the
158// effective (minimum) ceilings, the authenticated daemon identity and
159// incarnation, and the accepted policy, or a typed refusal. The effective
160// ceilings never exceed the client's offer, and max_aggregate_bytes stays a
161// per-direction budget: the daemon enforces the effective value
162// independently in each direction it buffers.
163message MuxPreambleResponse {
164 uint32 magic = 1;
165 uint32 epoch = 2;
166 uint32 version = 3;
167 PreambleRole role = 4; // must be 6 (daemonmux server)
168 uint64 max_receive_envelope_bytes = 5; // effective ceiling
169 uint64 stream_chunk_limit = 6; // effective ceiling
170 uint64 max_streams = 7; // effective ceiling
171 uint64 max_aggregate_bytes = 8; // effective ceiling, per direction, never above the offer
172 uint32 capability_bits = 9; // must be zero
173 BrokerWirePolicy accepted_policy = 10; // daemon-authoritative policy
174 bool accepted = 11;
175 PreambleRejectionCode rejection = 12; // set only when accepted is false
176 string daemon_identity = 13; // authenticated identity token
177 bytes incarnation = 14; // exactly 16 bytes
178}