@serve.zone/interfaces 32.42.0 → 32.43.0
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/changelog.md +22 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/networkegressretirement.d.ts +35 -11
- package/dist_ts/data/networkegressretirement.js +43 -14
- package/package.json +2 -2
- package/readme.md +36 -12
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/networkegressretirement.ts +50 -14
package/changelog.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-10-02 - 32.43.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- An egress owner that is lost no longer freezes the retirement of every other owner.
|
|
8
|
+
`IRuntimeNetworkEgressRetirement.clearedProjections` names every remaining owner except those
|
|
9
|
+
whose own `lost` fence is recorded against the same authority, and
|
|
10
|
+
`bindRuntimeNetworkProtectedAuthorityRetirement` takes an optional fifth argument `exemptions`
|
|
11
|
+
(default none) and answers them beside the fences: the `lost` fences of owners the step keeps,
|
|
12
|
+
each verified by its digest and recorded under the same controller against the exact `previous`,
|
|
13
|
+
for an owner both sides name and no fence or other exemption names, at most
|
|
14
|
+
`runtimeNetworkEgressRetirementContract.maximumExemptionsPerStep` (255). Every consumed fence's
|
|
15
|
+
clearance must name exactly the kept owners less the exempt ones. Lost owners can be fenced in
|
|
16
|
+
parallel and leave 16 per step, however many there are. `retiredOwners` is now the caller's
|
|
17
|
+
retired-owner history restricted to the owners `previous` or `next` names, and any entry refuses
|
|
18
|
+
the step; `maximumRetiredOwners` bounds that restriction at 512 instead of a lifetime history of
|
|
19
|
+
4096 owners, so a long-lived authority never runs out of retirements.
|
|
20
|
+
|
|
21
|
+
### Maintenance
|
|
22
|
+
|
|
23
|
+
- Release tooling: `@git.zone/tstest` 6.3.4.
|
|
24
|
+
|
|
3
25
|
## 2026-10-02 - 32.42.0
|
|
4
26
|
|
|
5
27
|
### Features
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/interfaces',
|
|
6
|
-
version: '32.
|
|
6
|
+
version: '32.43.0',
|
|
7
7
|
description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
|
|
@@ -29,7 +29,9 @@ export interface IRuntimeNetworkEgressRetirementClearance extends IRuntimeNetwor
|
|
|
29
29
|
* 4. every handoff and workload lease the node was granted is quarantined (`quarantinedHandoffs`,
|
|
30
30
|
* `quarantinedWorkloads`) and is never handed out again;
|
|
31
31
|
* 5. every remaining owner of `protectedAuthority` acknowledged a projection head that carries no
|
|
32
|
-
* endpoint of the node (`clearedProjections`).
|
|
32
|
+
* endpoint of the node (`clearedProjections`). An owner whose own `lost` fence is recorded
|
|
33
|
+
* against the same authority is not a remaining owner here: it never acknowledges anything again,
|
|
34
|
+
* so it can never clear another owner's endpoints, and it leaves behind its own fence.
|
|
33
35
|
*
|
|
34
36
|
* `lastReceipt` is the node's last accepted protection receipt. A `withdrawn` node took part in its
|
|
35
37
|
* retirement, so it always has one — its null withdrawal; a `lost` node may never have reported.
|
|
@@ -60,7 +62,10 @@ export interface IRuntimeNetworkEgressRetirement extends IRuntimeNetworkNodeScop
|
|
|
60
62
|
quarantinedHandoffs: IRuntimeAuthorityReference[];
|
|
61
63
|
/** Every workload lease the node was granted, each quarantined, in canonical order. */
|
|
62
64
|
quarantinedWorkloads: IRuntimeAuthorityReference[];
|
|
63
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* One entry per remaining owner of `protectedAuthority` whose own `lost` fence is not recorded
|
|
67
|
+
* against that authority, in canonical owner order.
|
|
68
|
+
*/
|
|
64
69
|
clearedProjections: IRuntimeNetworkEgressRetirementClearance[];
|
|
65
70
|
digest: TSha256Digest;
|
|
66
71
|
}
|
|
@@ -71,8 +76,16 @@ export declare const runtimeNetworkEgressRetirementContract: Readonly<{
|
|
|
71
76
|
readonly maximumClearedProjections: number;
|
|
72
77
|
/** Fences one step may consume. Each is read on its own, so one step stays bounded. */
|
|
73
78
|
readonly maximumFencesPerStep: 16;
|
|
74
|
-
/**
|
|
75
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Lost fences one step may hold as exemptions: owners it keeps that no consumed fence's clearance
|
|
81
|
+
* has to name. At least one owner is kept, so this is one fewer than the owners an authority has.
|
|
82
|
+
*/
|
|
83
|
+
readonly maximumExemptionsPerStep: number;
|
|
84
|
+
/**
|
|
85
|
+
* Retired owners one step may be given: the caller's history restricted to owners `previous` or
|
|
86
|
+
* `next` names, which is at most every owner of both.
|
|
87
|
+
*/
|
|
88
|
+
readonly maximumRetiredOwners: number;
|
|
76
89
|
readonly removal: "absent-owner-requires-fence-bound-to-exact-previous-authority";
|
|
77
90
|
readonly reentry: "a-fenced-owner-never-returns";
|
|
78
91
|
}>;
|
|
@@ -82,24 +95,35 @@ export declare const computeRuntimeNetworkEgressRetirementDigest: (value: IRunti
|
|
|
82
95
|
/**
|
|
83
96
|
* Whether `next` may succeed `previous`, judged on its egress owners and the address space it keeps.
|
|
84
97
|
*
|
|
85
|
-
* Pure and fail-closed. `fences` are the egress retirements this step consumes
|
|
86
|
-
*
|
|
87
|
-
*
|
|
98
|
+
* Pure and fail-closed. `fences` are the egress retirements this step consumes. `exemptions` are
|
|
99
|
+
* `lost` fences of owners this step keeps, recorded against the same `previous`: such an owner never
|
|
100
|
+
* acknowledges another projection head, so no consumed fence's clearance can wait for it. They are
|
|
101
|
+
* read, not consumed, and default to none. `retiredOwners` is the caller's durable history of
|
|
102
|
+
* fenced owners restricted to the owners `previous` or `next` names, in canonical owner order. The
|
|
103
|
+
* step is accepted only when:
|
|
88
104
|
*
|
|
89
105
|
* - `next` is the exact successor of `previous` under the same controller, and both digests verify;
|
|
90
106
|
* - an owner of `previous` is absent from `next` only when exactly one fence, recorded against the
|
|
91
107
|
* exact `previous` reference and verified by its digest, names it — a fence for an owner `next`
|
|
92
108
|
* still names, or for an owner `previous` does not name, refuses the step;
|
|
93
|
-
* - every
|
|
94
|
-
*
|
|
109
|
+
* - every exemption verifies by its digest, is `lost`, is recorded under the same controller against
|
|
110
|
+
* the exact `previous` reference, and names an owner both `previous` and `next` name, no fence
|
|
111
|
+
* names and no other exemption names; at most `maximumExemptionsPerStep`, in canonical owner order;
|
|
112
|
+
* - every fence's `clearedProjections` names exactly the owners of `previous` this step keeps, less
|
|
113
|
+
* the exempt owners; an exemption's own clearance is not judged here, only when its owner leaves;
|
|
114
|
+
* - `retiredOwners` is empty: any entry is either an owner a side names that was fenced before, or
|
|
115
|
+
* not part of the restriction at all, so a fenced owner never returns;
|
|
95
116
|
* - every pool of `previous` is kept unchanged and the prefixes of `next` cover those of `previous`;
|
|
96
117
|
* a step that removes an owner also keeps every resolver of `previous` unchanged.
|
|
97
118
|
*
|
|
98
119
|
* Owners that join in the same step are admitted. With no fence this is the additive owner rule:
|
|
99
|
-
* nobody leaves. It verifies no fence evidence against live state and
|
|
120
|
+
* nobody leaves, and exemptions relax nothing. It verifies no fence evidence against live state and
|
|
121
|
+
* fences no record: the caller fences every fence and exemption it read together with the authority
|
|
122
|
+
* it stages.
|
|
100
123
|
*/
|
|
101
|
-
export declare const bindRuntimeNetworkProtectedAuthorityRetirement: (previousValue: unknown, nextValue: unknown, fencesValue: unknown, retiredOwnersValue: unknown) => Promise<{
|
|
124
|
+
export declare const bindRuntimeNetworkProtectedAuthorityRetirement: (previousValue: unknown, nextValue: unknown, fencesValue: unknown, retiredOwnersValue: unknown, exemptionsValue?: unknown) => Promise<{
|
|
102
125
|
previous: IRuntimeNetworkProtectedAuthority;
|
|
103
126
|
next: IRuntimeNetworkProtectedAuthority;
|
|
104
127
|
fences: IRuntimeNetworkEgressRetirement[];
|
|
128
|
+
exemptions: IRuntimeNetworkEgressRetirement[];
|
|
105
129
|
}>;
|
|
@@ -10,8 +10,16 @@ export const runtimeNetworkEgressRetirementContract = Object.freeze({
|
|
|
10
10
|
maximumClearedProjections: runtimeNetworkAuthorityContract.maximumEgressAuthorities - 1,
|
|
11
11
|
/** Fences one step may consume. Each is read on its own, so one step stays bounded. */
|
|
12
12
|
maximumFencesPerStep: 16,
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Lost fences one step may hold as exemptions: owners it keeps that no consumed fence's clearance
|
|
15
|
+
* has to name. At least one owner is kept, so this is one fewer than the owners an authority has.
|
|
16
|
+
*/
|
|
17
|
+
maximumExemptionsPerStep: runtimeNetworkAuthorityContract.maximumEgressAuthorities - 1,
|
|
18
|
+
/**
|
|
19
|
+
* Retired owners one step may be given: the caller's history restricted to owners `previous` or
|
|
20
|
+
* `next` names, which is at most every owner of both.
|
|
21
|
+
*/
|
|
22
|
+
maximumRetiredOwners: 2 * runtimeNetworkAuthorityContract.maximumEgressAuthorities,
|
|
15
23
|
removal: 'absent-owner-requires-fence-bound-to-exact-previous-authority',
|
|
16
24
|
reentry: 'a-fenced-owner-never-returns',
|
|
17
25
|
});
|
|
@@ -55,23 +63,33 @@ export const computeRuntimeNetworkEgressRetirementDigest = (value) => networkHas
|
|
|
55
63
|
/**
|
|
56
64
|
* Whether `next` may succeed `previous`, judged on its egress owners and the address space it keeps.
|
|
57
65
|
*
|
|
58
|
-
* Pure and fail-closed. `fences` are the egress retirements this step consumes
|
|
59
|
-
*
|
|
60
|
-
*
|
|
66
|
+
* Pure and fail-closed. `fences` are the egress retirements this step consumes. `exemptions` are
|
|
67
|
+
* `lost` fences of owners this step keeps, recorded against the same `previous`: such an owner never
|
|
68
|
+
* acknowledges another projection head, so no consumed fence's clearance can wait for it. They are
|
|
69
|
+
* read, not consumed, and default to none. `retiredOwners` is the caller's durable history of
|
|
70
|
+
* fenced owners restricted to the owners `previous` or `next` names, in canonical owner order. The
|
|
71
|
+
* step is accepted only when:
|
|
61
72
|
*
|
|
62
73
|
* - `next` is the exact successor of `previous` under the same controller, and both digests verify;
|
|
63
74
|
* - an owner of `previous` is absent from `next` only when exactly one fence, recorded against the
|
|
64
75
|
* exact `previous` reference and verified by its digest, names it — a fence for an owner `next`
|
|
65
76
|
* still names, or for an owner `previous` does not name, refuses the step;
|
|
66
|
-
* - every
|
|
67
|
-
*
|
|
77
|
+
* - every exemption verifies by its digest, is `lost`, is recorded under the same controller against
|
|
78
|
+
* the exact `previous` reference, and names an owner both `previous` and `next` name, no fence
|
|
79
|
+
* names and no other exemption names; at most `maximumExemptionsPerStep`, in canonical owner order;
|
|
80
|
+
* - every fence's `clearedProjections` names exactly the owners of `previous` this step keeps, less
|
|
81
|
+
* the exempt owners; an exemption's own clearance is not judged here, only when its owner leaves;
|
|
82
|
+
* - `retiredOwners` is empty: any entry is either an owner a side names that was fenced before, or
|
|
83
|
+
* not part of the restriction at all, so a fenced owner never returns;
|
|
68
84
|
* - every pool of `previous` is kept unchanged and the prefixes of `next` cover those of `previous`;
|
|
69
85
|
* a step that removes an owner also keeps every resolver of `previous` unchanged.
|
|
70
86
|
*
|
|
71
87
|
* Owners that join in the same step are admitted. With no fence this is the additive owner rule:
|
|
72
|
-
* nobody leaves. It verifies no fence evidence against live state and
|
|
88
|
+
* nobody leaves, and exemptions relax nothing. It verifies no fence evidence against live state and
|
|
89
|
+
* fences no record: the caller fences every fence and exemption it read together with the authority
|
|
90
|
+
* it stages.
|
|
73
91
|
*/
|
|
74
|
-
export const bindRuntimeNetworkProtectedAuthorityRetirement = async (previousValue, nextValue, fencesValue, retiredOwnersValue) => {
|
|
92
|
+
export const bindRuntimeNetworkProtectedAuthorityRetirement = async (previousValue, nextValue, fencesValue, retiredOwnersValue, exemptionsValue = []) => {
|
|
75
93
|
try {
|
|
76
94
|
const contract = runtimeNetworkEgressRetirementContract;
|
|
77
95
|
const previous = snapshotRuntimeNetworkProtectedAuthority(captureNetwork(previousValue));
|
|
@@ -79,6 +97,9 @@ export const bindRuntimeNetworkProtectedAuthorityRetirement = async (previousVal
|
|
|
79
97
|
const elements = readDataElements(fencesValue, contract.maximumFencesPerStep);
|
|
80
98
|
requireNetwork(elements);
|
|
81
99
|
const fences = networkSorted(elements.map(snapshotRuntimeNetworkEgressRetirement), ownerKey);
|
|
100
|
+
const exemptionElements = readDataElements(exemptionsValue, contract.maximumExemptionsPerStep);
|
|
101
|
+
requireNetwork(exemptionElements);
|
|
102
|
+
const exemptions = networkSorted(exemptionElements.map(snapshotRuntimeNetworkEgressRetirement), ownerKey);
|
|
82
103
|
const retiredOwners = networkSorted(networkList(captureNetwork(retiredOwnersValue), contract.maximumRetiredOwners, readOwner), ownerKey);
|
|
83
104
|
requireNetwork(previous.digest === await computeRuntimeNetworkProtectedAuthorityDigest(previous)
|
|
84
105
|
&& next.digest === await computeRuntimeNetworkProtectedAuthorityDigest(next));
|
|
@@ -94,20 +115,28 @@ export const bindRuntimeNetworkProtectedAuthorityRetirement = async (previousVal
|
|
|
94
115
|
&& previousOwners.has(ownerKey(fence)) && !nextOwners.has(ownerKey(fence)));
|
|
95
116
|
}
|
|
96
117
|
requireNetwork([...previousOwners].every(owner => nextOwners.has(owner) || fenced.has(owner)));
|
|
97
|
-
const
|
|
118
|
+
for (const exemption of exemptions) {
|
|
119
|
+
requireNetwork(exemption.digest === await computeRuntimeNetworkEgressRetirementDigest(exemption)
|
|
120
|
+
&& exemption.disposition === 'lost' && networkEqual(exemption.controller, previous.controller)
|
|
121
|
+
&& networkEqual(exemption.protectedAuthority, reference) && previousOwners.has(ownerKey(exemption))
|
|
122
|
+
&& nextOwners.has(ownerKey(exemption)));
|
|
123
|
+
}
|
|
124
|
+
// A fence's owner is never named by `next`, so no exemption names an owner a fence names.
|
|
125
|
+
const exempt = new Set(exemptions.map(ownerKey));
|
|
126
|
+
const cleared = previous.egressAuthorities.map(ownerKey).filter(owner => !fenced.has(owner) && !exempt.has(owner));
|
|
98
127
|
for (const fence of fences) {
|
|
99
|
-
requireNetwork(networkEqual(fence.clearedProjections.map(ownerKey),
|
|
128
|
+
requireNetwork(networkEqual(fence.clearedProjections.map(ownerKey), cleared));
|
|
100
129
|
}
|
|
101
|
-
requireNetwork(retiredOwners.
|
|
130
|
+
requireNetwork(retiredOwners.length === 0);
|
|
102
131
|
requireNetwork(previous.pools.every(pool => next.pools.some(candidate => networkEqual(candidate, pool)))
|
|
103
132
|
&& previous.prefixes.every(prefix => networkCoveredBy(next.prefixes, prefix)));
|
|
104
133
|
if (fences.length > 0) {
|
|
105
134
|
requireNetwork(previous.resolvers.every(resolver => next.resolvers.some(candidate => networkEqual(candidate, resolver))));
|
|
106
135
|
}
|
|
107
|
-
return { previous, next, fences };
|
|
136
|
+
return { previous, next, fences, exemptions };
|
|
108
137
|
}
|
|
109
138
|
catch {
|
|
110
139
|
return failNetwork();
|
|
111
140
|
}
|
|
112
141
|
};
|
|
113
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
142
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmV0d29ya2VncmVzc3JldGlyZW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL25ldHdvcmtlZ3Jlc3NyZXRpcmVtZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUlBLE9BQU8sRUFBRSw2Q0FBNkMsRUFBRSwrQkFBK0IsRUFDckYsd0NBQXdDLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRSxPQUFPLEVBQUUsaUNBQWlDLEVBQTBDLE1BQU0scUJBQXFCLENBQUM7QUFDaEgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDaEUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUNuSCxXQUFXLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLFdBQVcsRUFDcEgsWUFBWSxFQUFFLGFBQWEsRUFBRSxjQUFjLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQW9FckYsTUFBTSxDQUFDLE1BQU0sc0NBQXNDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNsRSxZQUFZLEVBQUUsOENBQThDO0lBQzVELDBCQUEwQixFQUFFLCtCQUErQixDQUFDLDRCQUE0QjtJQUN4RiwyQkFBMkIsRUFBRSxHQUFHO0lBQ2hDLHlCQUF5QixFQUFFLCtCQUErQixDQUFDLHdCQUF3QixHQUFHLENBQUM7SUFDdkYsdUZBQXVGO0lBQ3ZGLG9CQUFvQixFQUFFLEVBQUU7SUFDeEI7OztPQUdHO0lBQ0gsd0JBQXdCLEVBQUUsK0JBQStCLENBQUMsd0JBQXdCLEdBQUcsQ0FBQztJQUN0Rjs7O09BR0c7SUFDSCxvQkFBb0IsRUFBRSxDQUFDLEdBQUcsK0JBQStCLENBQUMsd0JBQXdCO0lBQ2xGLE9BQU8sRUFBRSwrREFBK0Q7SUFDeEUsT0FBTyxFQUFFLDhCQUE4QjtDQUMvQixDQUFDLENBQUM7QUFFWixNQUFNLFFBQVEsR0FBRyxDQUFDLEtBQWlDLEVBQVUsRUFBRSxDQUFDLGdCQUFnQixDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDO0FBQ3pILE1BQU0sWUFBWSxHQUFHLENBQUMsS0FBaUMsRUFBVSxFQUFFLENBQUMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFNUYsTUFBTSxTQUFTLEdBQUcsQ0FBQyxLQUFjLEVBQThCLEVBQUU7SUFDL0QsTUFBTSxJQUFJLEdBQUcsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUM7SUFDbEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxhQUFhLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLGdCQUFnQixFQUFFLFNBQVMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxDQUFDO0FBQ3BHLENBQUMsQ0FBQztBQUNGLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBYyxFQUFFLE9BQWUsRUFBZ0MsRUFBRSxDQUN2RixhQUFhLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsZ0JBQWdCLENBQUMsRUFBRSxZQUFZLENBQUMsQ0FBQztBQUM3RSxNQUFNLGFBQWEsR0FBRyxDQUFDLEtBQWMsRUFBNEMsRUFBRTtJQUNqRixNQUFNLElBQUksR0FBRyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUMsUUFBUSxFQUFFLGtCQUFrQixFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7SUFDaEYsT0FBTyxFQUFFLEdBQUcsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDckYsVUFBVSxFQUFFLGdCQUFnQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO0FBQ3BELENBQUMsQ0FBQztBQUVGLHdHQUF3RztBQUN4RyxNQUFNLENBQUMsTUFBTSxzQ0FBc0MsR0FBRyxDQUFDLEtBQWMsRUFBbUMsRUFBRSxDQUFDLFdBQVcsQ0FBQyxHQUFHLEVBQUU7SUFDMUgsTUFBTSxJQUFJLEdBQUcsYUFBYSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksRUFBRSxRQUFRLEVBQUUsa0JBQWtCLEVBQUUsb0JBQW9CO1FBQ2pILGFBQWEsRUFBRSxhQUFhLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLHVCQUF1QixFQUFFLFVBQVU7UUFDcEcscUJBQXFCLEVBQUUsc0JBQXNCLEVBQUUsb0JBQW9CLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztJQUNsRixNQUFNLEtBQUssR0FBRyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDakMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUNyQyxjQUFjLENBQUMsT0FBTyxDQUFDLGlDQUFpQyxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7SUFDeEUsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLFdBQVcsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQzFGLGNBQWMsQ0FBQyxXQUFXLEtBQUssTUFBTSxJQUFJLFdBQVcsS0FBSyxJQUFJLENBQUMsQ0FBQztJQUMvRCxNQUFNLFdBQVcsR0FBRyxhQUFhLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7SUFDaEYsTUFBTSxTQUFTLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxxQkFBcUIsR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLENBQUM7SUFDckgsTUFBTSxRQUFRLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMvQyxjQUFjLENBQUMsUUFBUSxJQUFJLFNBQVMsSUFBSSxRQUFRLElBQUkscUJBQXFCLENBQUMsQ0FBQztJQUMzRSxNQUFNLFFBQVEsR0FBRyxzQ0FBc0MsQ0FBQztJQUN4RCxNQUFNLGtCQUFrQixHQUFHLGFBQWEsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLFFBQVEsQ0FBQyx5QkFBeUIsRUFDOUcsYUFBYSxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDNUIsY0FBYyxDQUFDLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsS0FBSyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZGLE9BQU8sRUFBRSxHQUFHLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsRUFBRSxXQUFXLEVBQUUsV0FBVztRQUN4RyxrQkFBa0IsRUFBRSxFQUFFLEtBQUssRUFBRSxjQUFjLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLE1BQU0sRUFBRSxjQUFjLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxFQUFFO1FBQzVHLFNBQVMsRUFBRSxxQkFBcUIsRUFBRSxRQUFRO1FBQzFDLG1CQUFtQixFQUFFLGNBQWMsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsUUFBUSxDQUFDLDBCQUEwQixDQUFDO1FBQ2xHLG9CQUFvQixFQUFFLGNBQWMsQ0FBQyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsUUFBUSxDQUFDLDJCQUEyQixDQUFDO1FBQ3JHLGtCQUFrQixFQUFFLE1BQU0sRUFBRSxhQUFhLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7QUFDN0QsQ0FBQyxDQUFDLENBQUM7QUFFSCxNQUFNLENBQUMsTUFBTSwyQ0FBMkMsR0FBRyxDQUFDLEtBQXNDLEVBQTBCLEVBQUUsQ0FDNUgsV0FBVyxDQUFDLHNDQUFzQyxDQUFDLFlBQVksRUFBRSxzQ0FBc0MsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO0FBRWxIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBNEJHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sOENBQThDLEdBQUcsS0FBSyxFQUNqRSxhQUFzQixFQUFFLFNBQWtCLEVBQUUsV0FBb0IsRUFBRSxrQkFBMkIsRUFDN0Ysa0JBQTJCLEVBQUUsRUFNNUIsRUFBRTtJQUNILElBQUksQ0FBQztRQUNILE1BQU0sUUFBUSxHQUFHLHNDQUFzQyxDQUFDO1FBQ3hELE1BQU0sUUFBUSxHQUFHLHdDQUF3QyxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDO1FBQ3pGLE1BQU0sSUFBSSxHQUFHLHdDQUF3QyxDQUFDLGNBQWMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBQ2pGLE1BQU0sUUFBUSxHQUFHLGdCQUFnQixDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUM5RSxjQUFjLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDekIsTUFBTSxNQUFNLEdBQUcsYUFBYSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsc0NBQXNDLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUM3RixNQUFNLGlCQUFpQixHQUFHLGdCQUFnQixDQUFDLGVBQWUsRUFBRSxRQUFRLENBQUMsd0JBQXdCLENBQUMsQ0FBQztRQUMvRixjQUFjLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNsQyxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLHNDQUFzQyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDMUcsTUFBTSxhQUFhLEdBQUcsYUFBYSxDQUFDLFdBQVcsQ0FBQyxjQUFjLENBQUMsa0JBQWtCLENBQUMsRUFBRSxRQUFRLENBQUMsb0JBQW9CLEVBQy9HLFNBQVMsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ3hCLGNBQWMsQ0FBQyxRQUFRLENBQUMsTUFBTSxLQUFLLE1BQU0sNkNBQTZDLENBQUMsUUFBUSxDQUFDO2VBQzNGLElBQUksQ0FBQyxNQUFNLEtBQUssTUFBTSw2Q0FBNkMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ2hGLE1BQU0sU0FBUyxHQUFHLGtCQUFrQixDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQy9DLGNBQWMsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksSUFBSSxDQUFDLEVBQUUsS0FBSyxRQUFRLENBQUMsRUFBRTtlQUN2RixJQUFJLENBQUMsVUFBVSxLQUFLLFFBQVEsQ0FBQyxVQUFVLEdBQUcsQ0FBQyxJQUFJLFlBQVksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFFNUYsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1FBQ3pFLE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNqRSxNQUFNLE1BQU0sR0FBRyxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDN0MsS0FBSyxNQUFNLEtBQUssSUFBSSxNQUFNLEVBQUUsQ0FBQztZQUMzQixjQUFjLENBQUMsS0FBSyxDQUFDLE1BQU0sS0FBSyxNQUFNLDJDQUEyQyxDQUFDLEtBQUssQ0FBQzttQkFDbkYsWUFBWSxDQUFDLEtBQUssQ0FBQyxVQUFVLEVBQUUsUUFBUSxDQUFDLFVBQVUsQ0FBQyxJQUFJLFlBQVksQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsU0FBUyxDQUFDO21CQUN4RyxjQUFjLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hGLENBQUM7UUFDRCxjQUFjLENBQUMsQ0FBQyxHQUFHLGNBQWMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDL0YsS0FBSyxNQUFNLFNBQVMsSUFBSSxVQUFVLEVBQUUsQ0FBQztZQUNuQyxjQUFjLENBQUMsU0FBUyxDQUFDLE1BQU0sS0FBSyxNQUFNLDJDQUEyQyxDQUFDLFNBQVMsQ0FBQzttQkFDM0YsU0FBUyxDQUFDLFdBQVcsS0FBSyxNQUFNLElBQUksWUFBWSxDQUFDLFNBQVMsQ0FBQyxVQUFVLEVBQUUsUUFBUSxDQUFDLFVBQVUsQ0FBQzttQkFDM0YsWUFBWSxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsRUFBRSxTQUFTLENBQUMsSUFBSSxjQUFjLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQzttQkFDaEcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFDRCwwRkFBMEY7UUFDMUYsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1FBQ2pELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQ25ILEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7WUFDM0IsY0FBYyxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDaEYsQ0FBQztRQUNELGNBQWMsQ0FBQyxhQUFhLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBRTNDLGNBQWMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO2VBQ25HLFFBQVEsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDakYsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3RCLGNBQWMsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1SCxDQUFDO1FBQ0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDO0lBQ2hELENBQUM7SUFBQyxNQUFNLENBQUM7UUFBQyxPQUFPLFdBQVcsRUFBRSxDQUFDO0lBQUMsQ0FBQztBQUNuQyxDQUFDLENBQUMifQ==
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/interfaces",
|
|
3
|
-
"version": "32.
|
|
3
|
+
"version": "32.43.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
|
|
6
6
|
"exports": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@git.zone/tsbuild": "^5.1.1",
|
|
31
31
|
"@git.zone/tsdoc": "^3.0.1",
|
|
32
32
|
"@git.zone/tsrun": "^3.0.2",
|
|
33
|
-
"@git.zone/tstest": "6.3.
|
|
33
|
+
"@git.zone/tstest": "6.3.4",
|
|
34
34
|
"@types/node": "26.6.2"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
package/readme.md
CHANGED
|
@@ -796,8 +796,11 @@ exact protected authority the owner is about to leave, once all five hold:
|
|
|
796
796
|
(`quarantinedHandoffs`, at most 256, and `quarantinedWorkloads`, at most 512,
|
|
797
797
|
both in canonical order) and is never handed out again;
|
|
798
798
|
5. every remaining owner of that authority acknowledged a projection head that
|
|
799
|
-
carries no endpoint of the node (`clearedProjections`,
|
|
800
|
-
|
|
799
|
+
carries no endpoint of the node (`clearedProjections`, in canonical owner order,
|
|
800
|
+
never the node itself). A remaining owner whose own `lost` fence is recorded
|
|
801
|
+
against the same authority is left out: it never acknowledges anything again, so
|
|
802
|
+
waiting for it would freeze every other owner's retirement, and it leaves behind
|
|
803
|
+
its own fence. Every other remaining owner has exactly one entry.
|
|
801
804
|
|
|
802
805
|
`lastReceipt` is the node's last accepted protection receipt. A `withdrawn` node
|
|
803
806
|
took part in its own retirement, so it always has one (its null withdrawal); a
|
|
@@ -810,19 +813,40 @@ evidence; the controller fences the rows it read together with the authority it
|
|
|
810
813
|
stages.
|
|
811
814
|
|
|
812
815
|
`bindRuntimeNetworkProtectedAuthorityRetirement(previous, next, fences,
|
|
813
|
-
retiredOwners)` is the pure successor rule for the egress owners
|
|
814
|
-
space of any step
|
|
816
|
+
retiredOwners, exemptions = [])` is the pure successor rule for the egress owners
|
|
817
|
+
and the address space of any step, and answers `{ previous, next, fences,
|
|
818
|
+
exemptions }`. `next` must be the exact successor of `previous` under the same
|
|
815
819
|
controller, with both digests verified. An owner of `previous` may be absent from
|
|
816
820
|
`next` only when exactly one fence recorded against the exact `previous` reference,
|
|
817
821
|
verified by its digest, names it; a fence for an owner `next` still names, or one
|
|
818
|
-
`previous` never named, refuses the step
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
822
|
+
`previous` never named, refuses the step.
|
|
823
|
+
|
|
824
|
+
`exemptions` are the `lost` fences of owners the step keeps, recorded against the
|
|
825
|
+
same `previous`. They are read and returned, not consumed: each verifies by its
|
|
826
|
+
digest, is recorded under the same controller against the exact `previous`
|
|
827
|
+
reference, and names an owner both `previous` and `next` name that no fence and no
|
|
828
|
+
other exemption names, in canonical owner order and at most 255 per step. A
|
|
829
|
+
withdrawn or stale exemption refuses the step. Every consumed fence's
|
|
830
|
+
`clearedProjections` must name exactly the owners the step keeps less the exempt
|
|
831
|
+
owners, in both directions: a clearance that names an exempt owner or misses a kept
|
|
832
|
+
one refuses the step. An exemption's own clearance is judged only in the step that
|
|
833
|
+
consumes it as a fence. Lost owners can therefore be fenced in parallel against one
|
|
834
|
+
authority and leave 16 per step: each step consumes up to 16 of their fences and
|
|
835
|
+
holds the rest as exemptions, and every later step re-records the remaining fences
|
|
836
|
+
against its own `previous`.
|
|
837
|
+
|
|
838
|
+
`retiredOwners` is the caller's durable history of fenced owners restricted to the
|
|
839
|
+
owners `previous` or `next` names, in canonical order and at most 512 entries. Any
|
|
840
|
+
entry refuses the step, so a fenced owner never returns, and the caller looks up
|
|
841
|
+
only the owners of the two authorities instead of supplying a lifetime list. The
|
|
842
|
+
caller must look up every owner that `previous` and `next` name: the binder cannot
|
|
843
|
+
tell a missed lookup from an empty history, so an omitted entry silently drops the
|
|
844
|
+
guarantee. Every
|
|
845
|
+
pool of `previous` stays unchanged and the prefixes of `next` cover those of
|
|
846
|
+
`previous`; a step that removes an owner also keeps every resolver. Owners may join
|
|
847
|
+
in the same step, and a step without fences is the additive rule, which exemptions
|
|
848
|
+
do not change. One step consumes at most 16 fences
|
|
849
|
+
(`runtimeNetworkEgressRetirementContract`).
|
|
826
850
|
|
|
827
851
|
A retirement step is a new protected authority: every remaining owner reports a
|
|
828
852
|
receipt citing it before anything new is allocated, while existing leases keep
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -39,7 +39,9 @@ export interface IRuntimeNetworkEgressRetirementClearance extends IRuntimeNetwor
|
|
|
39
39
|
* 4. every handoff and workload lease the node was granted is quarantined (`quarantinedHandoffs`,
|
|
40
40
|
* `quarantinedWorkloads`) and is never handed out again;
|
|
41
41
|
* 5. every remaining owner of `protectedAuthority` acknowledged a projection head that carries no
|
|
42
|
-
* endpoint of the node (`clearedProjections`).
|
|
42
|
+
* endpoint of the node (`clearedProjections`). An owner whose own `lost` fence is recorded
|
|
43
|
+
* against the same authority is not a remaining owner here: it never acknowledges anything again,
|
|
44
|
+
* so it can never clear another owner's endpoints, and it leaves behind its own fence.
|
|
43
45
|
*
|
|
44
46
|
* `lastReceipt` is the node's last accepted protection receipt. A `withdrawn` node took part in its
|
|
45
47
|
* retirement, so it always has one — its null withdrawal; a `lost` node may never have reported.
|
|
@@ -67,7 +69,10 @@ export interface IRuntimeNetworkEgressRetirement extends IRuntimeNetworkNodeScop
|
|
|
67
69
|
quarantinedHandoffs: IRuntimeAuthorityReference[];
|
|
68
70
|
/** Every workload lease the node was granted, each quarantined, in canonical order. */
|
|
69
71
|
quarantinedWorkloads: IRuntimeAuthorityReference[];
|
|
70
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* One entry per remaining owner of `protectedAuthority` whose own `lost` fence is not recorded
|
|
74
|
+
* against that authority, in canonical owner order.
|
|
75
|
+
*/
|
|
71
76
|
clearedProjections: IRuntimeNetworkEgressRetirementClearance[];
|
|
72
77
|
digest: TSha256Digest;
|
|
73
78
|
}
|
|
@@ -79,8 +84,16 @@ export const runtimeNetworkEgressRetirementContract = Object.freeze({
|
|
|
79
84
|
maximumClearedProjections: runtimeNetworkAuthorityContract.maximumEgressAuthorities - 1,
|
|
80
85
|
/** Fences one step may consume. Each is read on its own, so one step stays bounded. */
|
|
81
86
|
maximumFencesPerStep: 16,
|
|
82
|
-
/**
|
|
83
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Lost fences one step may hold as exemptions: owners it keeps that no consumed fence's clearance
|
|
89
|
+
* has to name. At least one owner is kept, so this is one fewer than the owners an authority has.
|
|
90
|
+
*/
|
|
91
|
+
maximumExemptionsPerStep: runtimeNetworkAuthorityContract.maximumEgressAuthorities - 1,
|
|
92
|
+
/**
|
|
93
|
+
* Retired owners one step may be given: the caller's history restricted to owners `previous` or
|
|
94
|
+
* `next` names, which is at most every owner of both.
|
|
95
|
+
*/
|
|
96
|
+
maximumRetiredOwners: 2 * runtimeNetworkAuthorityContract.maximumEgressAuthorities,
|
|
84
97
|
removal: 'absent-owner-requires-fence-bound-to-exact-previous-authority',
|
|
85
98
|
reentry: 'a-fenced-owner-never-returns',
|
|
86
99
|
} as const);
|
|
@@ -132,28 +145,40 @@ export const computeRuntimeNetworkEgressRetirementDigest = (value: IRuntimeNetwo
|
|
|
132
145
|
/**
|
|
133
146
|
* Whether `next` may succeed `previous`, judged on its egress owners and the address space it keeps.
|
|
134
147
|
*
|
|
135
|
-
* Pure and fail-closed. `fences` are the egress retirements this step consumes
|
|
136
|
-
*
|
|
137
|
-
*
|
|
148
|
+
* Pure and fail-closed. `fences` are the egress retirements this step consumes. `exemptions` are
|
|
149
|
+
* `lost` fences of owners this step keeps, recorded against the same `previous`: such an owner never
|
|
150
|
+
* acknowledges another projection head, so no consumed fence's clearance can wait for it. They are
|
|
151
|
+
* read, not consumed, and default to none. `retiredOwners` is the caller's durable history of
|
|
152
|
+
* fenced owners restricted to the owners `previous` or `next` names, in canonical owner order. The
|
|
153
|
+
* step is accepted only when:
|
|
138
154
|
*
|
|
139
155
|
* - `next` is the exact successor of `previous` under the same controller, and both digests verify;
|
|
140
156
|
* - an owner of `previous` is absent from `next` only when exactly one fence, recorded against the
|
|
141
157
|
* exact `previous` reference and verified by its digest, names it — a fence for an owner `next`
|
|
142
158
|
* still names, or for an owner `previous` does not name, refuses the step;
|
|
143
|
-
* - every
|
|
144
|
-
*
|
|
159
|
+
* - every exemption verifies by its digest, is `lost`, is recorded under the same controller against
|
|
160
|
+
* the exact `previous` reference, and names an owner both `previous` and `next` name, no fence
|
|
161
|
+
* names and no other exemption names; at most `maximumExemptionsPerStep`, in canonical owner order;
|
|
162
|
+
* - every fence's `clearedProjections` names exactly the owners of `previous` this step keeps, less
|
|
163
|
+
* the exempt owners; an exemption's own clearance is not judged here, only when its owner leaves;
|
|
164
|
+
* - `retiredOwners` is empty: any entry is either an owner a side names that was fenced before, or
|
|
165
|
+
* not part of the restriction at all, so a fenced owner never returns;
|
|
145
166
|
* - every pool of `previous` is kept unchanged and the prefixes of `next` cover those of `previous`;
|
|
146
167
|
* a step that removes an owner also keeps every resolver of `previous` unchanged.
|
|
147
168
|
*
|
|
148
169
|
* Owners that join in the same step are admitted. With no fence this is the additive owner rule:
|
|
149
|
-
* nobody leaves. It verifies no fence evidence against live state and
|
|
170
|
+
* nobody leaves, and exemptions relax nothing. It verifies no fence evidence against live state and
|
|
171
|
+
* fences no record: the caller fences every fence and exemption it read together with the authority
|
|
172
|
+
* it stages.
|
|
150
173
|
*/
|
|
151
174
|
export const bindRuntimeNetworkProtectedAuthorityRetirement = async (
|
|
152
175
|
previousValue: unknown, nextValue: unknown, fencesValue: unknown, retiredOwnersValue: unknown,
|
|
176
|
+
exemptionsValue: unknown = [],
|
|
153
177
|
): Promise<{
|
|
154
178
|
previous: IRuntimeNetworkProtectedAuthority;
|
|
155
179
|
next: IRuntimeNetworkProtectedAuthority;
|
|
156
180
|
fences: IRuntimeNetworkEgressRetirement[];
|
|
181
|
+
exemptions: IRuntimeNetworkEgressRetirement[];
|
|
157
182
|
}> => {
|
|
158
183
|
try {
|
|
159
184
|
const contract = runtimeNetworkEgressRetirementContract;
|
|
@@ -162,6 +187,9 @@ export const bindRuntimeNetworkProtectedAuthorityRetirement = async (
|
|
|
162
187
|
const elements = readDataElements(fencesValue, contract.maximumFencesPerStep);
|
|
163
188
|
requireNetwork(elements);
|
|
164
189
|
const fences = networkSorted(elements.map(snapshotRuntimeNetworkEgressRetirement), ownerKey);
|
|
190
|
+
const exemptionElements = readDataElements(exemptionsValue, contract.maximumExemptionsPerStep);
|
|
191
|
+
requireNetwork(exemptionElements);
|
|
192
|
+
const exemptions = networkSorted(exemptionElements.map(snapshotRuntimeNetworkEgressRetirement), ownerKey);
|
|
165
193
|
const retiredOwners = networkSorted(networkList(captureNetwork(retiredOwnersValue), contract.maximumRetiredOwners,
|
|
166
194
|
readOwner), ownerKey);
|
|
167
195
|
requireNetwork(previous.digest === await computeRuntimeNetworkProtectedAuthorityDigest(previous)
|
|
@@ -179,17 +207,25 @@ export const bindRuntimeNetworkProtectedAuthorityRetirement = async (
|
|
|
179
207
|
&& previousOwners.has(ownerKey(fence)) && !nextOwners.has(ownerKey(fence)));
|
|
180
208
|
}
|
|
181
209
|
requireNetwork([...previousOwners].every(owner => nextOwners.has(owner) || fenced.has(owner)));
|
|
182
|
-
const
|
|
210
|
+
for (const exemption of exemptions) {
|
|
211
|
+
requireNetwork(exemption.digest === await computeRuntimeNetworkEgressRetirementDigest(exemption)
|
|
212
|
+
&& exemption.disposition === 'lost' && networkEqual(exemption.controller, previous.controller)
|
|
213
|
+
&& networkEqual(exemption.protectedAuthority, reference) && previousOwners.has(ownerKey(exemption))
|
|
214
|
+
&& nextOwners.has(ownerKey(exemption)));
|
|
215
|
+
}
|
|
216
|
+
// A fence's owner is never named by `next`, so no exemption names an owner a fence names.
|
|
217
|
+
const exempt = new Set(exemptions.map(ownerKey));
|
|
218
|
+
const cleared = previous.egressAuthorities.map(ownerKey).filter(owner => !fenced.has(owner) && !exempt.has(owner));
|
|
183
219
|
for (const fence of fences) {
|
|
184
|
-
requireNetwork(networkEqual(fence.clearedProjections.map(ownerKey),
|
|
220
|
+
requireNetwork(networkEqual(fence.clearedProjections.map(ownerKey), cleared));
|
|
185
221
|
}
|
|
186
|
-
requireNetwork(retiredOwners.
|
|
222
|
+
requireNetwork(retiredOwners.length === 0);
|
|
187
223
|
|
|
188
224
|
requireNetwork(previous.pools.every(pool => next.pools.some(candidate => networkEqual(candidate, pool)))
|
|
189
225
|
&& previous.prefixes.every(prefix => networkCoveredBy(next.prefixes, prefix)));
|
|
190
226
|
if (fences.length > 0) {
|
|
191
227
|
requireNetwork(previous.resolvers.every(resolver => next.resolvers.some(candidate => networkEqual(candidate, resolver))));
|
|
192
228
|
}
|
|
193
|
-
return { previous, next, fences };
|
|
229
|
+
return { previous, next, fences, exemptions };
|
|
194
230
|
} catch { return failNetwork(); }
|
|
195
231
|
};
|