liminal_protocol/wire/authority/enrollment.rs
1//! Response authority bound to `ClientRequest::Enrollment` (`0x0001`).
2
3use alloc::boxed::Box;
4
5use super::super::{
6 ConnectionConversationBindingOccupied, ConnectionConversationCapacityExceeded,
7 ConversationOrderExhausted, ConversationSequenceExhausted, EnrollBound, EnrollmentEnvelope,
8 EnrollmentKnown, EnrollmentReceiptCapacityScope, EnrollmentSettlementBackpressure, Generation,
9 IdentityCapacityExceeded, MarkerClosureCapacityExceeded, ObserverBackpressure,
10 ObserverBackpressureState, ReceiptCapacityExceeded, ReceiptExpired, ReceiptExpiryReason,
11 ReceiptReplay, ResponseEnvelope, Retired, ServerDiscriminant, ServerValue,
12};
13
14/// Server response bound to one enrollment request.
15///
16/// Constructors exist only for the outcomes the frozen R-D1 register admits
17/// for enrollment; every other pairing is a compile error by construction.
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct EnrollmentResponse {
20 value: ServerValue,
21}
22
23impl EnrollmentResponse {
24 /// First decoded semantic operation for an untracked conversation
25 /// exceeded the connection-conversation limit (register row 5641).
26 #[must_use]
27 pub const fn connection_conversation_capacity_exceeded(
28 request: EnrollmentEnvelope,
29 limit: u64,
30 ) -> Self {
31 Self {
32 value: ServerValue::ConnectionConversationCapacityExceeded(
33 ConnectionConversationCapacityExceeded::SemanticRequest {
34 request: ResponseEnvelope::Enrollment(request),
35 limit,
36 },
37 ),
38 }
39 }
40
41 /// Enrollment binding attempt found an occupied connection/conversation
42 /// slot (register row 5643).
43 #[must_use]
44 pub const fn connection_conversation_binding_occupied(request: &EnrollmentEnvelope) -> Self {
45 Self {
46 value: ServerValue::ConnectionConversationBindingOccupied(
47 ConnectionConversationBindingOccupied::Enrollment {
48 conversation_id: request.conversation_id,
49 enrollment_token: request.enrollment_token,
50 },
51 ),
52 }
53 }
54
55 /// Enrollment required an unreserved `transaction_order` major and the
56 /// conversation order is exhausted (register row 5644).
57 ///
58 /// The payload is minted only by the shared order allocator invoked with
59 /// this request's own envelope.
60 pub(crate) const fn from_conversation_order_exhausted(
61 value: Box<ConversationOrderExhausted>,
62 ) -> Self {
63 Self {
64 value: ServerValue::ConversationOrderExhausted(value),
65 }
66 }
67
68 /// Enrollment token mapping resolved to a tombstone (register row 5653).
69 ///
70 /// The payload is minted only by `lookup_enrollment` for this exact
71 /// request.
72 pub(crate) const fn from_retired(value: Retired) -> Self {
73 Self {
74 value: ServerValue::Retired(value),
75 }
76 }
77
78 /// Closure-checked enrollment admission exceeded marker-closure capacity
79 /// (register row 5649).
80 ///
81 /// The payload is minted only by the shared remaining-closure selector
82 /// invoked with this request's own envelope.
83 pub(crate) const fn from_marker_closure_capacity_exceeded(
84 value: Box<MarkerClosureCapacityExceeded>,
85 ) -> Self {
86 Self {
87 value: ServerValue::MarkerClosureCapacityExceeded(value),
88 }
89 }
90
91 /// Successful enrollment attach (register row 5650).
92 #[must_use]
93 pub const fn enroll_bound(value: EnrollBound) -> Self {
94 Self {
95 value: ServerValue::EnrollBound(value),
96 }
97 }
98
99 /// Post-provenance replay for a live non-retired mapped identity
100 /// (register row 5651).
101 #[must_use]
102 pub const fn enrollment_known(value: EnrollmentKnown) -> Self {
103 Self {
104 value: ServerValue::EnrollmentKnown(value),
105 }
106 }
107
108 /// Exact enrollment provenance window response (register row 5652).
109 ///
110 /// The payload is minted only by `lookup_enrollment` for this exact
111 /// request.
112 pub(crate) const fn from_receipt_expired(value: ReceiptExpired) -> Self {
113 Self {
114 value: ServerValue::ReceiptExpired(value),
115 }
116 }
117
118 /// Exact enrollment provenance window response with the flattened
119 /// request-echo fields derived from the request's own envelope (register
120 /// row 5652) — the same public field-wise form as the credential-attach
121 /// authority's `receipt_expired`.
122 ///
123 /// The participant id and both generations must come from the identity
124 /// resolved by the lifetime token mapping and its retained provenance
125 /// record; `presented_generation` is structurally `None` for enrollment
126 /// and the marker option is structurally absent.
127 #[must_use]
128 pub const fn receipt_expired(
129 request: &EnrollmentEnvelope,
130 participant_id: u64,
131 result_generation: Generation,
132 current_generation: Generation,
133 reason: ReceiptExpiryReason,
134 ) -> Self {
135 Self {
136 value: ServerValue::ReceiptExpired(ReceiptExpired::Enrollment {
137 conversation_id: request.conversation_id,
138 token: request.enrollment_token,
139 participant_id,
140 result_generation,
141 current_generation,
142 reason,
143 }),
144 }
145 }
146
147 /// One of the three receipt/provenance scopes reachable before identity
148 /// mint is full (register row 5654).
149 #[must_use]
150 pub const fn receipt_capacity_exceeded(
151 request: EnrollmentEnvelope,
152 scope: EnrollmentReceiptCapacityScope,
153 limit: u64,
154 occupied: u64,
155 ) -> Self {
156 Self {
157 value: ServerValue::ReceiptCapacityExceeded(ReceiptCapacityExceeded::Enrollment {
158 request,
159 scope,
160 limit,
161 occupied,
162 }),
163 }
164 }
165
166 /// Server or conversation identity capacity is full (register row 5655).
167 #[must_use]
168 pub const fn identity_capacity_exceeded(value: IdentityCapacityExceeded) -> Self {
169 Self {
170 value: ServerValue::IdentityCapacityExceeded(value),
171 }
172 }
173
174 /// Hard-observer retention refused the enrollment append (register row
175 /// 5656).
176 #[must_use]
177 pub const fn observer_backpressure(
178 request: EnrollmentEnvelope,
179 state: ObserverBackpressureState,
180 ) -> Self {
181 Self {
182 value: ServerValue::ObserverBackpressure(ObserverBackpressure::Enrollment {
183 request,
184 state,
185 }),
186 }
187 }
188
189 /// A marker candidate is awaiting its drain (participant contract §0.16
190 /// condition 2, enrollment wrapper — amendment A5).
191 ///
192 /// NO epoch label and NO pushed event, by ratified law rather than by
193 /// omission. The enrollment wrapper carries NO membership predicate — an
194 /// `EnrollmentRequest` is `{ conversation_id, enrollment_token }` and the
195 /// token is a replay-dedup key, not a capability — so it cannot tell an
196 /// invited enrollee from a stranger, and any wake or epoch here would be
197 /// granted to both by construction. The enroller retries at its own
198 /// cadence.
199 #[must_use]
200 pub const fn settlement_backpressure(request: &EnrollmentEnvelope) -> Self {
201 Self {
202 value: ServerValue::EnrollmentSettlementBackpressure(
203 EnrollmentSettlementBackpressure {
204 conversation_id: request.conversation_id,
205 },
206 ),
207 }
208 }
209
210 /// Hard-observer retention refusal minted by the shared observer-floor
211 /// selector invoked with this request's own envelope (register row 5656).
212 pub(crate) const fn from_observer_backpressure(value: ObserverBackpressure) -> Self {
213 Self {
214 value: ServerValue::ObserverBackpressure(value),
215 }
216 }
217
218 /// Canonical resulting sequence-reserve check failed (register row 5657).
219 ///
220 /// The payload is minted only by the shared sequence allocator invoked
221 /// with this request's own envelope.
222 pub(crate) const fn from_conversation_sequence_exhausted(
223 value: Box<ConversationSequenceExhausted>,
224 ) -> Self {
225 Self {
226 value: ServerValue::ConversationSequenceExhausted(value),
227 }
228 }
229
230 /// Byte-identical receipt replay whose exact binding epoch still occupies
231 /// its origin slot (register row 5663).
232 #[must_use]
233 pub const fn bound(value: EnrollBound) -> Self {
234 Self {
235 value: ServerValue::Bound(ReceiptReplay::Enrollment(value)),
236 }
237 }
238
239 /// Byte-identical receipt replay whose origin slot is empty, replaced, or
240 /// at a later epoch (register row 5663).
241 #[must_use]
242 pub const fn unbound_receipt(value: EnrollBound) -> Self {
243 Self {
244 value: ServerValue::UnboundReceipt(ReceiptReplay::Enrollment(value)),
245 }
246 }
247
248 /// Byte-identical live-receipt replay minted by `lookup_enrollment`
249 /// (register row 5663).
250 pub(crate) const fn from_bound(value: ReceiptReplay) -> Self {
251 Self {
252 value: ServerValue::Bound(value),
253 }
254 }
255
256 /// Byte-identical unbound-receipt replay minted by `lookup_enrollment`
257 /// (register row 5663).
258 pub(crate) const fn from_unbound_receipt(value: ReceiptReplay) -> Self {
259 Self {
260 value: ServerValue::UnboundReceipt(value),
261 }
262 }
263
264 /// Borrows the bound wire value for encoding or inspection.
265 #[must_use]
266 pub const fn server_value(&self) -> &ServerValue {
267 &self.value
268 }
269
270 /// Returns the bound value's exact wire discriminant.
271 #[must_use]
272 pub const fn discriminant(&self) -> ServerDiscriminant {
273 self.value.discriminant()
274 }
275
276 /// Moves the bound wire value out for transmission.
277 #[must_use]
278 pub fn into_server_value(self) -> ServerValue {
279 self.value
280 }
281}