@serve.zone/interfaces 30.5.1 → 30.7.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-09-13 - 30.7.0
4
+
5
+ ### Features
6
+
7
+ - Add bounded canonical protected-network authority and immutable node handoff lease contracts, verify scope and transit-pool bindings, and reject retained port, conntrack zone and label collisions across generations.
8
+
9
+ ## 2026-09-13 - 30.6.0
10
+
11
+ ### Features
12
+
13
+ - Add current owner-organization summaries and paginated private-network service attachment candidates.
14
+
15
+ ### Fixes
16
+
17
+ - Use TSBuild 4.4.3 for release compilation and type checking.
18
+
3
19
  ## 2026-09-12 - 30.5.1
4
20
 
5
21
  ### Fixes
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '30.5.1',
6
+ version: '30.7.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
@@ -22,6 +22,7 @@ export * from './isolatedrestore.golden.js';
22
22
  export * from './mail.js';
23
23
  export * from './nodecredential.js';
24
24
  export * from './nodeenrollment.js';
25
+ export * from './networkauthority.js';
25
26
  export * from './privatenetwork.js';
26
27
  export * from './registry.js';
27
28
  export * from './runtimeassignment.js';
@@ -22,6 +22,7 @@ export * from './isolatedrestore.golden.js';
22
22
  export * from './mail.js';
23
23
  export * from './nodecredential.js';
24
24
  export * from './nodeenrollment.js';
25
+ export * from './networkauthority.js';
25
26
  export * from './privatenetwork.js';
26
27
  export * from './registry.js';
27
28
  export * from './runtimeassignment.js';
@@ -44,4 +45,4 @@ export * from './traffic.js';
44
45
  export * from './user.js';
45
46
  export * from './version.js';
46
47
  export * from './webpush.js';
