@serfab/cadre-core 1.13.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 +383 -0
- package/dist/cadre-invite-protocol.js +774 -0
- package/dist/cadre-invite-protocol.js.map +1 -0
- 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 +849 -210
- package/dist/cadre-node.js +1574 -374
- package/dist/cadre-node.js.map +1 -1
- package/dist/claim-proof.d.ts +45 -0
- package/dist/claim-proof.js +132 -0
- package/dist/claim-proof.js.map +1 -0
- package/dist/control-authorization.d.ts +80 -19
- package/dist/control-authorization.js +55 -13
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.js +1 -1
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +605 -108
- package/dist/control-database.js +1299 -280
- package/dist/control-database.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +142 -0
- package/dist/control-protocol-guard.js +293 -0
- package/dist/control-protocol-guard.js.map +1 -0
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +817 -1067
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +4 -0
- package/dist/control-stream.js +22 -7
- package/dist/control-stream.js.map +1 -1
- package/dist/delegate-admission.d.ts +1 -1
- package/dist/delegate-admission.js +1 -1
- package/dist/device-token.d.ts +11 -9
- package/dist/device-token.js +14 -12
- package/dist/device-token.js.map +1 -1
- package/dist/ed25519-key.d.ts +22 -9
- package/dist/ed25519-key.js +47 -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 +22 -10
- package/dist/index.js +54 -11
- 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 +25 -33
- package/dist/link-budget.js +25 -35
- 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 +191 -151
- package/dist/membership-connection-gater.js +236 -173
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-claim-payload.d.ts +74 -0
- package/dist/node-claim-payload.js +224 -0
- package/dist/node-claim-payload.js.map +1 -0
- 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/owner-anchor-sync.d.ts +22 -0
- package/dist/owner-anchor-sync.js +143 -0
- package/dist/owner-anchor-sync.js.map +1 -0
- package/dist/peer-authorization.d.ts +113 -18
- package/dist/peer-authorization.js +225 -29
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-dial.d.ts +143 -27
- package/dist/peer-dial.js +276 -31
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +26 -2
- package/dist/peer-join-backfill.js +60 -12
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/peer-record.d.ts +17 -52
- package/dist/peer-record.js +20 -58
- package/dist/peer-record.js.map +1 -1
- package/dist/pending-join-runner.d.ts +66 -58
- package/dist/pending-join-runner.js +136 -145
- package/dist/pending-join-runner.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/push-fanout.d.ts +8 -2
- package/dist/push-fanout.js +8 -2
- package/dist/push-fanout.js.map +1 -1
- 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 +165 -123
- package/dist/seed-bootstrap.js +313 -275
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.d.ts +98 -37
- package/dist/seed-trust-policy.js +188 -2
- package/dist/seed-trust-policy.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +24 -26
- package/dist/strand-addr-protocol.js +19 -26
- package/dist/strand-addr-protocol.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 +5 -2
- 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-revocation-enforcer.d.ts +11 -11
- package/dist/strand-revocation-enforcer.js +11 -11
- package/dist/strand-wake-protocol.d.ts +13 -16
- package/dist/strand-wake-protocol.js +23 -27
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/trusted-owner-store.d.ts +35 -5
- package/dist/trusted-owner-store.js +50 -8
- package/dist/trusted-owner-store.js.map +1 -1
- package/dist/types.d.ts +512 -89
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/package.json +19 -10
- package/src/cadre-invite-protocol.ts +1052 -0
- package/src/cadre-join-request.ts +167 -0
- package/src/cadre-node.ts +1703 -386
- package/src/claim-proof.ts +147 -0
- package/src/control-authorization.ts +203 -117
- package/src/control-cohort.ts +1 -1
- package/src/control-database.ts +4663 -3453
- package/src/control-protocol-guard.ts +348 -0
- package/src/control-schema.ts +828 -1078
- package/src/control-stream.ts +26 -7
- package/src/delegate-admission.ts +1 -1
- package/src/device-token.ts +14 -10
- package/src/ed25519-key.ts +47 -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 -648
- 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 +25 -36
- package/src/machine-removal.ts +216 -0
- package/src/membership-connection-gater.ts +259 -191
- package/src/node-claim-payload.ts +259 -0
- package/src/node-default-transports.ts +38 -0
- package/src/node-transports.ts +49 -0
- package/src/owner-anchor-sync.ts +171 -0
- package/src/peer-authorization.ts +258 -29
- package/src/peer-dial.ts +604 -315
- package/src/peer-join-backfill.ts +62 -11
- package/src/peer-record.ts +159 -197
- package/src/pending-join-runner.ts +729 -735
- 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/push-fanout.ts +8 -2
- package/src/relay-addrs.ts +9 -4
- package/src/relay-reservation.ts +1211 -1121
- package/src/seed-bootstrap.ts +1502 -1448
- package/src/seed-trust-policy.ts +353 -140
- package/src/strand-addr-protocol.ts +26 -35
- 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-revocation-enforcer.ts +11 -11
- package/src/strand-wake-protocol.ts +23 -30
- package/src/trusted-owner-store.ts +242 -171
- package/src/types.ts +2687 -2242
package/dist/types.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { StrandRevocationEnforcementConfig } from './strand-revocation-enfo
|
|
|
8
8
|
import type { StrandMembershipReconciliationConfig } from './strand-membership-reconciler.js';
|
|
9
9
|
import type { StrandFirstSyncConfig } from './strand-first-sync-gate.js';
|
|
10
10
|
import type { StrandDatabase } from './strand-database.js';
|
|
11
|
+
import type { StrandCommitDurability, StrandCommitHeld } from './strand-commit-durability.js';
|
|
11
12
|
import type { SeedTrustPolicy } from './seed-trust-policy.js';
|
|
12
13
|
import type { KeyStore, KeyId } from './key-store.js';
|
|
13
14
|
import type { TrustedOwnerStore, TrustSource } from './trusted-owner-store.js';
|
|
@@ -16,9 +17,11 @@ import type { StrandNetworkStateStore } from './strand-network-state.js';
|
|
|
16
17
|
import type { EnrolledMachineStore } from './enrolled-machine-store.js';
|
|
17
18
|
import type { JoinedStrandStore } from './joined-strand-store.js';
|
|
18
19
|
import type { PushNotifier } from './push-notifier.js';
|
|
19
|
-
import type { RevocableTable } from './control-authorization.js';
|
|
20
|
+
import type { CadreInviteSignedFields, RevocableTable } from './control-authorization.js';
|
|
21
|
+
import type { CadreInvitation, CadreInviteAddressOutcome } from './cadre-invite-protocol.js';
|
|
20
22
|
import type { ControlRetryAbandonment } from './control-retry.js';
|
|
21
23
|
import type { FormationRejectionCode } from './strand-formation-rejection.js';
|
|
24
|
+
import type { ListenTransportOptions } from './relay-addrs.js';
|
|
22
25
|
/**
|
|
23
26
|
* Extended Libp2p node with the coordinatedRepo attached by db-p2p's
|
|
24
27
|
* createLibp2pNode after node creation (not surfaced on the base Libp2p type).
|
|
@@ -368,6 +371,20 @@ export interface NetworkConfig {
|
|
|
368
371
|
* ```
|
|
369
372
|
*/
|
|
370
373
|
transports?: Libp2pTransports;
|
|
374
|
+
/**
|
|
375
|
+
* Builds `@optimystic/db-p2p`'s default transport list, for the one case where cadre has to
|
|
376
|
+
* pass a list itself while {@link transports} is unset: a node with configured relays, which
|
|
377
|
+
* is built with libp2p's relay discovery switched off (`node-transports.ts`). Called with the
|
|
378
|
+
* switches the node's listen entries imply (`wsPort` when one names WebSocket). Ignored when
|
|
379
|
+
* {@link transports} is set.
|
|
380
|
+
*
|
|
381
|
+
* A Node host passes `nodeDefaultTransports` from the Node-only
|
|
382
|
+
* `@serfab/cadre-core/node-default-transports` subpath, as `cadre-cli` does. It is injected
|
|
383
|
+
* rather than built in, because that list names `@libp2p/tcp`, which must stay out of the
|
|
384
|
+
* graph React Native and browser bundles reach. Left unset, such a node is built from
|
|
385
|
+
* db-p2p's own list with relay discovery on.
|
|
386
|
+
*/
|
|
387
|
+
defaultTransports?: (listen: ListenTransportOptions) => Libp2pTransports;
|
|
371
388
|
/**
|
|
372
389
|
* Crypto primitives for the Noise handshake and the encrypted connection that
|
|
373
390
|
* follows, threaded to both the control node and every strand's cohort node — every
|
|
@@ -534,7 +551,7 @@ export interface NetworkConfig {
|
|
|
534
551
|
/**
|
|
535
552
|
* Limit on dialing ONE peer — all of its candidate addresses together — in
|
|
536
553
|
* ms. Bounds a reconcile pass at (dialed siblings) × this. The same limit
|
|
537
|
-
* applies to the owner dials of `applySeed
|
|
554
|
+
* applies to the owner dials of `applySeed`. Defaults to
|
|
538
555
|
* {@link DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS}.
|
|
539
556
|
*/
|
|
540
557
|
dialTimeoutMs?: number;
|
|
@@ -591,6 +608,16 @@ export interface NetworkConfig {
|
|
|
591
608
|
* kept it (the same pair aborted at a 200ms stall only when the deadline was 300ms). An
|
|
592
609
|
* interval strictly above the deadline is what makes the 30 seconds real.
|
|
593
610
|
*
|
|
611
|
+
* WHY THE OTHER END'S PING HANDLER CAPS IT TOO. The node being pinged echoes through
|
|
612
|
+
* `@libp2p/ping`'s inbound handler, which aborts the ping stream when its own timer fires; the
|
|
613
|
+
* pinging node's monitor sees the reset and aborts the connection exactly as it would on its
|
|
614
|
+
* own timeout. So this deadline is only real up to the handler timeout of the node being
|
|
615
|
+
* pinged, which has to stay above it. The deployed relay sets its handler to 60 s for that
|
|
616
|
+
* reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's
|
|
617
|
+
* bare `ping()`, `@libp2p/ping`'s 10 s, and nothing here can move it
|
|
618
|
+
* (`tickets/blocked/optimystic-ping-handler-timeout-below-the-monitor-deadline.md`): between
|
|
619
|
+
* two cadre nodes, a ping whose echo takes longer than 10 s still drops the connection.
|
|
620
|
+
*
|
|
594
621
|
* WHAT THE LINK NEEDS OF IT. One ping opens a fresh stream and echoes over it: a protocol
|
|
595
622
|
* negotiation plus the echo, two link round trips by `link-budget.ts`'s counts (a `newStream`
|
|
596
623
|
* over a relayed circuit measured 3016 ms at 1500 ms one-way). At the slowest link sereus
|
|
@@ -777,7 +804,7 @@ export interface CadreNodeConfig {
|
|
|
777
804
|
* should enable it, because a node without it neither announces nor serves as part of the
|
|
778
805
|
* group a notification is rooted at. Enabling registers Optimystic's change-notification
|
|
779
806
|
* protocol handlers on the strand node, which the closed-strand per-stream gate does not
|
|
780
|
-
* cover, and each watched table
|
|
807
|
+
* cover, and each watched table registers again on every node build. Both
|
|
781
808
|
* are spelled out in `docs/strands.md` → "Change notifications (reactivity)". The control
|
|
782
809
|
* node never gets it.
|
|
783
810
|
*/
|
|
@@ -860,9 +887,43 @@ export interface CadreNodeConfig {
|
|
|
860
887
|
* inside SeedBootstrapService when unset — a node whose node-local trusted-owner
|
|
861
888
|
* anchor was never seeded then rejects every seed. A per-call
|
|
862
889
|
* `applySeed(seed, { trustPolicy })` override still wins over this default for
|
|
863
|
-
* callers that hold out-of-band material (e.g.
|
|
890
|
+
* callers that hold out-of-band material (e.g. an operator-pinned owner key).
|
|
864
891
|
*/
|
|
865
892
|
seedTrustPolicy?: SeedTrustPolicy;
|
|
893
|
+
/**
|
|
894
|
+
* Start this node waiting to be CLAIMED: it belongs to nobody until someone delivers
|
|
895
|
+
* a seed carrying a proof of this one-time secret (`claim-proof.ts`,
|
|
896
|
+
* `claimSecretTrustPolicy`), and that signer becomes its anchored owner. The node
|
|
897
|
+
* turns its seed listener on itself and, while unclaimed, lets a stranger do nothing
|
|
898
|
+
* but deliver a seed: connections are admitted, control-database streams and relay
|
|
899
|
+
* reservations are refused (`CadreNode.isAwaitingClaim`).
|
|
900
|
+
*
|
|
901
|
+
* A node has exactly one cold-start trust source, so `claim` beside a non-empty
|
|
902
|
+
* `trustedOwners.pinnedKeys` or a `seedTrustPolicy` fails `start()` closed, and
|
|
903
|
+
* `initializeSeedBootstrap` (an owner key) refuses on such a node: a hosted node never
|
|
904
|
+
* holds an owner key. A claimed node restarted with this still set ignores it (its
|
|
905
|
+
* anchor is non-empty), so the operator need not remove it.
|
|
906
|
+
*
|
|
907
|
+
* The claim also decides which party the node serves: `controlNetwork.partyId` on an
|
|
908
|
+
* unclaimed node is a placeholder, and the real party is the claim seed's. The node
|
|
909
|
+
* itself keeps serving the placeholder for the rest of the process — everything it
|
|
910
|
+
* holds is keyed on the party it started with — so `record` is where the embedder
|
|
911
|
+
* learns the claimed party, and it is the embedder's job to restart into it.
|
|
912
|
+
*/
|
|
913
|
+
claim?: {
|
|
914
|
+
/** The one-time claim secret, base64url of 32 bytes. Never written to replicated state or logs. */
|
|
915
|
+
secret: string;
|
|
916
|
+
/**
|
|
917
|
+
* Record the claim durably: called with the seed's party and the claimant's key after
|
|
918
|
+
* the proof verifies and BEFORE the claimant is anchored, and awaited. A rejection
|
|
919
|
+
* refuses the claim `claim-not-persisted` with nothing anchored, so the claimant
|
|
920
|
+
* retries. Record first, anchor second: a crash between the two leaves a record and
|
|
921
|
+
* an empty anchor, and an embedder that starts from its record (cadre-cli does)
|
|
922
|
+
* then comes up claimed by that owner, who is accepted idempotently when it retries.
|
|
923
|
+
* The other order would leave an anchored node with no record of its party.
|
|
924
|
+
*/
|
|
925
|
+
record: (claim: NodeClaimRecord) => Promise<void>;
|
|
926
|
+
};
|
|
866
927
|
/**
|
|
867
928
|
* Node-local trusted-owner anchor (see `trusted-owner-store.ts`): the
|
|
868
929
|
* NON-replicated, per-party record of owner keys established out of band —
|
|
@@ -883,16 +944,18 @@ export interface CadreNodeConfig {
|
|
|
883
944
|
/**
|
|
884
945
|
* Owner keys (base64url ed25519) established out of band and seeded into
|
|
885
946
|
* the store during start(): operator pins (e.g. cadre-cli
|
|
886
|
-
* `--pin-owner-key` / `CADRE_OWNER_KEYS`)
|
|
887
|
-
*
|
|
947
|
+
* `--pin-owner-key` / `CADRE_OWNER_KEYS`). A cadre invitation's `ownerKeys`
|
|
948
|
+
* are pinned at redemption instead (`CadreNode.redeemCadreInvitation`).
|
|
949
|
+
* Seeding is idempotent across restarts.
|
|
888
950
|
*/
|
|
889
951
|
pinnedKeys?: string[];
|
|
890
952
|
/**
|
|
891
953
|
* Provenance recorded for {@link pinnedKeys}. Default: 'operator'.
|
|
892
954
|
* ('genesis' is reserved for the node's own founding key, seeded
|
|
893
|
-
* internally by `initializeSeedBootstrap
|
|
955
|
+
* internally by `initializeSeedBootstrap`; 'chain' is written only by the
|
|
956
|
+
* anchor sync, `CadreNode.syncOwnerAnchor`.)
|
|
894
957
|
*/
|
|
895
|
-
pinnedSource?: Exclude<TrustSource, 'genesis'>;
|
|
958
|
+
pinnedSource?: Exclude<TrustSource, 'genesis' | 'chain'>;
|
|
896
959
|
};
|
|
897
960
|
/**
|
|
898
961
|
* Node-local cold-start bootstrap-peer store (see `bootstrap-peer-store.ts`):
|
|
@@ -1124,13 +1187,11 @@ export interface StrandRow {
|
|
|
1124
1187
|
FounderOwnerKey: string | null;
|
|
1125
1188
|
}
|
|
1126
1189
|
/**
|
|
1127
|
-
* A `CadreControl.
|
|
1128
|
-
* invitation, readable by every machine of the party.
|
|
1129
|
-
*
|
|
1130
|
-
* closed strand, `MembershipInvite`), `'failed'` sets `FailureCode`, and a pending row
|
|
1131
|
-
* (`Outcome: null`) sets neither.
|
|
1190
|
+
* A `CadreControl.JoinRequest` row: a join this party asked for through another party's
|
|
1191
|
+
* invitation, readable by every machine of the party. The row never changes; its outcome is
|
|
1192
|
+
* a `JoinSuccess` or `JoinFailure` row keyed by `StampId` (see {@link PendingJoin}).
|
|
1132
1193
|
*/
|
|
1133
|
-
export interface
|
|
1194
|
+
export interface JoinRequestRow {
|
|
1134
1195
|
/** base64url sha256 of the invitation token (`pendingJoinId`). */
|
|
1135
1196
|
Id: string;
|
|
1136
1197
|
/** `CadreNode.encodeInvitation` of the invitation. A bearer credential. */
|
|
@@ -1141,30 +1202,45 @@ export interface PendingJoinRow {
|
|
|
1141
1202
|
RequestedAt: number;
|
|
1142
1203
|
/** Epoch ms; no attempt starts at or after it. */
|
|
1143
1204
|
ExpiresAt: number;
|
|
1144
|
-
|
|
1145
|
-
/** Epoch ms the outcome was recorded; null while pending. */
|
|
1146
|
-
OutcomeAt: number | null;
|
|
1147
|
-
/** `'joined'`: the strand the formation returned. */
|
|
1148
|
-
StrandId: string | null;
|
|
1149
|
-
/** `'joined'`, closed strand: JSON of the {@link StrandMembershipInvite} the formation delivered. */
|
|
1150
|
-
MembershipInvite: string | null;
|
|
1151
|
-
/** `'failed'`: a `FormationRejectionCode`, `'expired'` or `'local'`. Not constrained by the schema. */
|
|
1152
|
-
FailureCode: string | null;
|
|
1153
|
-
/** `'failed'`: human-readable text. */
|
|
1154
|
-
FailureReason: string | null;
|
|
1155
|
-
/** Single-use nonce of this row incarnation; `ControlDatabase.replacePendingJoin` names it as the row it replaces. */
|
|
1205
|
+
/** Single-use nonce of this request incarnation; its outcome rows name it. */
|
|
1156
1206
|
StampId: string;
|
|
1157
1207
|
}
|
|
1208
|
+
/**
|
|
1209
|
+
* The outcome of a {@link JoinRequestRow}: a `CadreControl.JoinSuccess` row (`'joined'`) or
|
|
1210
|
+
* a `CadreControl.JoinFailure` row (`'failed'`), without the request stamp that keys it.
|
|
1211
|
+
*/
|
|
1212
|
+
export type JoinOutcome = {
|
|
1213
|
+
kind: 'joined';
|
|
1214
|
+
/** Epoch ms the outcome was recorded. */
|
|
1215
|
+
RecordedAt: number;
|
|
1216
|
+
/** The strand the formation returned. */
|
|
1217
|
+
StrandId: string;
|
|
1218
|
+
/** Closed strand: JSON of the {@link StrandMembershipInvite} the formation delivered. */
|
|
1219
|
+
MembershipInvite: string | null;
|
|
1220
|
+
} | {
|
|
1221
|
+
kind: 'failed';
|
|
1222
|
+
/** Epoch ms the outcome was recorded. */
|
|
1223
|
+
RecordedAt: number;
|
|
1224
|
+
/** A `FormationRejectionCode`, `'expired'` or `'local'`. Not constrained by the schema. */
|
|
1225
|
+
Code: string;
|
|
1226
|
+
/** Human-readable text. */
|
|
1227
|
+
Reason: string;
|
|
1228
|
+
};
|
|
1229
|
+
/** A join request with its outcome, as `ControlDatabase.queryPendingJoins` reads the three tables. */
|
|
1230
|
+
export interface PendingJoin extends JoinRequestRow {
|
|
1231
|
+
/** Null while pending. */
|
|
1232
|
+
outcome: JoinOutcome | null;
|
|
1233
|
+
}
|
|
1158
1234
|
/**
|
|
1159
1235
|
* A join this party asked for through `CadreNode.requestJoin`, as one machine sees it.
|
|
1160
1236
|
*
|
|
1161
|
-
* `pending`, `joined` and `failed` come from the party-wide
|
|
1237
|
+
* `pending`, `joined` and `failed` come from the party-wide join request and its outcome row and read the same
|
|
1162
1238
|
* on every machine. `trying` (an attempt is running on this machine) and `waiting` (this
|
|
1163
1239
|
* machine's last attempt failed in a way worth retrying) are this machine's own view of a
|
|
1164
1240
|
* pending row, so two machines can report different states for it.
|
|
1165
1241
|
*/
|
|
1166
1242
|
export interface PendingJoinStatus {
|
|
1167
|
-
/** `
|
|
1243
|
+
/** `JoinRequest.Id`: the sha256 of the invitation token. */
|
|
1168
1244
|
id: string;
|
|
1169
1245
|
sAppId: string;
|
|
1170
1246
|
/** Epoch ms. */
|
|
@@ -1377,7 +1453,7 @@ export interface StrandMembershipInvite {
|
|
|
1377
1453
|
* whoever holds it can `consumeInvite` exactly once. Same sensitivity class and
|
|
1378
1454
|
* handling as `memberPrivateKey` — delivered only inside the validated,
|
|
1379
1455
|
* post-approval formation result. The inviting side never writes it to its control
|
|
1380
|
-
* DB. The joining side may copy it into its own `
|
|
1456
|
+
* DB. The joining side may copy it into its own `JoinSuccess` row
|
|
1381
1457
|
* (`MembershipInvite`), owner-signed and party-private like
|
|
1382
1458
|
* `JoinedStrand.MemberPrivateKey`, so a join finished on one machine of the party
|
|
1383
1459
|
* can be seated by whichever machine launches the strand first.
|
|
@@ -1489,6 +1565,24 @@ export interface CadreNodeEvents {
|
|
|
1489
1565
|
'strand:writable': {
|
|
1490
1566
|
strandId: string;
|
|
1491
1567
|
};
|
|
1568
|
+
/**
|
|
1569
|
+
* Emitted after every commit on a strand's database that wrote something, with whether
|
|
1570
|
+
* another member already holds it (`heldElsewhere`) and whether `strand:commit-held` will
|
|
1571
|
+
* say when it does (`followed`). Synchronous, inside the commit, before the app's statement
|
|
1572
|
+
* returns: keep handlers short. Covers every commit on the strand database, cadre-core's own
|
|
1573
|
+
* membership writes included, so filter by the ids you hold — the `StampId()` a
|
|
1574
|
+
* `returning StampId() as Tx` statement gave you. The same record is readable afterwards with
|
|
1575
|
+
* `StrandDatabase.commitDurability(tx)`. See docs/strands.md, "A commit's durability".
|
|
1576
|
+
*/
|
|
1577
|
+
'strand:commit-durability': StrandCommitDurability;
|
|
1578
|
+
/**
|
|
1579
|
+
* Emitted once when a commit reported `followed` below full replication becomes held by every
|
|
1580
|
+
* member of its cohort — the moment a write made while the others were away reaches them.
|
|
1581
|
+
* Synchronous, like `strand:commit-durability`, and can come right after it inside the same
|
|
1582
|
+
* commit. Never emitted for a commit made before your listener attached, or one forgotten
|
|
1583
|
+
* because its database closed, hibernated or the node restarted.
|
|
1584
|
+
*/
|
|
1585
|
+
'strand:commit-held': StrandCommitHeld;
|
|
1492
1586
|
/**
|
|
1493
1587
|
* Emitted when this node discovers it is no longer a member of a CLOSED
|
|
1494
1588
|
* strand — its party was removed by a manager, or it left. Detected by the
|
|
@@ -1627,6 +1721,30 @@ export interface CadreNodeEvents {
|
|
|
1627
1721
|
partyId: string;
|
|
1628
1722
|
error: string;
|
|
1629
1723
|
};
|
|
1724
|
+
/**
|
|
1725
|
+
* Emitted once on a node started with `CadreNodeConfig.claim`, when a seed carrying a
|
|
1726
|
+
* valid claim proof has been recorded (`claim.record`) and has durably anchored its
|
|
1727
|
+
* signer as this node's owner. Carries the claim's party beside the owner key: the
|
|
1728
|
+
* party this node should serve from now on, which its own config did not know. Fires
|
|
1729
|
+
* BEFORE the seed is acknowledged to the claimant, its peers merged and its owners
|
|
1730
|
+
* dialed, so it precedes that seed's `seed:applied`; an embedder that restarts into
|
|
1731
|
+
* the claimed party on this event should wait for that `seed:applied` (or `seed:error`)
|
|
1732
|
+
* so the acknowledgement is not lost with the node. `CadreNode.isAwaitingClaim` is the
|
|
1733
|
+
* matching predicate for a reader that subscribed too late to see the event.
|
|
1734
|
+
*/
|
|
1735
|
+
'claim:accepted': NodeClaimRecord;
|
|
1736
|
+
/**
|
|
1737
|
+
* Emitted once per node object when this node learns that an owner removed it from the
|
|
1738
|
+
* cadre: a `CadrePeer` tombstone names its peer id, it has no live `CadrePeer` row, and a
|
|
1739
|
+
* membership refresh of this process had seen its live row before. `CadreNode.removedFromCadre`
|
|
1740
|
+
* is the matching query for a reader that subscribed too late.
|
|
1741
|
+
*
|
|
1742
|
+
* BEST-EFFORT: it fires only if the tombstone reached this node before the other machines
|
|
1743
|
+
* cut it off, so a machine that was offline at removal time usually never sees it. Nothing
|
|
1744
|
+
* is stopped or deleted for you; the node only stops its owner re-issue drains, which can no
|
|
1745
|
+
* longer succeed. Stopping the node and deleting the cadre's data are the embedder's.
|
|
1746
|
+
*/
|
|
1747
|
+
'cadre:removed': CadreRemoval;
|
|
1630
1748
|
}
|
|
1631
1749
|
/**
|
|
1632
1750
|
* A peer's self-published, freshness-stamped, self-signed address record.
|
|
@@ -1637,8 +1755,8 @@ export interface CadreNodeEvents {
|
|
|
1637
1755
|
* PeerId by reading the record, re-verifying the self-signature against
|
|
1638
1756
|
* `publicKey`, checking freshness, and applying a trust gate.
|
|
1639
1757
|
*
|
|
1640
|
-
* The signature covers `peerId`, the comma-joined `addrs`, and `
|
|
1641
|
-
* `peerRecordSignedPayload`. `publicKey` is NOT inside the signed payload: it is
|
|
1758
|
+
* The signature covers `peerId`, the comma-joined `addrs`, `updatedAt` and `profile` —
|
|
1759
|
+
* see `peerRecordSignedPayload`. `publicKey` is NOT inside the signed payload: it is
|
|
1642
1760
|
* the key the signature is verified *with*, and its libp2p-identity binding to
|
|
1643
1761
|
* `peerId` is checked separately at resolve time, so re-signing it would be
|
|
1644
1762
|
* redundant.
|
|
@@ -1652,34 +1770,244 @@ export interface PeerAddressRecord {
|
|
|
1652
1770
|
addrs: string[];
|
|
1653
1771
|
/** epoch ms — freshness; strictly increasing per peer. */
|
|
1654
1772
|
updatedAt: number;
|
|
1773
|
+
/**
|
|
1774
|
+
* The publisher's own {@link CadreNodeConfig.profile}: `'transaction'` is an app (phone,
|
|
1775
|
+
* browser), `'storage'` an always-on node. Null on a row the machine has not yet
|
|
1776
|
+
* self-published (an owner or an invitation seated it).
|
|
1777
|
+
*/
|
|
1778
|
+
profile: NodeProfile | null;
|
|
1655
1779
|
/** ed25519 self-signature over the signed payload, base64url. */
|
|
1656
1780
|
sig: string;
|
|
1657
1781
|
}
|
|
1658
1782
|
/**
|
|
1659
1783
|
* One `CadreControl.CadrePeer` row as read by `ControlDatabase.queryCadrePeers`:
|
|
1660
|
-
* the addressing columns plus the persisted membership
|
|
1784
|
+
* the addressing columns plus the persisted membership proof.
|
|
1661
1785
|
*
|
|
1662
|
-
*
|
|
1786
|
+
* A row carries exactly one proof. An owner-vouched row has `vouchSig`, the owner's
|
|
1787
|
+
* signature over the row's (`peerId`, `stampId`), which
|
|
1663
1788
|
* {@link CadreNode.listAuthorizedMembers} re-checks against the node-local
|
|
1664
|
-
* trusted-owner anchor
|
|
1665
|
-
*
|
|
1666
|
-
*
|
|
1667
|
-
*
|
|
1789
|
+
* trusted-owner anchor. An invitation-admitted row has `vouchSig` null and
|
|
1790
|
+
* `vouchUsage` naming the `CadreInviteUsage` row that admitted it; the reader
|
|
1791
|
+
* verifies the chain row → usage → invitation instead (`verifyInvitationAdmission`),
|
|
1792
|
+
* with `vouchOwner` the invitation's issuer. All columns are null on a row written
|
|
1793
|
+
* before they existed, and such a row is never authorizable. Named (rather than
|
|
1794
|
+
* restated inline at each read site) so a future column addition cannot reach the
|
|
1795
|
+
* query without the predicate seeing it.
|
|
1668
1796
|
*/
|
|
1669
1797
|
export interface CadrePeerRow {
|
|
1670
1798
|
/** libp2p peer ID (base58btc) — the row key. */
|
|
1671
1799
|
peerId: string;
|
|
1800
|
+
/** ed25519 public key (base64url) stored for the peer, or null (not ed25519, or a row written before the column). */
|
|
1801
|
+
publicKey: string | null;
|
|
1672
1802
|
/** Comma-joined dialable multiaddrs as stored, or null when unpublished. */
|
|
1673
1803
|
multiaddr: string | null;
|
|
1674
|
-
/**
|
|
1804
|
+
/** The machine's freshness stamp (epoch ms); 0 when it has never self-published. */
|
|
1805
|
+
updatedAt: number;
|
|
1806
|
+
/** The machine's self-declared node profile; null until it self-publishes. */
|
|
1807
|
+
profile: NodeProfile | null;
|
|
1808
|
+
/** The machine's self-signature over its address record (`peerRecordSignedPayload`); null until it self-publishes. */
|
|
1809
|
+
sig: string | null;
|
|
1810
|
+
/** Single-use anti-replay nonce the voucher signature, or the usage row, is bound to. */
|
|
1675
1811
|
stampId: string | null;
|
|
1676
|
-
/** ed25519 public key (base64url) of the owner that vouched this row. */
|
|
1812
|
+
/** ed25519 public key (base64url) of the owner that vouched this row, or that issued the invitation that admitted it. */
|
|
1677
1813
|
vouchOwner: string | null;
|
|
1678
|
-
/** That owner's signature over `digest('CadreControl.CadrePeer', 'vouch', peerId, stampId)`, base64url. */
|
|
1814
|
+
/** That owner's signature over `digest('CadreControl.CadrePeer', 'vouch', partyId, peerId, stampId)`, base64url; null on an invitation-admitted row. */
|
|
1679
1815
|
vouchSig: string | null;
|
|
1816
|
+
/** The `CadreInviteUsage.UsageStampId` that admitted this row; null on an owner-vouched row. */
|
|
1817
|
+
vouchUsage: string | null;
|
|
1818
|
+
}
|
|
1819
|
+
/** The proof-bearing subset of a {@link CadrePeerRow} — what the authorized-membership predicate reads. */
|
|
1820
|
+
export type CadrePeerVoucherFields = Pick<CadrePeerRow, 'peerId' | 'stampId' | 'vouchOwner' | 'vouchSig' | 'vouchUsage'>;
|
|
1821
|
+
/**
|
|
1822
|
+
* One live `CadreControl.OwnerKey` row as `ControlDatabase.queryOwnerKeyRows` reads it: the
|
|
1823
|
+
* key and the persisted proof of how it was seated. The three proof shapes are the schema's
|
|
1824
|
+
* (`OwnerKey.Authorized`): the founding row (all three null), an owner-signed add (`vouchOwner`
|
|
1825
|
+
* and `vouchSig`, the signer's signature over `ownerKeyAddDigest(key, stampId)`), or an
|
|
1826
|
+
* owner-granting invitation redeemed by consent (`vouchOwner` the issuer, `vouchSig` null,
|
|
1827
|
+
* `vouchUsage` the `CadreInviteUsage` row). Rows whose stamp is retired in `Revocation`, and
|
|
1828
|
+
* the stampless row the schema permits but no writer produces, never reach a reader, so
|
|
1829
|
+
* `stampId` is non-null here. The anchor sync (`deriveOwnerAnchor`) judges
|
|
1830
|
+
* the proof against the keys it already trusts, never against the table itself.
|
|
1831
|
+
*/
|
|
1832
|
+
export interface OwnerKeyRow {
|
|
1833
|
+
/** ed25519 public key (base64url) — the row key. */
|
|
1834
|
+
key: string;
|
|
1835
|
+
/** Single-use anti-replay nonce the voucher signature, or the usage row, is bound to. */
|
|
1836
|
+
stampId: string;
|
|
1837
|
+
/** The owner that signed the add, or that issued the invitation that admitted it; null on the founding row. */
|
|
1838
|
+
vouchOwner: string | null;
|
|
1839
|
+
/** That owner's signature over the row's `'add'` digest, base64url; null on the founding row and on an invitation-admitted row. */
|
|
1840
|
+
vouchSig: string | null;
|
|
1841
|
+
/** The `CadreInviteUsage.UsageStampId` that admitted this row; null otherwise. */
|
|
1842
|
+
vouchUsage: string | null;
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* One `CadreControl.CadreInvite` row: an owner-signed invitation to join the cadre,
|
|
1846
|
+
* as `ControlDatabase.insertCadreInvite` returns it and `queryCadreInvite` reads it.
|
|
1847
|
+
* Every column is here because the row travels: the holder carries it inside the
|
|
1848
|
+
* invitation bundle and a member re-seats it verbatim (`seatCadreInvite`), so
|
|
1849
|
+
* `expiresAt` is the engine-canonical `datetime` string the row stores, not epoch
|
|
1850
|
+
* ms — the `'add'` signature covers that exact text. The signed subset is
|
|
1851
|
+
* `CadreInviteSignedFields` (`control-authorization.ts`).
|
|
1852
|
+
*/
|
|
1853
|
+
export interface CadreInviteRow extends CadreInviteSignedFields {
|
|
1854
|
+
/** The owner that signed the row (`IssuerKey`, base64url ed25519); a reader checks it against its anchor. */
|
|
1855
|
+
issuerKey: string;
|
|
1856
|
+
/** That owner's `'add'` signature over the row (`IssuerSig`, base64url). */
|
|
1857
|
+
issuerSig: string;
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* One `CadreControl.CadreInviteUsage` row: a redemption of a {@link CadreInviteRow},
|
|
1861
|
+
* as `ControlDatabase.queryCadreInviteUsages` reads it. The two signatures are
|
|
1862
|
+
* stored so any reader can re-verify the admission chain (`verifyInvitationAdmission`).
|
|
1863
|
+
*/
|
|
1864
|
+
export interface CadreInviteUsageRow {
|
|
1865
|
+
/** The redemption's single-use nonce (the row key), signed into both signatures. */
|
|
1866
|
+
usageStampId: string;
|
|
1867
|
+
/** The `CadreInvite.Key` redeemed. */
|
|
1868
|
+
inviteKey: string;
|
|
1869
|
+
/** The admitted device's libp2p peer id — the identity multihash of `peerKey`. */
|
|
1870
|
+
peerId: string;
|
|
1871
|
+
/** The admitted device's ed25519 public key (base64url). */
|
|
1872
|
+
peerKey: string;
|
|
1873
|
+
/** The `CadrePeer.StampId` this redemption seated (or found seated). */
|
|
1874
|
+
peerStampId: string;
|
|
1875
|
+
/** The `OwnerKey.StampId` this redemption seated, when the invitation grants ownership; else null. */
|
|
1876
|
+
ownerStampId: string | null;
|
|
1877
|
+
/** The invitation private key's signature over the `'redeem'` digest, base64url. */
|
|
1878
|
+
inviteSig: string;
|
|
1879
|
+
/** The device's signature over the `'consent'` digest, base64url. */
|
|
1880
|
+
peerSig: string;
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* The usage and invitation rows an invitation-admitted row is verified through, keyed by
|
|
1884
|
+
* `UsageStampId` and `CadreInvite.Key`: a `CadrePeer` row through `verifyInvitationAdmission`
|
|
1885
|
+
* (`CadreNode.listAuthorizedMembers`) and an `OwnerKey` row through
|
|
1886
|
+
* `verifyInvitationOwnerAdmission` (`deriveOwnerAnchor`). Loaded once per membership refresh
|
|
1887
|
+
* (`CadreNode.loadInvitationChain`) and shared by both readers.
|
|
1888
|
+
*/
|
|
1889
|
+
export interface InvitationChain {
|
|
1890
|
+
usages: Map<string, CadreInviteUsageRow>;
|
|
1891
|
+
invites: Map<string, CadreInviteRow>;
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1894
|
+
* One machine of the cadre as `CadreNode.listMachines` reports it: a live `CadrePeer` row,
|
|
1895
|
+
* or a live `OwnerKey` whose key derives a peer id with no `CadrePeer` row (an owner key
|
|
1896
|
+
* seated for a machine not yet admitted, or a founder that never self-published).
|
|
1897
|
+
*
|
|
1898
|
+
* `profile` and `lastPublishedAt` are the machine's own claim only when the row's
|
|
1899
|
+
* self-signature verifies; both are null otherwise, so an owner re-vouch that bumped
|
|
1900
|
+
* `UpdatedAt` without re-signing, or a row the machine never published, reports nothing
|
|
1901
|
+
* rather than someone else's stamp.
|
|
1902
|
+
*/
|
|
1903
|
+
export interface CadreMachine {
|
|
1904
|
+
peerId: string;
|
|
1905
|
+
/** ed25519 public key (base64url) behind `peerId`; null when the id is not ed25519. */
|
|
1906
|
+
publicKey: string | null;
|
|
1907
|
+
/** This node itself. */
|
|
1908
|
+
isSelf: boolean;
|
|
1909
|
+
/** `publicKey` is a live `OwnerKey` here. */
|
|
1910
|
+
isOwner: boolean;
|
|
1911
|
+
/** Has a live `CadrePeer` row; false for an owner key with no machine row. */
|
|
1912
|
+
isMember: boolean;
|
|
1913
|
+
/** Passes this node's authorized-membership predicate now (`CadreNode.listAuthorizedMembers`; always false for self, which that set excludes). */
|
|
1914
|
+
trusted: boolean;
|
|
1915
|
+
/** The machine's self-published node profile; null until it publishes, or when its signature does not verify. */
|
|
1916
|
+
profile: NodeProfile | null;
|
|
1917
|
+
/** `UpdatedAt` of its self-signed record, re-stamped while it runs; null on the same terms as `profile`. */
|
|
1918
|
+
lastPublishedAt: number | null;
|
|
1919
|
+
/** `CadrePeer.VouchOwner` (or `OwnerKey.VouchOwner` for an owner-only entry): the owner that vouched it, or the issuer of the invitation that admitted it. */
|
|
1920
|
+
vouchedBy: string | null;
|
|
1921
|
+
}
|
|
1922
|
+
/** Why `CadreNode.removeMachine` refuses by name (`MachineRemovalRefusedError.code`). */
|
|
1923
|
+
export type MachineRemovalRefusal =
|
|
1924
|
+
/** This node holds no enrolled owner key, so it cannot sign the removal. */
|
|
1925
|
+
'not-an-owner'
|
|
1926
|
+
/** The machine is this node: an owner cannot sign its own `OwnerKey` removal, so ask another owner. */
|
|
1927
|
+
| 'self'
|
|
1928
|
+
/** The machine holds the only live owner key here (`MinOneOwner`). */
|
|
1929
|
+
| 'last-owner';
|
|
1930
|
+
/**
|
|
1931
|
+
* What `CadreNode.removeMachine` would do for one machine, read without writing
|
|
1932
|
+
* (`CadreNode.planMachineRemoval`), for an app's confirmation step.
|
|
1933
|
+
*/
|
|
1934
|
+
export interface MachineRemovalPlan {
|
|
1935
|
+
/** The machine as listed, or null when no live row names it here (already removed). */
|
|
1936
|
+
machine: CadreMachine | null;
|
|
1937
|
+
refusal: MachineRemovalRefusal | null;
|
|
1938
|
+
/** The machine's key is a live owner key, so the removal also retires it. */
|
|
1939
|
+
removesOwnerKey: boolean;
|
|
1940
|
+
/** Rows the machine's owner key vouched, which stop passing the membership predicate everywhere once it is removed unless re-vouched first. Empty unless `removesOwnerKey`. */
|
|
1941
|
+
dependents: {
|
|
1942
|
+
/** Live `OwnerKey` rows whose `VouchOwner` is the machine's key, this node's own excluded. */
|
|
1943
|
+
owners: string[];
|
|
1944
|
+
/** Live `CadrePeer` rows whose `VouchOwner` is the machine's key, the machine itself and this node excluded. */
|
|
1945
|
+
peers: string[];
|
|
1946
|
+
};
|
|
1947
|
+
/** Live `CadreInvite` keys the machine could redeem to come back: targeted at it, or already redeemed by it with uses left. */
|
|
1948
|
+
invitations: string[];
|
|
1949
|
+
/** This node's own `OwnerKey` or `CadrePeer` row was vouched by the machine. It cannot re-vouch its own rows; another owner has to re-add it afterwards. */
|
|
1950
|
+
ownDependency: boolean;
|
|
1951
|
+
}
|
|
1952
|
+
/** Options for `CadreNode.removeMachine`. */
|
|
1953
|
+
export interface RemoveMachineOptions {
|
|
1954
|
+
/** Dependents to re-vouch under this node's key before the removal; each must appear in the plan's `dependents`. */
|
|
1955
|
+
revouch?: {
|
|
1956
|
+
owners?: readonly string[];
|
|
1957
|
+
peers?: readonly string[];
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
/** What `CadreNode.removeMachine` wrote. All false and empty when no live row named the machine here. */
|
|
1961
|
+
export interface MachineRemovalResult {
|
|
1962
|
+
removedPeer: boolean;
|
|
1963
|
+
removedOwnerKey: boolean;
|
|
1964
|
+
removedDeviceToken: boolean;
|
|
1965
|
+
/** `CadreInvite` keys whose withdrawal tombstone this call filed. */
|
|
1966
|
+
withdrawnInvitations: string[];
|
|
1967
|
+
/** Dependents re-vouched under this node's key before the removal. */
|
|
1968
|
+
revouched: {
|
|
1969
|
+
owners: string[];
|
|
1970
|
+
peers: string[];
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* This node's own removal from its cadre, as it learned it: the `CadrePeer` tombstone naming
|
|
1975
|
+
* its peer id, read after it had seen its own live row (`CadreNode.removedFromCadre`, the
|
|
1976
|
+
* `cadre:removed` event; "Removing a machine" in docs/architecture.md).
|
|
1977
|
+
*/
|
|
1978
|
+
export interface CadreRemoval {
|
|
1979
|
+
/** The cadre this node was removed from (`CadreNode.partyId`). */
|
|
1980
|
+
partyId: string;
|
|
1981
|
+
/** The `StampId` the tombstone retired. */
|
|
1982
|
+
stampId: string;
|
|
1983
|
+
/** The owner key that filed the tombstone, when its signature verifies and the key is in this node's trusted-owner anchor; null otherwise. */
|
|
1984
|
+
signerKey: string | null;
|
|
1985
|
+
/** When this node learned of the removal (epoch ms). */
|
|
1986
|
+
at: number;
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* One {@link CadreInviteRow} with its standing, as `ControlDatabase.listCadreInviteStatuses`
|
|
1990
|
+
* (and `CadreNode.listCadreInvitations`) reports it. `live` is the redemption's own test:
|
|
1991
|
+
* not withdrawn, unexpired, uses left, issuer still an owner here.
|
|
1992
|
+
*/
|
|
1993
|
+
export interface CadreInviteStatus {
|
|
1994
|
+
invite: CadreInviteRow;
|
|
1995
|
+
live: boolean;
|
|
1996
|
+
withdrawn: boolean;
|
|
1997
|
+
/** Redemptions this node holds for it (`CadreInviteUsage` rows). */
|
|
1998
|
+
usesRecorded: number;
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* What `ControlDatabase.redeemCadreInvite` did. `alreadyMember` is the idempotent
|
|
2002
|
+
* retry after a dropped reply: the device's row (and, for an owner-granting
|
|
2003
|
+
* invitation, its `OwnerKey` row) was already present, so nothing was written and
|
|
2004
|
+
* no seat was spent. The stamps are the live rows' either way.
|
|
2005
|
+
*/
|
|
2006
|
+
export interface CadreInviteRedemptionResult {
|
|
2007
|
+
alreadyMember: boolean;
|
|
2008
|
+
peerStampId: string;
|
|
2009
|
+
ownerStampId: string | null;
|
|
1680
2010
|
}
|
|
1681
|
-
/** The voucher-bearing subset of a {@link CadrePeerRow} — what the authorized-membership predicate reads. */
|
|
1682
|
-
export type CadrePeerVoucherFields = Pick<CadrePeerRow, 'peerId' | 'stampId' | 'vouchOwner' | 'vouchSig'>;
|
|
1683
2011
|
/**
|
|
1684
2012
|
* One `CadreControl.Revocation` tombstone as read by
|
|
1685
2013
|
* `ControlDatabase.queryRevocations`: the identity triple plus the `ReissuedAt`
|
|
@@ -1697,6 +2025,10 @@ export interface RevocationRow {
|
|
|
1697
2025
|
stampId: string;
|
|
1698
2026
|
/** Monotonic re-issue counter; carries no semantics (see the schema comment). */
|
|
1699
2027
|
reissuedAt: number;
|
|
2028
|
+
/** The owner key that filed the tombstone (`Revocation.SignerKey`). */
|
|
2029
|
+
signerKey: string;
|
|
2030
|
+
/** That owner's signature over the tombstone's `'remove'` digest (`Revocation.SignerSig`). */
|
|
2031
|
+
signerSig: string;
|
|
1700
2032
|
}
|
|
1701
2033
|
/**
|
|
1702
2034
|
* Outcome of `ControlDatabase.openRevocationLedger`: `'opened'` when this call filed the
|
|
@@ -1907,6 +2239,47 @@ export interface SeedMessage {
|
|
|
1907
2239
|
signature: string;
|
|
1908
2240
|
/** The owner key that signed this message */
|
|
1909
2241
|
signerKey: string;
|
|
2242
|
+
/**
|
|
2243
|
+
* Proof that the sender holds the receiving node's one-time claim secret
|
|
2244
|
+
* (`claim-proof.ts`), sent only when claiming a brand-new node. Rides beside
|
|
2245
|
+
* the seed rather than inside `ControlNetworkSeed`: it is not part of what the
|
|
2246
|
+
* owner signs, and a claimed node never needs it again.
|
|
2247
|
+
*
|
|
2248
|
+
* The protocol id stays `/sereus/seed/1.0.0` and no version field is added:
|
|
2249
|
+
* there is no backwards compatibility yet, a receiver without this feature
|
|
2250
|
+
* ignores the field and refuses by its anchored policy, and a sender without it
|
|
2251
|
+
* gets a definite refusal (`claim-proof-invalid`) from an unclaimed node.
|
|
2252
|
+
*/
|
|
2253
|
+
claimProof?: string;
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Why a seed was refused, for callers that must branch on the cause without
|
|
2257
|
+
* parsing the human-readable `reason`. The phone claiming a node needs to tell
|
|
2258
|
+
* "someone else already claimed it" from "wrong secret".
|
|
2259
|
+
*
|
|
2260
|
+
* - `already-claimed`: the node's anchor already holds an owner key other than
|
|
2261
|
+
* the signer's; the claim secret is no longer consulted.
|
|
2262
|
+
* - `claim-proof-invalid`: the node is unclaimed and the seed carried no claim
|
|
2263
|
+
* proof, or a proof that does not verify.
|
|
2264
|
+
* - `claim-rate-limited`: too many failed proofs recently; the proof was not
|
|
2265
|
+
* verified. Retry after the window.
|
|
2266
|
+
* - `claim-not-persisted`: the proof verified but the node could not durably
|
|
2267
|
+
* record the claim; nothing was anchored, so the same claim may be retried.
|
|
2268
|
+
*/
|
|
2269
|
+
export type SeedRefusalCode = 'already-claimed' | 'claim-proof-invalid' | 'claim-rate-limited' | 'claim-not-persisted';
|
|
2270
|
+
/**
|
|
2271
|
+
* What a claim settles for the node that accepted it: the party it serves from now on
|
|
2272
|
+
* and the owner it belongs to. Both come from the claim seed — the party is the seed's
|
|
2273
|
+
* `partyId`, the owner the key that signed it — because an unclaimed node's own
|
|
2274
|
+
* configured party is a placeholder; nobody could know the real one before the claim.
|
|
2275
|
+
* Handed to `CadreNodeConfig.claim.record` before the owner is anchored, and carried by
|
|
2276
|
+
* `claim:accepted`.
|
|
2277
|
+
*/
|
|
2278
|
+
export interface NodeClaimRecord {
|
|
2279
|
+
/** The party the claim seed named: the cadre this node now serves. */
|
|
2280
|
+
partyId: string;
|
|
2281
|
+
/** The claimant's ed25519 owner key (base64url): the key the claim anchors. */
|
|
2282
|
+
ownerKey: string;
|
|
1910
2283
|
}
|
|
1911
2284
|
/**
|
|
1912
2285
|
* Acknowledgment message from new node to instigator.
|
|
@@ -1916,6 +2289,8 @@ export interface SeedAckMessage {
|
|
|
1916
2289
|
accepted: boolean;
|
|
1917
2290
|
/** Reason for rejection (if not accepted) */
|
|
1918
2291
|
reason?: string;
|
|
2292
|
+
/** Machine-readable refusal cause, when the refusing policy supplies one. */
|
|
2293
|
+
code?: SeedRefusalCode;
|
|
1919
2294
|
}
|
|
1920
2295
|
/**
|
|
1921
2296
|
* Request sent over the control network's `WAKE_PROTOCOL` from a same-cadre peer
|
|
@@ -1968,12 +2343,14 @@ export interface StrandAddrRequest {
|
|
|
1968
2343
|
*
|
|
1969
2344
|
* - `ok` — looked up; `multiaddrs` is the truth, possibly empty (strand not running here).
|
|
1970
2345
|
* - `unavailable` — the responder could not answer: over its concurrency cap, the
|
|
1971
|
-
* request was unreadable, or its own lookup threw
|
|
1972
|
-
*
|
|
1973
|
-
*
|
|
1974
|
-
*
|
|
2346
|
+
* request was unreadable, or its own lookup threw.
|
|
2347
|
+
*
|
|
2348
|
+
* A requester the responder does not count as an authorized member (possibly because
|
|
2349
|
+
* its `CadrePeer` row has not replicated there yet) gets no response at all: the
|
|
2350
|
+
* responder's protocol guard resets the stream, and any `delegatePeerId` it carried
|
|
2351
|
+
* is not recorded.
|
|
1975
2352
|
*/
|
|
1976
|
-
export type StrandAddrStatus = 'ok' | 'unavailable'
|
|
2353
|
+
export type StrandAddrStatus = 'ok' | 'unavailable';
|
|
1977
2354
|
/**
|
|
1978
2355
|
* Response to a {@link StrandAddrRequest}, carrying the responder's strand-node
|
|
1979
2356
|
* multiaddrs, returned on the same stream.
|
|
@@ -1998,6 +2375,17 @@ export interface AuthorizePeerOptions {
|
|
|
1998
2375
|
/** Optional multiaddrs for the peer */
|
|
1999
2376
|
multiaddrs?: string[];
|
|
2000
2377
|
}
|
|
2378
|
+
/**
|
|
2379
|
+
* Where `deliverSeed` sends a seed: one multiaddr string, dialed as given, or a
|
|
2380
|
+
* peer id with its known addresses. The object form merges the addresses into
|
|
2381
|
+
* the peer store and dials by peer id, so an existing connection is reused and
|
|
2382
|
+
* libp2p tries every address under the one delivery deadline. It is what a phone
|
|
2383
|
+
* uses to claim a node from the addresses it scanned.
|
|
2384
|
+
*/
|
|
2385
|
+
export type SeedDeliveryTarget = string | {
|
|
2386
|
+
peerId: string;
|
|
2387
|
+
multiaddrs: string[];
|
|
2388
|
+
};
|
|
2001
2389
|
/**
|
|
2002
2390
|
* Result of applying a seed to a node.
|
|
2003
2391
|
*/
|
|
@@ -2008,6 +2396,8 @@ export interface ApplySeedResult {
|
|
|
2008
2396
|
peersAdded: number;
|
|
2009
2397
|
/** Error message if not successful */
|
|
2010
2398
|
error?: string;
|
|
2399
|
+
/** Machine-readable refusal cause, when the refusing trust policy supplied one. */
|
|
2400
|
+
code?: SeedRefusalCode;
|
|
2011
2401
|
/**
|
|
2012
2402
|
* Owner-flagged seed peers this apply attempted to dial. Zero when the seed
|
|
2013
2403
|
* carried no owner peer with an address, or when the seed was rejected before
|
|
@@ -2023,7 +2413,8 @@ export interface ApplySeedResult {
|
|
|
2023
2413
|
* `CadreNode.dialColdStartBootstrap`), so this is a signal, not a fatal error.
|
|
2024
2414
|
*
|
|
2025
2415
|
* Zero failures is NOT proof of a live connection: the receiving node's
|
|
2026
|
-
*
|
|
2416
|
+
* connection gate decides AFTER the dialer's upgrade completes, and closes a
|
|
2417
|
+
* provisionally admitted connection at its deadline (see
|
|
2027
2418
|
* `createMembershipConnectionGater`), so a dial can resolve and then be torn
|
|
2028
2419
|
* down. Treat a non-zero value as certain failure and zero as "no failure
|
|
2029
2420
|
* observed".
|
|
@@ -2035,30 +2426,80 @@ export interface ApplySeedResult {
|
|
|
2035
2426
|
*/
|
|
2036
2427
|
export type NodeTopology = 'public' | 'nat';
|
|
2037
2428
|
/**
|
|
2038
|
-
*
|
|
2039
|
-
*
|
|
2429
|
+
* What an owner asks `CadreNode.createCadreInvitation` for. The invitation is a keypair the
|
|
2430
|
+
* node mints; whoever holds the returned bundle can redeem it at any member machine.
|
|
2040
2431
|
*/
|
|
2041
|
-
export interface
|
|
2042
|
-
/**
|
|
2043
|
-
|
|
2044
|
-
/**
|
|
2045
|
-
|
|
2046
|
-
/**
|
|
2047
|
-
*
|
|
2048
|
-
*
|
|
2049
|
-
*
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2432
|
+
export interface CreateCadreInvitationOptions {
|
|
2433
|
+
/** The one device that may redeem it; omit for whoever redeems first. */
|
|
2434
|
+
peerId?: string;
|
|
2435
|
+
/** Whether redemption also makes the device an owner (seats an `OwnerKey` row). */
|
|
2436
|
+
grantsOwner: boolean;
|
|
2437
|
+
/**
|
|
2438
|
+
* Lifetime from now, in ms. Defaults to 15 minutes for an untargeted owner-granting
|
|
2439
|
+
* invitation — a bearer credential for admin rights — and 24 hours otherwise. A negative
|
|
2440
|
+
* value mints an already-expired invitation (tests).
|
|
2441
|
+
*/
|
|
2442
|
+
expiresInMs?: number;
|
|
2443
|
+
/** How many redemptions it allows; default 1. */
|
|
2444
|
+
uses?: number;
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Where a phone could redeem a cadre invitation from, judged on the addresses of machines
|
|
2448
|
+
* other than the one that minted it — the answer to "can the new app join once this one is
|
|
2449
|
+
* closed?". A minting machine that is itself always on can therefore read `issuer-only`.
|
|
2450
|
+
*/
|
|
2451
|
+
export type CadreInvitationReach =
|
|
2452
|
+
/** Another machine has a relayed (`/p2p-circuit`), public-name or public-IP WebSocket address. */
|
|
2453
|
+
'anywhere'
|
|
2454
|
+
/** Other machines are named, but only at private or loopback addresses: works on their network only. */
|
|
2455
|
+
| 'local-network'
|
|
2456
|
+
/** The bundle names no other machine at an address a phone can dial. */
|
|
2457
|
+
| 'issuer-only';
|
|
2458
|
+
/** What `CadreNode.createCadreInvitation` returns: the bundle, and the same encoded for sharing. */
|
|
2459
|
+
export interface CreateCadreInvitationResult {
|
|
2460
|
+
invitation: CadreInvitation;
|
|
2461
|
+
/** `encodeCadreInvitation(invitation)`: `sereus-invite:1.` and base64url JSON, for a QR code, a paste or a link. */
|
|
2462
|
+
encoded: string;
|
|
2463
|
+
/** Whether a device could redeem it once the minting machine is gone; see {@link CadreInvitationReach}. */
|
|
2464
|
+
reach: CadreInvitationReach;
|
|
2465
|
+
}
|
|
2466
|
+
/** How `CadreNode.redeemCadreInvitation` runs. */
|
|
2467
|
+
export interface RedeemCadreInvitationOptions {
|
|
2468
|
+
/**
|
|
2469
|
+
* Keep making passes over the bundle's addresses while every answer is retryable, until a
|
|
2470
|
+
* member accepts, one refuses finally, the invitation expires, or `signal` aborts.
|
|
2471
|
+
* Default false: one pass.
|
|
2472
|
+
*/
|
|
2473
|
+
waitForMember?: boolean;
|
|
2474
|
+
/** Ends the pause and the pass in flight; the call then throws the signal's reason. */
|
|
2475
|
+
signal?: AbortSignal;
|
|
2476
|
+
/** Called at each pass and each pause; an exception it throws is logged and ignored. */
|
|
2477
|
+
onProgress?: (progress: CadreJoinProgress) => void;
|
|
2478
|
+
}
|
|
2479
|
+
/** A redemption's progress, as `RedeemCadreInvitationOptions.onProgress` reports it. */
|
|
2480
|
+
export type CadreJoinProgress = {
|
|
2481
|
+
state: 'redeeming';
|
|
2482
|
+
pass: number;
|
|
2483
|
+
} | {
|
|
2484
|
+
state: 'waiting';
|
|
2485
|
+
pass: number;
|
|
2486
|
+
/** Epoch ms of the next pass. */
|
|
2487
|
+
retryAtMs: number;
|
|
2488
|
+
/**
|
|
2489
|
+
* `'no-member-answered'`: every address failed to dial or answer. `'member-not-ready'`: at
|
|
2490
|
+
* least one member answered with a retryable refusal (issuer-unknown, busy, conflict, internal).
|
|
2491
|
+
*/
|
|
2492
|
+
reason: 'no-member-answered' | 'member-not-ready';
|
|
2493
|
+
outcomes: readonly CadreInviteAddressOutcome[];
|
|
2494
|
+
};
|
|
2495
|
+
/** What `CadreNode.redeemCadreInvitation` returns once a member accepted. */
|
|
2496
|
+
export interface RedeemCadreInvitationResult {
|
|
2497
|
+
/** The member machine that admitted this device, when its address named one. */
|
|
2498
|
+
peerId: string | null;
|
|
2499
|
+
/** Whether the invitation also seated this device's key as an owner. */
|
|
2500
|
+
grantsOwner: boolean;
|
|
2501
|
+
/** When the member accepted, ISO 8601. */
|
|
2502
|
+
redeemedAt: string;
|
|
2062
2503
|
}
|
|
2063
2504
|
/**
|
|
2064
2505
|
* Options for adding a drone via provider API.
|
|
@@ -2069,15 +2510,6 @@ export interface AddDroneOptions {
|
|
|
2069
2510
|
/** Multiaddrs of the drone (returned from provider) */
|
|
2070
2511
|
droneMultiaddrs: string[];
|
|
2071
2512
|
}
|
|
2072
|
-
/**
|
|
2073
|
-
* Options for adding a phone via invite flow.
|
|
2074
|
-
*/
|
|
2075
|
-
export interface AddPhoneOptions {
|
|
2076
|
-
/** Peer ID of the phone (sent by phone when it connects) */
|
|
2077
|
-
phonePeerId: string;
|
|
2078
|
-
/** Invite token for validation (must match issued invite) */
|
|
2079
|
-
token?: string;
|
|
2080
|
-
}
|
|
2081
2513
|
/**
|
|
2082
2514
|
* Result of preparing a seed for drone initialization.
|
|
2083
2515
|
*/
|
|
@@ -2087,12 +2519,3 @@ export interface DroneInitResult {
|
|
|
2087
2519
|
/** Base64url encoded seed for transport */
|
|
2088
2520
|
encodedSeed: string;
|
|
2089
2521
|
}
|
|
2090
|
-
/**
|
|
2091
|
-
* Result of creating an invite for a phone.
|
|
2092
|
-
*/
|
|
2093
|
-
export interface InviteResult {
|
|
2094
|
-
/** The invite to share out-of-band */
|
|
2095
|
-
invite: CadreInvite;
|
|
2096
|
-
/** Base64url encoded invite for QR/link */
|
|
2097
|
-
encodedInvite: string;
|
|
2098
|
-
}
|