Skip to main content

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}