47
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsZ0JBQWdCLENBQUM7QUFDL0IsY0FBYyxVQUFVLENBQUM7QUFDekIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxZQUFZLENBQUM7QUFDM0IsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGdCQUFnQixDQUFDO0FBQy9CLGNBQWMsWUFBWSxDQUFDO0FBQzNCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLGNBQWMsZUFBZSxDQUFDO0FBQzlCLGNBQWMsd0JBQXdCLENBQUM7QUFDdkMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxnQkFBZ0IsQ0FBQztBQUMvQixjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsZUFBZSxDQUFDO0FBQzlCLGNBQWMsc0JBQXNCLENBQUM7QUFDckMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxXQUFXLENBQUM7QUFDMUIsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxjQUFjLENBQUMifQ==
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsZ0JBQWdCLENBQUM7QUFDL0IsY0FBYyxVQUFVLENBQUM7QUFDekIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxZQUFZLENBQUM7QUFDM0IsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGdCQUFnQixDQUFDO0FBQy9CLGNBQWMsWUFBWSxDQUFDO0FBQzNCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsV0FBVyxDQUFDO0FBQzFCLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxjQUFjLHVCQUF1QixDQUFDO0FBQ3RDLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLG9CQUFvQixDQUFDO0FBQ25DLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyw2QkFBNkIsQ0FBQztBQUM1QyxjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLGdCQUFnQixDQUFDO0FBQy9CLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsbUJBQW1CLENBQUM7QUFDbEMsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLFdBQVcsQ0FBQztBQUMxQixjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGNBQWMsQ0FBQyJ9
@@ -0,0 +1,92 @@
1
+ import type { TSha256Digest } from './immutableimage.js';
2
+ import type { IRuntimeAuthorityReferenceV1, IRuntimeControllerV1 } from './runtimeassignment.js';
3
+ /** Controller declaration; a digest does not authenticate completeness or activate a pool. */
4
+ export interface IRuntimeNetworkProtectedAuthorityV1 {
5
+ schemaVersion: 1;
6
+ id: string;
7
+ generation: number;
8
+ controller: IRuntimeControllerV1;
9
+ previous: IRuntimeAuthorityReferenceV1 | null;
10
+ /** Complete canonical IPv4 union, including all pools and protected endpoints. */
11
+ prefixes: string[];
12
+ pools: Array<{
13
+ id: string;
14
+ purpose: 'workload' | 'transit';
15
+ prefix: string;
16
+ }>;
17
+ resolvers: Array<{
18
+ id: string;
19
+ address: string;
20
+ port: number;
21
+ }>;
22
+ /** Exact underlay exceptions; never inside a managed allocation pool. */
23
+ platformEndpoints: Array<{
24
+ id: string;
25
+ address: string;
26
+ protocol: 'tcp' | 'udp';
27
+ port: number;
28
+ }>;
29
+ /** Includes offline authorities with outstanding grants; omission does not fence them. */
30
+ egressAuthorities: Array<{
31
+ nodeId: string;
32
+ runtimeNamespace: string;
33
+ }>;
34
+ digest: TSha256Digest;
35
+ }
36
+ export interface IRuntimeNetworkHandoffPortRangeV1 {
37
+ protocol: 'tcp' | 'udp';
38
+ first: number;
39
+ last: number;
40
+ }
41
+ /** Immutable allocation, retained unchanged while active or quarantined. No expiry implies reuse. */
42
+ export interface IRuntimeNetworkHandoffLeaseV1 {
43
+ schemaVersion: 1;
44
+ id: string;
45
+ generation: number;
46
+ controller: IRuntimeControllerV1;
47
+ nodeId: string;
48
+ runtimeNamespace: string;
49
+ protectedAuthority: IRuntimeAuthorityReferenceV1;
50
+ /** Logical allocation incarnation, never a path, PID, inode, descriptor or native capability. */
51
+ routerIncarnationId: string;
52
+ transit: {
53
+ poolId: string;
54
+ prefix: string;
55
+ routerAddress: string;
56
+ hostAddress: string;
57
+ };
58
+ sourcePortRanges: IRuntimeNetworkHandoffPortRangeV1[];
59
+ conntrackZone: number;
60
+ conntrackLabel: string;
61
+ digest: TSha256Digest;
62
+ }
63
+ export declare const runtimeNetworkAuthorityV1Contract: Readonly<{
64
+ readonly protectedDigestDomain: "serve.zone/runtime-network-protected-authority-v1";
65
+ readonly handoffDigestDomain: "serve.zone/runtime-network-handoff-lease-v1";
66
+ readonly maximumPrefixes: 128;
67
+ readonly maximumPools: 64;
68
+ readonly maximumResolvers: 16;
69
+ readonly maximumPlatformEndpoints: 64;
70
+ readonly maximumEgressAuthorities: 256;
71
+ readonly maximumPortRanges: 16;
72
+ readonly maximumRetainedHandoffLeases: 256;
73
+ readonly activation: "all-affected-egress-barriers-or-independent-fences-before-allocation";
74
+ readonly quarantine: "retain-until-exact-authority-acknowledgements-or-independent-fence";
75
+ readonly omission: "no-effect";
76
+ }>;
77
+ /** Structural snapshot only. Complete inventory, signatures and activation barriers are caller-owned. */
78
+ export declare const snapshotRuntimeNetworkProtectedAuthorityV1: (value: unknown) => IRuntimeNetworkProtectedAuthorityV1;
79
+ export declare const snapshotRuntimeNetworkHandoffLeaseV1: (value: unknown) => IRuntimeNetworkHandoffLeaseV1;
80
+ export declare const computeRuntimeNetworkProtectedAuthorityV1Digest: (value: IRuntimeNetworkProtectedAuthorityV1) => Promise<TSha256Digest>;
81
+ export declare const computeRuntimeNetworkHandoffLeaseV1Digest: (value: IRuntimeNetworkHandoffLeaseV1) => Promise<TSha256Digest>;
82
+ /** Detached, digest-verified allocation binding. Does not establish pool activation or live link identity. */
83
+ export declare const bindRuntimeNetworkHandoffLeaseV1: (leaseValue: unknown, authorityValue: unknown) => Promise<{
84
+ lease: IRuntimeNetworkHandoffLeaseV1;
85
+ authority: IRuntimeNetworkProtectedAuthorityV1;
86
+ }>;
87
+ /**
88
+ * Validate ALL retained leases for one physical node/controller epoch, including quarantine.
89
+ * The owning transactional ledger must supply its complete history and serialize reservations.
90
+ * Namespace replacement, an empty CT dump or process exit never removes an entry from this set.
91
+ */
92
+ export declare const snapshotRuntimeNetworkHandoffLedgerV1: (value: unknown) => Promise<IRuntimeNetworkHandoffLeaseV1[]>;
@@ -0,0 +1,244 @@
1
+ import { canonicalizeStrictJson, createCanonicalJsonSha256Hex } from '../private/canonicaljson.js';
2
+ export const runtimeNetworkAuthorityV1Contract = Object.freeze({
3
+ protectedDigestDomain: 'serve.zone/runtime-network-protected-authority-v1',
4
+ handoffDigestDomain: 'serve.zone/runtime-network-handoff-lease-v1',
5
+ maximumPrefixes: 128,
6
+ maximumPools: 64,
7
+ maximumResolvers: 16,
8
+ maximumPlatformEndpoints: 64,
9
+ maximumEgressAuthorities: 256,
10
+ maximumPortRanges: 16,
11
+ maximumRetainedHandoffLeases: 256,
12
+ activation: 'all-affected-egress-barriers-or-independent-fences-before-allocation',
13
+ quarantine: 'retain-until-exact-authority-acknowledgements-or-independent-fence',
14
+ omission: 'no-effect',
15
+ });
16
+ const fail = () => { throw new Error('Runtime network authority contract rejected.'); };
17
+ function requireValue(value) { if (!value)
18
+ fail(); }
19
+ const safe = (operation) => { try {
20
+ return operation();
21
+ }
22
+ catch {
23
+ return fail();
24
+ } };
25
+ const record = (value, keys) => {
26
+ requireValue(value !== null && typeof value === 'object' && !Array.isArray(value));
27
+ const prototype = Object.getPrototypeOf(value);
28
+ requireValue(prototype === Object.prototype || prototype === null);
29
+ const actual = Reflect.ownKeys(value);
30
+ requireValue(actual.length === keys.length && keys.every(key => actual.includes(key)));
31
+ const result = Object.create(null);
32
+ for (const key of keys) {
33
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
34
+ requireValue(descriptor?.enumerable && Object.hasOwn(descriptor, 'value'));
35
+ result[key] = descriptor.value;
36
+ }
37
+ return result;
38
+ };
39
+ const list = (value, maximum, read) => {
40
+ requireValue(Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype);
41
+ const length = Object.getOwnPropertyDescriptor(value, 'length')?.value;
42
+ requireValue(Number.isSafeInteger(length) && length <= maximum && Reflect.ownKeys(value).length === length + 1);
43
+ const result = [];
44
+ for (let index = 0; index < length; index++) {
45
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
46
+ requireValue(descriptor?.enumerable && Object.hasOwn(descriptor, 'value'));
47
+ result.push(read(descriptor.value));
48
+ }
49
+ return result;
50
+ };
51
+ const id = (value) => {
52
+ requireValue(typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(value));
53
+ return value;
54
+ };
55
+ const integer = (value, maximum = Number.MAX_SAFE_INTEGER - 1) => {
56
+ requireValue(typeof value === 'number' && Number.isSafeInteger(value) && value > 0 && value <= maximum);
57
+ return value;
58
+ };
59
+ const digest = (value) => {
60
+ requireValue(typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value));
61
+ return value;
62
+ };
63
+ const controller = (value) => {
64
+ const item = record(value, ['kind', 'id', 'epoch']);
65
+ requireValue(item.kind === 'cloudly' || item.kind === 'onebox');
66
+ return { kind: item.kind, id: id(item.id), epoch: id(item.epoch) };
67
+ };
68
+ const reference = (value) => {
69
+ const item = record(value, ['id', 'generation', 'digest']);
70
+ return { id: id(item.id), generation: integer(item.generation), digest: digest(item.digest) };
71
+ };
72
+ const protocol = (value) => {
73
+ requireValue(value === 'tcp' || value === 'udp');
74
+ return value;
75
+ };
76
+ const sorted = (values, key) => {
77
+ requireValue(values.every((value, index) => index === 0 || key(values[index - 1]) < key(value)));
78
+ return values;
79
+ };
80
+ const address = (value) => {
81
+ requireValue(typeof value === 'string' && value.length <= 15);
82
+ const parts = value.split('.');
83
+ requireValue(parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/.test(part) && Number(part) <= 255));
84
+ return value;
85
+ };
86
+ const numericAddress = (value) => value.split('.').reduce((sum, part) => sum * 256 + Number(part), 0);
87
+ const endpointAddress = (value) => {
88
+ const result = address(value), first = Number(result.split('.')[0]);
89
+ requireValue(first > 0 && first < 224 && first !== 127);
90
+ return result;
91
+ };
92
+ const prefix = (value) => {
93
+ requireValue(typeof value === 'string' && value.length <= 18);
94
+ const parts = value.split('/');
95
+ requireValue(parts.length === 2 && /^(?:[0-9]|[12][0-9]|3[0-2])$/.test(parts[1]));
96
+ const base = numericAddress(address(parts[0])), width = 2 ** (32 - Number(parts[1]));
97
+ requireValue(base % width === 0);
98
+ return value;
99
+ };
100
+ const bounds = (value) => {
101
+ const [base, bits] = value.split('/'), first = numericAddress(base);
102
+ return [first, first + 2 ** (32 - Number(bits)) - 1];
103
+ };
104
+ const contains = (outer, inner) => {
105
+ const [a, b] = bounds(outer), [c, d] = bounds(inner);
106
+ return a <= c && b >= d;
107
+ };
108
+ const overlaps = (left, right) => {
109
+ const [a, b] = bounds(left), [c, d] = bounds(right);
110
+ return a <= d && c <= b;
111
+ };
112
+ const privatePrefix = (value) => {
113
+ const result = prefix(value);
114
+ requireValue(['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'].some(range => contains(range, result)));
115
+ return result;
116
+ };
117
+ const nonoverlapping = (values) => {
118
+ for (let index = 0; index < values.length; index++)
119
+ for (let other = 0; other < index; other++) {
120
+ requireValue(!overlaps(values[index], values[other]));
121
+ }
122
+ };
123
+ const canonical = (value) => canonicalizeStrictJson(value, fail);
124
+ const equals = (left, right) => canonical(left) === canonical(right);
125
+ /** Structural snapshot only. Complete inventory, signatures and activation barriers are caller-owned. */
126
+ export const snapshotRuntimeNetworkProtectedAuthorityV1 = (value) => safe(() => {
127
+ const item = record(value, ['schemaVersion', 'id', 'generation', 'controller', 'previous', 'prefixes',
128
+ 'pools', 'resolvers', 'platformEndpoints', 'egressAuthorities', 'digest']);
129
+ requireValue(item.schemaVersion === 1);
130
+ const identity = id(item.id), generation = integer(item.generation);
131
+ const previous = item.previous === null ? null : reference(item.previous);
132
+ requireValue(generation === 1 ? previous === null : previous?.id === identity && previous.generation === generation - 1);
133
+ const prefixes = sorted(list(item.prefixes, runtimeNetworkAuthorityV1Contract.maximumPrefixes, prefix), value => value);
134
+ requireValue(prefixes.length > 0);
135
+ nonoverlapping(prefixes);
136
+ const protectedPrefix = (value) => requireValue(prefixes.some(range => contains(range, value)));
137
+ const pools = sorted(list(item.pools, runtimeNetworkAuthorityV1Contract.maximumPools, value => {
138
+ const pool = record(value, ['id', 'purpose', 'prefix']);
139
+ requireValue(pool.purpose === 'workload' || pool.purpose === 'transit');
140
+ const result = {
141
+ id: id(pool.id), purpose: pool.purpose, prefix: privatePrefix(pool.prefix),
142
+ };
143
+ protectedPrefix(result.prefix);
144
+ return result;
145
+ }), value => value.id);
146
+ nonoverlapping(pools.map(pool => pool.prefix));
147
+ const readEndpoint = (value, withProtocol) => {
148
+ const endpoint = record(value, withProtocol ? ['id', 'address', 'protocol', 'port'] : ['id', 'address', 'port']);
149
+ const result = { id: id(endpoint.id), address: endpointAddress(endpoint.address), port: integer(endpoint.port, 65535) };
150
+ protectedPrefix(`${result.address}/32`);
151
+ requireValue(pools.every(pool => !contains(pool.prefix, `${result.address}/32`)));
152
+ return withProtocol ? { ...result, protocol: protocol(endpoint.protocol) } : result;
153
+ };
154
+ const resolvers = sorted(list(item.resolvers, runtimeNetworkAuthorityV1Contract.maximumResolvers, value => readEndpoint(value, false)), value => value.id);
155
+ const platformEndpoints = sorted(list(item.platformEndpoints, runtimeNetworkAuthorityV1Contract.maximumPlatformEndpoints, value => readEndpoint(value, true)), value => value.id);
156
+ const allEndpointKeys = [...resolvers.flatMap(value => ['tcp', 'udp'].map(transport => `${value.address}:${value.port}:${transport}`)),
157
+ ...platformEndpoints.map(value => `${value.address}:${value.port}:${value.protocol}`)];
158
+ requireValue(new Set(allEndpointKeys).size === allEndpointKeys.length);
159
+ const egressAuthorities = sorted(list(item.egressAuthorities, runtimeNetworkAuthorityV1Contract.maximumEgressAuthorities, value => {
160
+ const authority = record(value, ['nodeId', 'runtimeNamespace']);
161
+ return { nodeId: id(authority.nodeId), runtimeNamespace: id(authority.runtimeNamespace) };
162
+ }), value => canonical([value.nodeId, value.runtimeNamespace]));
163
+ requireValue(egressAuthorities.length > 0);
164
+ return { schemaVersion: 1, id: identity, generation, controller: controller(item.controller), previous,
165
+ prefixes, pools, resolvers, platformEndpoints, egressAuthorities, digest: digest(item.digest) };
166
+ });
167
+ export const snapshotRuntimeNetworkHandoffLeaseV1 = (value) => safe(() => {
168
+ const item = record(value, ['schemaVersion', 'id', 'generation', 'controller', 'nodeId', 'runtimeNamespace',
169
+ 'protectedAuthority', 'routerIncarnationId', 'transit', 'sourcePortRanges', 'conntrackZone', 'conntrackLabel', 'digest']);
170
+ requireValue(item.schemaVersion === 1);
171
+ const transit = record(item.transit, ['poolId', 'prefix', 'routerAddress', 'hostAddress']);
172
+ const transitPrefix = privatePrefix(transit.prefix), [first, last] = bounds(transitPrefix);
173
+ const routerAddress = endpointAddress(transit.routerAddress), hostAddress = endpointAddress(transit.hostAddress);
174
+ requireValue(first !== last && routerAddress !== hostAddress);
175
+ for (const value of [routerAddress, hostAddress]) {
176
+ const number = numericAddress(value);
177
+ requireValue(number >= first && number <= last && (last - first === 1 || (number !== first && number !== last)));
178
+ }
179
+ const sourcePortRanges = list(item.sourcePortRanges, runtimeNetworkAuthorityV1Contract.maximumPortRanges, value => {
180
+ const range = record(value, ['protocol', 'first', 'last']);
181
+ const result = { protocol: protocol(range.protocol), first: integer(range.first, 65535), last: integer(range.last, 65535) };
182
+ requireValue(result.first <= result.last);
183
+ return result;
184
+ });
185
+ requireValue(sourcePortRanges.length > 0);
186
+ for (let index = 1; index < sourcePortRanges.length; index++) {
187
+ const previous = sourcePortRanges[index - 1], current = sourcePortRanges[index];
188
+ requireValue(previous.protocol < current.protocol || (previous.protocol === current.protocol && previous.last < current.first));
189
+ }
190
+ requireValue(typeof item.conntrackLabel === 'string' && /^[a-f0-9]{32}$/.test(item.conntrackLabel)
191
+ && item.conntrackLabel !== '0'.repeat(32));
192
+ return { schemaVersion: 1, id: id(item.id), generation: integer(item.generation), controller: controller(item.controller),
193
+ nodeId: id(item.nodeId), runtimeNamespace: id(item.runtimeNamespace), protectedAuthority: reference(item.protectedAuthority),
194
+ routerIncarnationId: id(item.routerIncarnationId), transit: { poolId: id(transit.poolId), prefix: transitPrefix, routerAddress, hostAddress },
195
+ sourcePortRanges, conntrackZone: integer(item.conntrackZone, 65535), conntrackLabel: item.conntrackLabel, digest: digest(item.digest) };
196
+ });
197
+ const computeDigest = async (domain, value) => {
198
+ const { digest: omitted, ...payload } = value;
199
+ return `sha256:${await createCanonicalJsonSha256Hex(canonical({ domain, payload }), fail)}`;
200
+ };
201
+ export const computeRuntimeNetworkProtectedAuthorityV1Digest = (value) => computeDigest(runtimeNetworkAuthorityV1Contract.protectedDigestDomain, snapshotRuntimeNetworkProtectedAuthorityV1(value));
202
+ export const computeRuntimeNetworkHandoffLeaseV1Digest = (value) => computeDigest(runtimeNetworkAuthorityV1Contract.handoffDigestDomain, snapshotRuntimeNetworkHandoffLeaseV1(value));
203
+ /** Detached, digest-verified allocation binding. Does not establish pool activation or live link identity. */
204
+ export const bindRuntimeNetworkHandoffLeaseV1 = async (leaseValue, authorityValue) => {
205
+ const lease = snapshotRuntimeNetworkHandoffLeaseV1(leaseValue);
206
+ const authority = snapshotRuntimeNetworkProtectedAuthorityV1(authorityValue);
207
+ requireValue(lease.digest === await computeRuntimeNetworkHandoffLeaseV1Digest(lease));
208
+ requireValue(authority.digest === await computeRuntimeNetworkProtectedAuthorityV1Digest(authority));
209
+ requireValue(equals(lease.controller, authority.controller)
210
+ && equals(lease.protectedAuthority, { id: authority.id, generation: authority.generation, digest: authority.digest })
211
+ && authority.egressAuthorities.some(value => value.nodeId === lease.nodeId && value.runtimeNamespace === lease.runtimeNamespace));
212
+ const pool = authority.pools.find(value => value.id === lease.transit.poolId);
213
+ requireValue(pool?.purpose === 'transit' && contains(pool.prefix, lease.transit.prefix));
214
+ return { lease, authority };
215
+ };
216
+ /**
217
+ * Validate ALL retained leases for one physical node/controller epoch, including quarantine.
218
+ * The owning transactional ledger must supply its complete history and serialize reservations.
219
+ * Namespace replacement, an empty CT dump or process exit never removes an entry from this set.
220
+ */
221
+ export const snapshotRuntimeNetworkHandoffLedgerV1 = async (value) => {
222
+ const leases = safe(() => list(value, runtimeNetworkAuthorityV1Contract.maximumRetainedHandoffLeases, snapshotRuntimeNetworkHandoffLeaseV1));
223
+ for (let index = 0; index < leases.length; index++) {
224
+ const lease = leases[index];
225
+ requireValue(lease.digest === await computeRuntimeNetworkHandoffLeaseV1Digest(lease));
226
+ for (let other = 0; other < index; other++) {
227
+ const previous = leases[other];
228
+ requireValue(lease.nodeId === previous.nodeId && equals(lease.controller, previous.controller));
229
+ requireValue(lease.id !== previous.id || lease.generation !== previous.generation);
230
+ if (lease.runtimeNamespace === previous.runtimeNamespace && lease.routerIncarnationId === previous.routerIncarnationId) {
231
+ requireValue(lease.conntrackZone !== previous.conntrackZone && lease.conntrackLabel !== previous.conntrackLabel);
232
+ }
233
+ // Host handoff tuples remain reserved even across router or runtime namespace changes.
234
+ if (lease.transit.routerAddress === previous.transit.routerAddress) {
235
+ for (const left of lease.sourcePortRanges)
236
+ for (const right of previous.sourcePortRanges) {
237
+ requireValue(left.protocol !== right.protocol || left.last < right.first || right.last < left.first);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ return leases;
243
+ };
244
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmV0d29ya2F1dGhvcml0eS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2RhdGEvbmV0d29ya2F1dGhvcml0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQTRDbkcsTUFBTSxDQUFDLE1BQU0saUNBQWlDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUM3RCxxQkFBcUIsRUFBRSxtREFBbUQ7SUFDMUUsbUJBQW1CLEVBQUUsNkNBQTZDO0lBQ2xFLGVBQWUsRUFBRSxHQUFHO0lBQ3BCLFlBQVksRUFBRSxFQUFFO0lBQ2hCLGdCQUFnQixFQUFFLEVBQUU7SUFDcEIsd0JBQXdCLEVBQUUsRUFBRTtJQUM1Qix3QkFBd0IsRUFBRSxHQUFHO0lBQzdCLGlCQUFpQixFQUFFLEVBQUU7SUFDckIsNEJBQTRCLEVBQUUsR0FBRztJQUNqQyxVQUFVLEVBQUUsc0VBQXNFO0lBQ2xGLFVBQVUsRUFBRSxvRUFBb0U7SUFDaEYsUUFBUSxFQUFFLFdBQVc7Q0FDYixDQUFDLENBQUM7QUFFWixNQUFNLElBQUksR0FBRyxHQUFVLEVBQUUsR0FBRyxNQUFNLElBQUksS0FBSyxDQUFDLDhDQUE4QyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDL0YsU0FBUyxZQUFZLENBQUMsS0FBYyxJQUFtQixJQUFJLENBQUMsS0FBSztJQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztBQUM1RSxNQUFNLElBQUksR0FBRyxDQUFJLFNBQWtCLEVBQUssRUFBRSxHQUFHLElBQUksQ0FBQztJQUFDLE9BQU8sU0FBUyxFQUFFLENBQUM7QUFBQyxDQUFDO0FBQUMsTUFBTSxDQUFDO0lBQUMsT0FBTyxJQUFJLEVBQUUsQ0FBQztBQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDcEcsTUFBTSxNQUFNLEdBQUcsQ0FBQyxLQUFjLEVBQUUsSUFBYyxFQUEyQixFQUFF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
@@ -1,6 +1,39 @@
1
1
  import * as plugins from '../plugins.js';
2
2
  import type { IIdentityCredential } from '../data/user.js';
3
3
  import type { IPrivateNetworkV1, IServicePrivateNetworksV1, ISetServicePrivateNetworksV1, TPrivateNetworkMutationV1 } from '../data/privatenetwork.js';
4
+ /** Current organization picker data. Entries are never cached authorization grants. */
5
+ export interface IReq_Cloudly_ListOwnedOrganizationsV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_ListOwnedOrganizationsV1> {
6
+ method: 'listOwnedOrganizationsV1';
7
+ request: {
8
+ identity: IIdentityCredential;
9
+ };
10
+ /** Limited to the server-held OIDC token's grant and current canonical owner membership. */
11
+ response: {
12
+ organizations: Array<{
13
+ id: string;
14
+ name: string;
15
+ slug: string;
16
+ }>;
17
+ };
18
+ }
19
+ /** Organization-scoped attachment choices; excludes service configuration and secrets. */
20
+ export interface IReq_Cloudly_ListPrivateNetworkServiceCandidatesV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_ListPrivateNetworkServiceCandidatesV1> {
21
+ method: 'listPrivateNetworkServiceCandidatesV1';
22
+ /** Each page requires a fresh canonical organization-owner authority observation. */
23
+ request: {
24
+ identity: IIdentityCredential;
25
+ organizationId: string;
26
+ cursor: string | null;
27
+ limit: number;
28
+ };
29
+ response: {
30
+ services: Array<{
31
+ id: string;
32
+ name: string;
33
+ }>;
34
+ nextCursor: string | null;
35
+ };
36
+ }
4
37
  /** Every request is authorized against live canonical membership and Cloudly policy. */
5
38
  export interface IReq_Cloudly_ListPrivateNetworksV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_ListPrivateNetworksV1> {
6
39
  method: 'listPrivateNetworksV1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/interfaces",
3
- "version": "30.5.1",
3
+ "version": "30.7.0",
4
4
  "private": false,
5
5
  "description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
6
6
  "exports": {
@@ -27,7 +27,7 @@
27
27
  "@tsclass/tsclass": "^9.5.1"
28
28
  },
29
29
  "devDependencies": {
30
- "@git.zone/tsbuild": "^4.4.2",
30
+ "@git.zone/tsbuild": "^4.4.3",
31
31
  "@git.zone/tsdoc": "^2.2.0",
32
32
  "@git.zone/tsrun": "^2.0.6",
33
33
  "@git.zone/tstest": "6.0.0",
package/readme.md CHANGED
@@ -254,6 +254,36 @@ one second. DNS expiry does not prove packet revocation or permit identity reuse
254
254
  Retirement and attachment responses describe desired or historical metadata;
255
255
  consumers must separately track outstanding node revocations and readiness.
256
256
 
257
+ `IRuntimeNetworkProtectedAuthorityV1` declares the complete controller-owned IPv4
258
+ protection set, disjoint private workload/transit pools, protected resolver and
259
+ platform endpoints, and every affected egress authority, including offline nodes.
260
+ Its snapshot and digest helpers bind the exact predecessor and all declaration
261
+ content. They cannot prove that an operator's inventory is complete. Install the
262
+ union of old and new protection on every affected egress owner, or independently
263
+ fence that owner, before exposing a newly allocatable pool. Removing an entry from
264
+ the declaration is not a revocation acknowledgement. Prefix arrays are sorted
265
+ lexically and nonoverlapping; pools and endpoints are sorted by ID; egress owners
266
+ are ordered by canonical JSON of `[nodeId, runtimeNamespace]`.
267
+
268
+ `IRuntimeNetworkHandoffLeaseV1` binds an immutable node/router incarnation to its
269
+ protected-authority reference, transit subnet and peers, protocol-qualified source
270
+ ports, nonzero conntrack zone and 128-bit label. The label is exactly 32 lowercase
271
+ hex characters. Port ranges are inclusive, sorted by protocol then first port,
272
+ and disjoint within each protocol. `bindRuntimeNetworkHandoffLeaseV1` verifies both
273
+ digests, scope and transit-pool containment and returns detached values. It does
274
+ not authenticate a projection or attest to an actual namespace/interface.
275
+
276
+ `snapshotRuntimeNetworkHandoffLedgerV1` verifies at most 256 retained leases for
277
+ one node/controller epoch, including quarantined allocations. It rejects host
278
+ handoff-port collisions even across router/runtime namespace changes, and zone or
279
+ label reuse within one router incarnation. The caller must transactionally supply
280
+ the complete retained set, serialize allocation and fence controller-epoch changes.
281
+ These are collision checks, not an IPAM allocator or a native capability. No expiry,
282
+ empty conntrack dump, process exit or table removal establishes flow drainage or
283
+ permits handoff identity reuse. Signed complete projections, live interface proofs
284
+ and the separate router/host apply journal remain responsibilities of Cloudly and
285
+ Pallet; neither packet admission nor readiness follows from these helpers.
286
+
257
287
  ### Node-Bound Runtime Assignments
258
288
 
259
289
  The `data` namespace exports `IRuntimeAssignmentV1`, canonical digest helpers,
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '30.5.1',
6
+ version: '30.7.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  }
package/ts/data/index.ts CHANGED
@@ -22,6 +22,7 @@ export * from './isolatedrestore.golden.js';
22
22
  export * from './mail.js';
23
23
  export * from './nodecredential.js';
24
24
  export * from './nodeenrollment.js';
25
+ export * from './networkauthority.js';
25
26
  export * from './privatenetwork.js';
26
27
  export * from './registry.js';
27
28
  export * from './runtimeassignment.js';
@@ -0,0 +1,293 @@
1
+ import type { TSha256Digest } from './immutableimage.js';
2
+ import type { IRuntimeAuthorityReferenceV1, IRuntimeControllerV1 } from './runtimeassignment.js';
3
+ import { canonicalizeStrictJson, createCanonicalJsonSha256Hex } from '../private/canonicaljson.js';
4
+
5
+ /** Controller declaration; a digest does not authenticate completeness or activate a pool. */
6
+ export interface IRuntimeNetworkProtectedAuthorityV1 {
7
+ schemaVersion: 1;
8
+ id: string;
9
+ generation: number;
10
+ controller: IRuntimeControllerV1;
11
+ previous: IRuntimeAuthorityReferenceV1 | null;
12
+ /** Complete canonical IPv4 union, including all pools and protected endpoints. */
13
+ prefixes: string[];
14
+ pools: Array<{ id: string; purpose: 'workload' | 'transit'; prefix: string }>;
15
+ resolvers: Array<{ id: string; address: string; port: number }>;
16
+ /** Exact underlay exceptions; never inside a managed allocation pool. */
17
+ platformEndpoints: Array<{ id: string; address: string; protocol: 'tcp' | 'udp'; port: number }>;
18
+ /** Includes offline authorities with outstanding grants; omission does not fence them. */
19
+ egressAuthorities: Array<{ nodeId: string; runtimeNamespace: string }>;
20
+ digest: TSha256Digest;
21
+ }
22
+
23
+ export interface IRuntimeNetworkHandoffPortRangeV1 {
24
+ protocol: 'tcp' | 'udp';
25
+ first: number;
26
+ last: number;
27
+ }
28
+
29
+ /** Immutable allocation, retained unchanged while active or quarantined. No expiry implies reuse. */
30
+ export interface IRuntimeNetworkHandoffLeaseV1 {
31
+ schemaVersion: 1;
32
+ id: string;
33
+ generation: number;
34
+ controller: IRuntimeControllerV1;
35
+ nodeId: string;
36
+ runtimeNamespace: string;
37
+ protectedAuthority: IRuntimeAuthorityReferenceV1;
38
+ /** Logical allocation incarnation, never a path, PID, inode, descriptor or native capability. */
39
+ routerIncarnationId: string;
40
+ transit: { poolId: string; prefix: string; routerAddress: string; hostAddress: string };
41
+ sourcePortRanges: IRuntimeNetworkHandoffPortRangeV1[];
42
+ conntrackZone: number;
43
+ conntrackLabel: string;
44
+ digest: TSha256Digest;
45
+ }
46
+
47
+ export const runtimeNetworkAuthorityV1Contract = Object.freeze({
48
+ protectedDigestDomain: 'serve.zone/runtime-network-protected-authority-v1',
49
+ handoffDigestDomain: 'serve.zone/runtime-network-handoff-lease-v1',
50
+ maximumPrefixes: 128,
51
+ maximumPools: 64,
52
+ maximumResolvers: 16,
53
+ maximumPlatformEndpoints: 64,
54
+ maximumEgressAuthorities: 256,
55
+ maximumPortRanges: 16,
56
+ maximumRetainedHandoffLeases: 256,
57
+ activation: 'all-affected-egress-barriers-or-independent-fences-before-allocation',
58
+ quarantine: 'retain-until-exact-authority-acknowledgements-or-independent-fence',
59
+ omission: 'no-effect',
60
+ } as const);
61
+
62
+ const fail = (): never => { throw new Error('Runtime network authority contract rejected.'); };
63
+ function requireValue(value: unknown): asserts value { if (!value) fail(); }
64
+ const safe = <T>(operation: () => T): T => { try { return operation(); } catch { return fail(); } };
65
+ const record = (value: unknown, keys: string[]): Record<string, unknown> => {
66
+ requireValue(value !== null && typeof value === 'object' && !Array.isArray(value));
67
+ const prototype = Object.getPrototypeOf(value);
68
+ requireValue(prototype === Object.prototype || prototype === null);
69
+ const actual = Reflect.ownKeys(value);
70
+ requireValue(actual.length === keys.length && keys.every(key => actual.includes(key)));
71
+ const result: Record<string, unknown> = Object.create(null);
72
+ for (const key of keys) {
73
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
74
+ requireValue(descriptor?.enumerable && Object.hasOwn(descriptor, 'value'));
75
+ result[key] = descriptor.value;
76
+ }
77
+ return result;
78
+ };
79
+ const list = <T>(value: unknown, maximum: number, read: (item: unknown) => T): T[] => {
80
+ requireValue(Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype);
81
+ const length = Object.getOwnPropertyDescriptor(value, 'length')?.value;
82
+ requireValue(Number.isSafeInteger(length) && length <= maximum && Reflect.ownKeys(value).length === length + 1);
83
+ const result: T[] = [];
84
+ for (let index = 0; index < length; index++) {
85
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
86
+ requireValue(descriptor?.enumerable && Object.hasOwn(descriptor, 'value'));
87
+ result.push(read(descriptor.value));
88
+ }
89
+ return result;
90
+ };
91
+ const id = (value: unknown): string => {
92
+ requireValue(typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(value));
93
+ return value;
94
+ };
95
+ const integer = (value: unknown, maximum = Number.MAX_SAFE_INTEGER - 1): number => {
96
+ requireValue(typeof value === 'number' && Number.isSafeInteger(value) && value > 0 && value <= maximum);
97
+ return value;
98
+ };
99
+ const digest = (value: unknown): TSha256Digest => {
100
+ requireValue(typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value));
101
+ return value as TSha256Digest;
102
+ };
103
+ const controller = (value: unknown): IRuntimeControllerV1 => {
104
+ const item = record(value, ['kind', 'id', 'epoch']);
105
+ requireValue(item.kind === 'cloudly' || item.kind === 'onebox');
106
+ return { kind: item.kind, id: id(item.id), epoch: id(item.epoch) };
107
+ };
108
+ const reference = (value: unknown): IRuntimeAuthorityReferenceV1 => {
109
+ const item = record(value, ['id', 'generation', 'digest']);
110
+ return { id: id(item.id), generation: integer(item.generation), digest: digest(item.digest) };
111
+ };
112
+ const protocol = (value: unknown): 'tcp' | 'udp' => {
113
+ requireValue(value === 'tcp' || value === 'udp');
114
+ return value;
115
+ };
116
+ const sorted = <T>(values: T[], key: (value: T) => string): T[] => {
117
+ requireValue(values.every((value, index) => index === 0 || key(values[index - 1]) < key(value)));
118
+ return values;
119
+ };
120
+ const address = (value: unknown): string => {
121
+ requireValue(typeof value === 'string' && value.length <= 15);
122
+ const parts = value.split('.');
123
+ requireValue(parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/.test(part) && Number(part) <= 255));
124
+ return value;
125
+ };
126
+ const numericAddress = (value: string): number => value.split('.').reduce((sum, part) => sum * 256 + Number(part), 0);
127
+ const endpointAddress = (value: unknown): string => {
128
+ const result = address(value), first = Number(result.split('.')[0]);
129
+ requireValue(first > 0 && first < 224 && first !== 127);
130
+ return result;
131
+ };
132
+ const prefix = (value: unknown): string => {
133
+ requireValue(typeof value === 'string' && value.length <= 18);
134
+ const parts = value.split('/');
135
+ requireValue(parts.length === 2 && /^(?:[0-9]|[12][0-9]|3[0-2])$/.test(parts[1]));
136
+ const base = numericAddress(address(parts[0])), width = 2 ** (32 - Number(parts[1]));
137
+ requireValue(base % width === 0);
138
+ return value;
139
+ };
140
+ const bounds = (value: string): [number, number] => {
141
+ const [base, bits] = value.split('/'), first = numericAddress(base);
142
+ return [first, first + 2 ** (32 - Number(bits)) - 1];
143
+ };
144
+ const contains = (outer: string, inner: string): boolean => {
145
+ const [a, b] = bounds(outer), [c, d] = bounds(inner);
146
+ return a <= c && b >= d;
147
+ };
148
+ const overlaps = (left: string, right: string): boolean => {
149
+ const [a, b] = bounds(left), [c, d] = bounds(right);
150
+ return a <= d && c <= b;
151
+ };
152
+ const privatePrefix = (value: unknown): string => {
153
+ const result = prefix(value);
154
+ requireValue(['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'].some(range => contains(range, result)));
155
+ return result;
156
+ };
157
+ const nonoverlapping = (values: string[]): void => {
158
+ for (let index = 0; index < values.length; index++) for (let other = 0; other < index; other++) {
159
+ requireValue(!overlaps(values[index], values[other]));
160
+ }
161
+ };
162
+ const canonical = (value: unknown): string => canonicalizeStrictJson(value, fail);
163
+ const equals = (left: unknown, right: unknown): boolean => canonical(left) === canonical(right);
164
+
165
+ /** Structural snapshot only. Complete inventory, signatures and activation barriers are caller-owned. */
166
+ export const snapshotRuntimeNetworkProtectedAuthorityV1 = (value: unknown): IRuntimeNetworkProtectedAuthorityV1 => safe(() => {
167
+ const item = record(value, ['schemaVersion', 'id', 'generation', 'controller', 'previous', 'prefixes',
168
+ 'pools', 'resolvers', 'platformEndpoints', 'egressAuthorities', 'digest']);
169
+ requireValue(item.schemaVersion === 1);
170
+ const identity = id(item.id), generation = integer(item.generation);
171
+ const previous = item.previous === null ? null : reference(item.previous);
172
+ requireValue(generation === 1 ? previous === null : previous?.id === identity && previous.generation === generation - 1);
173
+ const prefixes = sorted(list(item.prefixes, runtimeNetworkAuthorityV1Contract.maximumPrefixes, prefix), value => value);
174
+ requireValue(prefixes.length > 0);
175
+ nonoverlapping(prefixes);
176
+ const protectedPrefix = (value: string) => requireValue(prefixes.some(range => contains(range, value)));
177
+ const pools = sorted(list(item.pools, runtimeNetworkAuthorityV1Contract.maximumPools, value => {
178
+ const pool = record(value, ['id', 'purpose', 'prefix']);
179
+ requireValue(pool.purpose === 'workload' || pool.purpose === 'transit');
180
+ const result: IRuntimeNetworkProtectedAuthorityV1['pools'][number] = {
181
+ id: id(pool.id), purpose: pool.purpose, prefix: privatePrefix(pool.prefix),
182
+ };
183
+ protectedPrefix(result.prefix);
184
+ return result;
185
+ }), value => value.id);
186
+ nonoverlapping(pools.map(pool => pool.prefix));
187
+ const readEndpoint = (value: unknown, withProtocol: boolean) => {
188
+ const endpoint = record(value, withProtocol ? ['id', 'address', 'protocol', 'port'] : ['id', 'address', 'port']);
189
+ const result = { id: id(endpoint.id), address: endpointAddress(endpoint.address), port: integer(endpoint.port, 65535) };
190
+ protectedPrefix(`${result.address}/32`);
191
+ requireValue(pools.every(pool => !contains(pool.prefix, `${result.address}/32`)));
192
+ return withProtocol ? { ...result, protocol: protocol(endpoint.protocol) } : result;
193
+ };
194
+ const resolvers = sorted(list(item.resolvers, runtimeNetworkAuthorityV1Contract.maximumResolvers,
195
+ value => readEndpoint(value, false)), value => value.id);
196
+ const platformEndpoints = sorted(list(item.platformEndpoints, runtimeNetworkAuthorityV1Contract.maximumPlatformEndpoints,
197
+ value => readEndpoint(value, true) as IRuntimeNetworkProtectedAuthorityV1['platformEndpoints'][number]), value => value.id);
198
+ const allEndpointKeys = [...resolvers.flatMap(value => ['tcp', 'udp'].map(transport => `${value.address}:${value.port}:${transport}`)),
199
+ ...platformEndpoints.map(value => `${value.address}:${value.port}:${value.protocol}`)];
200
+ requireValue(new Set(allEndpointKeys).size === allEndpointKeys.length);
201
+ const egressAuthorities = sorted(list(item.egressAuthorities, runtimeNetworkAuthorityV1Contract.maximumEgressAuthorities, value => {
202
+ const authority = record(value, ['nodeId', 'runtimeNamespace']);
203
+ return { nodeId: id(authority.nodeId), runtimeNamespace: id(authority.runtimeNamespace) };
204
+ }), value => canonical([value.nodeId, value.runtimeNamespace]));
205
+ requireValue(egressAuthorities.length > 0);
206
+ return { schemaVersion: 1, id: identity, generation, controller: controller(item.controller), previous,
207
+ prefixes, pools, resolvers, platformEndpoints, egressAuthorities, digest: digest(item.digest) };
208
+ });
209
+
210
+ export const snapshotRuntimeNetworkHandoffLeaseV1 = (value: unknown): IRuntimeNetworkHandoffLeaseV1 => safe(() => {
211
+ const item = record(value, ['schemaVersion', 'id', 'generation', 'controller', 'nodeId', 'runtimeNamespace',
212
+ 'protectedAuthority', 'routerIncarnationId', 'transit', 'sourcePortRanges', 'conntrackZone', 'conntrackLabel', 'digest']);
213
+ requireValue(item.schemaVersion === 1);
214
+ const transit = record(item.transit, ['poolId', 'prefix', 'routerAddress', 'hostAddress']);
215
+ const transitPrefix = privatePrefix(transit.prefix), [first, last] = bounds(transitPrefix);
216
+ const routerAddress = endpointAddress(transit.routerAddress), hostAddress = endpointAddress(transit.hostAddress);
217
+ requireValue(first !== last && routerAddress !== hostAddress);
218
+ for (const value of [routerAddress, hostAddress]) {
219
+ const number = numericAddress(value);
220
+ requireValue(number >= first && number <= last && (last - first === 1 || (number !== first && number !== last)));
221
+ }
222
+ const sourcePortRanges = list(item.sourcePortRanges, runtimeNetworkAuthorityV1Contract.maximumPortRanges, value => {
223
+ const range = record(value, ['protocol', 'first', 'last']);
224
+ const result = { protocol: protocol(range.protocol), first: integer(range.first, 65535), last: integer(range.last, 65535) };
225
+ requireValue(result.first <= result.last);
226
+ return result;
227
+ });
228
+ requireValue(sourcePortRanges.length > 0);
229
+ for (let index = 1; index < sourcePortRanges.length; index++) {
230
+ const previous = sourcePortRanges[index - 1], current = sourcePortRanges[index];
231
+ requireValue(previous.protocol < current.protocol || (previous.protocol === current.protocol && previous.last < current.first));
232
+ }
233
+ requireValue(typeof item.conntrackLabel === 'string' && /^[a-f0-9]{32}$/.test(item.conntrackLabel)
234
+ && item.conntrackLabel !== '0'.repeat(32));
235
+ return { schemaVersion: 1, id: id(item.id), generation: integer(item.generation), controller: controller(item.controller),
236
+ nodeId: id(item.nodeId), runtimeNamespace: id(item.runtimeNamespace), protectedAuthority: reference(item.protectedAuthority),
237
+ routerIncarnationId: id(item.routerIncarnationId), transit: { poolId: id(transit.poolId), prefix: transitPrefix, routerAddress, hostAddress },
238
+ sourcePortRanges, conntrackZone: integer(item.conntrackZone, 65535), conntrackLabel: item.conntrackLabel, digest: digest(item.digest) };
239
+ });
240
+
241
+ const computeDigest = async (domain: string, value: { digest: TSha256Digest }): Promise<TSha256Digest> => {
242
+ const { digest: omitted, ...payload } = value;
243
+ return `sha256:${await createCanonicalJsonSha256Hex(canonical({ domain, payload }), fail)}`;
244
+ };
245
+ export const computeRuntimeNetworkProtectedAuthorityV1Digest = (value: IRuntimeNetworkProtectedAuthorityV1): Promise<TSha256Digest> =>
246
+ computeDigest(runtimeNetworkAuthorityV1Contract.protectedDigestDomain, snapshotRuntimeNetworkProtectedAuthorityV1(value));
247
+ export const computeRuntimeNetworkHandoffLeaseV1Digest = (value: IRuntimeNetworkHandoffLeaseV1): Promise<TSha256Digest> =>
248
+ computeDigest(runtimeNetworkAuthorityV1Contract.handoffDigestDomain, snapshotRuntimeNetworkHandoffLeaseV1(value));
249
+
250
+ /** Detached, digest-verified allocation binding. Does not establish pool activation or live link identity. */
251
+ export const bindRuntimeNetworkHandoffLeaseV1 = async (leaseValue: unknown, authorityValue: unknown): Promise<{
252
+ lease: IRuntimeNetworkHandoffLeaseV1; authority: IRuntimeNetworkProtectedAuthorityV1;
253
+ }> => {
254
+ const lease = snapshotRuntimeNetworkHandoffLeaseV1(leaseValue);
255
+ const authority = snapshotRuntimeNetworkProtectedAuthorityV1(authorityValue);
256
+ requireValue(lease.digest === await computeRuntimeNetworkHandoffLeaseV1Digest(lease));
257
+ requireValue(authority.digest === await computeRuntimeNetworkProtectedAuthorityV1Digest(authority));
258
+ requireValue(equals(lease.controller, authority.controller)
259
+ && equals(lease.protectedAuthority, { id: authority.id, generation: authority.generation, digest: authority.digest })
260
+ && authority.egressAuthorities.some(value => value.nodeId === lease.nodeId && value.runtimeNamespace === lease.runtimeNamespace));
261
+ const pool = authority.pools.find(value => value.id === lease.transit.poolId);
262
+ requireValue(pool?.purpose === 'transit' && contains(pool.prefix, lease.transit.prefix));
263
+ return { lease, authority };
264
+ };
265
+
266
+ /**
267
+ * Validate ALL retained leases for one physical node/controller epoch, including quarantine.
268
+ * The owning transactional ledger must supply its complete history and serialize reservations.
269
+ * Namespace replacement, an empty CT dump or process exit never removes an entry from this set.
270
+ */
271
+ export const snapshotRuntimeNetworkHandoffLedgerV1 = async (value: unknown): Promise<IRuntimeNetworkHandoffLeaseV1[]> => {
272
+ const leases = safe(() => list(value, runtimeNetworkAuthorityV1Contract.maximumRetainedHandoffLeases,
273
+ snapshotRuntimeNetworkHandoffLeaseV1));
274
+ for (let index = 0; index < leases.length; index++) {
275
+ const lease = leases[index];
276
+ requireValue(lease.digest === await computeRuntimeNetworkHandoffLeaseV1Digest(lease));
277
+ for (let other = 0; other < index; other++) {
278
+ const previous = leases[other];
279
+ requireValue(lease.nodeId === previous.nodeId && equals(lease.controller, previous.controller));
280
+ requireValue(lease.id !== previous.id || lease.generation !== previous.generation);
281
+ if (lease.runtimeNamespace === previous.runtimeNamespace && lease.routerIncarnationId === previous.routerIncarnationId) {
282
+ requireValue(lease.conntrackZone !== previous.conntrackZone && lease.conntrackLabel !== previous.conntrackLabel);
283
+ }
284
+ // Host handoff tuples remain reserved even across router or runtime namespace changes.
285
+ if (lease.transit.routerAddress === previous.transit.routerAddress) {
286
+ for (const left of lease.sourcePortRanges) for (const right of previous.sourcePortRanges) {
287
+ requireValue(left.protocol !== right.protocol || left.last < right.first || right.last < left.first);
288
+ }
289
+ }
290
+ }
291
+ }
292
+ return leases;
293
+ };
@@ -5,6 +5,26 @@ import type {
5
5
  TPrivateNetworkMutationV1,
6
6
  } from '../data/privatenetwork.js';
7
7
 
8
+ /** Current organization picker data. Entries are never cached authorization grants. */
9
+ export interface IReq_Cloudly_ListOwnedOrganizationsV1 extends plugins.typedrequestInterfaces.implementsTR<
10
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_ListOwnedOrganizationsV1
11
+ > {
12
+ method: 'listOwnedOrganizationsV1';
13
+ request: { identity: IIdentityCredential };
14
+ /** Limited to the server-held OIDC token's grant and current canonical owner membership. */
15
+ response: { organizations: Array<{ id: string; name: string; slug: string }> };
16
+ }
17
+
18
+ /** Organization-scoped attachment choices; excludes service configuration and secrets. */
19
+ export interface IReq_Cloudly_ListPrivateNetworkServiceCandidatesV1 extends plugins.typedrequestInterfaces.implementsTR<
20
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_ListPrivateNetworkServiceCandidatesV1
21
+ > {
22
+ method: 'listPrivateNetworkServiceCandidatesV1';
23
+ /** Each page requires a fresh canonical organization-owner authority observation. */
24
+ request: { identity: IIdentityCredential; organizationId: string; cursor: string | null; limit: number };
25
+ response: { services: Array<{ id: string; name: string }>; nextCursor: string | null };
26
+ }
27
+
8
28
  /** Every request is authorized against live canonical membership and Cloudly policy. */
9
29
  export interface IReq_Cloudly_ListPrivateNetworksV1 extends plugins.typedrequestInterfaces.implementsTR<
10
30
  plugins.typedrequestInterfaces.ITypedRequest, IReq_Cloudly_ListPrivateNetworksV1