@serve.zone/interfaces 30.7.0 → 30.9.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 +18 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/index.d.ts +4 -0
- package/dist_ts/data/index.js +5 -1
- package/dist_ts/data/networkdelivery.d.ts +25 -0
- package/dist_ts/data/networkdelivery.js +38 -0
- package/dist_ts/data/networkprojection.d.ts +140 -0
- package/dist_ts/data/networkprojection.js +371 -0
- package/dist_ts/data/networkprotection.d.ts +68 -0
- package/dist_ts/data/networkprotection.js +129 -0
- package/dist_ts/data/networksigning.d.ts +46 -0
- package/dist_ts/data/networksigning.js +52 -0
- package/dist_ts/private/networkcontract.d.ts +31 -0
- package/dist_ts/private/networkcontract.js +134 -0
- package/dist_ts/requests/runtimesession.d.ts +20 -0
- package/dist_ts/runtime.d.ts +1 -0
- package/dist_ts/runtime.js +2 -1
- package/dist_ts/runtime.networkprojection.d.ts +15 -0
- package/dist_ts/runtime.networkprojection.js +58 -0
- package/package.json +1 -1
- package/readme.md +93 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/index.ts +4 -0
- package/ts/data/networkdelivery.ts +58 -0
- package/ts/data/networkprojection.ts +448 -0
- package/ts/data/networkprotection.ts +175 -0
- package/ts/data/networksigning.ts +84 -0
- package/ts/private/networkcontract.ts +117 -0
- package/ts/requests/runtimesession.ts +29 -0
- package/ts/runtime.networkprojection.ts +64 -0
- package/ts/runtime.ts +1 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { TSha256Digest } from './immutableimage.js';
|
|
2
|
+
import type { IRuntimeAuthorityReferenceV1, IRuntimeControllerV1 } from './runtimeassignment.js';
|
|
3
|
+
import type { IRuntimeNetworkProjectionV1 } from './networkprojection.js';
|
|
4
|
+
import { snapshotRuntimeNetworkProjectionV1 } from './networkprojection.js';
|
|
5
|
+
import { snapshotRuntimeSessionBindingV1 } from './runtimesession.js';
|
|
6
|
+
import { captureNetwork, failNetwork, networkController, networkDigest, networkEqual, networkHash,
|
|
7
|
+
networkRecord, networkReference, networkReferenceOf, networkRevision, networkSafe, requireNetwork } from '../private/networkcontract.js';
|
|
8
|
+
|
|
9
|
+
export interface ISignedRuntimeNetworkProjectionV1 {
|
|
10
|
+
schemaVersion: 1;
|
|
11
|
+
authority: IRuntimeAuthorityReferenceV1;
|
|
12
|
+
projection: IRuntimeNetworkProjectionV1;
|
|
13
|
+
/** Ed25519 signature of the complete domain-separated envelope, lowercase hex. */
|
|
14
|
+
signature: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const snapshotSignedRuntimeNetworkProjectionV1 = (value: unknown): ISignedRuntimeNetworkProjectionV1 => networkSafe(() => {
|
|
18
|
+
const item = networkRecord(captureNetwork(value), ['schemaVersion', 'authority', 'projection', 'signature']);
|
|
19
|
+
requireNetwork(item.schemaVersion === 1 && typeof item.signature === 'string' && /^[a-f0-9]{128}$/.test(item.signature));
|
|
20
|
+
return { schemaVersion: 1, authority: networkReference(item.authority), projection: snapshotRuntimeNetworkProjectionV1(item.projection), signature: item.signature };
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Dedicated controller signing authority, installed through the CURRENT authenticated
|
|
25
|
+
* physical session. Never obtain trusted authority from an envelope being verified.
|
|
26
|
+
* null revokes signing; rotation requires a new complete signed projection for DNS.
|
|
27
|
+
* Existing packet grants still require applied denial or independent fencing.
|
|
28
|
+
*/
|
|
29
|
+
export interface IRuntimeNetworkSigningAuthorityV1 {
|
|
30
|
+
schemaVersion: 1;
|
|
31
|
+
id: string;
|
|
32
|
+
generation: number;
|
|
33
|
+
previous: IRuntimeAuthorityReferenceV1 | null;
|
|
34
|
+
controller: IRuntimeControllerV1;
|
|
35
|
+
algorithm: 'Ed25519';
|
|
36
|
+
/** Canonical 32-byte raw public key, lowercase hex. No private material. */
|
|
37
|
+
publicKey: string | null;
|
|
38
|
+
digest: TSha256Digest;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const runtimeNetworkSigningV1Contract = Object.freeze({
|
|
42
|
+
authorityDigestDomain: 'serve.zone/runtime-network-signing-authority-v1',
|
|
43
|
+
signatureDomain: 'serve.zone/runtime-network-projection-signature-v1',
|
|
44
|
+
trust: 'current-authenticated-physical-session-and-durable-revision-fence',
|
|
45
|
+
rotation: 'replace-dns-authority-with-new-signed-projection',
|
|
46
|
+
revocation: 'no-implicit-packet-denial-or-allocation-reuse',
|
|
47
|
+
} as const);
|
|
48
|
+
|
|
49
|
+
export const snapshotRuntimeNetworkSigningAuthorityV1 = (value: unknown): IRuntimeNetworkSigningAuthorityV1 => networkSafe(() => {
|
|
50
|
+
const item = networkRecord(captureNetwork(value), ['schemaVersion', 'id', 'generation', 'previous', 'controller', 'algorithm', 'publicKey', 'digest']);
|
|
51
|
+
requireNetwork(item.schemaVersion === 1 && item.algorithm === 'Ed25519');
|
|
52
|
+
requireNetwork(item.publicKey === null || (typeof item.publicKey === 'string'
|
|
53
|
+
&& /^[a-f0-9]{64}$/.test(item.publicKey) && item.publicKey !== '0'.repeat(64)));
|
|
54
|
+
return { schemaVersion: 1, ...networkRevision(item), controller: networkController(item.controller),
|
|
55
|
+
algorithm: 'Ed25519', publicKey: item.publicKey, digest: networkDigest(item.digest) };
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const computeRuntimeNetworkSigningAuthorityV1Digest = (value: IRuntimeNetworkSigningAuthorityV1): Promise<TSha256Digest> =>
|
|
59
|
+
networkHash(runtimeNetworkSigningV1Contract.authorityDigestDomain, snapshotRuntimeNetworkSigningAuthorityV1(value));
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Detached content/CAS check. Caller must fence the live peer and commit atomically.
|
|
63
|
+
* A newly enrolled node bootstraps the controller's CURRENT authority at any generation;
|
|
64
|
+
* it must not fabricate local history or use this bootstrap path to erase existing trust.
|
|
65
|
+
*/
|
|
66
|
+
export const admitRuntimeNetworkSigningAuthorityV1 = async (
|
|
67
|
+
value: unknown, previousValue: unknown | null, trustedSessionValue: unknown,
|
|
68
|
+
): Promise<{ status: 'accepted' | 'replay'; authority: IRuntimeNetworkSigningAuthorityV1 }> => {
|
|
69
|
+
try {
|
|
70
|
+
const authority = snapshotRuntimeNetworkSigningAuthorityV1(value);
|
|
71
|
+
const previous = previousValue === null ? null : snapshotRuntimeNetworkSigningAuthorityV1(previousValue);
|
|
72
|
+
const session = snapshotRuntimeSessionBindingV1(trustedSessionValue);
|
|
73
|
+
requireNetwork(networkEqual(authority.controller, session.controller));
|
|
74
|
+
requireNetwork(authority.digest === await computeRuntimeNetworkSigningAuthorityV1Digest(authority));
|
|
75
|
+
if (previous === null) return { status: 'accepted', authority };
|
|
76
|
+
requireNetwork(previous.digest === await computeRuntimeNetworkSigningAuthorityV1Digest(previous)
|
|
77
|
+
&& authority.id === previous.id && networkEqual(previous.controller, authority.controller));
|
|
78
|
+
if (authority.generation === previous.generation) {
|
|
79
|
+
requireNetwork(networkEqual(authority, previous)); return { status: 'replay', authority };
|
|
80
|
+
}
|
|
81
|
+
requireNetwork(networkEqual(authority.previous, networkReferenceOf(previous)));
|
|
82
|
+
return { status: 'accepted', authority };
|
|
83
|
+
} catch { return failNetwork(); }
|
|
84
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { TSha256Digest } from '../data/immutableimage.js';
|
|
2
|
+
import type { IRuntimeAuthorityReferenceV1, IRuntimeControllerV1 } from '../data/runtimeassignment.js';
|
|
3
|
+
import { canonicalizeStrictJson, createCanonicalJsonSha256Hex } from './canonicaljson.js';
|
|
4
|
+
|
|
5
|
+
export const failNetwork = (): never => { throw new Error('Runtime network projection contract rejected.'); };
|
|
6
|
+
export function requireNetwork(value: unknown): asserts value { if (!value) failNetwork(); }
|
|
7
|
+
export const networkCanonical = (value: unknown): string => canonicalizeStrictJson(value, failNetwork);
|
|
8
|
+
export const networkEqual = (left: unknown, right: unknown): boolean => networkCanonical(left) === networkCanonical(right);
|
|
9
|
+
export const networkSafe = <T>(operation: () => T): T => { try { return operation(); } catch { return failNetwork(); } };
|
|
10
|
+
|
|
11
|
+
/** Bound decoded transport input before canonicalization; never execute getters/toJSON. */
|
|
12
|
+
export const captureNetwork = (value: unknown): unknown => networkSafe(() => {
|
|
13
|
+
const encoder = new TextEncoder(), ancestors = new Set<object>();
|
|
14
|
+
let nodes = 0, bytes = 0;
|
|
15
|
+
const account = (text: string) => {
|
|
16
|
+
requireNetwork(text.length <= 1_048_576);
|
|
17
|
+
bytes += encoder.encode(text).length;
|
|
18
|
+
requireNetwork(bytes <= 1_048_576);
|
|
19
|
+
};
|
|
20
|
+
const copy = (item: unknown, depth: number): unknown => {
|
|
21
|
+
requireNetwork(++nodes <= 65_536 && depth <= 24);
|
|
22
|
+
if (typeof item === 'string') { account(item); return item; }
|
|
23
|
+
if (item === null || typeof item === 'boolean') return item;
|
|
24
|
+
if (typeof item === 'number') {
|
|
25
|
+
requireNetwork(Number.isSafeInteger(item) && !Object.is(item, -0)); return item;
|
|
26
|
+
}
|
|
27
|
+
requireNetwork(item !== null && typeof item === 'object' && !ancestors.has(item));
|
|
28
|
+
const array = Array.isArray(item), prototype = Object.getPrototypeOf(item), keys = Reflect.ownKeys(item);
|
|
29
|
+
requireNetwork(array ? prototype === Array.prototype : prototype === Object.prototype || prototype === null);
|
|
30
|
+
requireNetwork(keys.length <= 4097);
|
|
31
|
+
ancestors.add(item);
|
|
32
|
+
try {
|
|
33
|
+
if (array) {
|
|
34
|
+
const length = Object.getOwnPropertyDescriptor(item, 'length')?.value;
|
|
35
|
+
requireNetwork(Number.isSafeInteger(length) && length >= 0 && length <= 4096 && keys.length === length + 1);
|
|
36
|
+
const result: unknown[] = [];
|
|
37
|
+
for (let index = 0; index < length; index++) {
|
|
38
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, String(index));
|
|
39
|
+
requireNetwork(descriptor?.enumerable && Object.hasOwn(descriptor, 'value'));
|
|
40
|
+
result.push(copy(descriptor.value, depth + 1));
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
const result: Record<string, unknown> = Object.create(null);
|
|
45
|
+
for (const key of keys) {
|
|
46
|
+
requireNetwork(typeof key === 'string'); account(key);
|
|
47
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, key);
|
|
48
|
+
requireNetwork(descriptor?.enumerable && Object.hasOwn(descriptor, 'value'));
|
|
49
|
+
result[key] = copy(descriptor.value, depth + 1);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
} finally { ancestors.delete(item); }
|
|
53
|
+
};
|
|
54
|
+
const result = copy(value, 0);
|
|
55
|
+
requireNetwork(encoder.encode(networkCanonical(result)).length <= 1_048_576);
|
|
56
|
+
return result;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const networkRecord = (value: unknown, keys: string[]): Record<string, unknown> => {
|
|
60
|
+
requireNetwork(value !== null && typeof value === 'object' && !Array.isArray(value));
|
|
61
|
+
const actual = Object.keys(value);
|
|
62
|
+
requireNetwork(actual.length === keys.length && keys.every(key => Object.hasOwn(value, key)));
|
|
63
|
+
return value as Record<string, unknown>;
|
|
64
|
+
};
|
|
65
|
+
export const networkList = <T>(value: unknown, maximum: number, read: (item: unknown) => T): T[] => {
|
|
66
|
+
requireNetwork(Array.isArray(value) && value.length <= maximum); return value.map(read);
|
|
67
|
+
};
|
|
68
|
+
export const networkSorted = <T>(values: T[], key: (item: T) => string): T[] => {
|
|
69
|
+
requireNetwork(values.every((value, index) => index === 0 || key(values[index - 1]) < key(value))); return values;
|
|
70
|
+
};
|
|
71
|
+
export const networkId = (value: unknown): string => {
|
|
72
|
+
requireNetwork(typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(value)); return value;
|
|
73
|
+
};
|
|
74
|
+
export const networkCounter = (value: unknown, maximum = Number.MAX_SAFE_INTEGER - 1): number => {
|
|
75
|
+
requireNetwork(typeof value === 'number' && Number.isSafeInteger(value) && value > 0 && value <= maximum); return value;
|
|
76
|
+
};
|
|
77
|
+
export const networkDigest = (value: unknown): TSha256Digest => {
|
|
78
|
+
requireNetwork(typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value)); return value as TSha256Digest;
|
|
79
|
+
};
|
|
80
|
+
export const networkController = (value: unknown): IRuntimeControllerV1 => {
|
|
81
|
+
const item = networkRecord(value, ['kind', 'id', 'epoch']);
|
|
82
|
+
requireNetwork(item.kind === 'cloudly' || item.kind === 'onebox');
|
|
83
|
+
return { kind: item.kind, id: networkId(item.id), epoch: networkId(item.epoch) };
|
|
84
|
+
};
|
|
85
|
+
export const networkReference = (value: unknown): IRuntimeAuthorityReferenceV1 => {
|
|
86
|
+
const item = networkRecord(value, ['id', 'generation', 'digest']);
|
|
87
|
+
return { id: networkId(item.id), generation: networkCounter(item.generation), digest: networkDigest(item.digest) };
|
|
88
|
+
};
|
|
89
|
+
export const networkReferenceOf = (value: IRuntimeAuthorityReferenceV1): IRuntimeAuthorityReferenceV1 =>
|
|
90
|
+
({ id: value.id, generation: value.generation, digest: value.digest });
|
|
91
|
+
export const networkRevision = (item: Record<string, unknown>) => {
|
|
92
|
+
const id = networkId(item.id), generation = networkCounter(item.generation);
|
|
93
|
+
const previous = item.previous === null ? null : networkReference(item.previous);
|
|
94
|
+
requireNetwork(generation === 1 ? previous === null : previous?.id === id && previous.generation === generation - 1);
|
|
95
|
+
return { id, generation, previous };
|
|
96
|
+
};
|
|
97
|
+
export const networkHash = async (domain: string, value: { digest: TSha256Digest }): Promise<TSha256Digest> => {
|
|
98
|
+
const { digest: omitted, ...payload } = value;
|
|
99
|
+
return `sha256:${await createCanonicalJsonSha256Hex(networkCanonical({ domain, payload }), failNetwork)}`;
|
|
100
|
+
};
|
|
101
|
+
export const networkIpv4 = (value: unknown): string => {
|
|
102
|
+
requireNetwork(typeof value === 'string' && value.length <= 15);
|
|
103
|
+
const parts = value.split('.');
|
|
104
|
+
requireNetwork(parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/.test(part) && Number(part) <= 255));
|
|
105
|
+
return value;
|
|
106
|
+
};
|
|
107
|
+
export const networkIpv4Number = (value: string): number => value.split('.').reduce((sum, part) => sum * 256 + Number(part), 0);
|
|
108
|
+
export const networkPrefixBounds = (value: string): [number, number] => {
|
|
109
|
+
const [address, bits] = value.split('/'), first = networkIpv4Number(address);
|
|
110
|
+
return [first, first + 2 ** (32 - Number(bits)) - 1];
|
|
111
|
+
};
|
|
112
|
+
export const networkContains = (outer: string, inner: string): boolean => {
|
|
113
|
+
const [a, b] = networkPrefixBounds(outer), [c, d] = networkPrefixBounds(inner); return a <= c && b >= d;
|
|
114
|
+
};
|
|
115
|
+
export const networkOverlaps = (left: string, right: string): boolean => {
|
|
116
|
+
const [a, b] = networkPrefixBounds(left), [c, d] = networkPrefixBounds(right); return a <= d && c <= b;
|
|
117
|
+
};
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import type * as data from '../data/runtimesession.js';
|
|
3
3
|
import type * as terminal from '../data/runtimeterminalreceipt.js';
|
|
4
|
+
import type * as network from '../data/networkdelivery.js';
|
|
5
|
+
import type * as protection from '../data/networkprotection.js';
|
|
6
|
+
|
|
7
|
+
/** Current authenticated peer reports its durable complete native protection journal. */
|
|
8
|
+
export interface IReq_Pallet_Controller_ReportRuntimeNetworkProtectionV1 extends plugins.typedrequestInterfaces.implementsTR<
|
|
9
|
+
plugins.typedrequestInterfaces.ITypedRequest, IReq_Pallet_Controller_ReportRuntimeNetworkProtectionV1
|
|
10
|
+
> {
|
|
11
|
+
method: 'reportRuntimeNetworkProtectionV1';
|
|
12
|
+
request: protection.IReportRuntimeNetworkProtectionRequestV1;
|
|
13
|
+
response: protection.IReportRuntimeNetworkProtectionResponseV1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Dedicated signing trust installed only through the current authenticated physical peer. */
|
|
17
|
+
export interface IReq_Controller_Pallet_ApplyRuntimeNetworkSigningAuthorityV1 extends plugins.typedrequestInterfaces.implementsTR<
|
|
18
|
+
plugins.typedrequestInterfaces.ITypedRequest, IReq_Controller_Pallet_ApplyRuntimeNetworkSigningAuthorityV1
|
|
19
|
+
> {
|
|
20
|
+
method: 'applyRuntimeNetworkSigningAuthorityV1';
|
|
21
|
+
request: network.IApplyRuntimeNetworkSigningAuthorityRequestV1;
|
|
22
|
+
response: network.IApplyRuntimeNetworkAuthorityResponseV1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Complete signed node state; an ACK asserts admission only. */
|
|
26
|
+
export interface IReq_Controller_Pallet_ApplyRuntimeNetworkProjectionV1 extends plugins.typedrequestInterfaces.implementsTR<
|
|
27
|
+
plugins.typedrequestInterfaces.ITypedRequest, IReq_Controller_Pallet_ApplyRuntimeNetworkProjectionV1
|
|
28
|
+
> {
|
|
29
|
+
method: 'applyRuntimeNetworkProjectionV1';
|
|
30
|
+
request: network.IApplyRuntimeNetworkProjectionRequestV1;
|
|
31
|
+
response: network.IApplyRuntimeNetworkAuthorityResponseV1;
|
|
32
|
+
}
|
|
4
33
|
|
|
5
34
|
/** WSS-only Pallet bootstrap. Derive physical peer/process/idempotency server-side. */
|
|
6
35
|
export interface IReq_Pallet_Cloudly_RegisterPalletRuntimeSessionV1 extends plugins.typedrequestInterfaces.implementsTR<
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as plugins from './plugins.runtime.js';
|
|
2
|
+
import type { IRuntimeAuthorityReferenceV1 } from './data/runtimeassignment.js';
|
|
3
|
+
import type { IRuntimeNetworkProjectionV1 } from './data/networkprojection.js';
|
|
4
|
+
import { snapshotRuntimeNetworkNodeScopeV1, snapshotRuntimeNetworkProjectionV1, validateRuntimeNetworkProjectionV1 } from './data/networkprojection.js';
|
|
5
|
+
import type { IRuntimeNetworkSigningAuthorityV1, ISignedRuntimeNetworkProjectionV1 } from './data/networksigning.js';
|
|
6
|
+
import { computeRuntimeNetworkSigningAuthorityV1Digest, runtimeNetworkSigningV1Contract,
|
|
7
|
+
snapshotRuntimeNetworkSigningAuthorityV1, snapshotSignedRuntimeNetworkProjectionV1 } from './data/networksigning.js';
|
|
8
|
+
import { failNetwork, networkCanonical, networkEqual, networkReferenceOf, requireNetwork } from './private/networkcontract.js';
|
|
9
|
+
|
|
10
|
+
/** The private KeyObject is process-local. Persist only through the controller's encrypted secret store. */
|
|
11
|
+
export const createRuntimeNetworkSigningKeyV1 = (): { publicKey: string; privateKey: plugins.crypto.KeyObject } => {
|
|
12
|
+
const pair = plugins.crypto.generateKeyPairSync('ed25519');
|
|
13
|
+
const jwk = pair.publicKey.export({ format: 'jwk' });
|
|
14
|
+
requireNetwork(jwk.kty === 'OKP' && jwk.crv === 'Ed25519' && typeof jwk.x === 'string');
|
|
15
|
+
return { publicKey: Buffer.from(jwk.x, 'base64url').toString('hex'), privateKey: pair.privateKey };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const signatureBytes = (authority: IRuntimeAuthorityReferenceV1, projection: IRuntimeNetworkProjectionV1): Uint8Array =>
|
|
19
|
+
new TextEncoder().encode(networkCanonical({ domain: runtimeNetworkSigningV1Contract.signatureDomain, schemaVersion: 1, authority, projection }));
|
|
20
|
+
const validateAuthority = async (authority: IRuntimeNetworkSigningAuthorityV1, projection: IRuntimeNetworkProjectionV1): Promise<void> => {
|
|
21
|
+
requireNetwork(authority.publicKey !== null && networkEqual(authority.controller, projection.controller)
|
|
22
|
+
&& authority.digest === await computeRuntimeNetworkSigningAuthorityV1Digest(authority));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Sign only validated complete state and a matching dedicated private key. */
|
|
26
|
+
export const signRuntimeNetworkProjectionV1 = async (
|
|
27
|
+
projectionValue: unknown, authorityValue: unknown, privateKey: plugins.crypto.KeyObject,
|
|
28
|
+
): Promise<ISignedRuntimeNetworkProjectionV1> => {
|
|
29
|
+
try {
|
|
30
|
+
const input = snapshotRuntimeNetworkProjectionV1(projectionValue), authority = snapshotRuntimeNetworkSigningAuthorityV1(authorityValue);
|
|
31
|
+
requireNetwork(privateKey instanceof plugins.crypto.KeyObject && privateKey.type === 'private' && privateKey.asymmetricKeyType === 'ed25519');
|
|
32
|
+
const publicKey = plugins.crypto.createPublicKey(privateKey).export({ format: 'jwk' });
|
|
33
|
+
requireNetwork(typeof publicKey.x === 'string' && Buffer.from(publicKey.x, 'base64url').toString('hex') === authority.publicKey);
|
|
34
|
+
const projection = await validateRuntimeNetworkProjectionV1(input);
|
|
35
|
+
await validateAuthority(authority, projection);
|
|
36
|
+
const reference = networkReferenceOf(authority);
|
|
37
|
+
const signature = plugins.crypto.sign(null, signatureBytes(reference, projection), privateKey).toString('hex');
|
|
38
|
+
return { schemaVersion: 1, authority: reference, projection, signature };
|
|
39
|
+
} catch { return failNetwork(); }
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* trustedAuthorityValue is receiver-owned persisted trust, NEVER envelope material.
|
|
44
|
+
* This verifies content/scope/signature only. The owner still performs atomic revision
|
|
45
|
+
* admission, live-session fencing, allocation barriers and conservative DNS time checks.
|
|
46
|
+
*/
|
|
47
|
+
export const verifyRuntimeNetworkProjectionV1 = async (
|
|
48
|
+
value: unknown, trustedAuthorityValue: unknown, trustedScopeValue: unknown,
|
|
49
|
+
): Promise<ISignedRuntimeNetworkProjectionV1> => {
|
|
50
|
+
try {
|
|
51
|
+
const signed = snapshotSignedRuntimeNetworkProjectionV1(value);
|
|
52
|
+
const authority = snapshotRuntimeNetworkSigningAuthorityV1(trustedAuthorityValue);
|
|
53
|
+
const trusted = snapshotRuntimeNetworkNodeScopeV1(trustedScopeValue);
|
|
54
|
+
requireNetwork(networkEqual(signed.authority, networkReferenceOf(authority))
|
|
55
|
+
&& signed.projection.nodeId === trusted.nodeId && signed.projection.runtimeNamespace === trusted.runtimeNamespace
|
|
56
|
+
&& networkEqual(signed.projection.controller, trusted.controller));
|
|
57
|
+
await validateAuthority(authority, signed.projection);
|
|
58
|
+
const key = plugins.crypto.createPublicKey({ format: 'jwk', key: { kty: 'OKP', crv: 'Ed25519',
|
|
59
|
+
x: Buffer.from(authority.publicKey!, 'hex').toString('base64url') } });
|
|
60
|
+
requireNetwork(plugins.crypto.verify(null, signatureBytes(signed.authority, signed.projection), key, Buffer.from(signed.signature, 'hex')));
|
|
61
|
+
await validateRuntimeNetworkProjectionV1(signed.projection);
|
|
62
|
+
return signed;
|
|
63
|
+
} catch { return failNetwork(); }
|
|
64
|
+
};
|
package/ts/runtime.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
} from './runtime.workloadinit.js';
|
|
40
40
|
|
|
41
41
|
export * from './runtime.workloadinit.js';
|
|
42
|
+
export * from './runtime.networkprojection.js';
|
|
42
43
|
export * from './runtime.corestore.js';
|
|
43
44
|
export * from './runtime.corestore.databaseexportpackage.js';
|
|
44
45
|
export * from './runtime.cloudlylegacydeploymentsettlement.js';
|