@engramx/idl 0.16.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +27 -0
- package/dist/engram.did +4045 -0
- package/dist/engram.idl.d.ts +3839 -0
- package/dist/engram.idl.js +1423 -0
- package/dist/market.did +1402 -0
- package/dist/market.idl.d.ts +1354 -0
- package/dist/market.idl.js +551 -0
- package/dist/registry.did +1671 -0
- package/dist/registry.idl.d.ts +1692 -0
- package/dist/registry.idl.js +522 -0
- package/package.json +50 -0
|
@@ -0,0 +1,1671 @@
|
|
|
1
|
+
type UpdatePreference =
|
|
2
|
+
variant {
|
|
3
|
+
AutoUpdate;
|
|
4
|
+
ManualOnly;
|
|
5
|
+
};
|
|
6
|
+
type TreesSpanned =
|
|
7
|
+
record {
|
|
8
|
+
fivePlus: nat;
|
|
9
|
+
one: nat;
|
|
10
|
+
threeToFour: nat;
|
|
11
|
+
two: nat;
|
|
12
|
+
};
|
|
13
|
+
type StandingLeaf =
|
|
14
|
+
record {
|
|
15
|
+
disputes: nat;
|
|
16
|
+
endedGuardianRevoked: nat;
|
|
17
|
+
endedKillSwitch: nat;
|
|
18
|
+
endedOwnerRevoked: nat;
|
|
19
|
+
endedReset: nat;
|
|
20
|
+
endedResigned: nat;
|
|
21
|
+
firstEngagementDay: int;
|
|
22
|
+
lastDisputeDay: opt int;
|
|
23
|
+
live: nat;
|
|
24
|
+
root: principal;
|
|
25
|
+
version: nat;
|
|
26
|
+
};
|
|
27
|
+
type RowEnding =
|
|
28
|
+
record {
|
|
29
|
+
day: int;
|
|
30
|
+
reason: EndReason;
|
|
31
|
+
};
|
|
32
|
+
type RoleGrant =
|
|
33
|
+
record {
|
|
34
|
+
holder: principal;
|
|
35
|
+
role: variant {
|
|
36
|
+
Admin;
|
|
37
|
+
Other: text;
|
|
38
|
+
Root;
|
|
39
|
+
WasmAdmin;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
type Role =
|
|
43
|
+
variant {
|
|
44
|
+
Admin;
|
|
45
|
+
Agent;
|
|
46
|
+
Anonymous;
|
|
47
|
+
Buyer;
|
|
48
|
+
Engram;
|
|
49
|
+
Guardian;
|
|
50
|
+
Other;
|
|
51
|
+
Owner;
|
|
52
|
+
Root;
|
|
53
|
+
Seller;
|
|
54
|
+
Viewer;
|
|
55
|
+
WasmAdmin;
|
|
56
|
+
};
|
|
57
|
+
type Result_9 =
|
|
58
|
+
variant {
|
|
59
|
+
Err: Error;
|
|
60
|
+
Ok: Page_2;
|
|
61
|
+
};
|
|
62
|
+
type Result_8 =
|
|
63
|
+
variant {
|
|
64
|
+
Err: Error;
|
|
65
|
+
Ok: Page_1;
|
|
66
|
+
};
|
|
67
|
+
type Result_7 =
|
|
68
|
+
variant {
|
|
69
|
+
Err: Error;
|
|
70
|
+
Ok: Page;
|
|
71
|
+
};
|
|
72
|
+
type Result_6 =
|
|
73
|
+
variant {
|
|
74
|
+
Err: Error;
|
|
75
|
+
Ok: vec text;
|
|
76
|
+
};
|
|
77
|
+
type Result_5 =
|
|
78
|
+
variant {
|
|
79
|
+
Err: Error;
|
|
80
|
+
Ok: CustodyMode;
|
|
81
|
+
};
|
|
82
|
+
type Result_4 =
|
|
83
|
+
variant {
|
|
84
|
+
Err: Error;
|
|
85
|
+
Ok: nat;
|
|
86
|
+
};
|
|
87
|
+
type Result_3 =
|
|
88
|
+
variant {
|
|
89
|
+
Err: Error;
|
|
90
|
+
Ok: MeterReading;
|
|
91
|
+
};
|
|
92
|
+
type Result_21 =
|
|
93
|
+
variant {
|
|
94
|
+
Err: Error;
|
|
95
|
+
Ok: FleetCycleHealth;
|
|
96
|
+
};
|
|
97
|
+
type Result_20 =
|
|
98
|
+
variant {
|
|
99
|
+
Err: Error;
|
|
100
|
+
Ok: CertifiedStanding;
|
|
101
|
+
};
|
|
102
|
+
type Result_2 =
|
|
103
|
+
variant {
|
|
104
|
+
Err: Error;
|
|
105
|
+
Ok: ControlStep;
|
|
106
|
+
};
|
|
107
|
+
type Result_19 =
|
|
108
|
+
variant {
|
|
109
|
+
Err: Error;
|
|
110
|
+
Ok: OpenedThrough;
|
|
111
|
+
};
|
|
112
|
+
type Result_18 =
|
|
113
|
+
variant {
|
|
114
|
+
Err: Error;
|
|
115
|
+
Ok: blob;
|
|
116
|
+
};
|
|
117
|
+
type Result_17 =
|
|
118
|
+
variant {
|
|
119
|
+
Err: Error;
|
|
120
|
+
Ok: PrivateMetrics;
|
|
121
|
+
};
|
|
122
|
+
type Result_16 =
|
|
123
|
+
variant {
|
|
124
|
+
Err: Error;
|
|
125
|
+
Ok: OpeningCounts;
|
|
126
|
+
};
|
|
127
|
+
type Result_15 =
|
|
128
|
+
variant {
|
|
129
|
+
Err: Error;
|
|
130
|
+
Ok: ReferralCounts;
|
|
131
|
+
};
|
|
132
|
+
type Result_14 =
|
|
133
|
+
variant {
|
|
134
|
+
Err: Error;
|
|
135
|
+
Ok: opt text;
|
|
136
|
+
};
|
|
137
|
+
type Result_13 =
|
|
138
|
+
variant {
|
|
139
|
+
Err: Error;
|
|
140
|
+
Ok: Page_6;
|
|
141
|
+
};
|
|
142
|
+
type Result_12 =
|
|
143
|
+
variant {
|
|
144
|
+
Err: Error;
|
|
145
|
+
Ok: Page_5;
|
|
146
|
+
};
|
|
147
|
+
type Result_11 =
|
|
148
|
+
variant {
|
|
149
|
+
Err: Error;
|
|
150
|
+
Ok: vec OpenedEngram;
|
|
151
|
+
};
|
|
152
|
+
type Result_10 =
|
|
153
|
+
variant {
|
|
154
|
+
Err: Error;
|
|
155
|
+
Ok: Page_3;
|
|
156
|
+
};
|
|
157
|
+
type Result_1 =
|
|
158
|
+
variant {
|
|
159
|
+
Err: Error;
|
|
160
|
+
Ok: principal;
|
|
161
|
+
};
|
|
162
|
+
type Result =
|
|
163
|
+
variant {
|
|
164
|
+
Err: Error;
|
|
165
|
+
Ok;
|
|
166
|
+
};
|
|
167
|
+
type ReserveGateConfig =
|
|
168
|
+
record {
|
|
169
|
+
enabled: bool;
|
|
170
|
+
inviteIssuer: principal;
|
|
171
|
+
trustedSigners: vec principal;
|
|
172
|
+
};
|
|
173
|
+
type ReservationView = record {reservedAtNs: int;};
|
|
174
|
+
type ReleaseClass =
|
|
175
|
+
variant {
|
|
176
|
+
Feature;
|
|
177
|
+
Security;
|
|
178
|
+
};
|
|
179
|
+
type ReferralCounts =
|
|
180
|
+
record {
|
|
181
|
+
agentsByTreesSpanned: TreesSpanned;
|
|
182
|
+
firstOrReopened: PairedSoon;
|
|
183
|
+
openedThroughParentCutOff: nat;
|
|
184
|
+
openedThroughParentLive: nat;
|
|
185
|
+
throughParent: PairedSoon;
|
|
186
|
+
};
|
|
187
|
+
type PrivateMetrics =
|
|
188
|
+
record {
|
|
189
|
+
activeEngrams: nat;
|
|
190
|
+
cycleReserve: nat;
|
|
191
|
+
cyclesLastUpdatedNs: opt int;
|
|
192
|
+
engramWasmHash: text;
|
|
193
|
+
registrySinceNs: opt int;
|
|
194
|
+
registryVersion: text;
|
|
195
|
+
sovereignEngrams: nat;
|
|
196
|
+
totalCyclesAllocated: nat;
|
|
197
|
+
totalEngrams: nat;
|
|
198
|
+
uniqueOwners: nat;
|
|
199
|
+
};
|
|
200
|
+
type PairedSoon =
|
|
201
|
+
record {
|
|
202
|
+
opened: nat;
|
|
203
|
+
pairedWithin7Days: nat;
|
|
204
|
+
};
|
|
205
|
+
type Page_6 =
|
|
206
|
+
record {
|
|
207
|
+
items: vec AnchorConflict;
|
|
208
|
+
total: nat;
|
|
209
|
+
withheld: nat;
|
|
210
|
+
};
|
|
211
|
+
type Page_5 =
|
|
212
|
+
record {
|
|
213
|
+
items: vec principal;
|
|
214
|
+
total: nat;
|
|
215
|
+
withheld: nat;
|
|
216
|
+
};
|
|
217
|
+
type Page_4 =
|
|
218
|
+
record {
|
|
219
|
+
items: vec ChangelogEntry;
|
|
220
|
+
total: nat;
|
|
221
|
+
withheld: nat;
|
|
222
|
+
};
|
|
223
|
+
type Page_3 =
|
|
224
|
+
record {
|
|
225
|
+
items: vec AuditEntry;
|
|
226
|
+
total: nat;
|
|
227
|
+
withheld: nat;
|
|
228
|
+
};
|
|
229
|
+
type Page_2 =
|
|
230
|
+
record {
|
|
231
|
+
items: vec EngagementRow;
|
|
232
|
+
total: nat;
|
|
233
|
+
withheld: nat;
|
|
234
|
+
};
|
|
235
|
+
type Page_1 =
|
|
236
|
+
record {
|
|
237
|
+
items: vec EngramRecordView;
|
|
238
|
+
total: nat;
|
|
239
|
+
withheld: nat;
|
|
240
|
+
};
|
|
241
|
+
type Page =
|
|
242
|
+
record {
|
|
243
|
+
items: vec record {
|
|
244
|
+
principal;
|
|
245
|
+
UpdatePreference;
|
|
246
|
+
};
|
|
247
|
+
total: nat;
|
|
248
|
+
withheld: nat;
|
|
249
|
+
};
|
|
250
|
+
type OpeningCounts =
|
|
251
|
+
record {
|
|
252
|
+
abandoned: nat;
|
|
253
|
+
cutOff: nat;
|
|
254
|
+
openedThroughParent: nat;
|
|
255
|
+
reopened: nat;
|
|
256
|
+
reservationAttemptsRefusedAtCapacity: nat;
|
|
257
|
+
spawnAttemptsRefusedAtCap: nat;
|
|
258
|
+
spawnedEver: nat;
|
|
259
|
+
};
|
|
260
|
+
type OpenedThrough =
|
|
261
|
+
record {
|
|
262
|
+
child: principal;
|
|
263
|
+
cutOffAtNs: opt int;
|
|
264
|
+
parent: principal;
|
|
265
|
+
};
|
|
266
|
+
type OpenedEngram =
|
|
267
|
+
record {
|
|
268
|
+
child: principal;
|
|
269
|
+
cutOffAtNs: opt int;
|
|
270
|
+
owner: principal;
|
|
271
|
+
};
|
|
272
|
+
type MeterReading =
|
|
273
|
+
record {
|
|
274
|
+
cycles: nat;
|
|
275
|
+
freezeLineCycles: nat;
|
|
276
|
+
idlePerDay: opt nat;
|
|
277
|
+
realizedPerDay: opt nat;
|
|
278
|
+
};
|
|
279
|
+
type LastUpgrade =
|
|
280
|
+
record {
|
|
281
|
+
atNs: int;
|
|
282
|
+
changedAtNs: opt int;
|
|
283
|
+
template: text;
|
|
284
|
+
upgraded: bool;
|
|
285
|
+
};
|
|
286
|
+
type GenesisNote =
|
|
287
|
+
record {
|
|
288
|
+
genesisAtNs: int;
|
|
289
|
+
note: text;
|
|
290
|
+
predecessorHeadHash: opt blob;
|
|
291
|
+
predecessorSize: opt nat;
|
|
292
|
+
version: text;
|
|
293
|
+
};
|
|
294
|
+
type FleetSummary =
|
|
295
|
+
record {
|
|
296
|
+
activeEngrams: nat;
|
|
297
|
+
cycleReserve: nat;
|
|
298
|
+
engramWasmHash: text;
|
|
299
|
+
registrySinceNs: opt int;
|
|
300
|
+
registryVersion: text;
|
|
301
|
+
sovereignEngrams: nat;
|
|
302
|
+
totalCyclesAllocated: nat;
|
|
303
|
+
totalEngrams: nat;
|
|
304
|
+
uniqueOwners: nat;
|
|
305
|
+
};
|
|
306
|
+
type FleetCycleHealth =
|
|
307
|
+
record {
|
|
308
|
+
active: nat;
|
|
309
|
+
deleted: nat;
|
|
310
|
+
frozen: nat;
|
|
311
|
+
lowCycles: nat;
|
|
312
|
+
probed: nat;
|
|
313
|
+
scanned: nat;
|
|
314
|
+
sovereignSkipped: nat;
|
|
315
|
+
thresholdCycles: nat;
|
|
316
|
+
total: nat;
|
|
317
|
+
unreadable: nat;
|
|
318
|
+
};
|
|
319
|
+
type Error =
|
|
320
|
+
variant {
|
|
321
|
+
Conflict: text;
|
|
322
|
+
Invalid: text;
|
|
323
|
+
NotFound: text;
|
|
324
|
+
Other: text;
|
|
325
|
+
Refused: text;
|
|
326
|
+
Unavailable: text;
|
|
327
|
+
};
|
|
328
|
+
type EnrolmentProof =
|
|
329
|
+
record {
|
|
330
|
+
enrolledKey: principal;
|
|
331
|
+
inviteCode: text;
|
|
332
|
+
rootPubkey: blob;
|
|
333
|
+
signature: blob;
|
|
334
|
+
};
|
|
335
|
+
type EngramWasmInfo =
|
|
336
|
+
record {
|
|
337
|
+
chunkBytes: nat;
|
|
338
|
+
chunks: nat;
|
|
339
|
+
hash: text;
|
|
340
|
+
totalBytes: nat;
|
|
341
|
+
};
|
|
342
|
+
type EngramStatus =
|
|
343
|
+
variant {
|
|
344
|
+
Active;
|
|
345
|
+
Deleted;
|
|
346
|
+
Frozen;
|
|
347
|
+
};
|
|
348
|
+
type EngramRecordView =
|
|
349
|
+
record {
|
|
350
|
+
canisterId: principal;
|
|
351
|
+
createdAtNs: int;
|
|
352
|
+
owner: principal;
|
|
353
|
+
status: EngramStatus;
|
|
354
|
+
};
|
|
355
|
+
type EngagementRow =
|
|
356
|
+
record {
|
|
357
|
+
abandonedDay: opt int;
|
|
358
|
+
ended: opt RowEnding;
|
|
359
|
+
engram: principal;
|
|
360
|
+
firstOutsideOnTree: bool;
|
|
361
|
+
openedThroughParent: bool;
|
|
362
|
+
ownerOnList: bool;
|
|
363
|
+
replacesAnother: bool;
|
|
364
|
+
resetDays: vec int;
|
|
365
|
+
rootIsTopOwner: bool;
|
|
366
|
+
rootOnList: bool;
|
|
367
|
+
sameRootAsPrevious: opt bool;
|
|
368
|
+
seq: nat;
|
|
369
|
+
startedDay: int;
|
|
370
|
+
top: principal;
|
|
371
|
+
topOnList: bool;
|
|
372
|
+
};
|
|
373
|
+
type Ending =
|
|
374
|
+
record {
|
|
375
|
+
day: int;
|
|
376
|
+
invite: text;
|
|
377
|
+
key: principal;
|
|
378
|
+
reason: EndReason;
|
|
379
|
+
};
|
|
380
|
+
type EndReason =
|
|
381
|
+
variant {
|
|
382
|
+
GuardianRevoked;
|
|
383
|
+
KillSwitch;
|
|
384
|
+
OwnerRevoked;
|
|
385
|
+
Reset;
|
|
386
|
+
Resigned;
|
|
387
|
+
};
|
|
388
|
+
type EdgeEvent =
|
|
389
|
+
variant {
|
|
390
|
+
Added: EnrolmentProof;
|
|
391
|
+
Ended: Ending;
|
|
392
|
+
};
|
|
393
|
+
type Dispute =
|
|
394
|
+
record {
|
|
395
|
+
atNs: int;
|
|
396
|
+
statement: text;
|
|
397
|
+
};
|
|
398
|
+
type CustodyMode =
|
|
399
|
+
variant {
|
|
400
|
+
Managed;
|
|
401
|
+
Sovereign;
|
|
402
|
+
};
|
|
403
|
+
type ControlStep =
|
|
404
|
+
variant {
|
|
405
|
+
EngramMustMove;
|
|
406
|
+
MovedByRegistry;
|
|
407
|
+
};
|
|
408
|
+
type ChangelogEntry =
|
|
409
|
+
record {
|
|
410
|
+
atNs: int;
|
|
411
|
+
releaseNotes: text;
|
|
412
|
+
version: text;
|
|
413
|
+
wasmHash: text;
|
|
414
|
+
};
|
|
415
|
+
type CertifiedStanding =
|
|
416
|
+
record {
|
|
417
|
+
certificate: opt blob;
|
|
418
|
+
leaf: StandingLeaf;
|
|
419
|
+
witness: blob;
|
|
420
|
+
};
|
|
421
|
+
type CertifiedAuditHead =
|
|
422
|
+
record {
|
|
423
|
+
certificate: opt blob;
|
|
424
|
+
headHash: blob;
|
|
425
|
+
size: nat;
|
|
426
|
+
total: nat;
|
|
427
|
+
witness: opt blob;
|
|
428
|
+
};
|
|
429
|
+
type CertifiedAnchorLeaf =
|
|
430
|
+
record {
|
|
431
|
+
atNs: int;
|
|
432
|
+
conflicts: nat;
|
|
433
|
+
disputed: bool;
|
|
434
|
+
engram: principal;
|
|
435
|
+
head: blob;
|
|
436
|
+
refusedConflicts: nat;
|
|
437
|
+
total: nat;
|
|
438
|
+
version: nat;
|
|
439
|
+
wasmHash: text;
|
|
440
|
+
};
|
|
441
|
+
type CertifiedAnchor =
|
|
442
|
+
record {
|
|
443
|
+
certificate: opt blob;
|
|
444
|
+
leaf: CertifiedAnchorLeaf;
|
|
445
|
+
witness: blob;
|
|
446
|
+
};
|
|
447
|
+
type AuditHead =
|
|
448
|
+
record {
|
|
449
|
+
headHash: blob;
|
|
450
|
+
size: nat;
|
|
451
|
+
};
|
|
452
|
+
type AuditEntry =
|
|
453
|
+
record {
|
|
454
|
+
caller: principal;
|
|
455
|
+
callerRole: Role;
|
|
456
|
+
details: text;
|
|
457
|
+
entryHash: blob;
|
|
458
|
+
id: nat;
|
|
459
|
+
operation: text;
|
|
460
|
+
success: bool;
|
|
461
|
+
timestampNs: int;
|
|
462
|
+
};
|
|
463
|
+
type AnchorView =
|
|
464
|
+
record {
|
|
465
|
+
atNs: int;
|
|
466
|
+
disputed: bool;
|
|
467
|
+
head: blob;
|
|
468
|
+
total: nat;
|
|
469
|
+
};
|
|
470
|
+
type AnchorStats =
|
|
471
|
+
record {
|
|
472
|
+
engramsAnchored: nat;
|
|
473
|
+
engramsWithConflicts: nat;
|
|
474
|
+
lastAnchorAtNs: opt int;
|
|
475
|
+
totalAnchors: nat;
|
|
476
|
+
};
|
|
477
|
+
type AnchorConflict =
|
|
478
|
+
record {
|
|
479
|
+
atNs: int;
|
|
480
|
+
storedHead: blob;
|
|
481
|
+
storedTotal: nat;
|
|
482
|
+
submittedHead: blob;
|
|
483
|
+
submittedTotal: nat;
|
|
484
|
+
};
|
|
485
|
+
type AdminRoles = record {roles: vec RoleGrant;};
|
|
486
|
+
type ActivationFunnel =
|
|
487
|
+
record {
|
|
488
|
+
dailyCap: nat;
|
|
489
|
+
provisioned: nat;
|
|
490
|
+
"reserved": nat;
|
|
491
|
+
reservedStale: nat;
|
|
492
|
+
spawnCeiling: nat;
|
|
493
|
+
spawnsToday: nat;
|
|
494
|
+
};
|
|
495
|
+
service : {
|
|
496
|
+
/// Abandon the caller's engram. Owner-initiated, terminal, and NOT deletion: the record stays,
|
|
497
|
+
/// stays readable, stays verifiable. It frees the owner to create ONE new engram, which will
|
|
498
|
+
/// permanently and publicly name this one as its predecessor.
|
|
499
|
+
///
|
|
500
|
+
/// This is deliberately NOT a recovery path. Nothing moves: content stays in the old canister,
|
|
501
|
+
/// and it cannot move — the vetKD memory key and the tECDSA/EVM address both derive from the
|
|
502
|
+
/// CANISTER ID, so an engram's ciphertext and wallet cannot follow it anywhere (engramx
|
|
503
|
+
/// docs/ENCRYPTION-OPERATIONS.md, "Canister-id binding"). Abandoning is starting over, not
|
|
504
|
+
/// migrating, and the caller must be told so before they do it.
|
|
505
|
+
abandonMyEngram: () -> (Result);
|
|
506
|
+
addAuditViewer: (viewer: principal) -> (Result);
|
|
507
|
+
/// Add a changelog entry (admin only)
|
|
508
|
+
addChangelogEntry: (version: text, releaseNotes: text) -> (Result);
|
|
509
|
+
/// ⟨2026-09-28, owner decision: an agent may append a dated dispute to its own record; nothing is
|
|
510
|
+
/// deleted; nobody adjudicates⟩ The CALLER appends one dated statement to ITS OWN track record.
|
|
511
|
+
///
|
|
512
|
+
/// AGENT-ONLY BY CONSTRUCTION: the record written is `msg.caller`'s, so nobody can write to
|
|
513
|
+
/// another agent's record, and a caller with no recorded engagement is REFUSED (there is no record
|
|
514
|
+
/// of theirs to dispute). Bounded: `Engagements.MAX_DISPUTE_BYTES` per statement,
|
|
515
|
+
/// `Engagements.MAX_DISPUTES_PER_AGENT` per agent, both refused loudly with nothing stored.
|
|
516
|
+
///
|
|
517
|
+
/// It edits nothing and deletes nothing — not an engagement, not an earlier statement — and
|
|
518
|
+
/// nobody at the registry reads, answers, or rules on it. The certified summary then shows that a
|
|
519
|
+
/// dispute exists and its day; the statement's words stay out of it, readable by the agent through
|
|
520
|
+
/// `listMyAgentDisputes`. The append is recorded in the chained audit log (length, not words).
|
|
521
|
+
///
|
|
522
|
+
/// ⟨2026-09-28⟩ The caller is the agent's ROOT key — records are kept per agent (owner decision:
|
|
523
|
+
/// one root key per agent, with an enrolled key per client), so a key for one engram has no record
|
|
524
|
+
/// here and is refused like any stranger. Disputing is the root's act, not an engram key's.
|
|
525
|
+
appendAgentDispute: (statement: text) -> (Result);
|
|
526
|
+
/// Take sole custody of your engram: the registry REMOVES ITSELF as a controller, leaving the
|
|
527
|
+
/// owner and the engram canister itself, and your update preference becomes `#ManualOnly`.
|
|
528
|
+
///
|
|
529
|
+
/// Refuses unless you own an engram and it is `#Active`.
|
|
530
|
+
///
|
|
531
|
+
/// ⚠ WHAT YOU GIVE UP, none of which is obvious from the name — the registry loses controllership,
|
|
532
|
+
/// so from here on:
|
|
533
|
+
/// · `upgradeMyEngram` and `upgradeEngram` can no longer install code on it. You upgrade it
|
|
534
|
+
/// yourself, including security fixes.
|
|
535
|
+
/// · `canister_status` is no longer readable by the registry, so your engram drops out of
|
|
536
|
+
/// `fleetCycleHealth` — it is counted in `sovereignSkipped` rather than watched.
|
|
537
|
+
///
|
|
538
|
+
/// ⚠ IT IS REVERSIBLE, which is the fact most worth publishing: the engram's own
|
|
539
|
+
/// `returnToManaged` adds the registry back ALONGSIDE the owner. Sovereignty is a door, not a
|
|
540
|
+
/// cliff.
|
|
541
|
+
claimSovereignty: () -> (Result);
|
|
542
|
+
/// Assemble the pushed chunks into the engram template and install it as the current one.
|
|
543
|
+
///
|
|
544
|
+
/// ⚠ THE HASH IS VERIFIED, exactly as in `setEngramWasm`: the assembled bytes are hashed and
|
|
545
|
+
/// compared, so this also catches a chunk that arrived out of order or was dropped — the
|
|
546
|
+
/// reassembly is checked, not just the transport. A mismatch refuses and nothing is installed.
|
|
547
|
+
///
|
|
548
|
+
/// Refuses `#Conflict("No chunks uploaded")` when nothing was pushed, rather than installing an
|
|
549
|
+
/// empty template.
|
|
550
|
+
///
|
|
551
|
+
/// Contrast `backupFinalize`, which does NOT verify its digest. These two look like the same
|
|
552
|
+
/// protocol and are not, and the difference matters most on this one: it decides what code every
|
|
553
|
+
/// engram in the fleet will run.
|
|
554
|
+
engramWasmFinalize: (hash: text) -> (Result_4);
|
|
555
|
+
/// Chunked WASM upload — for WASMs that exceed single-call ingress limits.
|
|
556
|
+
/// Flow: engramWasmInit → engramWasmPushChunk (×N) → engramWasmFinalize(hash)
|
|
557
|
+
/// ⟨0.16.0 — CORRECTED 2026-08-19⟩ This line named all THREE pre-rename methods, none of which
|
|
558
|
+
/// this canister serves. It is the only written record of the upload protocol, and the one
|
|
559
|
+
/// hand-IDL client that follows it (engramx scripts/upload-engram-wasm.mjs) had drifted the same
|
|
560
|
+
/// way — the upload died at its first call with IC0536. A stale flow comment is not cosmetic here.
|
|
561
|
+
engramWasmInit: () -> (Result);
|
|
562
|
+
/// Push one chunk of a chunked template upload started by `engramWasmInit`. Returns the running
|
|
563
|
+
/// chunk count. WASM-admin only, like every step of this family.
|
|
564
|
+
///
|
|
565
|
+
/// Two `#Conflict` ceilings, and BOTH tell you to restart rather than to retry: too many chunks,
|
|
566
|
+
/// and total bytes over the cap. Neither is recoverable by pushing again — call `engramWasmInit`
|
|
567
|
+
/// to clear and begin afresh. The messages say so because a caller who retries a chunk against a
|
|
568
|
+
/// full buffer will retry forever.
|
|
569
|
+
engramWasmPushChunk: (chunk: blob) -> (Result_4);
|
|
570
|
+
/// Fleet cycle-health scan. Admin or metrics-viewer, PAGINATED with a hard max of 50 per page —
|
|
571
|
+
/// each managed engram costs one `canister_status` await, so pages stay small to bound the
|
|
572
|
+
/// message. A caller must expect to iterate.
|
|
573
|
+
///
|
|
574
|
+
/// ⚠ IT DOES NOT SEE THE WHOLE FLEET, and reading it as though it does is the error to avoid.
|
|
575
|
+
/// SOVEREIGN engrams are skipped: the registry is not their controller, so `canister_status`
|
|
576
|
+
/// would trap. They are counted in `sovereignSkipped` rather than silently omitted — an
|
|
577
|
+
/// unwatchable engram and a healthy one must not look the same. A trap for any other reason
|
|
578
|
+
/// lands in `unreadable`, never fatal to the scan.
|
|
579
|
+
///
|
|
580
|
+
/// COUNTS ONLY — no per-engram data leaves the canister.
|
|
581
|
+
///
|
|
582
|
+
/// `thresholdCycles = 0` means the default 1T. An engram ALSO counts as `lowCycles` below twice
|
|
583
|
+
/// its LIVE freezing reservation, which is memory-scaled, so a large engram can be low at a
|
|
584
|
+
/// balance a small one would be comfortable at.
|
|
585
|
+
///
|
|
586
|
+
/// ⟨L7⟩ UPDATE, NOT QUERY, for BOTH of the reasons this law recognises — which is why it is worth
|
|
587
|
+
/// stating rather than leaving to be inferred from a body that scans a fleet. It awaits
|
|
588
|
+
/// `ic.canister_status` once per engram in the page, so no query could produce the figures at all;
|
|
589
|
+
/// and it calls `denied()` on refusal, a state write a query cannot perform, so making it a query
|
|
590
|
+
/// would also retire the record of who was turned away (ADR-0002's class, the same one
|
|
591
|
+
/// `listEngrams` settled at its own declaration).
|
|
592
|
+
///
|
|
593
|
+
/// ⟨Marker added 2026-08-23. The census row for this method already cited L7 and the site did not
|
|
594
|
+
/// carry it — invisible to `check-update-for-read.mjs`, whose population is read-VERB names and
|
|
595
|
+
/// this one is a noun. That gate now reads the census's L7 citations as a second population for
|
|
596
|
+
/// exactly this blind spot.⟩
|
|
597
|
+
fleetCycleHealth: (offset: nat, limit: nat, thresholdCycles: nat) ->
|
|
598
|
+
(Result_21);
|
|
599
|
+
/// PUBLIC: ⟨0.16.0, L2.4⟩ `fleetSummary` — the ONE public projection of this registry,
|
|
600
|
+
/// replacing `status()` and ABSORBING `publicMetrics()`.
|
|
601
|
+
///
|
|
602
|
+
/// Two public aggregates of one canister was the same L2.4 defect the engram had with
|
|
603
|
+
/// status()/metrics(): both answered "what is this registry like", they disagreed about which
|
|
604
|
+
/// counters they carried (status had cycles and the wasm hash, publicMetrics had unique owners,
|
|
605
|
+
/// sovereign engrams and registrySince), and a caller had to know which one held the field they
|
|
606
|
+
/// wanted. storage-sweep.sh cross-checked one against the other to infer a fact neither stated.
|
|
607
|
+
///
|
|
608
|
+
/// Merged, not concatenated: `totalEngrams` and `activeEngrams` appeared in BOTH and are counted
|
|
609
|
+
/// once here, by the same pass.
|
|
610
|
+
///
|
|
611
|
+
/// Everything in it identifies nobody — counts, a version, a hash, a cycle balance — which is why
|
|
612
|
+
/// it is ungated and marked for the L8 gate-grep.
|
|
613
|
+
fleetSummary: () -> (FleetSummary) query;
|
|
614
|
+
/// When an engram was abandoned, if it was.
|
|
615
|
+
///
|
|
616
|
+
/// PUBLIC: a stranger evaluating an engram must be able to see that its owner walked away from
|
|
617
|
+
/// it. Free, like getRentStatus — and for the same reason, since abandonment is the fact that
|
|
618
|
+
/// makes a lapsed rent term mean something different.
|
|
619
|
+
getAbandonedAtNs: (canisterId: principal) -> (opt int) query;
|
|
620
|
+
/// Acquisition -> activation funnel (deferred provisioning). reserved = signed up but the canister
|
|
621
|
+
/// hasn't spawned yet (each holds a live cycle earmark); provisioned = active engrams (activated);
|
|
622
|
+
/// reservedStale = past the TTL and pending prune (lapsed earmarks, released on the next reserve/
|
|
623
|
+
/// provision) — the "stuck between acquisition and activation" cohort.
|
|
624
|
+
/// Conversion = provisioned / (reserved + provisioned). (Query: can't prune here, so stale may be > 0.)
|
|
625
|
+
///
|
|
626
|
+
/// PUBLIC: anonymous fleet observability — four counts, no principal and no per-engram row. The
|
|
627
|
+
/// fleet's size and how much of it is stuck are claims this project makes about itself, and a
|
|
628
|
+
/// claim nobody can check is not one.
|
|
629
|
+
getActivationFunnel: () -> (ActivationFunnel) query;
|
|
630
|
+
/// PUBLIC: who holds authority here. A refusal a caller cannot attribute is a refusal they
|
|
631
|
+
/// cannot appeal, so the roster is readable by everyone the roster can refuse.
|
|
632
|
+
///
|
|
633
|
+
/// ⟨0.16.0, L2.2⟩ The shared `AdminRoles` shape, not a registry-specific record. It was
|
|
634
|
+
/// `{ admin; wasmAdmin }`, which could not express the ROOT identity at all — the principal that
|
|
635
|
+
/// bootstraps the first admin and can reclaim the role was simply absent from the published
|
|
636
|
+
/// answer, so "who can take this canister back" was a fact only the source told you. A list of
|
|
637
|
+
/// (role, holder) carries every authority this canister has, and the same shape on every canister
|
|
638
|
+
/// means one reader answers the question fleet-wide.
|
|
639
|
+
getAdminRoles: () -> (AdminRoles) query;
|
|
640
|
+
/// PUBLIC: ⟨0.16.0⟩ RENAMED from `operatorEdgeStats`, and the GATE IS DROPPED.
|
|
641
|
+
///
|
|
642
|
+
/// The old refusal existed because a ZEROED aggregate reads as "no agent runs any engram" — a
|
|
643
|
+
/// growth figure the status console renders as fact — and an unreadable aggregate must show as
|
|
644
|
+
/// UNKNOWN, never as zero. That reasoning was right about the danger and wrong about the remedy:
|
|
645
|
+
/// two counts that name nobody are not a disclosure, and gating them created the very
|
|
646
|
+
/// unreadable-aggregate problem the comment was worried about. Answering everyone removes both
|
|
647
|
+
/// the fabricated zero AND the need to distinguish it from a real one.
|
|
648
|
+
///
|
|
649
|
+
/// ⟨2026-09-28⟩ `distinctOperators` now counts distinct AGENTS (roots), not pairings: the index is
|
|
650
|
+
/// keyed by root since the owner chose one root key per agent with an enrolled key per client, so
|
|
651
|
+
/// an agent on twenty-five engrams counts once. The field keeps its name because a hand-written
|
|
652
|
+
/// reader of it exists outside this repo; the meaning is the one that name always claimed.
|
|
653
|
+
/// ⟨CORRECTED 2026-09-29: one name for one thing — both fields are renamed.⟩
|
|
654
|
+
/// · `agents` (was `distinctOperators`): agents, by root key, with at least one live
|
|
655
|
+
/// engagement. The old name was kept for a hand-written reader outside this repo, which has
|
|
656
|
+
/// since had to translate it back to "agents" in two places.
|
|
657
|
+
/// · `liveEngagements` (was `totalEdges`): agent–engram engagements live NOW, one per agent
|
|
658
|
+
/// per engram. "Total" read as all-time, but an engagement leaves this count the moment it
|
|
659
|
+
/// ends (the live index forgets it; the agent's record keeps it — `getAgentStanding`), and
|
|
660
|
+
/// "edge" is a word for the index, not for anything a reader counts.
|
|
661
|
+
/// Both come from the live index (`agentToEngrams`), which `sysRecordAgentEdge` updates in
|
|
662
|
+
/// the same step as the agent's record.
|
|
663
|
+
getAgentEdgeStats: () -> (record {
|
|
664
|
+
agents: nat;
|
|
665
|
+
liveEngagements: nat;
|
|
666
|
+
}) query;
|
|
667
|
+
/// ⟨2026-09-28, owner decisions: build the full agent track record; an agent may read its own
|
|
668
|
+
/// record⟩ ONE AGENT'S CERTIFIED TRACK RECORD — counts by ending reason, live engagements, the
|
|
669
|
+
/// day of the first engagement, and whether (and on what day) the agent disputed it.
|
|
670
|
+
///
|
|
671
|
+
/// SERVES THE STORED BYTES, on the `getCertifiedAnchor` pattern: the leaf is decoded from exactly
|
|
672
|
+
/// what `putStandingLeaf` certified, so what is served equals what is proven by construction.
|
|
673
|
+
/// Verify as for `getCertifiedAnchor`, path ["standing", <agent principal>].
|
|
674
|
+
///
|
|
675
|
+
/// OPEN, with one refusal. A proof a caller must hold a role to fetch rests on the authority it
|
|
676
|
+
/// was meant to replace, so there is no role gate. But a record spanning fewer than
|
|
677
|
+
/// `Engagements.STANDING_MIN_DISTINCT_ENGRAMS` distinct engrams is served ONLY to the agent itself:
|
|
678
|
+
/// at one or two engrams the summary IS their owners' experience, which is identification by
|
|
679
|
+
/// inference (R-6 §4.5 L4-1 — our own reading of the law, not legal advice). That refusal is
|
|
680
|
+
/// LOUD, names this method, and covers a record of zero engagements with the same words, so the
|
|
681
|
+
/// refusal itself does not distinguish "never engaged" from "engaged in one to four engrams".
|
|
682
|
+
/// The agent itself reading an empty record gets `#NotFound` — it is asking about itself, so
|
|
683
|
+
/// there is nothing to hide from it.
|
|
684
|
+
///
|
|
685
|
+
/// ⟨2026-09-28, review finding OPS-2⟩ A THIN SELF-READ IS UNCERTIFIED: below the threshold the
|
|
686
|
+
/// record is not in the certified tree at all (its hash would leak it through every other
|
|
687
|
+
/// witness), so the agent is served its summary folded from the records, with `certificate =
|
|
688
|
+
/// null` and an empty `witness`. At and above the threshold every reader gets the stored bytes
|
|
689
|
+
/// with their proof.
|
|
690
|
+
///
|
|
691
|
+
/// ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ The threshold counts TOPS, not engrams
|
|
692
|
+
/// (`Engagements.distinctTops`): engrams opened from one engram count as one, and an engram counts
|
|
693
|
+
/// as one with the engrams it replaced. The refusal's sentence says so.
|
|
694
|
+
///
|
|
695
|
+
/// WHAT IT NEVER DOES: score, rank, recommend, or refuse anyone work. The decision to refuse an
|
|
696
|
+
/// agent belongs to the owner's own engram (L4-3).
|
|
697
|
+
///
|
|
698
|
+
/// ⟨2026-09-28, owner decision: one root key per agent, with an enrolled key per client⟩ `agent`
|
|
699
|
+
/// is the agent's ROOT principal — the identity an owner sees as `root` on each key in their own
|
|
700
|
+
/// `listAgents`. "The agent itself" is a caller holding that root key. A key for one engram has no
|
|
701
|
+
/// record of its own here; asking about one gets the refusal or, from the key itself, `#NotFound`.
|
|
702
|
+
getAgentStanding: (agent: principal) -> (Result_20) query;
|
|
703
|
+
/// Fleet-wide anchoring liveness. Exists because ABSENCE is otherwise uninformative: an engram
|
|
704
|
+
/// that never wrote, one still on a template without the push, and one whose push is broken all
|
|
705
|
+
/// render as an empty getAnchors. Nothing distinguishes the first from the third, so a silently
|
|
706
|
+
/// dead push looks exactly like a quiet engram (consumer finding, 2026-08-03).
|
|
707
|
+
///
|
|
708
|
+
/// A non-zero engramsAnchored with a recent lastAnchorAtNs is positive evidence that the path is
|
|
709
|
+
/// alive SOMEWHERE, which is what makes an individual engram's silence interpretable.
|
|
710
|
+
///
|
|
711
|
+
/// PUBLIC: the same reader who needs the anchors needs to know whether to believe their absence.
|
|
712
|
+
getAnchorStats: () -> (AnchorStats) query;
|
|
713
|
+
/// PUBLIC: the chain head exists to be compared, so an external monitor can detect tampering
|
|
714
|
+
/// without admin access (D3). A head only whoever runs this deployment can read witnesses nothing.
|
|
715
|
+
getAuditHead: () -> (AuditHead) query;
|
|
716
|
+
/// An engram's LATEST witnessed anchor, certified. The leaf (Types.CertifiedAnchorLeaf, Candid —
|
|
717
|
+
/// decode with to_candid's standard encoding) carries the anchor AND its conflict count, so the
|
|
718
|
+
/// certified answer is self-contained: a verifier learns "this head, at this count, disputed or
|
|
719
|
+
/// not" from subnet-signed data alone. null = no anchor witnessed (which getAnchorStats makes
|
|
720
|
+
/// interpretable). Same verification recipe as getCertifiedHead, path ["anchors", <principal>].
|
|
721
|
+
///
|
|
722
|
+
/// PUBLIC: a proof a caller must be authorized to fetch rests on the authority it was meant to
|
|
723
|
+
/// replace. The certificate's whole value is that a stranger can check it against the NNS root
|
|
724
|
+
/// key without trusting this registry, and a stranger is exactly who cannot hold a role here.
|
|
725
|
+
getCertifiedAnchor: (canisterId: principal) -> (opt CertifiedAnchor) query;
|
|
726
|
+
/// ⟨0.16.0, L2.2⟩ RENAMED BACK to `getCertifiedAuditHead`, the fleet-wide name.
|
|
727
|
+
///
|
|
728
|
+
/// It was renamed AWAY from that on 2026-08-03, deliberately and for a good reason: the semantics
|
|
729
|
+
/// changed (certified_data became a merkle tree root covering the head AND the anchor set, so the
|
|
730
|
+
/// old certified_data == headHash byte-compare was gone), and a loud method-not-found was judged
|
|
731
|
+
/// better than a stale verifier quietly reporting verified=false forever.
|
|
732
|
+
///
|
|
733
|
+
/// THAT ARGUMENT'S PREMISE IS NOW FALSE. It protected deployed verifiers; B4 restarts every chain
|
|
734
|
+
/// at the 0.16.0 genesis and there are none. What remains is a fleet where three canisters answer
|
|
735
|
+
/// the same question under two names, which is the cost the rename was paying to avoid a hazard
|
|
736
|
+
/// that no longer exists. `witness` becomes `?Blob` to match the shared type — null where a
|
|
737
|
+
/// canister has no witness to give, rather than an empty blob that decodes as one.
|
|
738
|
+
///
|
|
739
|
+
/// Verify: (1) validate `certificate` against the IC root key; (2) extract
|
|
740
|
+
/// /canister/<id>/certified_data from it; (3) reconstruct the root of `witness` (IC-spec hash
|
|
741
|
+
/// tree, CBOR); (4) require equality; (5) lookup_path(["audit_head"]) in the witness == headHash.
|
|
742
|
+
/// total = cumulative entries the chained head covers (monotonic across trims); size = ring
|
|
743
|
+
/// occupancy (display). Anchoring keys on `total` so it stays correct once the ring trims.
|
|
744
|
+
///
|
|
745
|
+
/// PUBLIC: the certificate is the point. It lets a stranger verify against the NNS root key
|
|
746
|
+
/// instead of trusting this replica, and a proof that requires a role to fetch rests on exactly
|
|
747
|
+
/// the authority it was introduced to remove.
|
|
748
|
+
getCertifiedAuditHead: () -> (CertifiedAuditHead) query;
|
|
749
|
+
/// The ATTESTABLE custody-mode fact (agent-native model item 19): "managed" | "sovereign" |
|
|
750
|
+
/// "unknown", derived from the registry's OWN transition records (claimSovereignty /
|
|
751
|
+
/// returnToManaged maintain sovereignEngramIds) — NOT from a canister_status probe, which needs
|
|
752
|
+
/// controllership and traps for exactly the sovereign engrams it should describe. Public, free,
|
|
753
|
+
/// forever (free-parity): custody mode is a trust fact every warranted proof will carry —
|
|
754
|
+
/// managed = engramx.ai can warrant its own controller conduct; sovereign = registry-side facts
|
|
755
|
+
/// only. (Money-origin, by contrast, is payer-blind and never a record fact.)
|
|
756
|
+
/// PUBLIC: ⟨0.16.0, L3⟩ Custody as a VARIANT, and the no-claim case says WHY rather than just
|
|
757
|
+
/// being a third string.
|
|
758
|
+
///
|
|
759
|
+
/// ⟨CORRECTED 2026-08-24⟩ This block named an `#Unknown` ARM. There is none, and never was in
|
|
760
|
+
/// the shipped type: `CustodyMode` is `#Managed | #Sovereign`, and the no-claim case answers
|
|
761
|
+
/// `#Err(#NotFound)` — see the `case null` below. The argument the block makes is unchanged and
|
|
762
|
+
/// correct; the mechanism it named was not the one implemented, and a reader trusting it would
|
|
763
|
+
/// have written a match arm that does not compile.
|
|
764
|
+
///
|
|
765
|
+
/// "unknown" as text sat beside "sovereign" and "managed" as if it were a third custody mode. It
|
|
766
|
+
/// is not a mode at all — it is this registry declining to make a claim about a canister it did
|
|
767
|
+
/// not spawn, which is a different kind of answer and now looks like one.
|
|
768
|
+
///
|
|
769
|
+
/// Derived from records, never probed: a canister_status call would trap for exactly the
|
|
770
|
+
/// canisters we are not the controller of, i.e. the sovereign ones this read exists to identify.
|
|
771
|
+
getCustodyMode: (canisterId: principal) -> (Result_5) query;
|
|
772
|
+
/// Cycles-health probe — slim, cycles-only shape; the money fields moved to the cashier's
|
|
773
|
+
/// getCycleReserveStatus. Read by health-alert.mjs in registry mode.
|
|
774
|
+
///
|
|
775
|
+
/// PUBLIC: whether this registry can still afford to spawn is a fact every prospective owner
|
|
776
|
+
/// depends on and none of them can otherwise observe. Cycles only, by construction — ADR-0005
|
|
777
|
+
/// keeps the money on the cashier, so there is no balance here to withhold.
|
|
778
|
+
getCycleReserveStatus: () ->
|
|
779
|
+
(record {
|
|
780
|
+
cycleBalance: nat;
|
|
781
|
+
minOperatingReserveCycles: nat;
|
|
782
|
+
}) query;
|
|
783
|
+
/// The engram's permanent spawn ordinal — public: a number is a provable, non-sensitive
|
|
784
|
+
/// fact (its assignment is in the hash-chained audit log). null = spawned before numbering
|
|
785
|
+
/// existed, or by another registry.
|
|
786
|
+
///
|
|
787
|
+
/// ⟨0.16.0, tier-2 ruling 2026-08-17⟩ RENAMED from `sysEngramNumberOf`. The `sys` prefix names
|
|
788
|
+
/// the CALLER TIER a method was built for, and this one was never gated — it is a bare public
|
|
789
|
+
/// query, as the line above says. A prefix that reads as an authorization claim while carrying
|
|
790
|
+
/// none is worse than no prefix: it invites a reader to assume a gate that does not exist, and
|
|
791
|
+
/// an L8 sweep grepping `sys*` would have counted this as tiered surface it is not.
|
|
792
|
+
///
|
|
793
|
+
/// PUBLIC: the spawn ordinal is a permanent, dual-recorded fact about an engram that its owner
|
|
794
|
+
/// publishes by existing — engram #7 is #7 to everyone. It identifies a canister, never a person.
|
|
795
|
+
///
|
|
796
|
+
/// ⟨RENAMED 2026-10-02 from `getEngramNumber`⟩ The engram serves `getEngramNumber()` with no
|
|
797
|
+
/// argument; this registry served the same name taking the engram's id. One name with two arities
|
|
798
|
+
/// on two canisters is what a hand-written client cannot tell apart (a structural actor type
|
|
799
|
+
/// matches either target), so it gets the rule `getMarketCanister` already follows: distinct
|
|
800
|
+
/// names on distinct canisters. `…Of` says what the argument is for.
|
|
801
|
+
getEngramNumberOf: (canisterId: principal) -> (opt nat) query;
|
|
802
|
+
/// ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ What a lasting key opened through a parent:
|
|
803
|
+
/// the engram, the engram it was opened from, and the cut-off's time if there is one. GATED:
|
|
804
|
+
/// readable by the key itself (an agent asking about its own), the owner of the engram it was
|
|
805
|
+
/// opened from (whose console shows it), an admin or an audit viewer. Anyone else is REFUSED — the
|
|
806
|
+
/// same words whether or not the key opened anything, so the refusal tells a stranger nothing.
|
|
807
|
+
/// `#NotFound` only to a permitted reader, for a key that opened nothing: a true answer, like
|
|
808
|
+
/// `listMyAgentDisputes`'s empty list.
|
|
809
|
+
getEngramOpenedThrough: (key: principal) -> (Result_19) query;
|
|
810
|
+
/// The i-th 1 MiB slice of the attested engram wasm (0-indexed). The caller reassembles all
|
|
811
|
+
/// chunks in order and MUST verify sha256(reassembled) == getEngramWasmHash before installing —
|
|
812
|
+
/// the hash is the attestation; the bytes are a convenience. Out-of-range index → empty blob.
|
|
813
|
+
/// PUBLIC: ⟨0.16.0, L11⟩ An out-of-range index is `#NotFound`, not an empty blob.
|
|
814
|
+
///
|
|
815
|
+
/// The empty blob was indistinguishable from a legitimately empty chunk AND from "no wasm is
|
|
816
|
+
/// staged at all" — and this is the surface a verifier walks to reassemble a template and check
|
|
817
|
+
/// its hash. A reassembler that reads `""` as end-of-stream cannot tell a finished download from
|
|
818
|
+
/// a truncated one, so it would hash a partial wasm and report a mismatch against the official
|
|
819
|
+
/// list, which reads as "this template is unofficial" rather than "I stopped reading too early".
|
|
820
|
+
getEngramWasmChunk: (index: nat) -> (Result_18) query;
|
|
821
|
+
/// The stored template's release class — FREE and UNGATED, because it is the checkable half of a
|
|
822
|
+
/// promise made to people who are not our customers. Pair it with getRentStatus to verify the
|
|
823
|
+
/// funding rule was applied: #Security funds every reachable engram; #Feature funds metered ones.
|
|
824
|
+
/// PUBLIC: ⟨0.16.0, L3⟩ The release class as a VARIANT, not a stringly-typed enum.
|
|
825
|
+
///
|
|
826
|
+
/// It returned "security" or "feature" as `Text`, so every consumer compared strings and no
|
|
827
|
+
/// compiler anywhere could catch a typo or a third class arriving. A variant makes the set closed
|
|
828
|
+
/// and the comparison checked — and this value gates whether an upgrade is funded, so a caller
|
|
829
|
+
/// that silently fails to match "security" funds nothing and reports no error.
|
|
830
|
+
getEngramWasmClass: () -> (ReleaseClass) query;
|
|
831
|
+
/// The WASM hash of the engram template this registry will install.
|
|
832
|
+
///
|
|
833
|
+
/// PUBLIC: it is the thing `isOfficialEngramWasm` is checked AGAINST. An owner about to accept a
|
|
834
|
+
/// spawn, and a stranger auditing one that already happened, both need to read what this registry
|
|
835
|
+
/// says it installs — from the registry, not from a release note that could describe a different
|
|
836
|
+
/// build.
|
|
837
|
+
getEngramWasmHash: () -> (text) query;
|
|
838
|
+
/// Total attested-wasm size + chunk size, so a puller knows how many getEngramWasmChunk calls to make.
|
|
839
|
+
///
|
|
840
|
+
/// PUBLIC: the pull path it describes is itself ungated, so a gate here would only stop a caller
|
|
841
|
+
/// planning the calls it is already permitted to make.
|
|
842
|
+
getEngramWasmInfo: () -> (EngramWasmInfo) query;
|
|
843
|
+
/// ⟨L7⟩ UPDATE, NOT QUERY, and NOT because of a system call — this body awaits nothing. The
|
|
844
|
+
/// update exists so the DENIAL IS RECORDED: `denied()` is a state write, and a query cannot
|
|
845
|
+
/// write. Exactly the argument `listEngrams` settled at its own declaration, and the same
|
|
846
|
+
/// conclusion: a query here would silently retire the record of who was refused.
|
|
847
|
+
///
|
|
848
|
+
/// So L7's "the denial-logging motive for update-for-read is retired by L1" does not hold at this
|
|
849
|
+
/// site. L1 retired the motive of trapping-to-refuse; it did not give queries the ability to
|
|
850
|
+
/// write, which is the thing this actually depends on.
|
|
851
|
+
getFleetMetrics: () -> (Result_17);
|
|
852
|
+
/// PUBLIC: a genesis note that only privileged callers could read would defeat its purpose —
|
|
853
|
+
/// the reader who most needs it is the stranger deciding whether to trust this chain.
|
|
854
|
+
getGenesisNote: () -> (GenesisNote) query;
|
|
855
|
+
/// What the last upgrade attempt actually DID — the additive answer to "was I upgraded, or was
|
|
856
|
+
/// I already current?". A #Skipped result variant would have broken every deployed decoder and
|
|
857
|
+
/// scored a skip as a FAILURE in our own tooling (which greps the result for "Ok"). PUBLIC:
|
|
858
|
+
/// an owner deciding whether their engram got a security fix should not need a key to find out.
|
|
859
|
+
/// ⚠ `at` is the time of the last upgrade CALL, including a no-op on an already-current engram.
|
|
860
|
+
/// It does NOT date the engram's running code — a re-run fleet push marches `at` forward while
|
|
861
|
+
/// nothing changes. Use `changedAt` for that: it is stamped only when code actually changed, and
|
|
862
|
+
/// is null for an engram that has never been upgraded since this field shipped.
|
|
863
|
+
/// ⟨0.16.0, L4⟩ `atNs`/`changedAtNs`: ns timestamps carry the suffix. The honesty split the two
|
|
864
|
+
/// fields encode is unchanged — `atNs` is when THIS registry last upgraded the canister, and
|
|
865
|
+
/// `changedAtNs` is when its module hash last moved by any hand, so a self-upgrade or a manual
|
|
866
|
+
/// install shows up as a change with no matching upgrade. Renaming here is free: the record is
|
|
867
|
+
/// built at the query boundary, and the stable state it reads keeps its own spelling.
|
|
868
|
+
/// ⟨CORRECTED 2026-10-02: "by any hand … shows up" is false, and so is "stamped only when code
|
|
869
|
+
/// actually changed" above, read without its limit.⟩ `changedAtNs` is written at exactly two
|
|
870
|
+
/// places, both right after THIS registry's own successful install on the engram
|
|
871
|
+
/// (`upgradeMyEngram` and `upgradeEngram`), in the same step that writes `atNs` with
|
|
872
|
+
/// `upgraded = true`. Nothing here reads the engram's module hash from the network to stamp it. So
|
|
873
|
+
/// an install made by any other controller — a manual upgrade or reinstall — or a sovereign
|
|
874
|
+
/// engram's own upgrade does NOT move it, and does NOT show up in this read: it dates only the code
|
|
875
|
+
/// changes this registry made. To see a change made by another hand, read the engram's module hash
|
|
876
|
+
/// from the network directly (it is public in the canister's state) and check it with
|
|
877
|
+
/// `isOfficialEngramWasm`; nothing in this canister makes that comparison for you.
|
|
878
|
+
getLastUpgrade: (canisterId: principal) -> (opt LastUpgrade) query;
|
|
879
|
+
/// Named `getMarketCanister`, NOT `getMarketId` — the engram already serves `getMarketId`, and a
|
|
880
|
+
/// hand-written client IDL that names the same method on two canisters goes undetected by both
|
|
881
|
+
/// drift gates (a structural actor type matches either target). Distinct names on distinct
|
|
882
|
+
/// canisters is the cheap prevention.
|
|
883
|
+
///
|
|
884
|
+
/// PUBLIC: every newborn engram is told this id at spawn and gates its signing on it, so a
|
|
885
|
+
/// counterparty deciding whether to trust an engram's market claim needs to read the same value
|
|
886
|
+
/// the engram was given. `null` says the pointer is unset, which is a fact worth serving too —
|
|
887
|
+
/// it is why `requireMarket` refuses fleet-wide.
|
|
888
|
+
getMarketCanister: () -> (opt principal) query;
|
|
889
|
+
/// The metrics reader every newborn engram is told — `null` until wired.
|
|
890
|
+
///
|
|
891
|
+
/// PUBLIC: an owner deciding whether to turn their engram's reader off needs to know who it is,
|
|
892
|
+
/// and a stranger comparing an engram's own copy (which it serves) against the registry's needs
|
|
893
|
+
/// the same value. A canister id is not a secret; a pointer that will not name its holder cannot
|
|
894
|
+
/// be checked by anyone.
|
|
895
|
+
getMetricsReader: () -> (opt principal) query;
|
|
896
|
+
/// The caller's engram record, or `null` if they have none. One engram per owner, so this is a
|
|
897
|
+
/// single record rather than a page.
|
|
898
|
+
getMyEngram: () -> (opt EngramRecordView) query;
|
|
899
|
+
/// The caller's reservation, if they signed up but have not yet provisioned — no canister has
|
|
900
|
+
/// spawned yet. `null` once `provisionEngram` succeeds, or if they never reserved.
|
|
901
|
+
///
|
|
902
|
+
/// This is also how the half-finished state is made visible: reserve and provision are two calls,
|
|
903
|
+
/// so a caller can hold a reservation with no engram. Seeing it here is what tells them to retry
|
|
904
|
+
/// provision rather than reserve again.
|
|
905
|
+
getMyReservation: () -> (opt ReservationView) query;
|
|
906
|
+
/// ⟨2026-10-02⟩ How many engrams were ever opened, and how — lifetime sizes of state this registry
|
|
907
|
+
/// never deletes, plus the two counters of refused openings (`Funnel.OpeningCounts` names each).
|
|
908
|
+
/// All O(1); a reader differences them day to day.
|
|
909
|
+
///
|
|
910
|
+
/// CALLERS: the admin and the audit viewers (the deployment's metrics reader holds a viewer
|
|
911
|
+
/// grant). Anyone else is REFUSED by name with their class — never a zero, which would read as
|
|
912
|
+
/// "nothing was ever opened". A QUERY: counts only, and cheap; its refusal is not in the denial
|
|
913
|
+
/// log (`denied` says why a query cannot write).
|
|
914
|
+
getOpeningCounts: () -> (Result_16) query;
|
|
915
|
+
/// ⟨2026-10-02⟩ How far agents reach across owners — COUNTS ONLY: agents by how many trees their
|
|
916
|
+
/// record spans (1 · 2 · 3–4 · 5+), engrams opened through a parent that are live or cut off, and
|
|
917
|
+
/// engrams that paired an agent within seven days of opening. No principal, no row, no per-agent
|
|
918
|
+
/// figure (`Funnel.referralCounts` says what is folded and how `exclude` drops a tree).
|
|
919
|
+
///
|
|
920
|
+
/// `exclude`: the deployment's own accounts (owner keys, agent roots, engram ids), at most 1,000.
|
|
921
|
+
/// An agent on it is dropped whole; an engagement whose engram's tree touches it is dropped.
|
|
922
|
+
///
|
|
923
|
+
/// CALLERS: the admin and the audit viewers (the deployment's metrics reader holds a viewer grant),
|
|
924
|
+
/// weekly. Anyone else is REFUSED by name with their class — never zeros. A QUERY: its cost grows
|
|
925
|
+
/// with the agents and engrams it walks (one pass over each), so a reader asks weekly, not hourly.
|
|
926
|
+
getReferralCounts: (exclude: vec principal) -> (Result_15) query;
|
|
927
|
+
/// PUBLIC: the biller identity is an operational fact a
|
|
928
|
+
/// payer's cashier must verify BEFORE taking money (paying into a cashier the registry does not
|
|
929
|
+
/// recognize as biller would collect funds it cannot convert into credit).
|
|
930
|
+
getRentBiller: () -> (opt principal) query;
|
|
931
|
+
/// Public transparency: is the gate armed, and which identity issuers are exempt?
|
|
932
|
+
/// ⟨0.16.0 — census row⟩ Gains `inviteIssuer`, closing the R5 setter-without-a-getter gap
|
|
933
|
+
/// without adding a method: `setInviteIssuer` could write a principal that nothing could read
|
|
934
|
+
/// back, so "who may mint reserve invites" was unanswerable from outside.
|
|
935
|
+
///
|
|
936
|
+
/// It serves the EFFECTIVE issuer, not the raw `?Principal`. The stored value is null until
|
|
937
|
+
/// someone sets it and `effectiveInviteIssuer()` then falls back to the admin — which is what
|
|
938
|
+
/// `mintReserveInvites` actually gates on. Publishing the option would make every caller
|
|
939
|
+
/// re-implement that fallback to learn the same fact, and a caller that forgot would read `null`
|
|
940
|
+
/// as "nobody may mint", which is the opposite of the truth.
|
|
941
|
+
///
|
|
942
|
+
/// PUBLIC: whether the reserve gate is armed, and who may mint into it, decides whether a
|
|
943
|
+
/// stranger can sign up at all. An admission rule only the admitted can read is not a rule a
|
|
944
|
+
/// prospective user can act on.
|
|
945
|
+
getReserveGateConfig: () -> (ReserveGateConfig) query;
|
|
946
|
+
/// PUBLIC: transparency without a doxx — how many principals the deployment currently screens (0 = seam
|
|
947
|
+
/// disarmed, the shipped default). The list itself is never enumerable publicly.
|
|
948
|
+
getScreeningDenylistSize: () -> (nat) query;
|
|
949
|
+
/// How many engram template releases this registry has declared `#Security` — the class that
|
|
950
|
+
/// skips any delay and is funded for every engram. ⟨2026-09-28, owner decision: publish the
|
|
951
|
+
/// count only; the status page shows the number.⟩ It moves only when a template's class is
|
|
952
|
+
/// declared INTO `#Security` (`setEngramWasmClass`), never on `#Feature`, never down.
|
|
953
|
+
/// ⟨CORRECTED 2026-09-29⟩ It counts TEMPLATES, not declarations: each distinct template (by its
|
|
954
|
+
/// module hash) counts once, the first time it is declared `#Security`. Re-declaring it — which
|
|
955
|
+
/// the deploy does after every upload — or flipping it to `#Feature` and back changes nothing.
|
|
956
|
+
/// Before this, one security release deployed five times counted five.
|
|
957
|
+
///
|
|
958
|
+
/// PUBLIC: it is the checkable half of an unenforced policy — the owner published the intention
|
|
959
|
+
/// and the feature/security rule without code holding us to it, so the one thing a stranger can
|
|
960
|
+
/// watch is how often we reach for the class that skips the wait. A gated count would be a check
|
|
961
|
+
/// on us that only we can read.
|
|
962
|
+
getSecurityReleaseCount: () -> (nat) query;
|
|
963
|
+
/// ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ Cumulative cycles accepted at the sponsored
|
|
964
|
+
/// door (`sysSpawnChild`) and forwarded into engrams opened through another — cycles the calling
|
|
965
|
+
/// engrams attached, never the registry's own. Kept apart from the grant counter
|
|
966
|
+
/// (`totalCyclesAllocated`) so neither figure is inflated by the other.
|
|
967
|
+
///
|
|
968
|
+
/// PUBLIC: for the reason `getUpgradeSubsidyTotal` is — a number we would not publish is one we
|
|
969
|
+
/// should not forward. It names no engram and no owner. (⟨2026-10-01⟩ The marker was first written
|
|
970
|
+
/// "PUBLIC," and a rationale check that looks for the colon form read the method as unexplained.)
|
|
971
|
+
getSponsoredCyclesTotal: () -> (nat) query;
|
|
972
|
+
/// Which canister's money chain this registry anchors — `null` until wired, the honest unwired
|
|
973
|
+
/// answer rather than a placeholder principal.
|
|
974
|
+
///
|
|
975
|
+
/// PUBLIC: an anchor is a claim that one chain witnesses another, and a claim that will not name
|
|
976
|
+
/// its counterparty cannot be checked by anyone. A canister id is not a secret; withholding it
|
|
977
|
+
/// would leave a verifier holding an anchor with nothing to anchor it to.
|
|
978
|
+
getTreasuryAnchorId: () -> (opt principal) query;
|
|
979
|
+
/// ⟨0.16.0, L11⟩ `null` MEANS NEVER CHOSE. It used to mean `#AutoUpdate`.
|
|
980
|
+
///
|
|
981
|
+
/// Returning the shipped default for a caller who has never expressed a preference is a
|
|
982
|
+
/// fabricated CONSENT: the registry answered "this owner opted into automatic upgrades" when the
|
|
983
|
+
/// truth was "this owner has never been asked". The two are different facts and only one of them
|
|
984
|
+
/// is a decision, and `listUpdatePrefs`' own refusal message names the same hazard from the other
|
|
985
|
+
/// end — a successor seeded from those defaults silently reverses every explicit ManualOnly choice.
|
|
986
|
+
///
|
|
987
|
+
/// The default still APPLIES; it is just no longer REPORTED as a choice. A caller that wants
|
|
988
|
+
/// effective behaviour reads null as "AutoUpdate applies", and one that wants to know whether the
|
|
989
|
+
/// owner ever decided can finally tell.
|
|
990
|
+
getUpdatePreference: () -> (opt UpdatePreference) query;
|
|
991
|
+
/// Which template an engram has already been FUNDED for, if any — the anti-drain ledger made
|
|
992
|
+
/// observable. Admin/viewer-gated: it reveals which engrams needed help, which is a nudge toward
|
|
993
|
+
/// who is underfunded.
|
|
994
|
+
///
|
|
995
|
+
/// This exists because the ledger was the least-verifiable thing in the release. It is the guard
|
|
996
|
+
/// that bounds funding to one deposit per engram per release even when install_code never
|
|
997
|
+
/// succeeds — closing the interleave drain where an owner burns the deposit between our
|
|
998
|
+
/// canister_status read and the install, leaving the module hash unchanged and the engram
|
|
999
|
+
/// eligible again. Its upgrade-coverage was "acknowledged", not "tested", precisely because
|
|
1000
|
+
/// nothing could observe it. Untestable state guarding against a farming attack is the wrong
|
|
1001
|
+
/// thing to leave unobserved.
|
|
1002
|
+
/// ⟨0.16.0⟩ The refusal becomes a value, and the envelope finally separates the two nulls.
|
|
1003
|
+
///
|
|
1004
|
+
/// `?Text` had to carry both "you may not ask" and "this engram was never funded for an upgrade"
|
|
1005
|
+
/// — and the second is a claim about OUR OWN SPEND, not an absence of one. `#Ok(null)` now means
|
|
1006
|
+
/// never funded, `#Err(#Refused)` means not your business, and no caller has to guess which.
|
|
1007
|
+
getUpgradeFundedTemplate: (canisterId: principal) -> (Result_14) query;
|
|
1008
|
+
/// Cumulative cycles the registry has granted to engrams for upgrades — the meter on the
|
|
1009
|
+
/// never-gated pull path.
|
|
1010
|
+
///
|
|
1011
|
+
/// PUBLIC: a subsidy we would not publish is one we should not spend. Free and ungated.
|
|
1012
|
+
getUpgradeSubsidyTotal: () -> (nat) query;
|
|
1013
|
+
/// Current upgrade top-up floor. PUBLIC: it is a spending policy, and an owner deciding whether
|
|
1014
|
+
/// they must fund an upgrade themselves deserves to know whether the registry will.
|
|
1015
|
+
getUpgradeTopUpFloor: () -> (nat) query;
|
|
1016
|
+
/// PUBLIC: a wallet calls this BEFORE its user has any standing with this registry — that is what
|
|
1017
|
+
/// the call is for — so gating it would make the message unreadable at exactly the moment it is
|
|
1018
|
+
/// needed, and a signature given without it is a signature given blind.
|
|
1019
|
+
icrc21_canister_call_consent_message: (request:
|
|
1020
|
+
record {
|
|
1021
|
+
arg: blob;
|
|
1022
|
+
method: text;
|
|
1023
|
+
user_preferences: record {};
|
|
1024
|
+
}) ->
|
|
1025
|
+
(variant {
|
|
1026
|
+
Err: record {
|
|
1027
|
+
description: text;
|
|
1028
|
+
error_code: nat;
|
|
1029
|
+
};
|
|
1030
|
+
Ok:
|
|
1031
|
+
record {
|
|
1032
|
+
consent_message: variant {GenericDisplayMessage: text;};
|
|
1033
|
+
metadata: record {
|
|
1034
|
+
language: text;
|
|
1035
|
+
utc_offset_minutes: opt int16;
|
|
1036
|
+
};
|
|
1037
|
+
};
|
|
1038
|
+
}) query;
|
|
1039
|
+
/// Is `hash` (lowercase hex sha256) a build we published? The trust question for an engram whose
|
|
1040
|
+
/// code we cannot verify by controllership — a sovereign engram, or a managed one whose owner is
|
|
1041
|
+
/// a co-controller and could have installed their own wasm.
|
|
1042
|
+
///
|
|
1043
|
+
/// PUBLIC: this IS the verification surface, and a verification surface nobody can call verifies
|
|
1044
|
+
/// nothing. Free and ungated for the same reason.
|
|
1045
|
+
isOfficialEngramWasm: (hash: text) -> (bool) query;
|
|
1046
|
+
/// "Is this a registered, `#Active` engram?" — no owner argument, only a Bool. The cashier uses
|
|
1047
|
+
/// it to authorize an engram funding its OWN cycles: on that path the caller IS the engram
|
|
1048
|
+
/// canister, and this confirms it is a live one.
|
|
1049
|
+
///
|
|
1050
|
+
/// PUBLIC: it returns strictly less than `verifyEngramActiveOwner` above and leaks no
|
|
1051
|
+
/// canisterId→owner mapping — the very leak that kept the deleted `getEngramByCanisterId` gated.
|
|
1052
|
+
/// Gating a strictly narrower answer than the ungated one beside it would protect nothing.
|
|
1053
|
+
isRegisteredActiveEngram: (canisterId: principal) -> (bool) query;
|
|
1054
|
+
/// ⟨0.16.0⟩ RENAMED from `engramsOperatedBy`; the refusal becomes a value.
|
|
1055
|
+
///
|
|
1056
|
+
/// `[]` here is a SUBSTANTIVE CLAIM ABOUT ANOTHER PRINCIPAL'S FLEET — "this agent works
|
|
1057
|
+
/// nowhere" — handed to a caller who merely lacks a grant. Readable by an admin, an audit viewer,
|
|
1058
|
+
/// or that agent about itself.
|
|
1059
|
+
listAgentEngrams: (agent: principal) -> (Result_12) query;
|
|
1060
|
+
/// Contradictory submissions, oldest-first. Empty is the normal case. Never trimmed.
|
|
1061
|
+
///
|
|
1062
|
+
/// ⟨0.16.0, L3 — PAGED 2026-08-18⟩ This returned the WHOLE list, and the list is one an engram
|
|
1063
|
+
/// can grow itself: `sysAnchorHead` appends a conflict on every contradictory submission and the
|
|
1064
|
+
/// policy is deliberately never-evict. An uncapped reply over a caller-growable list eventually
|
|
1065
|
+
/// exceeds the response limit, and an oversized Candid reply does not truncate — it fails to
|
|
1066
|
+
/// decode ENTIRELY, so the conflicts become unreadable. That is the exact opposite of what a
|
|
1067
|
+
/// never-evicted evidence store is for: the record survives and nobody can read it.
|
|
1068
|
+
///
|
|
1069
|
+
/// `total` is the full conflict count, so a caller holding one page still learns how many exist.
|
|
1070
|
+
///
|
|
1071
|
+
/// ⚠ THE WRITE SIDE IS STILL UNBOUNDED and is NOT fixed here. An engram can append to its own
|
|
1072
|
+
/// conflict list without limit, which is unbounded state on a shared canister paid for by the
|
|
1073
|
+
/// registry's cycles. Capping it means deciding what to drop from an evidence store whose whole
|
|
1074
|
+
/// promise is that nothing is dropped — a retention ruling, not a shape fix. Recorded in the
|
|
1075
|
+
/// backlog rather than decided here.
|
|
1076
|
+
/// ⟨CORRECTED 2026-09-28 — the paragraph above is no longer true: the owner ruled "never delete;
|
|
1077
|
+
/// cap submissions; publish the refusal count". Nothing is dropped; the WRITE side is now bounded
|
|
1078
|
+
/// at `AnchorConflicts.MAX_CONFLICTS_PER_ENGRAM` rows per engram, `sysAnchorHead` refuses further
|
|
1079
|
+
/// contradictions by name, and the refusals are counted in the certified leaf's
|
|
1080
|
+
/// `refusedConflicts`. So this list never exceeds that bound and the page clamp below is ample.⟩
|
|
1081
|
+
///
|
|
1082
|
+
/// PUBLIC: a conflict is the anchoring system reporting against ITSELF, and a self-report only
|
|
1083
|
+
/// its author can read is worth nothing. Withholding it would let this registry publish anchors
|
|
1084
|
+
/// while keeping the evidence that two of them disagree — the fabricate-on-refusal shape aimed at
|
|
1085
|
+
/// the one reader the anchors exist for.
|
|
1086
|
+
listAnchorConflicts: (canisterId: principal, start: nat, limit: nat) ->
|
|
1087
|
+
(Result_13) query;
|
|
1088
|
+
/// Witnessed heads for an engram, oldest-first. PUBLIC and ungated — an anchor a stranger cannot
|
|
1089
|
+
/// read is not an anchor (ratified: "recorded where counterparties can check them").
|
|
1090
|
+
///
|
|
1091
|
+
/// Each entry carries `disputed`, derived at read time from the conflict log. Without it a
|
|
1092
|
+
/// counterparty reading ONLY this query would take an already-contradicted head at face value —
|
|
1093
|
+
/// on a same-total/different-head conflict the registry keeps both, the original here and the
|
|
1094
|
+
/// contradiction in getAnchorConflicts. Requiring readers to fetch two queries and join them
|
|
1095
|
+
/// correctly is a requirement most will not meet; carrying the flag inline removes the trap.
|
|
1096
|
+
/// (Consumer finding, a consumer 2026-08-03.)
|
|
1097
|
+
///
|
|
1098
|
+
/// ⚠ This query is NOT certified. The certificate slot is held by the registry's own audit head,
|
|
1099
|
+
/// and a canister has exactly one. A reader therefore trusts the replica or boundary node that
|
|
1100
|
+
/// answered — anchoring is verifiable by a COOPERATIVE counterparty, not an adversarial one.
|
|
1101
|
+
/// ⟨CORRECTED 2026-10-02: the reason is out of date and the conclusion is too broad.⟩ Since
|
|
1102
|
+
/// 2026-08-03 the registry's one certified value is the root of a labelled tree (the "Certified
|
|
1103
|
+
/// state tree" note near the top of this file), not the bare audit head, and the tree carries each
|
|
1104
|
+
/// engram's LATEST anchor. So the latest checkpoint IS checkable by a sceptical outsider:
|
|
1105
|
+
/// `getCertifiedAnchor` serves it with a certificate and a witness, checked against the network's
|
|
1106
|
+
/// root key without trusting the replica that answered. What stays true is narrower: THIS query,
|
|
1107
|
+
/// the full history oldest-first, is not certified, because only the latest anchor per engram is in
|
|
1108
|
+
/// the tree. For an earlier checkpoint a reader trusts the answering replica, or kept the
|
|
1109
|
+
/// certified latest anchor when it was the latest.
|
|
1110
|
+
///
|
|
1111
|
+
/// PUBLIC: an anchor is evidence offered to a third party, so the third party has to be able to
|
|
1112
|
+
/// read it. Gating this would leave the registry asserting that an engram's history is witnessed
|
|
1113
|
+
/// while refusing to show the witness — which is the shape of claim nobody should accept.
|
|
1114
|
+
///
|
|
1115
|
+
/// ⟨2026-10-02, decided: KEPT a bare list⟩ Every other list on these canisters is a `Page`; this
|
|
1116
|
+
/// one stays `[AnchorView]`, and the choice is recorded so the free moment does not pass silently.
|
|
1117
|
+
/// It is bounded by construction — at most `MAX_ANCHORS` (64) per engram, log-spaced with the
|
|
1118
|
+
/// genesis anchor pinned — and nothing is withheld from anyone (it is public), so a page's `total`
|
|
1119
|
+
/// would equal the length and its `withheld` would always be 0: two fields that could never say
|
|
1120
|
+
/// anything. A cap that is part of the ratified design is not the unbounded growth paging exists
|
|
1121
|
+
/// for. WHAT WOULD MAKE THIS WRONG: the ring outgrowing one reply, or rows withheld per caller —
|
|
1122
|
+
/// then it pages, and that is a break.
|
|
1123
|
+
listAnchors: (canisterId: principal) -> (vec AnchorView) query;
|
|
1124
|
+
/// ⟨0.16.0, L1 + L2.1⟩ RENAMED from `readAuditLog`, and the refusal is now a VALUE.
|
|
1125
|
+
///
|
|
1126
|
+
/// It refused by `Debug.trap`, which was the right call when the alternative was a fabricated
|
|
1127
|
+
/// answer — and the fabrication it replaced is worth restating, because it is the sharpest one
|
|
1128
|
+
/// this canister ever produced. The old empty answer carried `headHash = Text.encodeUtf8("")`,
|
|
1129
|
+
/// the GENESIS SENTINEL. An unauthorized caller was not told "empty log"; they were told THIS
|
|
1130
|
+
/// REGISTRY HAS NEVER BEEN WRITTEN TO, in a tamper-evident chain, which is exactly the false
|
|
1131
|
+
/// answer an external verifier would act on.
|
|
1132
|
+
///
|
|
1133
|
+
/// A trap is still not the right shape. It is not a value: it reaches a Motoko caller as an
|
|
1134
|
+
/// uncatchable failure and a JS caller as a rejection, so a client that wants to say "you may not
|
|
1135
|
+
/// read this, here is what you CAN read" has to parse a rejection string to do it. `#Err(#Refused)`
|
|
1136
|
+
/// carries the same sentence as data. The message survives verbatim — it earned its wording.
|
|
1137
|
+
///
|
|
1138
|
+
/// The inline record dies for a `Page`: `earliestId` was redundant with the ring's own paging,
|
|
1139
|
+
/// and `size`/`headHash` are `getAuditHead`'s answer, published free to everyone.
|
|
1140
|
+
listAuditEntries: (start: nat, limit: nat) -> (Result_10) query;
|
|
1141
|
+
/// ⟨0.16.0, L2.2⟩ RENAMED from `getMetricsViewers` — the viewer triple unifies on "audit viewer"
|
|
1142
|
+
/// across every canister, because a roster that grants audit reads should not be named after
|
|
1143
|
+
/// metrics on one canister and audit on another.
|
|
1144
|
+
///
|
|
1145
|
+
/// MEASURED FABRICATION, kept in the record: this arm answered `(vec {})` to an ANONYMOUS caller
|
|
1146
|
+
/// on 2026-08-11 while viewers existed. An empty vec reads as "nobody holds read-only ops access"
|
|
1147
|
+
/// when the truth was "you are not on the roster".
|
|
1148
|
+
listAuditViewers: () -> (Result_12) query;
|
|
1149
|
+
/// PUBLIC: the release history, paged.
|
|
1150
|
+
///
|
|
1151
|
+
/// ⟨0.16.0, L2.1⟩ A `Page`, not a bare vec. The old shape returned the WHOLE changelog on every
|
|
1152
|
+
/// call with no way to say it had been truncated — and a list that cannot say "there is more"
|
|
1153
|
+
/// looks complete at exactly the size where it stops being so.
|
|
1154
|
+
listChangelogEntries: (start: nat, limit: nat) -> (Page_4) query;
|
|
1155
|
+
/// ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ The engrams opened through `engram`, each with
|
|
1156
|
+
/// its owner now and its cut-off time if any. GATED: readable by `engram`'s owner, an admin or an
|
|
1157
|
+
/// audit viewer. The link says who works for whom, so it is not public (unlike the predecessor
|
|
1158
|
+
/// chain, which exists to be walked by a stranger).
|
|
1159
|
+
///
|
|
1160
|
+
/// REFUSED LOUDLY, never `[]`, to anyone else (ADR-0002): an empty list handed to a caller who
|
|
1161
|
+
/// merely lacks a grant would be a claim about another owner's engram. A QUERY, so the refusal is
|
|
1162
|
+
/// not in the denial log (`denied` says why a query cannot write).
|
|
1163
|
+
///
|
|
1164
|
+
/// Built by a pass over `parentOf` rather than from a second, reverse index: one stored fact and a
|
|
1165
|
+
/// pass bounded by the number of opened engrams, instead of two facts to keep in step.
|
|
1166
|
+
listChildrenOf: (engram: principal) -> (Result_11) query;
|
|
1167
|
+
/// ⟨0.16.0⟩ RENAMED from `readDenialLog`; the refusal becomes a value.
|
|
1168
|
+
///
|
|
1169
|
+
/// An empty denial log reads as NOBODY HAS EVER BEEN REFUSED HERE, which is the opposite of what
|
|
1170
|
+
/// this log exists to record — so "empty" was never an acceptable answer to a caller who simply
|
|
1171
|
+
/// lacks the grant. The old trap said so; this says the same thing as data.
|
|
1172
|
+
listDenialEntries: (start: nat, limit: nat) -> (Result_10) query;
|
|
1173
|
+
/// ⟨2026-10-02⟩ ONE ROW PER ENGAGEMENT, for the deployment's own count of use — DAYS, never
|
|
1174
|
+
/// instants, and NEVER the agent's root, enrolled key or invite.
|
|
1175
|
+
///
|
|
1176
|
+
/// CALLERS: the admin, and the audit viewers — the reader the deployment runs for this purpose
|
|
1177
|
+
/// holds a viewer grant and calls this once a day. Anyone else is REFUSED by name, with their
|
|
1178
|
+
/// class (the anonymous principal, an engram, the template key, an engram's owner, a stranger) —
|
|
1179
|
+
/// never an empty page, which is what a lost history would also look like (ADR-0002).
|
|
1180
|
+
///
|
|
1181
|
+
/// ARGUMENTS. `sinceDay`: any instant, floored to its UTC day; a row is served when the LATEST of
|
|
1182
|
+
/// its start, its ending, its engram's abandonment and any reset since it started is on or after
|
|
1183
|
+
/// that day — so an ending recorded after a reader's cursor passed the start is served on the
|
|
1184
|
+
/// reader's next pass. `start`: the first position in the recording order to serve from; ask
|
|
1185
|
+
/// again from the last row's `seq` + 1. `limit`: rows wanted, at most 1,000; a page shorter than
|
|
1186
|
+
/// the limit is the last. `companyList`: the deployment's own accounts (owner keys, agent roots,
|
|
1187
|
+
/// engram ids), at most 1,000, folded into each row's yes/no `…OnList` answers and into
|
|
1188
|
+
/// `firstOutsideOnTree`; pass `[]` and every engagement is outside by that list ("includes us").
|
|
1189
|
+
///
|
|
1190
|
+
/// The reply is a page whose `total` is every engagement ever recorded here. Each row's fields
|
|
1191
|
+
/// are described at `Funnel.EngagementRow`; the walk and its cost at `Funnel.engagementRows`.
|
|
1192
|
+
///
|
|
1193
|
+
/// ⟨L7⟩ An UPDATE, not a query, so a refused attempt is RECORDED: `denied()` is a state write,
|
|
1194
|
+
/// and a query cannot write (the reason `listEngrams` is an update). This is the most sensitive of
|
|
1195
|
+
/// the three reads — one row per engagement — so who was turned away is worth a record.
|
|
1196
|
+
listEngagementRows: (sinceDay: int, start: nat, limit: nat, companyList:
|
|
1197
|
+
vec principal) -> (Result_9);
|
|
1198
|
+
/// ⟨0.16.0, L1⟩ THE `authorized : ?Bool` FLAG IS GONE, AND SO IS EVERYTHING BUILT AROUND IT.
|
|
1199
|
+
///
|
|
1200
|
+
/// The flag was the most carefully-argued compromise in this canister, and every word of that
|
|
1201
|
+
/// argument rested on ONE premise: this method could not refuse. It is an UPDATE because
|
|
1202
|
+
/// `denied()` is a state write, and a trap would roll that write back — trading a false answer
|
|
1203
|
+
/// for a missing audit trail, which is strictly worse. So the refusal had to be smuggled out
|
|
1204
|
+
/// beside a fabricated `{ []; 0 }` payload, and readers had to be taught to check a sibling flag
|
|
1205
|
+
/// before believing a total.
|
|
1206
|
+
///
|
|
1207
|
+
/// THE ENVELOPE REMOVES THE PREMISE. `#Err(#Refused)` is a VALUE, not a trap: the message
|
|
1208
|
+
/// commits, the denial is recorded, and the caller gets a refusal they cannot mistake for data.
|
|
1209
|
+
/// There is no masked payload left to check a flag against.
|
|
1210
|
+
///
|
|
1211
|
+
/// What goes with it: the three-state ?true/?false/null protocol and the "a reader that collapses
|
|
1212
|
+
/// null to false fabricates a refusal" rule; the ADR-0018 opt-from-birth ceremony this field was
|
|
1213
|
+
/// the motivating example for; the fleet collector's dual-IDL split; and the cross-check against
|
|
1214
|
+
/// the public engram count that storage-sweep.sh used as a discriminator. All of it was
|
|
1215
|
+
/// scaffolding around "we cannot say no in a value", and none of it is load-bearing once we can.
|
|
1216
|
+
///
|
|
1217
|
+
/// ⚠ CENSUS CONFLICT, resolved at the site. The census says "query again" — reasoning that the
|
|
1218
|
+
/// only thing forcing an update was the masking problem. It was not: the update exists so the
|
|
1219
|
+
/// DENIAL IS RECORDED, and a query cannot write, so making this a query would silently retire
|
|
1220
|
+
/// the audit trail the original comment correctly called worth more than the false answer.
|
|
1221
|
+
/// Staying an update keeps both — the legible refusal AND the record of who was refused — which
|
|
1222
|
+
/// is strictly better than either option the old design had to choose between.
|
|
1223
|
+
///
|
|
1224
|
+
/// ⟨L7⟩ MARKER ADDED 2026-08-23, reasoning above UNCHANGED. This method's reason was already
|
|
1225
|
+
/// written and correct; what it lacked was the glyph a gate can see. Its case is the one L7's
|
|
1226
|
+
/// own text had to be corrected for — the forcing call is not a system API at all, it is
|
|
1227
|
+
/// `denied()`, a state write. See `scripts/check-update-for-read.mjs`.
|
|
1228
|
+
listEngrams: (offset: nat, limit: nat) -> (Result_8);
|
|
1229
|
+
/// ⟨2026-09-28, owner decision: an agent may read its own record⟩ The caller's OWN dispute
|
|
1230
|
+
/// statements, oldest first, verbatim. Keyed by `msg.caller`, so it cannot be pointed at anyone
|
|
1231
|
+
/// else, and an empty list is a true answer about the caller ("you have appended none"), never a
|
|
1232
|
+
/// refusal in disguise. Without this read the statements would be a record nobody can read —
|
|
1233
|
+
/// the failure mode `listAnchorConflicts`' declaration names.
|
|
1234
|
+
listMyAgentDisputes: () -> (vec Dispute) query;
|
|
1235
|
+
/// Every hash we have published, current first. Union of the explicit list and the changelog's
|
|
1236
|
+
/// per-release stamps, so it is complete across the boundary where the list was introduced.
|
|
1237
|
+
///
|
|
1238
|
+
/// PUBLIC: the same standing as `isOfficialEngramWasm` above — that one answers about a hash you
|
|
1239
|
+
/// already hold, this one is how you get the set to compare against.
|
|
1240
|
+
listOfficialEngramWasmHashes: () -> (vec text) query;
|
|
1241
|
+
/// The FULL predecessor lineage, newest-first: [immediate predecessor, its predecessor, …].
|
|
1242
|
+
/// Empty for an engram that replaced nothing.
|
|
1243
|
+
///
|
|
1244
|
+
/// Why transitive (owner-ratified 2026-08-02, "free to leave, impossible to hide"): abandonment
|
|
1245
|
+
/// is deliberately NOT rate-limited — friction punishes the owner who mis-provisions on day one,
|
|
1246
|
+
/// and it does not stop anyone determined. What protects the record is that walking away stays
|
|
1247
|
+
/// VISIBLE. Single-hop visibility does not: A→B→C is three cheap steps, and a stranger checking C
|
|
1248
|
+
/// would see only B and read it as a near-clean record. Reputation laundering with extra hops is
|
|
1249
|
+
/// still laundering, and it would be our own feature performing it. The chain is the answer, not
|
|
1250
|
+
/// the cooldown.
|
|
1251
|
+
///
|
|
1252
|
+
/// TERMINATION: `seen` is not defensive dressing. predecessorOf is written once per engram at
|
|
1253
|
+
/// creation and never rewritten, so a cycle should be impossible — but this is a QUERY over
|
|
1254
|
+
/// stable state that a future migration or a restore could corrupt, and an unbounded loop in a
|
|
1255
|
+
/// query does not return an error, it burns the instruction limit and the caller gets nothing
|
|
1256
|
+
/// back. Terminating on a repeat is strictly better than trapping, and the length cap bounds the
|
|
1257
|
+
/// walk even if `seen` itself were somehow defeated.
|
|
1258
|
+
///
|
|
1259
|
+
/// PUBLIC: this is the anti-laundering chain — it says an engram is a successor rather than a
|
|
1260
|
+
/// fresh start. Abandon-and-respawn is reputation laundering performed by our own feature unless
|
|
1261
|
+
/// a stranger can walk this, so the walk has to be answerable to a stranger.
|
|
1262
|
+
///
|
|
1263
|
+
/// ⟨2026-09-30, the owner's answer 3 of that date⟩ An engram can now name more than one
|
|
1264
|
+
/// predecessor (`ControlHandover.predecessorsFor`), so the lineage is walked a generation at a time:
|
|
1265
|
+
/// every engram this one named, then every engram those named, each listed once. A chain with one
|
|
1266
|
+
/// predecessor per engram reads exactly as before. The walk is `ControlHandover.lineage`, where
|
|
1267
|
+
/// test/control-handover.test.mo runs it; the `seen` rule and the length cap above still hold.
|
|
1268
|
+
listPredecessors: (canisterId: principal) -> (vec principal) query;
|
|
1269
|
+
/// ⟨0.16.0⟩ The refusal becomes a value.
|
|
1270
|
+
///
|
|
1271
|
+
/// `total = 0` reads as "nobody has expressed a preference", and a successor registry seeded from
|
|
1272
|
+
/// that zero applies the SHIPPED DEFAULT — silently reversing every explicit ManualOnly consent
|
|
1273
|
+
/// choice in the fleet. That is the consequence this refusal exists to prevent, and it belongs in
|
|
1274
|
+
/// the message a caller actually receives.
|
|
1275
|
+
listUpdatePrefs: (start: nat, limit: nat) -> (Result_7) query;
|
|
1276
|
+
/// Mint one-shot reserve invites (invite-issuer or admin). Codes use IC-secure randomness over a
|
|
1277
|
+
/// 32-symbol no-ambiguous alphabet (8 chars ≈ 40 bits). Batch-capped; table hard-capped.
|
|
1278
|
+
mintReserveInvites: (count: nat) -> (Result_6);
|
|
1279
|
+
/// The PROBED twin of `getCustodyMode`: same question, asked of the IC rather than of our
|
|
1280
|
+
/// records — hence the `probe` verb rather than a second noun.
|
|
1281
|
+
///
|
|
1282
|
+
/// ⟨0.16.0, L2.2⟩ RENAMED from `getSovereigntyStatus`. Both methods answer the same type,
|
|
1283
|
+
/// `Result<CustodyMode>`, so the old name introduced a SovereigntyStatus noun that no type in
|
|
1284
|
+
/// this interface has. A reader comparing the two names had to open both to learn they were
|
|
1285
|
+
/// one question; comparing the stems now shows it. What differs is HOW the answer is obtained
|
|
1286
|
+
/// (a live canister_status probe vs the transition records), which is exactly what a verb is
|
|
1287
|
+
/// for. The arity difference is real and stays: `getCustodyMode` answers about any canister,
|
|
1288
|
+
/// this one about the CALLER's own engram.
|
|
1289
|
+
///
|
|
1290
|
+
/// ⟨0.16.0, L7⟩ Both arms of the old `#Unknown` move into the envelope, and they are DIFFERENT
|
|
1291
|
+
/// facts that the single tag was flattening: "you have no engram here" is #NotFound, while
|
|
1292
|
+
/// "canister_status did not answer" is #Unavailable and means retry.
|
|
1293
|
+
///
|
|
1294
|
+
/// The distinction matters more than usual because of what a wrong answer would say.
|
|
1295
|
+
/// canister_status traps for reasons unrelated to controllership — an engram stopped, frozen out
|
|
1296
|
+
/// of cycles, or a transient subnet error — so a trap does NOT prove the registry was removed as
|
|
1297
|
+
/// a controller. Reporting #Sovereign there would tell an owner they had achieved sovereignty
|
|
1298
|
+
/// they do not have.
|
|
1299
|
+
///
|
|
1300
|
+
/// ⟨L7⟩ UPDATE, NOT QUERY, and this is the system call that forces it: the body awaits
|
|
1301
|
+
/// `ic.canister_status`. That is also the whole point of the `probe` verb — the value is read
|
|
1302
|
+
/// LIVE from the management canister rather than from a stored transition record, so a query
|
|
1303
|
+
/// could not produce it at all, not merely more cheaply.
|
|
1304
|
+
probeCustodyMode: () -> (Result_5);
|
|
1305
|
+
/// ⟨0.16.0⟩ `Ok` CARRIES THE PRINCIPAL DIRECTLY, not a one-field record wrapping it.
|
|
1306
|
+
///
|
|
1307
|
+
/// `#Ok : { canisterId : Principal }` was a record with nothing to disambiguate: the success of a
|
|
1308
|
+
/// spawn IS the canister it produced. The wrapper cost every caller a field access to reach the
|
|
1309
|
+
/// only thing the call returns, and invited a second field later that would belong somewhere else.
|
|
1310
|
+
provisionEngram: (opts:
|
|
1311
|
+
opt
|
|
1312
|
+
record {
|
|
1313
|
+
genesis: opt record {
|
|
1314
|
+
content: blob;
|
|
1315
|
+
path: text;
|
|
1316
|
+
};
|
|
1317
|
+
invite: opt text;
|
|
1318
|
+
walletProof: opt blob;
|
|
1319
|
+
}) -> (Result_1);
|
|
1320
|
+
removeAuditViewer: (viewer: principal) -> (Result);
|
|
1321
|
+
/// ⟨0.16.0⟩ `Ok` IS THE ANSWER — the `{ reserved : Bool }` wrapper is gone.
|
|
1322
|
+
///
|
|
1323
|
+
/// It could only ever hold `true`: the false case was already an `#Err`. A boolean whose only
|
|
1324
|
+
/// legal value is true is a field that teaches callers to branch on something that never varies,
|
|
1325
|
+
/// and the one that eventually forgets reads a refusal as a reservation.
|
|
1326
|
+
reserveEngram: (opts:
|
|
1327
|
+
opt record {
|
|
1328
|
+
invite: opt text;
|
|
1329
|
+
walletProof: opt blob;
|
|
1330
|
+
}) -> (Result);
|
|
1331
|
+
/// Return to managed: user calls AFTER adding registry back as controller
|
|
1332
|
+
returnToManaged: () -> (Result);
|
|
1333
|
+
/// Set admin. Authority decision lives in the pure Auth module (unit-tested); this func keeps only
|
|
1334
|
+
/// the async controller check + applies the outcome. When admin is null: REGISTRY_ROOT or a canister
|
|
1335
|
+
/// controller can set it. When admin is set: the current admin hands off, or REGISTRY_ROOT reclaims.
|
|
1336
|
+
setAdmin: (newAdmin: principal) -> (Result);
|
|
1337
|
+
/// Upload the engram template in ONE message — the single-shot path. For a wasm too large to
|
|
1338
|
+
/// fit, use `engramWasmInit` → `engramWasmPushChunk` × N → `engramWasmFinalize` instead; the two
|
|
1339
|
+
/// paths produce the same result and differ only in how the bytes arrive.
|
|
1340
|
+
///
|
|
1341
|
+
/// ⚠ THE HASH IS VERIFIED HERE, unlike the one on `backupInit`. This computes sha256 over the
|
|
1342
|
+
/// bytes it received and refuses `#Invalid` when it does not match, naming both values. So
|
|
1343
|
+
/// passing a hash is a CHECK, not a label — the opposite of the backup protocol, whose digest is
|
|
1344
|
+
/// stored unverified. Two chunked-upload families with the same shape and opposite semantics is
|
|
1345
|
+
/// exactly the pair a reader generalises across wrongly, and the dangerous direction is assuming
|
|
1346
|
+
/// the backup is verified.
|
|
1347
|
+
///
|
|
1348
|
+
/// Gated on the WASM ADMIN — a distinct role from admin and root, because deciding what code the
|
|
1349
|
+
/// whole fleet runs is a different authority from operating the registry.
|
|
1350
|
+
setEngramWasm: (wasm: blob, hash: text) -> (Result);
|
|
1351
|
+
/// Declare the stored template's release class. Wasm-admin only, and deliberately a SEPARATE call
|
|
1352
|
+
/// from the upload: "this is a security release" is a promise to every owner who is not paying
|
|
1353
|
+
/// us, so it should be its own decision with its own audit line — not a parameter someone passes
|
|
1354
|
+
/// by habit. Always re-declare AFTER uploading; an upload resets this to #Feature.
|
|
1355
|
+
setEngramWasmClass: (class: ReleaseClass) -> (Result);
|
|
1356
|
+
/// Record the predecessor's final head beside this chain's genesis. Admin-only, and ONE-SHOT.
|
|
1357
|
+
///
|
|
1358
|
+
/// One-shot because a note that can be rewritten is not evidence. Once a predecessor head is
|
|
1359
|
+
/// published, changing it would let the same surface that admits a wipe also revise the account
|
|
1360
|
+
/// of it — which is exactly the power this note exists to deny.
|
|
1361
|
+
///
|
|
1362
|
+
/// ⟨RENAMED 2026-10-02 from `sysSetGenesisPredecessor` — the sys-tier retier ruled 2026-08-24,
|
|
1363
|
+
/// executed under the owner's 2026-10-02 ruling to carry out the pre-cut interface decisions as
|
|
1364
|
+
/// recommended.⟩ CALLER: this registry's admin, a person, once, after the predecessor chain's
|
|
1365
|
+
/// final head is captured. The `sys` prefix names the canister-to-canister tier, so an
|
|
1366
|
+
/// admin-called method inside it made "a `sys` method has no human caller" false and therefore
|
|
1367
|
+
/// uncheckable. The audit label moves with the name (`setGenesisPredecessor.denied`).
|
|
1368
|
+
setGenesisPredecessor: (headHash: blob, size: nat) -> (Result);
|
|
1369
|
+
/// Set (or clear) the least-privilege invite-mint key (admin-only). Mirrors setWasmAdmin.
|
|
1370
|
+
setInviteIssuer: (p: opt principal) -> (Result);
|
|
1371
|
+
setMarketCanister: (p: opt principal) -> (Result);
|
|
1372
|
+
/// Designate the metrics reader (admin only; `null` clears it, and newborns are then told none).
|
|
1373
|
+
/// Canister principals only — see the variable's declaration.
|
|
1374
|
+
setMetricsReader: (p: opt principal) -> (Result);
|
|
1375
|
+
/// Admin-tunable liveness floor for the registry's own cycles (a frozen registry = the whole
|
|
1376
|
+
/// managed fleet stops). Monitored externally via getCycleReserveStatus.
|
|
1377
|
+
setMinCycleReserve: (cycles: nat) -> (Result);
|
|
1378
|
+
setRentBiller: (p: opt principal) -> (Result);
|
|
1379
|
+
/// Flip the reserve gate (admin-only; audit-logged). OFF restores fully-open admission.
|
|
1380
|
+
setReserveGateEnabled: (enabled: bool) -> (Result);
|
|
1381
|
+
setScreeningDenylist: (list: vec principal) -> (Result);
|
|
1382
|
+
setSpawnCeiling: (n: nat) -> (Result_4);
|
|
1383
|
+
setTreasuryAnchorId: (p: opt principal) -> (Result);
|
|
1384
|
+
/// Set the wallet-canister allowlist for the wallet-rooted exemption (admin-only). Small by
|
|
1385
|
+
/// design — these are identity ISSUERS (e.g. Internet Identity), not users.
|
|
1386
|
+
setTrustedSigners: (signers: vec principal) -> (Result);
|
|
1387
|
+
/// Choose whether this registry may upgrade your engram automatically. The caller must own an
|
|
1388
|
+
/// ACTIVE engram; `#ManualOnly` means only `upgradeMyEngram` moves it, never `upgradeEngram`.
|
|
1389
|
+
///
|
|
1390
|
+
/// `claimSovereignty` sets this to `#ManualOnly` as a side effect, since a registry that is no
|
|
1391
|
+
/// longer a controller could not honour anything else.
|
|
1392
|
+
setUpdatePreference: (pref: UpdatePreference) -> (Result);
|
|
1393
|
+
/// Set the spawn-ceiling dial (admin-only, audit-logged, bounded 1..SPAWN_CEILING_MAX).
|
|
1394
|
+
/// The effective daily cap stays min(ceiling, reserve-derived) — raising the dial never
|
|
1395
|
+
/// bypasses the funding constraint.
|
|
1396
|
+
/// Admin dial for the upgrade top-up floor (0 = disabled). Capped at one endowment so the dial
|
|
1397
|
+
/// itself cannot turn upgrades into an unbounded drain.
|
|
1398
|
+
setUpgradeTopUpFloor: (n: nat) -> (Result_4);
|
|
1399
|
+
/// Root admin assigns the role key (pass null to clear, reverting the role to the root admin).
|
|
1400
|
+
setWasmAdmin: (p: opt principal) -> (Result);
|
|
1401
|
+
/// An engram witnesses its own audit head. Callable ONLY by a registered engram — msg.caller is
|
|
1402
|
+
/// the engram principal, so attribution needs no signature and cannot be forged by a third party.
|
|
1403
|
+
///
|
|
1404
|
+
/// The registry witnesses; it does not adjudicate. The anchoring design carries the write table.
|
|
1405
|
+
/// Two shapes are permanently recorded as conflicts rather than rejected, because a rejection
|
|
1406
|
+
/// leaves no evidence and evidence is the entire product here:
|
|
1407
|
+
/// - same total, different head → the log was rewritten under a fixed count
|
|
1408
|
+
/// - total went backwards → an append-only count cannot decrease honestly
|
|
1409
|
+
/// ⟨2026-09-28, owner decision: never delete; cap submissions; publish the refusal count⟩ One
|
|
1410
|
+
/// exception to "recorded rather than rejected": an engram that already has
|
|
1411
|
+
/// `AnchorConflicts.MAX_CONFLICTS_PER_ENGRAM` recorded contradictions gets `#Conflict` naming
|
|
1412
|
+
/// this method, and the refusal is COUNTED in its certified leaf (`refusedConflicts`). The
|
|
1413
|
+
/// evidence is not lost — the recorded ones stay forever and the count certifies that more came.
|
|
1414
|
+
/// ⟨0.16.0, L2.3⟩ RENAMED from `anchorHead` — a mutation wearing a noun, and a `sys` edge that
|
|
1415
|
+
/// did not say so. It is called canister-to-canister (an engram or the market pushing its head
|
|
1416
|
+
/// here), never by a person, and the `sys` prefix is what tells a reader scanning the surface
|
|
1417
|
+
/// which methods are machine-to-machine.
|
|
1418
|
+
///
|
|
1419
|
+
/// The market already calls it under this name, which is how the rename got its urgency: a
|
|
1420
|
+
/// hand-written client IDL type-checks against a method that does not exist.
|
|
1421
|
+
sysAnchorHead: (head: blob, total: nat) -> (Result);
|
|
1422
|
+
/// ⟨2026-09-29, review PR-3: a half-finished change of owner⟩ A SOVEREIGN engram reports that it
|
|
1423
|
+
/// moved control of its code to `newOwner`, which ends the change of owner this registry marked
|
|
1424
|
+
/// when it moved its record (`ControlHandover.Pending`). Until this arrives the registry refuses the
|
|
1425
|
+
/// new owner's control-changing calls on the engram, because it cannot see a sovereign engram's
|
|
1426
|
+
/// controllers. Gated like `sysNotifyOwnershipTransfer` (the caller must BE the engram), and only for
|
|
1427
|
+
/// an engram recorded as sovereign: a managed engram's transfer ends inside
|
|
1428
|
+
/// `sysNotifyOwnershipTransfer`, where the registry moves control itself. `#Ok` when nothing is
|
|
1429
|
+
/// marked (a report sent again), refused by name when the mark is for another owner.
|
|
1430
|
+
///
|
|
1431
|
+
/// TRUST: the same as `sysNotifyOwnershipTransfer` and `sysRevertOwnershipTransfer` — the
|
|
1432
|
+
/// registry takes a sovereign engram's word, and whoever controls a sovereign engram's code can
|
|
1433
|
+
/// make it say anything. WHAT WOULD MAKE THIS WRONG: an engram that reports control moved before
|
|
1434
|
+
/// it has moved it (the engram's `transferOwnership` calls this only after its own
|
|
1435
|
+
/// `update_settings` succeeded).
|
|
1436
|
+
sysConfirmOwnershipTransfer: (newOwner: principal) -> (Result);
|
|
1437
|
+
/// Single-engram metering probe — the push-on-payment sizing read. Biller-gated and
|
|
1438
|
+
/// cycles-attached like the sweep; same row semantics, one engram, no heartbeat stamp
|
|
1439
|
+
/// (a single probe is not a completed round).
|
|
1440
|
+
sysMeterEngram: (canisterId: principal) -> (Result_3);
|
|
1441
|
+
/// ⟨2026-10-02⟩ An engram tells this registry that its owner reset it.
|
|
1442
|
+
///
|
|
1443
|
+
/// CALLER: a registered engram, about itself — `canisterToOwner` membership, checked first
|
|
1444
|
+
/// (ADR-0020: every `sys` method names and checks its caller). The engram sends it from its reset
|
|
1445
|
+
/// path. WHY IT EXISTS: a reset purges the engram's roster, and the endings it sends reach this
|
|
1446
|
+
/// registry only for agents still active, so a reset after the last agent left reached this
|
|
1447
|
+
/// registry nowhere and the engram read as kept.
|
|
1448
|
+
///
|
|
1449
|
+
/// Records the DAY the registry heard it (`resetDaysOf`), once per day: a second notice the same
|
|
1450
|
+
/// day answers `#Ok` and writes nothing. Past `Funnel.MAX_RESET_DAYS_PER_ENGRAM` days it is refused
|
|
1451
|
+
/// `#Conflict` and nothing is stored. Served only on `listEngagementRows`' rows, to the admin and
|
|
1452
|
+
/// audit viewers — never public.
|
|
1453
|
+
sysNoteReset: () -> (Result);
|
|
1454
|
+
/// ⟨0.16.0, RULED 2026-08-17, BUILT 2026-08-20⟩ The engram tells this registry that custody moved.
|
|
1455
|
+
///
|
|
1456
|
+
/// WHAT IT FIXES: `transferOwnership` lives on the ENGRAM and, until this method existed, told the
|
|
1457
|
+
/// registry nothing. `verifyEngramActiveOwner` — the cashier's money gate — therefore kept
|
|
1458
|
+
/// answering for the ORIGINAL owner, so a new owner could not fund the engram they now own. The
|
|
1459
|
+
/// old comment above `verifyEngramActiveOwner` called that gap "by design"; it was not, and the
|
|
1460
|
+
/// 2026-08-17 ruling shipped this seam to close it inside the free-break window.
|
|
1461
|
+
///
|
|
1462
|
+
/// THE TELLER PROBLEM IS ALREADY SOLVED NEXT DOOR, and this deliberately copies the answer rather
|
|
1463
|
+
/// than inventing one: `sysNotifyOperatorEdge` below takes `msg.caller` and looks it up in
|
|
1464
|
+
/// `canisterToOwner`. ⟨2026-09-28: that method is now `sysRecordAgentEdge`; the identity
|
|
1465
|
+
/// check is the same.⟩ The caller IS the engram whose ownership changed, and this registry spawned
|
|
1466
|
+
/// it, so no claim in the argument needs trusting — the identity is the authority.
|
|
1467
|
+
///
|
|
1468
|
+
/// ⟨2026-09-28: the mirror is now `sysRecordAgentEdge`, and the difference below is gone — the
|
|
1469
|
+
/// silent `()` notify was deleted and its successor answers `Wire.Result` too, for this reason.⟩
|
|
1470
|
+
/// ONE DELIBERATE DIFFERENCE FROM THAT MIRROR: it returns `Wire.Result`, where the agent-edge
|
|
1471
|
+
/// notify returns `()` and silently ignores strangers. The engram must COMMIT ONLY ON `#Ok` (see
|
|
1472
|
+
/// its `transferOwnership`), so a silent refusal here would be read as success there and put the
|
|
1473
|
+
/// two canisters back into exactly the disagreement this seam exists to end. A refusal that cannot
|
|
1474
|
+
/// be heard is worse than no refusal.
|
|
1475
|
+
///
|
|
1476
|
+
/// COLLISION IS REFUSED, NOT ABSORBED (owner ruling 2026-08-20). `engrams` is keyed BY OWNER, and
|
|
1477
|
+
/// three guards enforce one-engram-per-owner. Relaxing that rule was considered and declined: the
|
|
1478
|
+
/// spawn path's own comment warns that "a change to that rule must touch all three, and grepping
|
|
1479
|
+
/// the error TEXT finds only two of them", so the relaxation is the option most likely to leave a
|
|
1480
|
+
/// guard behind. The check below mirrors the spawn guard EXACTLY — active AND not abandoned —
|
|
1481
|
+
/// because an abandoned engram is not a live claim on its owner's slot.
|
|
1482
|
+
///
|
|
1483
|
+
/// ⟨2026-09-29, owner ruling follow-up 8 (option A): a change of owner hands over control of the
|
|
1484
|
+
/// code⟩ THIS METHOD NOW MOVES CONTROL TOO, or tells the engram to. It used to move the record and
|
|
1485
|
+
/// nothing else, so the former owner stayed a controller and could still replace the code. Now:
|
|
1486
|
+
/// · MANAGED (the registry is a controller): the registry sets the controllers to itself and the
|
|
1487
|
+
/// new owner and answers `#MovedByRegistry`. If that fails, the record is put back and the
|
|
1488
|
+
/// answer is `#Err` — the engram then does not change owner.
|
|
1489
|
+
/// · SOVEREIGN (the registry is not a controller): the registry moves its record and answers
|
|
1490
|
+
/// `#EngramMustMove`; the engram changes its own controllers, and calls
|
|
1491
|
+
/// `sysRevertOwnershipTransfer` if it cannot.
|
|
1492
|
+
/// The record moves BEFORE the controller change, not after, so the one-engram-per-owner check and
|
|
1493
|
+
/// the move happen in one message — a spawn by the new owner during the await then meets the
|
|
1494
|
+
/// moved record and is refused. A report of the owner the registry already records is a RETRY of
|
|
1495
|
+
/// a transfer the engram did not finish, and moves control again (that is the way out `stuck`
|
|
1496
|
+
/// names). The rules are in `src/shared/ControlHandover.mo`.
|
|
1497
|
+
sysNotifyOwnershipTransfer: (newOwner: principal) -> (Result_2);
|
|
1498
|
+
/// ⟨2026-09-28, owner decision: build the full agent track-record design for the launch release⟩
|
|
1499
|
+
/// An engram tells this registry that one of its agents started, or ended and WHY.
|
|
1500
|
+
///
|
|
1501
|
+
/// REPLACES `sysNotifyOperatorEdge(operator, added) : ()`, which is DELETED rather than kept
|
|
1502
|
+
/// beside this (the owner's building-phase rule of 2026-09-27: no backward compatibility; the
|
|
1503
|
+
/// next release installs every engram fresh). Three things changed, each on purpose:
|
|
1504
|
+
/// 1. THE REASON IS REQUIRED. `#Ended` carries an `Engagements.EndReason` sent from the one
|
|
1505
|
+
/// engram site that knows it. There is no reasonless ending and no "unknown" arm.
|
|
1506
|
+
/// 2. IT IS ACKNOWLEDGED. The old method returned `()` and silently ignored a stranger, so the
|
|
1507
|
+
/// engram discarded the outcome and a lost ending was invisible on both sides. This answers
|
|
1508
|
+
/// `Wire.Result`; the engram logs any non-`#Ok` where its owner can see it.
|
|
1509
|
+
/// 3. ONE RECORD ARGUMENT, not positional args — the same reshape `signX402Payment` took, so a
|
|
1510
|
+
/// later field is a named addition rather than a tuple that silently shifts.
|
|
1511
|
+
///
|
|
1512
|
+
/// AUTH: the caller MUST be an engram this registry spawned (`canisterToOwner` membership), so an
|
|
1513
|
+
/// engram can only assert edges about ITSELF — `msg.caller` IS the engram, and no claim in the
|
|
1514
|
+
/// argument needs trusting. Idempotent: a repeated `#Added` mints no duplicate and a repeated
|
|
1515
|
+
/// `#Ended` writes nothing, and both answer `#Ok` because the state the caller asked for holds.
|
|
1516
|
+
///
|
|
1517
|
+
/// It writes three things and nothing else: the live reverse index (`agentToEngrams`, which
|
|
1518
|
+
/// still forgets an agent at its last edge), the remembered engagement (`agentEngagements`,
|
|
1519
|
+
/// which never forgets), and that agent's certified summary (`putStandingLeaf`).
|
|
1520
|
+
/// ⟨CORRECTED 2026-10-02, beside the sentence above⟩ Five things now: a recorded ending can also
|
|
1521
|
+
/// cut off an engram opened through this one (`cutOff`, since 2026-10-01), and a start that opens
|
|
1522
|
+
/// an engagement takes the next place in the recording order (`engagementOrder`), in the same step
|
|
1523
|
+
/// as the record, so the order never names a record that is not there.
|
|
1524
|
+
///
|
|
1525
|
+
/// ⟨RESHAPED 2026-09-28, owner decision: one lasting identity per agent — one root key per agent,
|
|
1526
|
+
/// with an enrolled key per client⟩ The subject is the agent's ROOT (`ev.root`), not the key that
|
|
1527
|
+
/// operates on the engram. The engram verified the root's signed enrolment statement before it
|
|
1528
|
+
/// admitted the key, and this registry trusts the engram for that exactly as it trusts it for the
|
|
1529
|
+
/// ending's reason — `msg.caller` is an engram this registry spawned. So every structure written
|
|
1530
|
+
/// here is keyed by agent: one agent across twenty-five engrams is one record, one live-index
|
|
1531
|
+
/// row and one summary. The key never reaches the registry; the plan's separate new `sys-` method
|
|
1532
|
+
/// for the root is not built — this method was itself new in the same release, so it carries the
|
|
1533
|
+
/// root instead of the key, and a second method would be a parallel path for one fact.
|
|
1534
|
+
/// ⟨CORRECTED 2026-09-28, review finding OPS-1: the trust in this paragraph was misplaced, and
|
|
1535
|
+
/// "the key never reaches the registry" is no longer true. An engram's owner is a controller of
|
|
1536
|
+
/// its canister and can install code that reports any root, so a start now carries the root's
|
|
1537
|
+
/// enrolment statement (`Engagements.EnrolmentProof`) and THIS method verifies it
|
|
1538
|
+
/// (`EdgeProof.rootOf`), and refuses past `Engagements.MAX_ENGAGEMENTS_PER_ENGRAM` (OPS-3). The
|
|
1539
|
+
/// enrolled key therefore DOES reach the registry, stored on the engagement record and never
|
|
1540
|
+
/// served. The ending's reason is still the owner's assertion; the agent's remedy for a false
|
|
1541
|
+
/// one is its dispute. Records stay keyed by the agent's root, as above.⟩
|
|
1542
|
+
/// ⟨MOVED 2026-09-29, review finding LIFE-11: the correction above stood in the middle of this
|
|
1543
|
+
/// paragraph, so the sentence it corrects came after it and read as the last word — in this file
|
|
1544
|
+
/// and in the published registry.did.⟩
|
|
1545
|
+
/// ⟨2026-09-29, review finding LIFE-1⟩ An engagement is matched on the enrolled key AND the invite
|
|
1546
|
+
/// its statement names, not on the key alone: one statement opens at most one engagement, and a
|
|
1547
|
+
/// key the engram re-admits after a reset, under a fresh statement for a fresh invite, opens a new
|
|
1548
|
+
/// one (`Engagements.admit`).
|
|
1549
|
+
/// ⟨CORRECTED 2026-09-29, review finding LIFE-2: "a repeated `#Ended` writes nothing, and both
|
|
1550
|
+
/// answer `#Ok`" is now narrower. An ending names the engagement it ends (the enrolled key and the
|
|
1551
|
+
/// invite) and the day the engram recorded it, because a lost ending is now RE-SENT by the
|
|
1552
|
+
/// engram's `resyncAgentEdges`. The same ending again answers `#Ok` and writes nothing; one
|
|
1553
|
+
/// giving another reason for an engagement already ended is refused `#Conflict`; one naming an
|
|
1554
|
+
/// engagement never recorded here is refused `#NotFound` (`Engagements.closeVerdict`).⟩
|
|
1555
|
+
sysRecordAgentEdge: (ev: record {
|
|
1556
|
+
event: EdgeEvent;
|
|
1557
|
+
root: principal;
|
|
1558
|
+
}) -> (Result);
|
|
1559
|
+
/// ⟨2026-09-29, owner ruling follow-up 8⟩ A SOVEREIGN engram could not change its own controllers
|
|
1560
|
+
/// after this registry moved its record, and asks for the record back so the transfer does not
|
|
1561
|
+
/// complete. Gated like `sysNotifyOwnershipTransfer` (the caller must BE the engram), and only for
|
|
1562
|
+
/// an engram the registry records as sovereign — a managed engram's controllers are this
|
|
1563
|
+
/// registry's to move, and its record is put back inside `sysNotifyOwnershipTransfer` itself.
|
|
1564
|
+
/// Refuses, saying why, when the record cannot be put back (`ControlHandover.revertRefusal`); the
|
|
1565
|
+
/// engram then answers its owner with `ControlHandover.stuck`.
|
|
1566
|
+
sysRevertOwnershipTransfer: (formerOwner: principal) -> (Result);
|
|
1567
|
+
/// ⟨2026-10-01, the owner's ruling of that day: the October concept's decision 1, "build it now"⟩
|
|
1568
|
+
/// THE SPONSORED DOOR. An engram opens an engram for one of its agents: the caller is the engram
|
|
1569
|
+
/// (the engram it is opened from — `parentOf` in code), `ownerKey` is the agent's LASTING key, which
|
|
1570
|
+
/// owns what is opened, and the cycles the opened engram lives on are the ones the caller ATTACHES.
|
|
1571
|
+
///
|
|
1572
|
+
/// CALLER: a registered engram — `canisterToOwner` membership, checked first, exactly as
|
|
1573
|
+
/// `sysRecordAgentEdge` checks it (ADR-0020: every `sys` method names and checks its caller). The
|
|
1574
|
+
/// engram's own rules (the owner's standing allowance for this agent, its frozen-payments state,
|
|
1575
|
+
/// its balance) are the engram's to check before it calls; this method checks what only the
|
|
1576
|
+
/// registry can know.
|
|
1577
|
+
///
|
|
1578
|
+
/// NOTHING IS ACCEPTED UNTIL EVERY CHECK HAS PASSED (`ChildDoor.admission`, cheapest first), so
|
|
1579
|
+
/// every refusal before the accept returns the attached cycles to the caller by construction —
|
|
1580
|
+
/// the IC returns unaccepted cycles when the call replies. After the accept:
|
|
1581
|
+
/// · the network refuses the canister → what it refunded is deposited back to the caller, and
|
|
1582
|
+
/// the reply says how much came back;
|
|
1583
|
+
/// · the template will not install after one retry → the cycles are INSIDE the new canister and
|
|
1584
|
+
/// no canister can withdraw another's cycles. The one honest exception to "returned on every
|
|
1585
|
+
/// failure": nothing is recorded, and the reply names the canister, its controllers (this
|
|
1586
|
+
/// registry and the key) and what the key can do with it;
|
|
1587
|
+
/// · the key came to own an engram during the awaits → the new canister is named, nothing is
|
|
1588
|
+
/// recorded (the same third guard `spawnEngramFor` carries).
|
|
1589
|
+
///
|
|
1590
|
+
/// WHAT IT DOES NOT DO, deliberately: it never attaches the registry's own cycles (the grant stays
|
|
1591
|
+
/// with first engrams); it never writes a predecessor (a key with any record was refused); it never
|
|
1592
|
+
/// consults the reserve gate (the parent owner's approval is this door's admission — the gate's
|
|
1593
|
+
/// arming note stays on `provisionEngram`); it never tells the calling engram anything but this
|
|
1594
|
+
/// reply. No payment AMOUNT reaches it (ADR-0005): it carries cycles and a key, nothing else.
|
|
1595
|
+
///
|
|
1596
|
+
/// THE RATE LIMIT ⟨2026-10-01, evening — the owner's ruling "1B"; until then this door shared
|
|
1597
|
+
/// first engrams' limiter in full: their 24-hour count, their in-flight count and
|
|
1598
|
+
/// `effectiveDailyCap`⟩. The door shares only the DIAL (`spawnCeiling`) and the WINDOW LENGTH (24
|
|
1599
|
+
/// hours). Its count is its own (`sponsoredCreationTimestampsBuf`, pruned by
|
|
1600
|
+
/// `pruneSponsoredWindow`), its in-flight count is its own (`sponsoredInFlight`), and its cap is
|
|
1601
|
+
/// `spawnCeiling` alone — the reserve-derived term never applies, because the opened engram lives
|
|
1602
|
+
/// on the cycles the caller attached and the registry funds nothing. So an opening never uses up a
|
|
1603
|
+
/// first engram's slot, and is never refused for the registry's own low balance. The per-key
|
|
1604
|
+
/// pending mark (`createEngramPending`) stays shared: it is about ONE key, not a count, and a key
|
|
1605
|
+
/// must not be mid-spawn at both doors at once.
|
|
1606
|
+
///
|
|
1607
|
+
/// THE CUT-OFF AT BIRTH. If the caller ended the key's engagement while the canister was being
|
|
1608
|
+
/// created (the ending is recorded by `sysRecordAgentEdge` between this method's awaits), the
|
|
1609
|
+
/// opened engram is recorded AND cut off in the same step — the cycles are already in it, so the
|
|
1610
|
+
/// key keeps it, but it starts on nobody's record. Without this the ending's cut-off writer would
|
|
1611
|
+
/// have found no `openedThroughParent` row yet, and the engram would never be cut off.
|
|
1612
|
+
/// WHAT WOULD MAKE THIS WRONG: a second path that writes `parentOf`, or an `await` placed between
|
|
1613
|
+
/// the post-await re-check and the record below.
|
|
1614
|
+
sysSpawnChild: (ownerKey: principal) -> (Result_1);
|
|
1615
|
+
/// Admin: upgrade one engram to the current template. RESPECTS the owner's update preference —
|
|
1616
|
+
/// an engram set to `#ManualOnly` is not moved by this method, which is what makes the preference
|
|
1617
|
+
/// meaningful rather than advisory. The owner's own `upgradeMyEngram` is unaffected either way.
|
|
1618
|
+
upgradeEngram: (canisterId: principal) -> (Result);
|
|
1619
|
+
/// Upgrade YOUR OWN engram to the registry's current template. Self-service — no admin, no
|
|
1620
|
+
/// queue, no approval.
|
|
1621
|
+
///
|
|
1622
|
+
/// ⚠ THE PULL IS ALWAYS FUNDED, regardless of release class or rent state, and that is a
|
|
1623
|
+
/// commitment rather than an implementation detail: `install_code` needs the balance to cover the
|
|
1624
|
+
/// install ABOVE the freezing threshold, so the registry lifts the engram over that bar if cycles
|
|
1625
|
+
/// are what is stopping it. Refusing an owner their own fix for nonpayment is the ransom the
|
|
1626
|
+
/// continuity-rent model forbids, and withholding a security fix would be worse than anything
|
|
1627
|
+
/// that model gates. The lift is best-effort: if it fails, the install still runs and returns the
|
|
1628
|
+
/// REAL error, so a top-up problem can never masquerade as an upgrade problem or hide one.
|
|
1629
|
+
/// ⟨2026-09-28, owner decision: the registry only pays for upgrades of engrams running our
|
|
1630
|
+
/// published code⟩ One exception to "always funded": an engram whose installed module is not one
|
|
1631
|
+
/// this registry published is not lifted (`fundUpgradeIfNeeded`, audit
|
|
1632
|
+
/// `upgrade.topUp.unpublishedCode`). The install is still attempted — the owner is never refused
|
|
1633
|
+
/// their fix, only our cycles for it.
|
|
1634
|
+
/// ⟨2026-09-29⟩ And when that install then FAILS, the reply says why the registry did not pay and
|
|
1635
|
+
/// what the owner can do (`TemplatePolicy.unfundedUpgradeFailure`) — before, the refusal was only
|
|
1636
|
+
/// in the registry's own log and the owner saw a bare out-of-cycles error. Same reply type.
|
|
1637
|
+
/// ⟨2026-10-01, the owner's ruling of that day (2 A)⟩ A second exception beside the first: an
|
|
1638
|
+
/// engram opened through a parent (`parentOf`, written by `sysSpawnChild`) is funded by the
|
|
1639
|
+
/// registry never — not at birth, not at an upgrade (`fundUpgradeIfNeeded`, audit
|
|
1640
|
+
/// `upgrade.topUp.childEngram`). The install is still attempted; the owner is never refused their
|
|
1641
|
+
/// fix, only our cycles for it, and a failed install says so
|
|
1642
|
+
/// (`TemplatePolicy.childUnfundedUpgradeFailure`).
|
|
1643
|
+
///
|
|
1644
|
+
/// An engram already on the current template is a silent no-op. A SOVEREIGN engram gets
|
|
1645
|
+
/// "registry is no longer a controller" — accurate, because it is no longer ours to install on.
|
|
1646
|
+
///
|
|
1647
|
+
/// ⚠ THERE IS NO REGISTRY-SIDE SIZE PRE-FLIGHT. The FD budget check lives in
|
|
1648
|
+
/// the deployment's fleet-upgrade tooling, so a caller reaching this method directly does not
|
|
1649
|
+
/// get it.
|
|
1650
|
+
upgradeMyEngram: () -> (Result);
|
|
1651
|
+
/// True iff `canisterId` is a registered, `#Active` engram owned by `claimedOwner`. The cashier's
|
|
1652
|
+
/// money gate: fund ONLY an active engram the paying caller owns, since minting cycles to a
|
|
1653
|
+
/// frozen or deleted canister strands the funds.
|
|
1654
|
+
///
|
|
1655
|
+
/// PUBLIC: it CONFIRMS a claim the caller must already hold — both the canister id and the owner
|
|
1656
|
+
/// — so it discloses no mapping either way. That is the difference between this and the deleted
|
|
1657
|
+
/// `getEngramByCanisterId`, which was gated precisely because it turned one id into the other.
|
|
1658
|
+
///
|
|
1659
|
+
/// ⚠ WHAT IT DOES NOT ATTEST. The answer is the owner AS THIS REGISTRY RECORDS IT. Custody can
|
|
1660
|
+
/// move on the engram via `transferOwnership`, and while `sysNotifyOwnershipTransfer` now closes
|
|
1661
|
+
/// that gap going forward, a caller for whom CURRENCY matters should compose with the engram's
|
|
1662
|
+
/// `listOwnershipTransfers` — current custody is the `to` of its latest entry — as the /verify
|
|
1663
|
+
/// born-clean badge does. A query cannot make inter-canister calls, so this one cannot ask.
|
|
1664
|
+
///
|
|
1665
|
+
/// ⟨Promoted from `//` to `///` 2026-08-21. Everything above was written as plain comments, so
|
|
1666
|
+
/// registry.did published the cashier's money gate with NO explanation at all while 53 lines of
|
|
1667
|
+
/// it sat in the source. The correction history stays `//` on purpose — it is addressed to an
|
|
1668
|
+
/// editor, and only what a CALLER needs belongs in the interface.⟩
|
|
1669
|
+
verifyEngramActiveOwner: (canisterId: principal, claimedOwner:
|
|
1670
|
+
principal) -> (bool) query;
|
|
1671
|
+
}
|