@serfab/cadre-core 0.10.0 → 0.11.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/README.md +255 -255
- package/dist/cadre-node.d.ts +203 -28
- package/dist/cadre-node.js +445 -84
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-cohort.d.ts +26 -0
- package/dist/control-cohort.js +26 -0
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +82 -117
- package/dist/control-database.js +217 -295
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +8 -7
- package/dist/control-formation-recorder.js +8 -7
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +789 -774
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +15 -5
- package/dist/control-write-retry.js +19 -18
- package/dist/control-write-retry.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/peer-addr-book.d.ts +92 -0
- package/dist/peer-addr-book.js +220 -0
- package/dist/peer-addr-book.js.map +1 -0
- package/dist/seed-bootstrap.d.ts +7 -0
- package/dist/seed-bootstrap.js +15 -13
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/strand-cohort.d.ts +4 -37
- package/dist/strand-cohort.js +4 -15
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +11 -16
- package/dist/strand-database.js +19 -7
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-formation-manager.d.ts +8 -8
- package/dist/strand-formation-manager.js +15 -18
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +52 -21
- package/dist/strand-instance-manager.js +100 -34
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +6 -4
- package/dist/strand-membership-writer.js +38 -36
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/types.d.ts +39 -31
- package/dist/types.js.map +1 -1
- package/package.json +8 -7
- package/src/arachnode-stub.ts +159 -159
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +172 -172
- package/src/cadre-node.ts +5158 -4771
- package/src/canonical-json.ts +30 -30
- package/src/control-authorization.ts +114 -114
- package/src/control-cohort.ts +130 -103
- package/src/control-database.ts +2406 -2506
- package/src/control-formation-recorder.ts +305 -304
- package/src/control-schema.ts +801 -786
- package/src/control-stream.ts +205 -205
- package/src/control-write-retry.ts +483 -473
- package/src/delegate-admission.ts +302 -302
- package/src/device-token.ts +94 -94
- package/src/diagnostics/connection-path.ts +277 -277
- package/src/diagnostics/webrtc-turn-tracker.ts +222 -222
- package/src/ed25519-key.ts +134 -134
- package/src/enrollment.ts +199 -199
- package/src/file-durable-slot.ts +61 -61
- package/src/formation-approval.ts +604 -604
- package/src/fs-atomic.ts +107 -107
- package/src/hibernation-manager.ts +437 -437
- package/src/identity-key.ts +109 -109
- package/src/index.ts +1 -0
- package/src/key-store-file.ts +132 -132
- package/src/key-store.ts +99 -99
- package/src/membership-connection-gater.ts +204 -204
- package/src/node-local-snapshot.ts +260 -260
- package/src/peer-addr-book.ts +250 -0
- package/src/peer-authorization.ts +259 -259
- package/src/peer-record.ts +164 -164
- package/src/push-credentials.ts +84 -84
- package/src/push-node.ts +64 -64
- package/src/push-notifier-apns.ts +247 -247
- package/src/push-notifier-fcm.ts +220 -220
- package/src/push-notifier-shared.ts +28 -28
- package/src/push-notifier.ts +52 -52
- package/src/relay-addrs.ts +111 -111
- package/src/relay-reservation.ts +823 -823
- package/src/seed-bootstrap.ts +15 -15
- package/src/seed-trust-policy.ts +140 -140
- package/src/strand-addr-protocol.ts +441 -441
- package/src/strand-backfill.ts +366 -366
- package/src/strand-cohort.ts +38 -77
- package/src/strand-database.ts +202 -204
- package/src/strand-formation-manager.ts +491 -494
- package/src/strand-formation-protocol.ts +692 -692
- package/src/strand-instance-manager.ts +108 -50
- package/src/strand-member-key.ts +39 -39
- package/src/strand-member-registry.ts +182 -182
- package/src/strand-membership-writer.ts +1432 -1430
- package/src/strand-solicitation.ts +472 -472
- package/src/strand-transport-key.ts +58 -58
- package/src/strand-wake-payload.ts +32 -32
- package/src/strand-wake-protocol.ts +314 -314
- package/src/strand-watcher.ts +314 -314
- package/src/trusted-owner-store-file.ts +41 -41
- package/src/trusted-owner-store.ts +171 -171
- package/src/types.ts +1309 -1303
package/src/device-token.ts
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Device-token record helpers: the single source of truth for the bytes a node
|
|
3
|
-
* signs when publishing its own `DeviceToken` row and re-verifies when resolving
|
|
4
|
-
* another peer's row.
|
|
5
|
-
*
|
|
6
|
-
* Modeled directly on {@link peerRecordSignedPayload} (peer-record.ts): the signed
|
|
7
|
-
* payload is a single SHA-256 digest over the shared domain-tagged field vector
|
|
8
|
-
* (see control-authorization.ts) — the crypto plugin's injective multi-field
|
|
9
|
-
* encoding, so no field split is ambiguous even with an opaque platform token. It
|
|
10
|
-
* is intentionally NOT JSON-canonicalized (deterministic across node/browser/RN,
|
|
11
|
-
* no key ordering) and is reconstructable inside the `DeviceToken.AuthorizedUpdate`
|
|
12
|
-
* SQL constraint from the row's own columns:
|
|
13
|
-
*
|
|
14
|
-
* digest('CadreControl.DeviceToken', 'publish',
|
|
15
|
-
* new.PeerId, new.Platform, new.Token, cast(new.UpdatedAt as text))
|
|
16
|
-
*
|
|
17
|
-
* Keep {@link deviceTokenSignedPayload} and that constraint byte-for-byte in sync.
|
|
18
|
-
* The `'publish'` action tag marks this as a peer's SELF-signed record (signed with
|
|
19
|
-
* the peer's own key, not an owner key) and keeps it disjoint from every
|
|
20
|
-
* owner-signed digest.
|
|
21
|
-
*
|
|
22
|
-
* Unlike a peer-address record, a device-token record carries NO public key: the
|
|
23
|
-
* signature is verified against the `CadrePeer.PublicKey` bound to the same PeerId
|
|
24
|
-
* (the resolver supplies it), so the key binding is checked exactly once, where the
|
|
25
|
-
* membership row already lives.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { digest, sign, verify } from '@optimystic/quereus-plugin-crypto';
|
|
29
|
-
import { controlAuthorizationFields } from './control-authorization.js';
|
|
30
|
-
import type { DeviceTokenRecord, PushPlatform } from './types.js';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Build the base64url SHA-256 digest that the self-signature covers. Mirrors the
|
|
34
|
-
* `DeviceToken.AuthorizedUpdate` constraint exactly; both sides take the default
|
|
35
|
-
* base64url output of a single `digest(...)`, which round-trips cleanly.
|
|
36
|
-
*/
|
|
37
|
-
export function deviceTokenSignedPayload(record: Omit<DeviceTokenRecord, 'sig'>): string {
|
|
38
|
-
const fields = controlAuthorizationFields(
|
|
39
|
-
'CadreControl.DeviceToken',
|
|
40
|
-
'publish',
|
|
41
|
-
[record.peerId, record.platform, record.token, String(record.updatedAt)]
|
|
42
|
-
);
|
|
43
|
-
return digest(fields, 'sha256', 'base64url') as string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Sign a device-token record with the ed25519 private key behind its `peerId`.
|
|
48
|
-
* Returns a fully-populated {@link DeviceTokenRecord}.
|
|
49
|
-
*
|
|
50
|
-
* @param fields - the record fields to sign
|
|
51
|
-
* @param privateKeyB64 - base64url ed25519 seed (see `ed25519KeyPairFromLibp2p`)
|
|
52
|
-
*/
|
|
53
|
-
export function signDeviceTokenRecord(
|
|
54
|
-
fields: Omit<DeviceTokenRecord, 'sig'>,
|
|
55
|
-
privateKeyB64: string
|
|
56
|
-
): DeviceTokenRecord {
|
|
57
|
-
const payloadDigest = deviceTokenSignedPayload(fields);
|
|
58
|
-
const sig = sign(
|
|
59
|
-
payloadDigest,
|
|
60
|
-
privateKeyB64,
|
|
61
|
-
'ed25519',
|
|
62
|
-
'base64url',
|
|
63
|
-
'base64url',
|
|
64
|
-
'base64url'
|
|
65
|
-
) as string;
|
|
66
|
-
return { peerId: fields.peerId, platform: fields.platform, token: fields.token, updatedAt: fields.updatedAt, sig };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Verify a record's self-signature against the `CadrePeer.PublicKey` (base64url)
|
|
71
|
-
* bound to its `peerId`. Reconstructs the signed bytes from the record exactly as
|
|
72
|
-
* {@link signDeviceTokenRecord} produced them. Returns false on a missing key/sig or
|
|
73
|
-
* any verification failure.
|
|
74
|
-
*/
|
|
75
|
-
export function verifyDeviceTokenSignature(record: DeviceTokenRecord, publicKeyB64: string): boolean {
|
|
76
|
-
if (!publicKeyB64 || !record.sig) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
const payloadDigest = deviceTokenSignedPayload(record);
|
|
80
|
-
return verify(
|
|
81
|
-
payloadDigest,
|
|
82
|
-
record.sig,
|
|
83
|
-
publicKeyB64,
|
|
84
|
-
'ed25519',
|
|
85
|
-
'base64url',
|
|
86
|
-
'base64url',
|
|
87
|
-
'base64url'
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Narrow an arbitrary stored string to a known {@link PushPlatform}. */
|
|
92
|
-
export function isPushPlatform(value: string): value is PushPlatform {
|
|
93
|
-
return value === 'fcm' || value === 'apns';
|
|
94
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Device-token record helpers: the single source of truth for the bytes a node
|
|
3
|
+
* signs when publishing its own `DeviceToken` row and re-verifies when resolving
|
|
4
|
+
* another peer's row.
|
|
5
|
+
*
|
|
6
|
+
* Modeled directly on {@link peerRecordSignedPayload} (peer-record.ts): the signed
|
|
7
|
+
* payload is a single SHA-256 digest over the shared domain-tagged field vector
|
|
8
|
+
* (see control-authorization.ts) — the crypto plugin's injective multi-field
|
|
9
|
+
* encoding, so no field split is ambiguous even with an opaque platform token. It
|
|
10
|
+
* is intentionally NOT JSON-canonicalized (deterministic across node/browser/RN,
|
|
11
|
+
* no key ordering) and is reconstructable inside the `DeviceToken.AuthorizedUpdate`
|
|
12
|
+
* SQL constraint from the row's own columns:
|
|
13
|
+
*
|
|
14
|
+
* digest('CadreControl.DeviceToken', 'publish',
|
|
15
|
+
* new.PeerId, new.Platform, new.Token, cast(new.UpdatedAt as text))
|
|
16
|
+
*
|
|
17
|
+
* Keep {@link deviceTokenSignedPayload} and that constraint byte-for-byte in sync.
|
|
18
|
+
* The `'publish'` action tag marks this as a peer's SELF-signed record (signed with
|
|
19
|
+
* the peer's own key, not an owner key) and keeps it disjoint from every
|
|
20
|
+
* owner-signed digest.
|
|
21
|
+
*
|
|
22
|
+
* Unlike a peer-address record, a device-token record carries NO public key: the
|
|
23
|
+
* signature is verified against the `CadrePeer.PublicKey` bound to the same PeerId
|
|
24
|
+
* (the resolver supplies it), so the key binding is checked exactly once, where the
|
|
25
|
+
* membership row already lives.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { digest, sign, verify } from '@optimystic/quereus-plugin-crypto';
|
|
29
|
+
import { controlAuthorizationFields } from './control-authorization.js';
|
|
30
|
+
import type { DeviceTokenRecord, PushPlatform } from './types.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Build the base64url SHA-256 digest that the self-signature covers. Mirrors the
|
|
34
|
+
* `DeviceToken.AuthorizedUpdate` constraint exactly; both sides take the default
|
|
35
|
+
* base64url output of a single `digest(...)`, which round-trips cleanly.
|
|
36
|
+
*/
|
|
37
|
+
export function deviceTokenSignedPayload(record: Omit<DeviceTokenRecord, 'sig'>): string {
|
|
38
|
+
const fields = controlAuthorizationFields(
|
|
39
|
+
'CadreControl.DeviceToken',
|
|
40
|
+
'publish',
|
|
41
|
+
[record.peerId, record.platform, record.token, String(record.updatedAt)]
|
|
42
|
+
);
|
|
43
|
+
return digest(fields, 'sha256', 'base64url') as string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Sign a device-token record with the ed25519 private key behind its `peerId`.
|
|
48
|
+
* Returns a fully-populated {@link DeviceTokenRecord}.
|
|
49
|
+
*
|
|
50
|
+
* @param fields - the record fields to sign
|
|
51
|
+
* @param privateKeyB64 - base64url ed25519 seed (see `ed25519KeyPairFromLibp2p`)
|
|
52
|
+
*/
|
|
53
|
+
export function signDeviceTokenRecord(
|
|
54
|
+
fields: Omit<DeviceTokenRecord, 'sig'>,
|
|
55
|
+
privateKeyB64: string
|
|
56
|
+
): DeviceTokenRecord {
|
|
57
|
+
const payloadDigest = deviceTokenSignedPayload(fields);
|
|
58
|
+
const sig = sign(
|
|
59
|
+
payloadDigest,
|
|
60
|
+
privateKeyB64,
|
|
61
|
+
'ed25519',
|
|
62
|
+
'base64url',
|
|
63
|
+
'base64url',
|
|
64
|
+
'base64url'
|
|
65
|
+
) as string;
|
|
66
|
+
return { peerId: fields.peerId, platform: fields.platform, token: fields.token, updatedAt: fields.updatedAt, sig };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Verify a record's self-signature against the `CadrePeer.PublicKey` (base64url)
|
|
71
|
+
* bound to its `peerId`. Reconstructs the signed bytes from the record exactly as
|
|
72
|
+
* {@link signDeviceTokenRecord} produced them. Returns false on a missing key/sig or
|
|
73
|
+
* any verification failure.
|
|
74
|
+
*/
|
|
75
|
+
export function verifyDeviceTokenSignature(record: DeviceTokenRecord, publicKeyB64: string): boolean {
|
|
76
|
+
if (!publicKeyB64 || !record.sig) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const payloadDigest = deviceTokenSignedPayload(record);
|
|
80
|
+
return verify(
|
|
81
|
+
payloadDigest,
|
|
82
|
+
record.sig,
|
|
83
|
+
publicKeyB64,
|
|
84
|
+
'ed25519',
|
|
85
|
+
'base64url',
|
|
86
|
+
'base64url',
|
|
87
|
+
'base64url'
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Narrow an arbitrary stored string to a known {@link PushPlatform}. */
|
|
92
|
+
export function isPushPlatform(value: string): value is PushPlatform {
|
|
93
|
+
return value === 'fcm' || value === 'apns';
|
|
94
|
+
}
|