@serfab/cadre-core 0.13.0 → 1.1.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 +20 -6
- package/dist/bootstrap-peer-store-file.js +2 -2
- package/dist/bootstrap-peer-store.d.ts +32 -12
- package/dist/bootstrap-peer-store.js +41 -15
- package/dist/bootstrap-peer-store.js.map +1 -1
- package/dist/cadre-node.d.ts +558 -80
- package/dist/cadre-node.js +1060 -170
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +2 -2
- package/dist/control-authorization.js +1 -0
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +13 -26
- package/dist/control-cohort.js +0 -26
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +299 -44
- package/dist/control-database.js +469 -69
- package/dist/control-database.js.map +1 -1
- package/dist/control-read-retry.d.ts +40 -11
- package/dist/control-read-retry.js +48 -15
- package/dist/control-read-retry.js.map +1 -1
- package/dist/control-retry.d.ts +58 -3
- package/dist/control-retry.js +58 -3
- package/dist/control-retry.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +103 -19
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +80 -11
- package/dist/control-write-retry.js +176 -29
- package/dist/control-write-retry.js.map +1 -1
- package/dist/enrolled-machine-store.js +2 -2
- package/dist/index.d.ts +14 -7
- package/dist/index.js +33 -6
- package/dist/index.js.map +1 -1
- package/dist/node-local-snapshot.d.ts +15 -7
- package/dist/node-local-snapshot.js +23 -8
- package/dist/node-local-snapshot.js.map +1 -1
- package/dist/peer-dial.d.ts +143 -0
- package/dist/peer-dial.js +176 -0
- package/dist/peer-dial.js.map +1 -0
- package/dist/relay-addrs.d.ts +90 -52
- package/dist/relay-addrs.js +91 -59
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +71 -3
- package/dist/relay-reservation.js +116 -8
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +30 -1
- package/dist/seed-bootstrap.js +62 -17
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/storage-scope.d.ts +62 -0
- package/dist/storage-scope.js +75 -0
- package/dist/storage-scope.js.map +1 -0
- package/dist/strand-database.d.ts +34 -16
- package/dist/strand-database.js +38 -24
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +152 -0
- package/dist/strand-first-sync-gate.js +208 -0
- package/dist/strand-first-sync-gate.js.map +1 -0
- package/dist/strand-formation-manager.d.ts +87 -5
- package/dist/strand-formation-manager.js +109 -5
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +21 -1
- package/dist/strand-formation-protocol.js +30 -0
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +243 -6
- package/dist/strand-instance-manager.js +470 -37
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-member-key.d.ts +20 -12
- package/dist/strand-member-key.js +20 -12
- package/dist/strand-member-key.js.map +1 -1
- package/dist/strand-member-registry.js +3 -0
- package/dist/strand-member-registry.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +361 -0
- package/dist/strand-membership-reconciler.js +548 -0
- package/dist/strand-membership-reconciler.js.map +1 -0
- package/dist/strand-membership-writer.d.ts +131 -11
- package/dist/strand-membership-writer.js +396 -134
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-network-config.d.ts +46 -14
- package/dist/strand-network-config.js +79 -23
- package/dist/strand-network-config.js.map +1 -1
- package/dist/strand-solicitation.d.ts +10 -1
- package/dist/strand-solicitation.js +5 -0
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +11 -7
- package/dist/strand-wake-protocol.js +20 -52
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/strand-watcher.js +6 -0
- package/dist/strand-watcher.js.map +1 -1
- package/dist/timed-step.d.ts +12 -0
- package/dist/timed-step.js +28 -0
- package/dist/timed-step.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +19 -0
- package/dist/timeout-scheduler.js +22 -0
- package/dist/timeout-scheduler.js.map +1 -0
- package/dist/types.d.ts +262 -30
- package/dist/types.js.map +1 -1
- package/package.json +8 -8
- package/src/bootstrap-peer-store-file.ts +2 -2
- package/src/bootstrap-peer-store.ts +69 -27
- package/src/cadre-node.ts +7301 -6353
- package/src/control-authorization.ts +2 -1
- package/src/control-cohort.ts +122 -130
- package/src/control-database.ts +537 -69
- package/src/control-read-retry.ts +202 -167
- package/src/control-retry.ts +297 -190
- package/src/control-schema.ts +103 -19
- package/src/control-write-retry.ts +533 -383
- package/src/enrolled-machine-store.ts +2 -2
- package/src/index.ts +83 -6
- package/src/node-local-snapshot.ts +25 -8
- package/src/peer-dial.ts +215 -0
- package/src/relay-addrs.ts +458 -436
- package/src/relay-reservation.ts +951 -828
- package/src/seed-bootstrap.ts +76 -18
- package/src/storage-scope.ts +78 -0
- package/src/strand-database.ts +50 -27
- package/src/strand-first-sync-gate.ts +258 -0
- package/src/strand-formation-manager.ts +140 -5
- package/src/strand-formation-protocol.ts +43 -1
- package/src/strand-instance-manager.ts +577 -37
- package/src/strand-member-key.ts +20 -12
- package/src/strand-member-registry.ts +3 -0
- package/src/strand-membership-reconciler.ts +680 -0
- package/src/strand-membership-writer.ts +527 -189
- package/src/strand-network-config.ts +102 -24
- package/src/strand-solicitation.ts +16 -1
- package/src/strand-wake-protocol.ts +21 -66
- package/src/strand-watcher.ts +6 -0
- package/src/timed-step.ts +28 -0
- package/src/timeout-scheduler.ts +28 -0
- package/src/types.ts +1789 -1554
package/src/control-schema.ts
CHANGED
|
@@ -305,6 +305,66 @@ declare schema CadreControl {
|
|
|
305
305
|
)
|
|
306
306
|
) with context (OwnerKey text, Signature text);
|
|
307
307
|
|
|
308
|
+
-- ONE party's own membership identity for one strand: the private key whose public
|
|
309
|
+
-- half seats this party's \`Strand.Member\` / \`Strand.Manager\` rows. Deliberately a
|
|
310
|
+
-- SEPARATE key from Strand.MemberPrivateKey — that column is the strand-wide read
|
|
311
|
+
-- secret formation hands to EVERY joining party, so an identity derived from it is
|
|
312
|
+
-- one every joiner can forge (gotchoices/sereus#4). A side table rather than a
|
|
313
|
+
-- Strand column because a JOINER holds no control Strand row at all (reference apps
|
|
314
|
+
-- attach formed strands from an in-memory row) yet must persist its own party key
|
|
315
|
+
-- too; keyed by strand id it serves founder and joiner uniformly, and it leaves the
|
|
316
|
+
-- heavily-audited Strand insert digest untouched. Deliberately NO closed-strands-only
|
|
317
|
+
-- cross-table check: the joiner writes this row with no local Strand row to check
|
|
318
|
+
-- against. Party-private in the same sense as MemberPrivateKey — replicated in
|
|
319
|
+
-- plaintext to every machine the party owns (docs/strands.md → "Closed-Strand Member
|
|
320
|
+
-- Key Handling"; that replication is what lets any of the party's machines sign
|
|
321
|
+
-- membership writes) — but unlike MemberPrivateKey it is NEVER put on the formation
|
|
322
|
+
-- wire.
|
|
323
|
+
table StrandPartyKey (
|
|
324
|
+
Id text primary key, -- the strand id this key is for
|
|
325
|
+
PrivateKey text not null, -- THIS party's ed25519 strand member private key, base64
|
|
326
|
+
-- protobuf — the same encoding as Strand.MemberPrivateKey;
|
|
327
|
+
-- decode with cadre-core's strandMemberKeyPair.
|
|
328
|
+
StampId text not null unique, -- single-use authorization nonce (anti-replay).
|
|
329
|
+
-- \`unique\` holds over LIVE rows only; a removed row's stamp is
|
|
330
|
+
-- retired permanently into Revocation (NotRevoked below).
|
|
331
|
+
-- A removed row's StampId is retired into Revocation, and this refuses any insert
|
|
332
|
+
-- naming a retired stamp: the approval that seated this row can never re-seat it
|
|
333
|
+
-- after removal. Same rationale as OwnerKey.NotRevoked, stated in full there.
|
|
334
|
+
constraint NotRevoked check on insert (
|
|
335
|
+
not exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = new.StampId)
|
|
336
|
+
),
|
|
337
|
+
-- Retirement is MANDATORY: a delete must carry the matching Revocation row in the same
|
|
338
|
+
-- transaction, or the stamp would free up again. Deferred (subquery), so the sibling
|
|
339
|
+
-- insert is visible at commit regardless of statement order. The tombstone must also
|
|
340
|
+
-- name the removed ROW, not just its stamp — see Revocation.RowKey.
|
|
341
|
+
constraint RevocationRecorded check on delete (
|
|
342
|
+
exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = old.StampId and R.RowKey = old.Id)
|
|
343
|
+
),
|
|
344
|
+
-- Insert + delete only; a rotation is remove-then-insert (fresh stamp, fresh
|
|
345
|
+
-- signature). Required because AuthorizedInsert / AuthorizedDelete below cover
|
|
346
|
+
-- neither update — an in-place rewrite would swap the party's identity key with
|
|
347
|
+
-- no signature at all. Mirrors ValidationKey.NoUpdate.
|
|
348
|
+
constraint NoUpdate check on update (false),
|
|
349
|
+
constraint AuthorizedInsert check on insert (
|
|
350
|
+
-- Owners authorize by signing over THIS row (Id, PrivateKey, StampId);
|
|
351
|
+
-- single-use via unique StampId. Binding PrivateKey means a captured approval
|
|
352
|
+
-- can only ever reproduce the exact key it approved, never seat an
|
|
353
|
+
-- attacker-chosen identity under the party's name.
|
|
354
|
+
exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest('CadreControl.StrandPartyKey', 'add', new.Id, new.PrivateKey, new.StampId), context.Signature, A.Key, 'ed25519'))
|
|
355
|
+
),
|
|
356
|
+
constraint AuthorizedDelete check on delete (
|
|
357
|
+
-- Delete is authorized by a signature over the DISTINCT 'remove'-tagged digest
|
|
358
|
+
-- bound to the STORED row (Id, StampId), so the enrollment approval — which
|
|
359
|
+
-- never expires — can never be replayed as a removal.
|
|
360
|
+
-- DELIBERATELY no REAP branch (cf. ValidationKey.AuthorizedDelete): like
|
|
361
|
+
-- Strand.MemberPrivateKey, PrivateKey is a secret stored nowhere else, so this
|
|
362
|
+
-- delete's effect is unrecoverable and stays strictly owner-signed. Same
|
|
363
|
+
-- reasoning as Strand.AuthorizedDelete.
|
|
364
|
+
exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest('CadreControl.StrandPartyKey', 'remove', old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))
|
|
365
|
+
)
|
|
366
|
+
) with context (OwnerKey text, Signature text);
|
|
367
|
+
|
|
308
368
|
-- A peer (node) that is part of the cadre, carrying a self-published,
|
|
309
369
|
-- freshness-stamped, self-signed address record (see PeerAddressRecord in cadre-core).
|
|
310
370
|
-- The row IS the peer-address record: a resolver reads it, re-verifies Sig against
|
|
@@ -704,25 +764,26 @@ declare schema CadreControl {
|
|
|
704
764
|
|
|
705
765
|
-- Token is no longer a primary-key prefix (the key is the redemption nonce), so the
|
|
706
766
|
-- per-token filters — the cap count in Authorized above, and cadre-core's
|
|
707
|
-
-- countFormationUsage / isTokenUsed / hasOutstandingFormationInvite — are served by
|
|
708
|
-
--
|
|
709
|
-
--
|
|
767
|
+
-- countFormationUsage / isTokenUsed / hasOutstandingFormationInvite — are served by this
|
|
768
|
+
-- index rather than by a full scan of a table that is append-only and grows for the life
|
|
769
|
+
-- of the party.
|
|
710
770
|
--
|
|
711
|
-
--
|
|
712
|
-
--
|
|
713
|
-
--
|
|
714
|
-
--
|
|
715
|
-
--
|
|
716
|
-
--
|
|
717
|
-
--
|
|
718
|
-
-- sits at a different revision while their copies of the table itself agree, and the
|
|
719
|
-
-- refresh that runs immediately before the descent does not close the gap. Tracked in
|
|
720
|
-
-- \`tickets/blocked/secondary-index-seek-blind-to-sibling-rows.md\`, and upstream as
|
|
721
|
-
-- \`bug-index-subcollection-sits-one-revision-behind-on-the-sibling\`.
|
|
771
|
+
-- The cap count is only ever as correct as this index's convergence across machines. That
|
|
772
|
+
-- convergence failed once: from 2026-08-04 to 2026-08-25 a descent on a second machine
|
|
773
|
+
-- returned only the rows THAT machine wrote, so the index was removed and the reads went
|
|
774
|
+
-- back to the scan. The engine defect was fixed upstream and re-measured here on
|
|
775
|
+
-- 2026-09-17, so the index is declared again. Its guard is the integration-tests scenario
|
|
776
|
+
-- \`strand-formation-concurrent-redemption\`, which asserts BOTH machines' views of a raced
|
|
777
|
+
-- redemption and therefore goes red if that convergence regresses.
|
|
722
778
|
--
|
|
723
|
-
--
|
|
724
|
-
--
|
|
725
|
-
--
|
|
779
|
+
-- NOTE: the write side has its own exposure. Every FormationUsage insert now maintains this
|
|
780
|
+
-- sub-collection as well as the table, and an intermittent engine failure once hit exactly that
|
|
781
|
+
-- step on other tables (a table's rows committed while its index did not; closed 2026-09-17 with
|
|
782
|
+
-- the upstream sync fixes, \`tickets/complete/strand-unique-index-sync-stale-revision\`). Nothing
|
|
783
|
+
-- has been observed here, and redemptions are rare writes; but if joins start failing
|
|
784
|
+
-- intermittently with an index-sync error naming FormationUsage, this declaration is the first
|
|
785
|
+
-- thing to suspect.
|
|
786
|
+
index FormationUsageByToken on FormationUsage (Token);
|
|
726
787
|
|
|
727
788
|
-- Append-only retirement record for the one-off StampId nonces of removed OwnerKey /
|
|
728
789
|
-- CadrePeer / ValidationKey / Strand rows. Without it a removal was undoable: the add
|
|
@@ -744,10 +805,20 @@ declare schema CadreControl {
|
|
|
744
805
|
-- A tombstone committed while the node was alone is local-only (never broadcast); the
|
|
745
806
|
-- ReissuedAt counter below exists so an owner can re-write — and therefore re-broadcast —
|
|
746
807
|
-- such a tombstone once peers are reachable, without changing what it means.
|
|
808
|
+
-- One row retires nothing: the singleton ledger marker ('Revocation', 'ledger', 'opened'),
|
|
809
|
+
-- filed once by an owner's connected reconcile pass (ControlDatabase.openRevocationLedger).
|
|
810
|
+
-- It exists only so this table is never a never-written block. The storage layer consults
|
|
811
|
+
-- a block's cohort on EVERY read of a block it does not hold, and every membership lookup
|
|
812
|
+
-- and every guarded insert (NotRevoked) reads this table; once any row exists the block is
|
|
813
|
+
-- held and re-checked on the storage layer's normal read-repair schedule, like every other
|
|
814
|
+
-- populated table. It cannot read as a retirement: every reader of this table filters on
|
|
815
|
+
-- its own guarded TableName, 'Revocation' is not one, and ControlDatabase.queryRevocations
|
|
816
|
+
-- skips the marker, so nothing reaps or re-issues it.
|
|
747
817
|
table Revocation (
|
|
748
|
-
TableName text, -- 'OwnerKey' | 'CadrePeer' | 'ValidationKey' | 'Strand' | '
|
|
818
|
+
TableName text, -- 'OwnerKey' | 'CadrePeer' | 'ValidationKey' | 'Strand' | 'StrandPartyKey' | 'DeviceToken',
|
|
819
|
+
-- or 'Revocation' for the ledger marker only (all confined by RowIsGone below)
|
|
749
820
|
RowKey text not null, -- primary key of the removed row: OwnerKey.Key / ValidationKey.Key /
|
|
750
|
-
-- CadrePeer.PeerId / DeviceToken.PeerId / Strand.Id
|
|
821
|
+
-- CadrePeer.PeerId / DeviceToken.PeerId / Strand.Id / StrandPartyKey.Id
|
|
751
822
|
-- Every guarded table's RevocationRecorded CHECK requires the
|
|
752
823
|
-- accompanying tombstone to carry the removed row's key here, so
|
|
753
824
|
-- a REMOVAL cannot file a misnamed tombstone. A tombstone with no
|
|
@@ -787,12 +858,19 @@ declare schema CadreControl {
|
|
|
787
858
|
-- it, stamp1) then re-admitted (stamp2) would leave a node that converges on the
|
|
788
859
|
-- re-add FIRST rejecting the stamp1 tombstone forever, never learning that stamp is
|
|
789
860
|
-- retired. The stamp-only form passes there (stamp1 is not live).
|
|
861
|
+
-- The last branch admits the ledger marker (see the table comment) and nothing else under
|
|
862
|
+
-- TableName 'Revocation': the whole triple is pinned, so with the primary key
|
|
863
|
+
-- (TableName, StampId) it is a singleton and the append-only growth surface is unchanged.
|
|
864
|
+
-- 'opened' cannot collide with a real stamp (43 base64url characters, generateStampId),
|
|
865
|
+
-- and the primary key includes TableName anyway.
|
|
790
866
|
constraint RowIsGone check on insert (
|
|
791
867
|
(new.TableName = 'OwnerKey' and not exists (select 1 from OwnerKey K where K.StampId = new.StampId))
|
|
792
868
|
or (new.TableName = 'CadrePeer' and not exists (select 1 from CadrePeer P where P.StampId = new.StampId))
|
|
793
869
|
or (new.TableName = 'ValidationKey' and not exists (select 1 from ValidationKey V where V.StampId = new.StampId))
|
|
794
870
|
or (new.TableName = 'Strand' and not exists (select 1 from Strand S where S.StampId = new.StampId))
|
|
871
|
+
or (new.TableName = 'StrandPartyKey' and not exists (select 1 from StrandPartyKey K where K.StampId = new.StampId))
|
|
795
872
|
or (new.TableName = 'DeviceToken' and not exists (select 1 from DeviceToken D where D.StampId = new.StampId))
|
|
873
|
+
or (new.TableName = 'Revocation' and new.RowKey = 'ledger' and new.StampId = 'opened')
|
|
796
874
|
),
|
|
797
875
|
-- Appending a tombstone is an OWNER action, like every other write in this schema.
|
|
798
876
|
-- Ungated, any writer that could reach the control database could retire a stamp:
|
|
@@ -826,6 +904,12 @@ declare schema CadreControl {
|
|
|
826
904
|
-- than that one.
|
|
827
905
|
-- RowIsGone and NoDelete are unchanged: they guard an owner retiring a stamp early
|
|
828
906
|
-- or withdrawing one later, which authorization does not cover.
|
|
907
|
+
-- The ledger marker is signed under this same rule, over
|
|
908
|
+
-- digest('CadreControl.Revocation', 'remove', 'Revocation', 'ledger', 'opened'). The
|
|
909
|
+
-- 'remove' tag on a row that retires nothing is deliberate: reusing the one append rule
|
|
910
|
+
-- keeps every append owner-signed without adding an unsigned or differently tagged
|
|
911
|
+
-- branch, and a captured marker signature can only re-file the identical row, whose
|
|
912
|
+
-- primary key is already taken.
|
|
829
913
|
constraint Authorized check on insert (
|
|
830
914
|
exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest('CadreControl.Revocation', 'remove', new.TableName, new.RowKey, new.StampId), context.Signature, A.Key, 'ed25519'))
|
|
831
915
|
),
|