@serfab/cadre-core 1.14.0 → 1.15.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 +293 -274
- package/dist/cadre-invite-protocol.d.ts +64 -29
- package/dist/cadre-invite-protocol.js +207 -38
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-join-request.d.ts +42 -0
- package/dist/cadre-join-request.js +144 -0
- package/dist/cadre-join-request.js.map +1 -0
- package/dist/cadre-node.d.ts +375 -79
- package/dist/cadre-node.js +758 -186
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-database.d.ts +92 -12
- package/dist/control-database.js +323 -142
- package/dist/control-database.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +19 -3
- package/dist/control-schema.js.map +1 -1
- package/dist/ed25519-key.d.ts +7 -9
- package/dist/ed25519-key.js +20 -10
- package/dist/ed25519-key.js.map +1 -1
- package/dist/fetch-impl.d.ts +12 -0
- package/dist/fetch-impl.js +23 -0
- package/dist/fetch-impl.js.map +1 -0
- package/dist/file-durable-slot.js +2 -1
- package/dist/file-durable-slot.js.map +1 -1
- package/dist/file-safe-name.d.ts +25 -0
- package/dist/file-safe-name.js +29 -0
- package/dist/file-safe-name.js.map +1 -0
- package/dist/formation-approval.js +5 -11
- package/dist/formation-approval.js.map +1 -1
- package/dist/fs-atomic.d.ts +0 -19
- package/dist/fs-atomic.js +3 -24
- package/dist/fs-atomic.js.map +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +31 -6
- package/dist/index.js.map +1 -1
- package/dist/invitation-bootstrap.d.ts +50 -12
- package/dist/invitation-bootstrap.js +76 -13
- package/dist/invitation-bootstrap.js.map +1 -1
- package/dist/joined-strand-store.d.ts +7 -0
- package/dist/joined-strand-store.js +9 -0
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store-file.js +2 -1
- package/dist/key-store-file.js.map +1 -1
- package/dist/link-budget.d.ts +9 -4
- package/dist/link-budget.js +9 -4
- package/dist/link-budget.js.map +1 -1
- package/dist/machine-removal.d.ts +78 -0
- package/dist/machine-removal.js +166 -0
- package/dist/machine-removal.js.map +1 -0
- package/dist/membership-connection-gater.d.ts +6 -5
- package/dist/membership-connection-gater.js +6 -5
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-claim-payload.d.ts +55 -0
- package/dist/node-claim-payload.js +108 -4
- package/dist/node-claim-payload.js.map +1 -1
- package/dist/node-default-transports.d.ts +27 -0
- package/dist/node-default-transports.js +35 -0
- package/dist/node-default-transports.js.map +1 -0
- package/dist/node-transports.d.ts +30 -0
- package/dist/node-transports.js +40 -0
- package/dist/node-transports.js.map +1 -0
- package/dist/peer-dial.d.ts +124 -27
- package/dist/peer-dial.js +254 -30
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.js +6 -0
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/peer-record.d.ts +6 -45
- package/dist/peer-record.js +8 -50
- package/dist/peer-record.js.map +1 -1
- package/dist/primary-lan-address.d.ts +8 -0
- package/dist/primary-lan-address.js +46 -0
- package/dist/primary-lan-address.js.map +1 -0
- package/dist/provider/client.d.ts +131 -0
- package/dist/provider/client.js +226 -0
- package/dist/provider/client.js.map +1 -0
- package/dist/provider/descriptor.d.ts +41 -0
- package/dist/provider/descriptor.js +103 -0
- package/dist/provider/descriptor.js.map +1 -0
- package/dist/provider/index.d.ts +9 -0
- package/dist/provider/index.js +10 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/provider/oauth.d.ts +125 -0
- package/dist/provider/oauth.js +199 -0
- package/dist/provider/oauth.js.map +1 -0
- package/dist/provider/rent-node.d.ts +101 -0
- package/dist/provider/rent-node.js +220 -0
- package/dist/provider/rent-node.js.map +1 -0
- package/dist/relay-addrs.d.ts +9 -4
- package/dist/relay-addrs.js +9 -4
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +51 -16
- package/dist/relay-reservation.js +107 -24
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +22 -5
- package/dist/seed-bootstrap.js +48 -25
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/strand-commit-durability.d.ts +54 -0
- package/dist/strand-commit-durability.js +74 -0
- package/dist/strand-commit-durability.js.map +1 -0
- package/dist/strand-database.d.ts +23 -8
- package/dist/strand-database.js +41 -13
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +7 -1
- package/dist/strand-first-sync-gate.js +7 -1
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-protocol.js +4 -1
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-fret-addrs.d.ts +19 -1
- package/dist/strand-fret-addrs.js +27 -9
- package/dist/strand-fret-addrs.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +81 -19
- package/dist/strand-instance-manager.js +216 -105
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-move.d.ts +124 -0
- package/dist/strand-move.js +438 -0
- package/dist/strand-move.js.map +1 -0
- package/dist/strand-network-config.js +7 -6
- package/dist/strand-network-config.js.map +1 -1
- package/dist/strand-peer-redial.d.ts +113 -0
- package/dist/strand-peer-redial.js +236 -0
- package/dist/strand-peer-redial.js.map +1 -0
- package/dist/strand-wake-protocol.js +10 -7
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +212 -4
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/package.json +18 -10
- package/src/cadre-invite-protocol.ts +233 -45
- package/src/cadre-join-request.ts +167 -0
- package/src/cadre-node.ts +10003 -9381
- package/src/control-database.ts +4663 -4451
- package/src/control-schema.ts +19 -3
- package/src/ed25519-key.ts +20 -10
- package/src/fetch-impl.ts +23 -0
- package/src/file-durable-slot.ts +64 -63
- package/src/file-safe-name.ts +32 -0
- package/src/formation-approval.ts +604 -610
- package/src/fs-atomic.ts +92 -117
- package/src/index.ts +789 -722
- package/src/invitation-bootstrap.ts +91 -13
- package/src/joined-strand-store.ts +508 -498
- package/src/key-store-file.ts +142 -141
- package/src/link-budget.ts +9 -4
- package/src/machine-removal.ts +216 -0
- package/src/membership-connection-gater.ts +638 -637
- package/src/node-claim-payload.ts +138 -4
- package/src/node-default-transports.ts +38 -0
- package/src/node-transports.ts +49 -0
- package/src/peer-dial.ts +604 -336
- package/src/peer-join-backfill.ts +6 -0
- package/src/peer-record.ts +159 -202
- package/src/primary-lan-address.ts +47 -0
- package/src/provider/client.ts +326 -0
- package/src/provider/descriptor.ts +129 -0
- package/src/provider/index.ts +59 -0
- package/src/provider/oauth.ts +285 -0
- package/src/provider/rent-node.ts +275 -0
- package/src/relay-addrs.ts +9 -4
- package/src/relay-reservation.ts +1211 -1121
- package/src/seed-bootstrap.ts +1502 -1471
- package/src/strand-commit-durability.ts +116 -0
- package/src/strand-database.ts +46 -14
- package/src/strand-first-sync-gate.ts +7 -1
- package/src/strand-formation-protocol.ts +1039 -1036
- package/src/strand-fret-addrs.ts +179 -145
- package/src/strand-instance-manager.ts +2026 -1883
- package/src/strand-move.ts +585 -0
- package/src/strand-network-config.ts +7 -6
- package/src/strand-peer-redial.ts +307 -0
- package/src/strand-wake-protocol.ts +10 -7
- package/src/types.ts +220 -4
package/dist/peer-record.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* SQL constraint from the row's own columns and the node-local `party_id()`:
|
|
12
12
|
*
|
|
13
13
|
* digest('CadreControl.CadrePeer', 'publish', party_id(),
|
|
14
|
-
* new.PeerId, new.Multiaddr, cast(new.UpdatedAt as text)
|
|
14
|
+
* new.PeerId, new.Multiaddr, cast(new.UpdatedAt as text),
|
|
15
|
+
* coalesce(new.Profile, ''))
|
|
15
16
|
*
|
|
16
17
|
* Keep {@link peerRecordSignedPayload} and that constraint byte-for-byte in
|
|
17
18
|
* sync. The `'publish'` action tag marks this as a peer's SELF-signed record
|
|
@@ -50,9 +51,10 @@ const SIGNALING_PREFIX = '/p2p-circuit';
|
|
|
50
51
|
* @param peerId - base58btc libp2p peer id (the row key)
|
|
51
52
|
* @param multiaddr - the comma-joined `Multiaddr` string EXACTLY as stored
|
|
52
53
|
* @param updatedAt - epoch-ms freshness stamp
|
|
54
|
+
* @param profile - the publisher's node profile; null signs as `''`, as the constraint coalesces it
|
|
53
55
|
*/
|
|
54
|
-
export function peerRecordSignedPayload(partyId, peerId, multiaddr, updatedAt) {
|
|
55
|
-
const fields = controlAuthorizationFields('CadreControl.CadrePeer', 'publish', partyId, [peerId, multiaddr, String(updatedAt)]);
|
|
56
|
+
export function peerRecordSignedPayload(partyId, peerId, multiaddr, updatedAt, profile) {
|
|
57
|
+
const fields = controlAuthorizationFields('CadreControl.CadrePeer', 'publish', partyId, [peerId, multiaddr, String(updatedAt), profile ?? '']);
|
|
56
58
|
return digest(fields, 'sha256', 'base64url');
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
@@ -65,9 +67,9 @@ export function peerRecordSignedPayload(partyId, peerId, multiaddr, updatedAt) {
|
|
|
65
67
|
* @param privateKeyB64 - base64url ed25519 seed (see `ed25519KeyPairFromLibp2p`)
|
|
66
68
|
*/
|
|
67
69
|
export function signPeerRecord(partyId, fields, privateKeyB64) {
|
|
68
|
-
const payloadDigest = peerRecordSignedPayload(partyId, fields.peerId, fields.addrs.join(','), fields.updatedAt);
|
|
70
|
+
const payloadDigest = peerRecordSignedPayload(partyId, fields.peerId, fields.addrs.join(','), fields.updatedAt, fields.profile);
|
|
69
71
|
const sig = sign(payloadDigest, privateKeyB64, 'ed25519', 'base64url', 'base64url', 'base64url');
|
|
70
|
-
return { peerId: fields.peerId, publicKey: fields.publicKey, addrs: [...fields.addrs], updatedAt: fields.updatedAt, sig };
|
|
72
|
+
return { peerId: fields.peerId, publicKey: fields.publicKey, addrs: [...fields.addrs], updatedAt: fields.updatedAt, profile: fields.profile, sig };
|
|
71
73
|
}
|
|
72
74
|
/**
|
|
73
75
|
* Verify a record's self-signature against its own `publicKey`, for `partyId`.
|
|
@@ -78,7 +80,7 @@ export function verifyPeerRecordSignature(partyId, record) {
|
|
|
78
80
|
if (!record.publicKey || !record.sig) {
|
|
79
81
|
return false;
|
|
80
82
|
}
|
|
81
|
-
const payloadDigest = peerRecordSignedPayload(partyId, record.peerId, record.addrs.join(','), record.updatedAt);
|
|
83
|
+
const payloadDigest = peerRecordSignedPayload(partyId, record.peerId, record.addrs.join(','), record.updatedAt, record.profile);
|
|
82
84
|
return verify(payloadDigest, record.sig, record.publicKey, 'ed25519', 'base64url', 'base64url', 'base64url');
|
|
83
85
|
}
|
|
84
86
|
/**
|
|
@@ -96,50 +98,6 @@ export function isPeerRecordFresh(updatedAt, maxAgeMs, now) {
|
|
|
96
98
|
export function isSignalingAddr(addr) {
|
|
97
99
|
return addr.includes(SIGNALING_PREFIX);
|
|
98
100
|
}
|
|
99
|
-
/**
|
|
100
|
-
* The peer id a multiaddr terminates in, or `null` when it names none.
|
|
101
|
-
*
|
|
102
|
-
* The LAST `/p2p/` component is the dial target: a circuit address
|
|
103
|
-
* (`…/p2p/<relay>/p2p-circuit/p2p/<target>`) names the relay first and the peer
|
|
104
|
-
* being reached last. Replaces the deprecated `Multiaddr.getPeerId()`, which
|
|
105
|
-
* cannot express that distinction.
|
|
106
|
-
*/
|
|
107
|
-
export function trailingPeerId(addr) {
|
|
108
|
-
const p2p = addr.getComponents().filter((c) => c.name === 'p2p');
|
|
109
|
-
return p2p.length > 0 ? (p2p[p2p.length - 1].value ?? null) : null;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* `addr` guaranteed to terminate in `/p2p/<peerId>` — the shape every dial path
|
|
113
|
-
* needs — or `null` when it cannot, because the address already terminates in a
|
|
114
|
-
* DIFFERENT peer id and therefore does not reach `peerId` at all.
|
|
115
|
-
*
|
|
116
|
-
* Three input shapes, matching what a `CadrePeer` record can legitimately carry:
|
|
117
|
-
*
|
|
118
|
-
* - no `/p2p/` component (`/ip4/1.2.3.4/tcp/4001`) — the suffix is appended;
|
|
119
|
-
* - a relay hop with the destination missing (`…/p2p/<relay>/p2p-circuit`) — the
|
|
120
|
-
* last component is the circuit, not a peer id, so the suffix is appended and
|
|
121
|
-
* the address becomes the full `…/p2p/<relay>/p2p-circuit/p2p/<peerId>`;
|
|
122
|
-
* - already terminating in `/p2p/<X>` — returned untouched when `X` is `peerId`,
|
|
123
|
-
* dropped otherwise.
|
|
124
|
-
*
|
|
125
|
-
* This is deliberately the same rule libp2p applies internally in
|
|
126
|
-
* `calculateMultiaddrs` (append when the LAST component is not `p2p`) followed by
|
|
127
|
-
* its wrong-peer-id filter, so a normalized list matches what libp2p would have
|
|
128
|
-
* built anyway. Normalizing before the dial is what keeps a list from mixing
|
|
129
|
-
* suffixed and unsuffixed entries, which `getPeerAddress` rejects outright
|
|
130
|
-
* ("Multiaddrs must all have the same peer id or have no peer id") — taking the
|
|
131
|
-
* whole peer down with it, not just the odd address.
|
|
132
|
-
*
|
|
133
|
-
* Never use before signature verification: verification runs against the
|
|
134
|
-
* original on-record strings.
|
|
135
|
-
*/
|
|
136
|
-
export function withTrailingPeerId(addr, peerId) {
|
|
137
|
-
const components = addr.getComponents();
|
|
138
|
-
if (components[components.length - 1]?.name !== 'p2p') {
|
|
139
|
-
return addr.encapsulate(`/p2p/${peerId}`);
|
|
140
|
-
}
|
|
141
|
-
return trailingPeerId(addr) === peerId ? addr : null;
|
|
142
|
-
}
|
|
143
101
|
/**
|
|
144
102
|
* Return `addrs` with signaling (`/p2p-circuit`) addrs first, otherwise stable.
|
|
145
103
|
* Used to present the WebRTC dial input ahead of direct addrs. Does NOT mutate
|
package/dist/peer-record.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peer-record.js","sourceRoot":"","sources":["../src/peer-record.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"peer-record.js","sourceRoot":"","sources":["../src/peer-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The machine's primary IPv4 address, or undefined when there is no route (offline) or the
|
|
3
|
+
* probe fails. Callers then fall back to keeping every LAN address. `probe` is for tests.
|
|
4
|
+
*/
|
|
5
|
+
export declare function primaryLanAddress(probe?: {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
}): Promise<string | undefined>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node-only: the machine's main LAN address, for {@link selectNodeClaimAddresses}'s `lan`.
|
|
3
|
+
*
|
|
4
|
+
* The address the OS would send from to reach the internet, learned by "connecting" a UDP
|
|
5
|
+
* socket to a public address: a UDP connect only picks a route and sends nothing. On a
|
|
6
|
+
* server that also has Docker bridges, a VPN or other interfaces, that is the one a phone on
|
|
7
|
+
* the same network reaches. A subpath module (`@serfab/cadre-core/primary-lan-address`), like
|
|
8
|
+
* `key-store-file`, so `node:dgram` never reaches a React Native bundle.
|
|
9
|
+
*/
|
|
10
|
+
import dgram from 'node:dgram';
|
|
11
|
+
import debug from 'debug';
|
|
12
|
+
const log = debug('sereus:cadre:primary-lan-address');
|
|
13
|
+
/** Any public IPv4 address works: nothing is sent to it. */
|
|
14
|
+
const ROUTE_PROBE = { host: '1.1.1.1', port: 53 };
|
|
15
|
+
/**
|
|
16
|
+
* The machine's primary IPv4 address, or undefined when there is no route (offline) or the
|
|
17
|
+
* probe fails. Callers then fall back to keeping every LAN address. `probe` is for tests.
|
|
18
|
+
*/
|
|
19
|
+
export async function primaryLanAddress(probe = ROUTE_PROBE) {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const socket = dgram.createSocket('udp4');
|
|
22
|
+
let settled = false;
|
|
23
|
+
const finish = (address) => {
|
|
24
|
+
if (settled)
|
|
25
|
+
return;
|
|
26
|
+
settled = true;
|
|
27
|
+
socket.close();
|
|
28
|
+
resolve(address);
|
|
29
|
+
};
|
|
30
|
+
socket.on('error', (err) => {
|
|
31
|
+
log('route probe failed: %s', err.message);
|
|
32
|
+
finish();
|
|
33
|
+
});
|
|
34
|
+
socket.connect(probe.port, probe.host, (err) => {
|
|
35
|
+
// A failed connect reports here, not as 'error', and leaves the socket bound to
|
|
36
|
+
// 0.0.0.0, so its address() must not be read.
|
|
37
|
+
if (err) {
|
|
38
|
+
log('no route to probe: %s', err.message);
|
|
39
|
+
finish();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
finish(socket.address().address);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=primary-lan-address.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-lan-address.js","sourceRoot":"","sources":["../src/primary-lan-address.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,GAAG,GAAG,KAAK,CAAC,kCAAkC,CAAC,CAAC;AAEtD,4DAA4D;AAC5D,MAAM,WAAW,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAW,CAAC;AAE3D;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAwC,WAAW;IAC1F,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,MAAM,GAAG,CAAC,OAAgB,EAAQ,EAAE;YACzC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,OAAO,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC1B,GAAG,CAAC,wBAAwB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC3C,MAAM,EAAE,CAAC;QACV,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE;YACtD,gFAAgF;YAChF,8CAA8C;YAC9C,IAAI,GAAG,EAAE,CAAC;gBACT,GAAG,CAAC,uBAAuB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC1C,MAAM,EAAE,CAAC;gBACT,OAAO;YACR,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A typed client for a hosting provider's container API (`docs/architecture.md` → "Provider
|
|
3
|
+
* Integration"; the wire shapes are cadre-provider's, `packages/cadre-provider/README.md`).
|
|
4
|
+
* The interface is what `rentNode` and the apps depend on; a hand-written object implementing
|
|
5
|
+
* it is how tests avoid HTTP.
|
|
6
|
+
*
|
|
7
|
+
* One request helper does the HTTP: the bearer from the token source, JSON in and out, a 401
|
|
8
|
+
* answered once by `tokens.invalidate()` and a retry, every other non-2xx a
|
|
9
|
+
* {@link ProviderApiError} from the provider's `{ ok: false, error }` envelope. Dates arrive
|
|
10
|
+
* as ISO strings and are parsed into epoch milliseconds here, so nothing above the client
|
|
11
|
+
* handles date strings. Global `fetch` only: loads on React Native and in the browser.
|
|
12
|
+
*/
|
|
13
|
+
import type { TokenSource } from './oauth.js';
|
|
14
|
+
/** How a container was asked to join its cadre (`POST /containers` `join`). */
|
|
15
|
+
export type ContainerJoinRequest = {
|
|
16
|
+
kind: 'claim';
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'invitation';
|
|
19
|
+
invitation: string;
|
|
20
|
+
};
|
|
21
|
+
/** The join as the node last reported it, folded onto the record by the provider's `/status` reads. */
|
|
22
|
+
export type ContainerJoinState = {
|
|
23
|
+
kind: 'claim';
|
|
24
|
+
state: 'awaiting';
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'claim';
|
|
27
|
+
state: 'claimed';
|
|
28
|
+
ownerKey: string;
|
|
29
|
+
partyId: string;
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'invitation';
|
|
32
|
+
state: 'pending';
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'invitation';
|
|
35
|
+
state: 'accepted';
|
|
36
|
+
memberPeerId?: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'invitation';
|
|
39
|
+
state: 'failed';
|
|
40
|
+
error: string;
|
|
41
|
+
retryable: boolean;
|
|
42
|
+
};
|
|
43
|
+
export type RentedContainerStatus = 'pending' | 'creating' | 'enrolling' | 'running' | 'stopping' | 'stopped' | 'error';
|
|
44
|
+
/** Why a container was stopped: its customer asked, or the customer's subscription lapsed. */
|
|
45
|
+
export type ContainerStopReason = 'customer' | 'non-payment';
|
|
46
|
+
/** A container as the API returns it to its customer (the provider's `RedactedContainer`), dates in epoch ms. */
|
|
47
|
+
export interface RentedContainer {
|
|
48
|
+
id: string;
|
|
49
|
+
/** The cadre the container serves; a claim container's is the placeholder `unclaimed` until the claim is folded. */
|
|
50
|
+
partyId: string;
|
|
51
|
+
profile: 'storage' | 'transaction';
|
|
52
|
+
status: RentedContainerStatus;
|
|
53
|
+
createdAt: number;
|
|
54
|
+
updatedAt: number;
|
|
55
|
+
/** The failure, when `status` is `error`. */
|
|
56
|
+
error?: string;
|
|
57
|
+
/** The node's peer id once it first came up. */
|
|
58
|
+
peerId?: string;
|
|
59
|
+
/** How the container joins; the credential itself never leaves the provider this way. */
|
|
60
|
+
join?: {
|
|
61
|
+
kind: ContainerJoinRequest['kind'];
|
|
62
|
+
};
|
|
63
|
+
joinState?: ContainerJoinState;
|
|
64
|
+
/** The owner key a claim named, once folded. */
|
|
65
|
+
ownerKey?: string;
|
|
66
|
+
stopReason?: ContainerStopReason;
|
|
67
|
+
tags: Record<string, string>;
|
|
68
|
+
}
|
|
69
|
+
/** The node's live `/status`, as far as an app reads it. */
|
|
70
|
+
export interface RentedContainerHealth {
|
|
71
|
+
status: 'healthy' | 'unhealthy' | 'starting';
|
|
72
|
+
peerId?: string;
|
|
73
|
+
multiaddrs?: string[];
|
|
74
|
+
}
|
|
75
|
+
/** `GET /containers/:id`: the record after a live read, the read itself, and the join state lifted out of the record. */
|
|
76
|
+
export interface RentedContainerStatusReport {
|
|
77
|
+
container: RentedContainer;
|
|
78
|
+
health?: RentedContainerHealth;
|
|
79
|
+
join?: ContainerJoinState;
|
|
80
|
+
}
|
|
81
|
+
export interface CreateContainerBody {
|
|
82
|
+
join: ContainerJoinRequest;
|
|
83
|
+
/** Default `storage`. */
|
|
84
|
+
profile?: 'storage' | 'transaction';
|
|
85
|
+
tags?: Record<string, string>;
|
|
86
|
+
}
|
|
87
|
+
export type SubscriptionStatus = 'active' | 'past-due' | 'lapsed';
|
|
88
|
+
/** `GET /billing/status`, dates in epoch ms. */
|
|
89
|
+
export interface BillingStatus {
|
|
90
|
+
planId: string;
|
|
91
|
+
subscription: {
|
|
92
|
+
status: SubscriptionStatus;
|
|
93
|
+
/** When the current period renews; null once lapsed. */
|
|
94
|
+
renewsAt: number | null;
|
|
95
|
+
/** `past-due` only: when the grace period ends and the containers are stopped. */
|
|
96
|
+
lapsesAt: number | null;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** `GET /containers/:id/code`: the code the owner claims the container with, or not yet. */
|
|
100
|
+
export type NodeCodeAnswer = {
|
|
101
|
+
code: string;
|
|
102
|
+
peerId: string;
|
|
103
|
+
multiaddrs: string[];
|
|
104
|
+
}
|
|
105
|
+
/** The node has not reported its peer id and an announced address yet (503); poll. */
|
|
106
|
+
| {
|
|
107
|
+
notReady: true;
|
|
108
|
+
};
|
|
109
|
+
/** A non-2xx answer the provider explained: its `error.code` (`NOT_FOUND`, `ALREADY_CLAIMED`, `QUOTA_EXCEEDED`, ...) and message. */
|
|
110
|
+
export declare class ProviderApiError extends Error {
|
|
111
|
+
readonly status: number;
|
|
112
|
+
readonly code: string;
|
|
113
|
+
constructor(status: number, code: string, message: string);
|
|
114
|
+
}
|
|
115
|
+
export interface ProviderClient {
|
|
116
|
+
createContainer(body: CreateContainerBody): Promise<RentedContainer>;
|
|
117
|
+
getContainer(id: string): Promise<RentedContainerStatusReport>;
|
|
118
|
+
listContainers(): Promise<RentedContainer[]>;
|
|
119
|
+
deleteContainer(id: string): Promise<void>;
|
|
120
|
+
/** Undefined when the customer has no billing record yet. */
|
|
121
|
+
getBillingStatus(): Promise<BillingStatus | undefined>;
|
|
122
|
+
/** `notReady` on 503; a `ProviderApiError` for the 409s (`NOT_CLAIMABLE`, `ALREADY_CLAIMED`, `CONTAINER_GONE`). */
|
|
123
|
+
getNodeCode(id: string): Promise<NodeCodeAnswer>;
|
|
124
|
+
}
|
|
125
|
+
export interface ProviderClientOptions {
|
|
126
|
+
/** The descriptor's `apiBaseUrl`; the bearer is sent here and nowhere else. */
|
|
127
|
+
apiBaseUrl: string;
|
|
128
|
+
tokens: TokenSource;
|
|
129
|
+
fetch?: typeof fetch;
|
|
130
|
+
}
|
|
131
|
+
export declare function createProviderClient(options: ProviderClientOptions): ProviderClient;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A typed client for a hosting provider's container API (`docs/architecture.md` → "Provider
|
|
3
|
+
* Integration"; the wire shapes are cadre-provider's, `packages/cadre-provider/README.md`).
|
|
4
|
+
* The interface is what `rentNode` and the apps depend on; a hand-written object implementing
|
|
5
|
+
* it is how tests avoid HTTP.
|
|
6
|
+
*
|
|
7
|
+
* One request helper does the HTTP: the bearer from the token source, JSON in and out, a 401
|
|
8
|
+
* answered once by `tokens.invalidate()` and a retry, every other non-2xx a
|
|
9
|
+
* {@link ProviderApiError} from the provider's `{ ok: false, error }` envelope. Dates arrive
|
|
10
|
+
* as ISO strings and are parsed into epoch milliseconds here, so nothing above the client
|
|
11
|
+
* handles date strings. Global `fetch` only: loads on React Native and in the browser.
|
|
12
|
+
*/
|
|
13
|
+
import debug from 'debug';
|
|
14
|
+
import { requireFetch } from '../fetch-impl.js';
|
|
15
|
+
const log = debug('sereus:cadre:provider-client');
|
|
16
|
+
/** A non-2xx answer the provider explained: its `error.code` (`NOT_FOUND`, `ALREADY_CLAIMED`, `QUOTA_EXCEEDED`, ...) and message. */
|
|
17
|
+
export class ProviderApiError extends Error {
|
|
18
|
+
constructor(status, code, message) {
|
|
19
|
+
super(`Provider answered ${status} ${code}: ${message}`);
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.name = 'ProviderApiError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function createProviderClient(options) {
|
|
26
|
+
const doFetch = requireFetch(options.fetch);
|
|
27
|
+
const base = options.apiBaseUrl.replace(/\/+$/, '');
|
|
28
|
+
const tokens = options.tokens;
|
|
29
|
+
/** One authenticated JSON request; see the module doc for the 401 retry and the error envelope. */
|
|
30
|
+
async function request(method, path, body, allow = []) {
|
|
31
|
+
const headers = { accept: 'application/json' };
|
|
32
|
+
if (body !== undefined)
|
|
33
|
+
headers['content-type'] = 'application/json';
|
|
34
|
+
const encoded = body === undefined ? undefined : JSON.stringify(body);
|
|
35
|
+
const send = async (bearer) => doFetch(`${base}/${path}`, {
|
|
36
|
+
method,
|
|
37
|
+
headers: { ...headers, authorization: `Bearer ${bearer}` },
|
|
38
|
+
body: encoded,
|
|
39
|
+
});
|
|
40
|
+
const first = await tokens.accessToken();
|
|
41
|
+
let response = await send(first);
|
|
42
|
+
if (response.status === 401) {
|
|
43
|
+
log('%s %s answered 401; refreshing the token and retrying once', method, path);
|
|
44
|
+
response = await send(await tokens.invalidate(first));
|
|
45
|
+
}
|
|
46
|
+
const parsed = await readEnvelope(response);
|
|
47
|
+
if (allow.includes(response.status))
|
|
48
|
+
return { status: response.status, data: parsed.data };
|
|
49
|
+
if (!response.ok || parsed.ok === false) {
|
|
50
|
+
throw new ProviderApiError(response.status, parsed.code ?? `HTTP_${response.status}`, parsed.message ?? `${method} ${path} failed`);
|
|
51
|
+
}
|
|
52
|
+
return { status: response.status, data: parsed.data };
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
async createContainer(body) {
|
|
56
|
+
const { data } = await request('POST', 'containers', body);
|
|
57
|
+
return parseContainer(requireObject(data, 'POST containers').container);
|
|
58
|
+
},
|
|
59
|
+
async getContainer(id) {
|
|
60
|
+
const { data } = await request('GET', `containers/${encodeURIComponent(id)}`);
|
|
61
|
+
const report = requireObject(data, 'GET containers/:id');
|
|
62
|
+
const container = parseContainer(report.container);
|
|
63
|
+
const health = parseHealth(report.health);
|
|
64
|
+
return { container, ...(health ? { health } : {}), ...(container.joinState ? { join: container.joinState } : {}) };
|
|
65
|
+
},
|
|
66
|
+
async listContainers() {
|
|
67
|
+
const { data } = await request('GET', 'containers');
|
|
68
|
+
const list = requireObject(data, 'GET containers').containers;
|
|
69
|
+
if (!Array.isArray(list))
|
|
70
|
+
throw new Error('The provider\'s container list is not a list');
|
|
71
|
+
return list.map(parseContainer);
|
|
72
|
+
},
|
|
73
|
+
async deleteContainer(id) {
|
|
74
|
+
await request('DELETE', `containers/${encodeURIComponent(id)}`);
|
|
75
|
+
},
|
|
76
|
+
async getBillingStatus() {
|
|
77
|
+
const { data } = await request('GET', 'billing/status');
|
|
78
|
+
const billing = requireObject(data, 'GET billing/status').billing;
|
|
79
|
+
return billing === null || billing === undefined ? undefined : parseBilling(billing);
|
|
80
|
+
},
|
|
81
|
+
async getNodeCode(id) {
|
|
82
|
+
const { status, data } = await request('GET', `containers/${encodeURIComponent(id)}/code`, undefined, [503]);
|
|
83
|
+
if (status === 503)
|
|
84
|
+
return { notReady: true };
|
|
85
|
+
const code = requireObject(data, 'GET containers/:id/code');
|
|
86
|
+
return {
|
|
87
|
+
code: requireString(code.code, 'code'),
|
|
88
|
+
peerId: requireString(code.peerId, 'peerId'),
|
|
89
|
+
multiaddrs: requireStrings(code.multiaddrs, 'multiaddrs'),
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** The provider's `{ ok, data }` / `{ ok: false, error: { code, message } }` envelope, tolerating a body that is not one (a proxy's error page). */
|
|
95
|
+
async function readEnvelope(response) {
|
|
96
|
+
let body;
|
|
97
|
+
try {
|
|
98
|
+
body = await response.json();
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
log('answer %d carried a body that is not JSON: %o', response.status, error);
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
if (typeof body !== 'object' || body === null)
|
|
105
|
+
return {};
|
|
106
|
+
const envelope = body;
|
|
107
|
+
const error = typeof envelope.error === 'object' && envelope.error !== null ? envelope.error : undefined;
|
|
108
|
+
return {
|
|
109
|
+
ok: typeof envelope.ok === 'boolean' ? envelope.ok : undefined,
|
|
110
|
+
data: envelope.data,
|
|
111
|
+
code: typeof error?.code === 'string' ? error.code : undefined,
|
|
112
|
+
// `DELETE` reports a failed termination as `{ ok: false, message }` with no `error`.
|
|
113
|
+
message: typeof error?.message === 'string' ? error.message : typeof envelope.message === 'string' ? envelope.message : undefined,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function parseContainer(value) {
|
|
117
|
+
const record = requireObject(value, 'a container record');
|
|
118
|
+
const container = {
|
|
119
|
+
id: requireString(record.id, 'id'),
|
|
120
|
+
partyId: requireString(record.partyId, 'partyId'),
|
|
121
|
+
profile: record.profile === 'transaction' ? 'transaction' : 'storage',
|
|
122
|
+
status: requireStatus(record.status),
|
|
123
|
+
createdAt: parseIsoMs(record.createdAt, 'createdAt'),
|
|
124
|
+
updatedAt: parseIsoMs(record.updatedAt, 'updatedAt'),
|
|
125
|
+
tags: typeof record.tags === 'object' && record.tags !== null ? record.tags : {},
|
|
126
|
+
};
|
|
127
|
+
if (typeof record.error === 'string')
|
|
128
|
+
container.error = record.error;
|
|
129
|
+
if (typeof record.peerId === 'string')
|
|
130
|
+
container.peerId = record.peerId;
|
|
131
|
+
if (typeof record.ownerKey === 'string')
|
|
132
|
+
container.ownerKey = record.ownerKey;
|
|
133
|
+
if (record.stopReason === 'customer' || record.stopReason === 'non-payment')
|
|
134
|
+
container.stopReason = record.stopReason;
|
|
135
|
+
const join = record.join;
|
|
136
|
+
if (typeof join === 'object' && join !== null) {
|
|
137
|
+
const kind = join.kind;
|
|
138
|
+
if (kind === 'claim' || kind === 'invitation')
|
|
139
|
+
container.join = { kind };
|
|
140
|
+
}
|
|
141
|
+
if (record.joinState !== undefined)
|
|
142
|
+
container.joinState = parseJoinState(record.joinState);
|
|
143
|
+
return container;
|
|
144
|
+
}
|
|
145
|
+
function parseJoinState(value) {
|
|
146
|
+
const state = requireObject(value, 'a join state');
|
|
147
|
+
const key = `${String(state.kind)}/${String(state.state)}`;
|
|
148
|
+
switch (key) {
|
|
149
|
+
case 'claim/awaiting':
|
|
150
|
+
return { kind: 'claim', state: 'awaiting' };
|
|
151
|
+
case 'claim/claimed':
|
|
152
|
+
return { kind: 'claim', state: 'claimed', ownerKey: requireString(state.ownerKey, 'joinState.ownerKey'), partyId: requireString(state.partyId, 'joinState.partyId') };
|
|
153
|
+
case 'invitation/pending':
|
|
154
|
+
return { kind: 'invitation', state: 'pending' };
|
|
155
|
+
case 'invitation/accepted':
|
|
156
|
+
return { kind: 'invitation', state: 'accepted', ...(typeof state.memberPeerId === 'string' ? { memberPeerId: state.memberPeerId } : {}) };
|
|
157
|
+
case 'invitation/failed':
|
|
158
|
+
return { kind: 'invitation', state: 'failed', error: requireString(state.error, 'joinState.error'), retryable: state.retryable === true };
|
|
159
|
+
default:
|
|
160
|
+
throw new Error(`The provider reported a join state this client does not know (${key})`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function parseHealth(value) {
|
|
164
|
+
if (typeof value !== 'object' || value === null)
|
|
165
|
+
return undefined;
|
|
166
|
+
const health = value;
|
|
167
|
+
const status = health.status === 'healthy' || health.status === 'unhealthy' || health.status === 'starting' ? health.status : undefined;
|
|
168
|
+
if (!status)
|
|
169
|
+
return undefined;
|
|
170
|
+
return {
|
|
171
|
+
status,
|
|
172
|
+
...(typeof health.peerId === 'string' ? { peerId: health.peerId } : {}),
|
|
173
|
+
...(Array.isArray(health.multiaddrs) ? { multiaddrs: health.multiaddrs.filter((addr) => typeof addr === 'string') } : {}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function parseBilling(value) {
|
|
177
|
+
const billing = requireObject(value, 'a billing record');
|
|
178
|
+
const subscription = requireObject(billing.subscription, 'a billing record\'s subscription');
|
|
179
|
+
const status = subscription.status;
|
|
180
|
+
if (status !== 'active' && status !== 'past-due' && status !== 'lapsed') {
|
|
181
|
+
throw new Error(`The provider reported a subscription status this client does not know (${String(status)})`);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
planId: requireString(billing.planId, 'planId'),
|
|
185
|
+
subscription: {
|
|
186
|
+
status,
|
|
187
|
+
renewsAt: parseNullableIsoMs(subscription.renewsAt, 'subscription.renewsAt'),
|
|
188
|
+
lapsesAt: parseNullableIsoMs(subscription.lapsesAt, 'subscription.lapsesAt'),
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const CONTAINER_STATUSES = ['pending', 'creating', 'enrolling', 'running', 'stopping', 'stopped', 'error'];
|
|
193
|
+
function requireStatus(value) {
|
|
194
|
+
if (!CONTAINER_STATUSES.includes(value)) {
|
|
195
|
+
throw new Error(`The provider reported a container status this client does not know (${String(value)})`);
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
function requireObject(value, what) {
|
|
200
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
201
|
+
throw new Error(`The provider's answer to ${what} is not the JSON object the API documents`);
|
|
202
|
+
}
|
|
203
|
+
return value;
|
|
204
|
+
}
|
|
205
|
+
function requireString(value, field) {
|
|
206
|
+
if (typeof value !== 'string')
|
|
207
|
+
throw new Error(`The provider's answer lacks the string field ${field}`);
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
210
|
+
function requireStrings(value, field) {
|
|
211
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === 'string')) {
|
|
212
|
+
throw new Error(`The provider's answer lacks the string list ${field}`);
|
|
213
|
+
}
|
|
214
|
+
return value;
|
|
215
|
+
}
|
|
216
|
+
/** An ISO 8601 date string as epoch ms; the client's boundary, so nothing above it handles date strings. */
|
|
217
|
+
function parseIsoMs(value, field) {
|
|
218
|
+
const ms = typeof value === 'string' ? Date.parse(value) : Number.NaN;
|
|
219
|
+
if (Number.isNaN(ms))
|
|
220
|
+
throw new Error(`The provider's answer carries no ISO date in ${field}`);
|
|
221
|
+
return ms;
|
|
222
|
+
}
|
|
223
|
+
function parseNullableIsoMs(value, field) {
|
|
224
|
+
return value === null || value === undefined ? null : parseIsoMs(value, field);
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/provider/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provider descriptor: what a hosting provider says about itself (`GET /descriptor`,
|
|
3
|
+
* `docs/architecture.md` → "Provider descriptor and sign-in"), as an app's store screen
|
|
4
|
+
* renders it and as the sign-in and the provider client read it. The parser refuses, with a
|
|
5
|
+
* plain message, a document the rest of this directory could not work with.
|
|
6
|
+
*
|
|
7
|
+
* Imports only the package's `fetch` helper: it loads on React Native and in the browser as
|
|
8
|
+
* well as Node.
|
|
9
|
+
*/
|
|
10
|
+
/** Version 1 of the descriptor, the only one an app reads. */
|
|
11
|
+
export interface ProviderDescriptor {
|
|
12
|
+
v: 1;
|
|
13
|
+
/** Stable identifier an app keys its rental records by. */
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
/** The root every API route hangs off (`https://nodes.acme.example/api/v1`); the bearer goes here and nowhere else. */
|
|
17
|
+
apiBaseUrl: string;
|
|
18
|
+
/** The sign-in endpoints, and the union of the scopes any registered app may be granted. */
|
|
19
|
+
oauth: {
|
|
20
|
+
authorizeUrl: string;
|
|
21
|
+
tokenUrl: string;
|
|
22
|
+
scopes: string[];
|
|
23
|
+
};
|
|
24
|
+
/** The public plan list, readable before sign-in. */
|
|
25
|
+
plansUrl: string;
|
|
26
|
+
termsUrl?: string;
|
|
27
|
+
/** Regions in the provider's own words. */
|
|
28
|
+
regions?: string[];
|
|
29
|
+
icon?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read a descriptor document. Refuses: any version but 1; a missing or blank `id` or `name`;
|
|
33
|
+
* an `apiBaseUrl`, `oauth.authorizeUrl`, `oauth.tokenUrl` or `plansUrl` that is not an
|
|
34
|
+
* absolute URL, or is `http` at a host other than loopback, since the node code and the tokens
|
|
35
|
+
* travel over them; a `termsUrl` or `icon` that is not an absolute web URL; `regions` or
|
|
36
|
+
* `oauth.scopes` that are not string lists. A descriptor without `oauth` is refused too:
|
|
37
|
+
* no app could sign in to that provider.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseProviderDescriptor(json: unknown): ProviderDescriptor;
|
|
40
|
+
/** `GET url`, parsed with {@link parseProviderDescriptor}, for an app that holds a URL rather than the document. */
|
|
41
|
+
export declare function fetchProviderDescriptor(url: string, fetchImpl?: typeof fetch): Promise<ProviderDescriptor>;
|