@fairfox/polly 0.69.0 → 0.71.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/dist/src/client/index.js +17 -20
- package/dist/src/client/index.js.map +4 -4
- package/dist/src/mesh.js +462 -154
- package/dist/src/mesh.js.map +9 -7
- package/dist/src/peer.js +153 -9
- package/dist/src/peer.js.map +5 -4
- package/dist/src/polly-ui/markdown.js +3 -3
- package/dist/src/polly-ui/markdown.js.map +2 -2
- package/dist/src/shared/lib/mesh-client.d.ts +29 -0
- package/dist/src/shared/lib/mesh-diagnostics.d.ts +129 -0
- package/dist/src/shared/lib/mesh-network-adapter.d.ts +90 -3
- package/dist/src/shared/lib/revocation-summary.d.ts +54 -0
- package/dist/tools/test/src/e2e-mesh/console-allowlist.d.ts +31 -0
- package/dist/tools/test/src/e2e-mesh/index.d.ts +27 -0
- package/dist/tools/test/src/e2e-mesh/index.js +1183 -0
- package/dist/tools/test/src/e2e-mesh/index.js.map +22 -0
- package/dist/tools/test/src/e2e-mesh/keys.d.ts +55 -0
- package/dist/tools/test/src/e2e-mesh/launch-peer.d.ts +98 -0
- package/dist/tools/test/src/e2e-mesh/mesh-assertions.d.ts +53 -0
- package/dist/tools/test/src/e2e-mesh/serve-consumer.d.ts +32 -0
- package/dist/tools/test/src/e2e-mesh/wait-for-convergence.d.ts +38 -0
- package/dist/tools/test/src/e2e-mesh/with-relay.d.ts +53 -0
- package/dist/tools/test/src/visual/index.js +24 -24
- package/dist/tools/test/src/visual/index.js.map +2 -2
- package/dist/tools/verify/src/cli.js +361 -22
- package/dist/tools/verify/src/cli.js.map +6 -6
- package/dist/tools/verify/src/config.d.ts +26 -1
- package/dist/tools/verify/src/config.js +9 -1
- package/dist/tools/verify/src/config.js.map +4 -4
- package/dist/tools/verify/src/primitives/index.d.ts +30 -0
- package/dist/tools/visualize/src/cli.js +43 -1
- package/dist/tools/visualize/src/cli.js.map +3 -3
- package/package.json +11 -8
- package/LICENSE +0 -21
- package/README.md +0 -362
package/dist/src/peer.js
CHANGED
|
@@ -650,6 +650,30 @@ import {
|
|
|
650
650
|
NetworkAdapter
|
|
651
651
|
} from "@automerge/automerge-repo/slim";
|
|
652
652
|
|
|
653
|
+
// src/shared/lib/mesh-diagnostics.ts
|
|
654
|
+
var listeners = new Set;
|
|
655
|
+
function emitMeshDiagnostic(diagnostic) {
|
|
656
|
+
const event = { ...diagnostic, timestamp: Date.now() };
|
|
657
|
+
for (const listener of listeners) {
|
|
658
|
+
try {
|
|
659
|
+
listener(event);
|
|
660
|
+
} catch {}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function subscribeToMeshDiagnostics(listener) {
|
|
664
|
+
listeners.add(listener);
|
|
665
|
+
return () => {
|
|
666
|
+
listeners.delete(listener);
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function recordMeshDiagnostics() {
|
|
670
|
+
const captured = [];
|
|
671
|
+
const stop = subscribeToMeshDiagnostics((event) => {
|
|
672
|
+
captured.push(event);
|
|
673
|
+
});
|
|
674
|
+
return { events: captured, stop };
|
|
675
|
+
}
|
|
676
|
+
|
|
653
677
|
// src/shared/lib/signing.ts
|
|
654
678
|
import nacl2 from "tweetnacl";
|
|
655
679
|
var PUBLIC_KEY_BYTES = 32;
|
|
@@ -735,11 +759,17 @@ function decodeSignedEnvelope(bytes) {
|
|
|
735
759
|
|
|
736
760
|
// src/shared/lib/mesh-network-adapter.ts
|
|
737
761
|
var DEFAULT_MESH_KEY_ID = "polly-mesh-default";
|
|
762
|
+
var MESH_CONTROL_TYPE = {
|
|
763
|
+
Sync: 0,
|
|
764
|
+
Revocation: 1,
|
|
765
|
+
RevocationSummary: 2
|
|
766
|
+
};
|
|
738
767
|
|
|
739
768
|
class MeshNetworkAdapter extends NetworkAdapter {
|
|
740
769
|
base;
|
|
741
770
|
keyringSource;
|
|
742
771
|
encryptionEnabled;
|
|
772
|
+
onControlMessage;
|
|
743
773
|
get keyring() {
|
|
744
774
|
return this.keyringSource();
|
|
745
775
|
}
|
|
@@ -748,6 +778,7 @@ class MeshNetworkAdapter extends NetworkAdapter {
|
|
|
748
778
|
this.base = options.base;
|
|
749
779
|
this.keyringSource = options.keyringSource;
|
|
750
780
|
this.encryptionEnabled = options.encryptionEnabled ?? true;
|
|
781
|
+
this.onControlMessage = options.onControlMessage;
|
|
751
782
|
this.base.on("close", () => this.emit("close"));
|
|
752
783
|
this.base.on("peer-candidate", (payload) => this.emit("peer-candidate", payload));
|
|
753
784
|
this.base.on("peer-disconnected", (payload) => this.emit("peer-disconnected", payload));
|
|
@@ -781,16 +812,17 @@ class MeshNetworkAdapter extends NetworkAdapter {
|
|
|
781
812
|
wrap(message) {
|
|
782
813
|
const keyring = this.keyringSource();
|
|
783
814
|
const serialised = serialiseMessage(message);
|
|
815
|
+
const tagged = prependControlTag(MESH_CONTROL_TYPE.Sync, serialised);
|
|
784
816
|
let payloadToSign;
|
|
785
817
|
if (this.encryptionEnabled) {
|
|
786
818
|
const docKey = keyring.documentKeys.get(DEFAULT_MESH_KEY_ID);
|
|
787
819
|
if (!docKey) {
|
|
788
820
|
throw new Error(`MeshNetworkAdapter: missing document encryption key under id "${DEFAULT_MESH_KEY_ID}". Provision the key in the keyring before sending.`);
|
|
789
821
|
}
|
|
790
|
-
const encrypted = sealEnvelope(
|
|
822
|
+
const encrypted = sealEnvelope(tagged, DEFAULT_MESH_KEY_ID, docKey);
|
|
791
823
|
payloadToSign = encodeEncryptedEnvelope(encrypted);
|
|
792
824
|
} else {
|
|
793
|
-
payloadToSign =
|
|
825
|
+
payloadToSign = tagged;
|
|
794
826
|
}
|
|
795
827
|
const signed = signEnvelope(payloadToSign, message.senderId, keyring.identity.secretKey);
|
|
796
828
|
const signedBytes = encodeSignedEnvelope(signed);
|
|
@@ -811,44 +843,156 @@ class MeshNetworkAdapter extends NetworkAdapter {
|
|
|
811
843
|
let signed;
|
|
812
844
|
try {
|
|
813
845
|
signed = decodeSignedEnvelope(message.data);
|
|
814
|
-
} catch {
|
|
846
|
+
} catch (err) {
|
|
847
|
+
emitMeshDiagnostic({
|
|
848
|
+
kind: "drop:malformed-signed-envelope",
|
|
849
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
850
|
+
});
|
|
815
851
|
return;
|
|
816
852
|
}
|
|
817
853
|
const keyring = this.keyringSource();
|
|
818
854
|
if (keyring.revokedPeers.has(signed.senderId)) {
|
|
855
|
+
emitMeshDiagnostic({
|
|
856
|
+
kind: "drop:revoked-peer",
|
|
857
|
+
senderId: signed.senderId
|
|
858
|
+
});
|
|
819
859
|
return;
|
|
820
860
|
}
|
|
821
861
|
const senderKey = keyring.knownPeers.get(signed.senderId);
|
|
822
862
|
if (!senderKey) {
|
|
863
|
+
emitMeshDiagnostic({
|
|
864
|
+
kind: "drop:unknown-peer",
|
|
865
|
+
senderId: signed.senderId
|
|
866
|
+
});
|
|
823
867
|
return;
|
|
824
868
|
}
|
|
825
869
|
let verifiedPayload;
|
|
826
870
|
try {
|
|
827
871
|
verifiedPayload = openEnvelope2(signed, senderKey);
|
|
828
|
-
} catch {
|
|
872
|
+
} catch (err) {
|
|
873
|
+
emitMeshDiagnostic({
|
|
874
|
+
kind: "drop:bad-signature",
|
|
875
|
+
senderId: signed.senderId,
|
|
876
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
877
|
+
});
|
|
829
878
|
return;
|
|
830
879
|
}
|
|
831
880
|
if (!this.encryptionEnabled) {
|
|
832
|
-
return
|
|
881
|
+
return this.dispatchTaggedPayload(verifiedPayload, signed.senderId);
|
|
833
882
|
}
|
|
834
883
|
let encrypted;
|
|
835
884
|
try {
|
|
836
885
|
encrypted = decodeEncryptedEnvelope(verifiedPayload);
|
|
837
|
-
} catch {
|
|
886
|
+
} catch (err) {
|
|
887
|
+
emitMeshDiagnostic({
|
|
888
|
+
kind: "drop:malformed-encrypted-envelope",
|
|
889
|
+
senderId: signed.senderId,
|
|
890
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
891
|
+
});
|
|
838
892
|
return;
|
|
839
893
|
}
|
|
840
894
|
const docKey = keyring.documentKeys.get(encrypted.documentId);
|
|
841
895
|
if (!docKey) {
|
|
896
|
+
emitMeshDiagnostic({
|
|
897
|
+
kind: "drop:missing-doc-key",
|
|
898
|
+
senderId: signed.senderId,
|
|
899
|
+
documentId: encrypted.documentId
|
|
900
|
+
});
|
|
842
901
|
return;
|
|
843
902
|
}
|
|
844
903
|
let plaintext;
|
|
845
904
|
try {
|
|
846
905
|
plaintext = openEnvelope(encrypted, docKey);
|
|
847
|
-
} catch {
|
|
906
|
+
} catch (err) {
|
|
907
|
+
emitMeshDiagnostic({
|
|
908
|
+
kind: "drop:bad-decryption",
|
|
909
|
+
senderId: signed.senderId,
|
|
910
|
+
documentId: encrypted.documentId,
|
|
911
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
912
|
+
});
|
|
848
913
|
return;
|
|
849
914
|
}
|
|
850
|
-
return
|
|
915
|
+
return this.dispatchTaggedPayload(plaintext, signed.senderId);
|
|
851
916
|
}
|
|
917
|
+
dispatchTaggedPayload(payload, senderId) {
|
|
918
|
+
if (payload.byteLength < 1) {
|
|
919
|
+
emitMeshDiagnostic({ kind: "drop:empty-control-payload", senderId });
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
const tag = payload[0];
|
|
923
|
+
const body = payload.subarray(1);
|
|
924
|
+
switch (tag) {
|
|
925
|
+
case MESH_CONTROL_TYPE.Sync:
|
|
926
|
+
return deserialiseMessage(body);
|
|
927
|
+
case MESH_CONTROL_TYPE.Revocation:
|
|
928
|
+
emitMeshDiagnostic({ kind: "ctrl:revocation-received", senderId });
|
|
929
|
+
this.invokeControlHandler(tag, body, senderId);
|
|
930
|
+
return;
|
|
931
|
+
case MESH_CONTROL_TYPE.RevocationSummary:
|
|
932
|
+
emitMeshDiagnostic({
|
|
933
|
+
kind: "ctrl:revocation-summary-received",
|
|
934
|
+
senderId
|
|
935
|
+
});
|
|
936
|
+
this.invokeControlHandler(tag, body, senderId);
|
|
937
|
+
return;
|
|
938
|
+
default:
|
|
939
|
+
emitMeshDiagnostic({
|
|
940
|
+
kind: "drop:unknown-control-type",
|
|
941
|
+
senderId,
|
|
942
|
+
tag
|
|
943
|
+
});
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
invokeControlHandler(tag, body, senderId) {
|
|
948
|
+
if (!this.onControlMessage)
|
|
949
|
+
return;
|
|
950
|
+
try {
|
|
951
|
+
this.onControlMessage(tag, body, senderId);
|
|
952
|
+
} catch (err) {
|
|
953
|
+
emitMeshDiagnostic({
|
|
954
|
+
kind: "drop:control-handler-threw",
|
|
955
|
+
senderId,
|
|
956
|
+
tag,
|
|
957
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
sendControlMessage(tag, body, targetPeerIds) {
|
|
962
|
+
if (targetPeerIds.length === 0)
|
|
963
|
+
return;
|
|
964
|
+
const keyring = this.keyringSource();
|
|
965
|
+
const tagged = prependControlTag(tag, body);
|
|
966
|
+
let payloadToSign;
|
|
967
|
+
if (this.encryptionEnabled) {
|
|
968
|
+
const docKey = keyring.documentKeys.get(DEFAULT_MESH_KEY_ID);
|
|
969
|
+
if (!docKey) {
|
|
970
|
+
throw new Error(`MeshNetworkAdapter.sendControlMessage: missing document encryption key under id "${DEFAULT_MESH_KEY_ID}".`);
|
|
971
|
+
}
|
|
972
|
+
const encrypted = sealEnvelope(tagged, DEFAULT_MESH_KEY_ID, docKey);
|
|
973
|
+
payloadToSign = encodeEncryptedEnvelope(encrypted);
|
|
974
|
+
} else {
|
|
975
|
+
payloadToSign = tagged;
|
|
976
|
+
}
|
|
977
|
+
const senderId = this.peerId ?? "";
|
|
978
|
+
const signed = signEnvelope(payloadToSign, senderId, keyring.identity.secretKey);
|
|
979
|
+
const signedBytes = encodeSignedEnvelope(signed);
|
|
980
|
+
for (const targetId of targetPeerIds) {
|
|
981
|
+
const outer = {
|
|
982
|
+
type: "sync",
|
|
983
|
+
senderId,
|
|
984
|
+
targetId,
|
|
985
|
+
data: signedBytes
|
|
986
|
+
};
|
|
987
|
+
this.base.send(outer);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
function prependControlTag(tag, body) {
|
|
992
|
+
const out = new Uint8Array(body.byteLength + 1);
|
|
993
|
+
out[0] = tag;
|
|
994
|
+
out.set(body, 1);
|
|
995
|
+
return out;
|
|
852
996
|
}
|
|
853
997
|
function serialiseMessage(message) {
|
|
854
998
|
const headerObj = {
|
|
@@ -1083,4 +1227,4 @@ export {
|
|
|
1083
1227
|
$crdtCounter
|
|
1084
1228
|
};
|
|
1085
1229
|
|
|
1086
|
-
//# debugId=
|
|
1230
|
+
//# debugId=C92D0D86B1D86EE064756E2164756E21
|