1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
//! R-D1 stage-8 enrollment capacity production-path tests.
//!
//! Each test drives the live dispatch seam with real wire frames over a real
//! on-disk store and pins the enrollment identity capacity family (register
//! row 5655): the exact scope in the frozen order, the signed limit, and the
//! true occupancy — plus the cold-restart exactness of the server-scope
//! ledger (a restart must not forget reserved identity slots).
//!
//! # Lane p0-39: what these tests stopped being able to say
//!
//! Three RECEIPT scopes used to refuse enrollment here (`LiveReceiptServer`,
//! `ProvenanceServer`, `ProvenanceConversation`, register row 5654). They no
//! longer refuse anything, so the pins that walked them are rewritten below to
//! assert the law that replaced them — an honest arrival lands — rather than
//! deleted, which would have removed the only evidence anyone checks that
//! behaviour at all. Each rewrite names the pin it replaces.
//!
//! The IDENTITY scopes are untouched by that lane and their pins stand exactly
//! as they were. The credential attach scopes live in
//! [`super::tests_capacity_attach`].
use std::error::Error;
use liminal_protocol::wire::{
ClientRequest, ConnectionIncarnation, EnrollmentRequest, EnrollmentToken,
IdentityCapacityExceeded, IdentityCapacityScope, ServerValue,
};
use crate::config::types::ParticipantConfig;
use super::ProductionParticipantHandler;
use super::tests::{dispatch, open_disk_store_for_tests, test_participant_config};
use super::tests_receipts::{enroll, enroll_proving_provenance};
/// Deployment-shaped config with one capacity knob turned down.
pub(super) fn capacity_config(mutate: impl FnOnce(&mut ParticipantConfig)) -> ParticipantConfig {
let mut config = test_participant_config();
mutate(&mut config);
config
}
fn enrollment_request(conversation_id: u64, token: [u8; 16]) -> ClientRequest {
ClientRequest::Enrollment(EnrollmentRequest {
conversation_id,
enrollment_token: EnrollmentToken::new(token),
})
}
/// Server-scope identity capacity (register row 5655): the third identity
/// across the whole server refuses with scope `Server` (tested BEFORE the
/// conversation scope, whose per-conversation occupancy is far below its
/// limit) — and the refusal SURVIVES a cold restart, proving the startup
/// restore rebuilds the identity ledger from durable truth.
///
/// Lane p0-39 leaves this pin untouched: identity capacity is a GATE and
/// stays one.
#[test]
fn enrollment_identity_server_scope_refuses_and_survives_restart() -> Result<(), Box<dyn Error>> {
let home = tempfile::tempdir()?;
let data_dir = home.path().join("durability");
let incarnation = ConnectionIncarnation::new(71, 1);
let config = capacity_config(|c| c.max_retired_identity_slots_server = 2);
{
let store = open_disk_store_for_tests(&data_dir)?;
let handler = ProductionParticipantHandler::new(store, config)?;
enroll(&handler, incarnation, 701, [1; 16])?;
enroll(&handler, incarnation, 702, [2; 16])?;
let refused = dispatch(&handler, incarnation, enrollment_request(703, [3; 16]))?;
let ServerValue::IdentityCapacityExceeded(IdentityCapacityExceeded {
request,
scope,
limit,
occupied,
}) = refused
else {
return Err(format!(
"third server-wide identity must refuse with IdentityCapacityExceeded, got: \
{refused:?}"
)
.into());
};
assert_eq!(request.conversation_id, 703);
assert_eq!(scope, IdentityCapacityScope::Server);
assert_eq!(limit, 2);
assert_eq!(occupied, 2);
}
// COLD RESTART: the ledger is rebuilt from the durable conversation
// streams alone; the server scope must still refuse.
let store = open_disk_store_for_tests(&data_dir)?;
let handler = ProductionParticipantHandler::new(store, config)?;
let refused = dispatch(&handler, incarnation, enrollment_request(703, [3; 16]))?;
let ServerValue::IdentityCapacityExceeded(IdentityCapacityExceeded {
scope,
limit,
occupied,
..
}) = refused
else {
return Err(format!(
"the server identity scope must survive a cold restart, got: {refused:?}"
)
.into());
};
assert_eq!(scope, IdentityCapacityScope::Server);
assert_eq!(limit, 2);
assert_eq!(occupied, 2);
Ok(())
}
/// Lane p0-39 REWRITE of `enrollment_live_receipt_server_scope_refuses_and_survives_restart`.
///
/// That pin asserted a `LiveReceiptServer` refusal and its survival across a
/// cold restart. The refusal is gone, so both of its assertions read a wire row
/// that can no longer be produced. What it was really guarding — that the
/// server-scope live-receipt ledger is rebuilt exactly from durable truth — is
/// preserved here in its non-refusing form: the pool is deliberately driven far
/// past its old cap, before and after a restart, and every honest arrival still
/// lands.
#[test]
fn shared_live_receipt_pool_never_refuses_before_or_after_a_restart() -> Result<(), Box<dyn Error>>
{
let home = tempfile::tempdir()?;
let data_dir = home.path().join("durability");
let incarnation = ConnectionIncarnation::new(72, 1);
let config = capacity_config(|c| c.live_receipt_server_report_threshold = 1);
{
let store = open_disk_store_for_tests(&data_dir)?;
let handler = ProductionParticipantHandler::new(store, config)?;
enroll(&handler, incarnation, 711, [11; 16])?;
// Third party, far past the old cap of one.
enroll(&handler, incarnation, 712, [12; 16])?;
enroll(&handler, incarnation, 713, [13; 16])?;
}
let store = open_disk_store_for_tests(&data_dir)?;
let handler = ProductionParticipantHandler::new(store, config)?;
enroll(&handler, incarnation, 714, [14; 16])?;
Ok(())
}
/// Lane p0-39 REWRITE of `enrollment_provenance_server_scope_refusal`.
///
/// The shared server provenance pool no longer gates, so the refusal that pin
/// asserted cannot occur. Its earned-fingerprint premise is kept — the fixture
/// still pays for a real retained fingerprint through a rotation, so this is
/// not a green bought by an empty pool.
#[test]
fn shared_server_provenance_pool_never_refuses_an_enrollment() -> Result<(), Box<dyn Error>> {
let home = tempfile::tempdir()?;
let data_dir = home.path().join("durability");
let incarnation = ConnectionIncarnation::new(73, 1);
let store = open_disk_store_for_tests(&data_dir)?;
let config = capacity_config(|c| c.receipt_provenance_server_report_threshold = 1);
let handler = ProductionParticipantHandler::new(store, config)?;
enroll_proving_provenance(&handler, incarnation, 721, [[21; 16], [121; 16], [221; 16]])?;
enroll(&handler, incarnation, 722, [22; 16])?;
Ok(())
}
/// Lane p0-39 REWRITE of `enrollment_over_limit_scope_refuses_with_true_numbers`.
///
/// The out-of-model over-limit arm SURVIVES — a configured number lowered
/// beneath restored durable occupancy still refuses with its true numbers
/// rather than admitting past a signed cap — but only for the scopes that are
/// still gates. The original drove it through `ProvenanceServer`, which no
/// longer refuses; this drives the identical mechanism through the identity
/// server scope, whose cap is lowered to 1 beneath two durable identities.
#[test]
fn enrollment_over_limit_identity_scope_refuses_with_true_numbers() -> Result<(), Box<dyn Error>> {
let home = tempfile::tempdir()?;
let data_dir = home.path().join("durability");
let incarnation = ConnectionIncarnation::new(80, 1);
{
let store = open_disk_store_for_tests(&data_dir)?;
let handler = ProductionParticipantHandler::new(store, test_participant_config())?;
enroll(&handler, incarnation, 751, [61; 16])?;
enroll(&handler, incarnation, 752, [62; 16])?;
}
// RESTART with the server identity cap lowered beneath the two durable
// identities.
let store = open_disk_store_for_tests(&data_dir)?;
let config = capacity_config(|c| c.max_retired_identity_slots_server = 1);
let handler = ProductionParticipantHandler::new(store, config)?;
let refused = dispatch(&handler, incarnation, enrollment_request(753, [63; 16]))?;
let ServerValue::IdentityCapacityExceeded(IdentityCapacityExceeded {
scope,
limit,
occupied,
..
}) = refused
else {
return Err(format!(
"an identity cap lowered beneath durable occupancy must refuse with its true \
numbers, got: {refused:?}"
)
.into());
};
assert_eq!(scope, IdentityCapacityScope::Server);
assert_eq!(limit, 1);
assert_eq!(occupied, 2);
Ok(())
}
/// Lane p0-39 REWRITE of `enrollment_mixed_full_and_over_limit_refuses_the_earlier_full_scope`.
///
/// # The model-boundary variant is now UNCONSTRUCTIBLE, and that is a finding
///
/// The original played an in-model exactly-full scope (identity Server) off
/// against a later OVER-LIMIT scope (`ProvenanceServer`, its cap lowered
/// beneath durable occupancy) and asserted the earlier one answered. Both
/// halves of that setup are gone: the receipt scopes no longer refuse, and the
/// only surviving later scope — identity Conversation — cannot be driven
/// over-limit at all. Lowering `identity_slots` beneath already-minted ordinals
/// makes the conversation REFUSE TO REPLAY (the protocol's initial-enrollment
/// slot allocator rejects an ordinal outside `0..I` during restore, long before
/// stage-8 capacity is consulted), so the state the old pin needed cannot be
/// reached through any sequence of operations. Measured, not assumed: the
/// attempt answers `ConversationUnloadable … "durable initial enrollment was
/// refused during protocol replay"`.
///
/// What IS still constructible, and is pinned here, is the in-model half of
/// the same law: with BOTH identity scopes exactly full, the refusal names the
/// EARLIER one — Server — and discloses no later occupancy.
#[test]
fn enrollment_first_full_identity_scope_answers_before_the_later_one() -> Result<(), Box<dyn Error>>
{
let home = tempfile::tempdir()?;
let data_dir = home.path().join("durability");
let incarnation = ConnectionIncarnation::new(81, 1);
let store = open_disk_store_for_tests(&data_dir)?;
// Two identities, one conversation, and BOTH scopes sized to exactly two:
// server and conversation are full together.
let config = capacity_config(|c| {
c.max_retired_identity_slots_server = 2;
c.identity_slots = 2;
});
let handler = ProductionParticipantHandler::new(store, config)?;
enroll(&handler, incarnation, 761, [64; 16])?;
enroll(&handler, ConnectionIncarnation::new(81, 2), 761, [65; 16])?;
let refused = dispatch(
&handler,
ConnectionIncarnation::new(81, 3),
enrollment_request(761, [66; 16]),
)?;
let ServerValue::IdentityCapacityExceeded(IdentityCapacityExceeded {
request,
scope,
limit,
occupied,
}) = refused
else {
return Err(format!(
"two full identity scopes must refuse with IdentityCapacityExceeded, got: {refused:?}"
)
.into());
};
assert_eq!(request.conversation_id, 761);
assert_eq!(
scope,
IdentityCapacityScope::Server,
"the EARLIER scope in the frozen order must answer"
);
assert_eq!(limit, 2);
assert_eq!(occupied, 2);
Ok(())
}
/// Lane p0-39 REWRITE of `enrollment_provenance_conversation_scope_refusal_is_scoped`.
///
/// The conversation provenance pool no longer refuses, so the original's
/// refusal assertion is gone; its second half — that the same enrollment
/// succeeds against a fresh conversation — is kept and generalised. Both
/// participants now land, which is the whole point: the second participant of a
/// conversation is a third party to the first participant's churn.
#[test]
fn shared_conversation_provenance_pool_never_refuses_either_participant()
-> Result<(), Box<dyn Error>> {
let home = tempfile::tempdir()?;
let data_dir = home.path().join("durability");
let incarnation = ConnectionIncarnation::new(74, 1);
let store = open_disk_store_for_tests(&data_dir)?;
let config = capacity_config(|c| c.receipt_provenance_per_conversation_report_threshold = 1);
let handler = ProductionParticipantHandler::new(store, config)?;
enroll_proving_provenance(&handler, incarnation, 731, [[31; 16], [131; 16], [231; 16]])?;
// Same conversation, past the old cap.
enroll(&handler, ConnectionIncarnation::new(74, 2), 731, [32; 16])?;
// And a fresh conversation, exactly as before.
enroll(&handler, ConnectionIncarnation::new(74, 3), 732, [33; 16])?;
Ok(())
}