wire-mesh-core 1.43.0 → 1.44.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.
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_domain_erasure_coding = require("./erasure-coding.cjs");
|
|
3
|
+
let cbor2 = require("cbor2");
|
|
4
|
+
//#region src/domain/shard-manifest.ts
|
|
5
|
+
/**
|
|
6
|
+
* The shard manifest for wire-mesh#36's mailbox-opacity delivery (the composition half of #34's erasure-coded shard design): a small self-certifying-enough record published as an ordinary opaque core/data entry, naming which device holds which shard and how many are needed to reconstruct. The manifest deliberately carries NO key material and NO shard bytes -- a mailbox reading it learns only the distribution topology, and the encrypt-then- shard ordering means even a full set of shards without the epoch key is ciphertext.
|
|
7
|
+
*
|
|
8
|
+
* The entry encoding is plain canonical CDE CBOR (the same discipline every core/data application entry uses), with structural validation on both encode (self-check before publishing) and decode (a malformed or internally inconsistent manifest fails loudly rather than guiding a reader into reconstructing garbage).
|
|
9
|
+
*/
|
|
10
|
+
/** The content-type a core/data entry carrying a shard manifest declares. */
|
|
11
|
+
const SHARD_MANIFEST_CONTENT_TYPE = "application/x-wire-mesh-shard-manifest";
|
|
12
|
+
function encodeShardManifest(manifest) {
|
|
13
|
+
validateManifest(manifest);
|
|
14
|
+
return new Uint8Array((0, cbor2.encode)({
|
|
15
|
+
"total-shards": manifest["total-shards"],
|
|
16
|
+
threshold: manifest.threshold,
|
|
17
|
+
"content-type": manifest["content-type"],
|
|
18
|
+
"original-length": manifest["original-length"],
|
|
19
|
+
shards: manifest.shards.map((shard) => ({
|
|
20
|
+
device: shard.device,
|
|
21
|
+
"transfer-id": shard["transfer-id"]
|
|
22
|
+
}))
|
|
23
|
+
}, cbor2.cdeEncodeOptions));
|
|
24
|
+
}
|
|
25
|
+
function decodeShardManifest(entry) {
|
|
26
|
+
const decoded = (0, cbor2.decode)(entry, cbor2.cdeDecodeOptions);
|
|
27
|
+
if (!isStringKeyedRecord(decoded)) throw new Error("shard manifest entry is not a CBOR map");
|
|
28
|
+
const totalShards = decoded["total-shards"];
|
|
29
|
+
const threshold = decoded.threshold;
|
|
30
|
+
const contentType = decoded["content-type"];
|
|
31
|
+
const originalLength = decoded["original-length"];
|
|
32
|
+
const shards = decoded.shards;
|
|
33
|
+
if (typeof totalShards !== "number" || typeof threshold !== "number") throw new Error("shard manifest is missing numeric total-shards/threshold");
|
|
34
|
+
if (typeof contentType !== "string" || typeof originalLength !== "number") throw new Error("shard manifest is missing content-type/original-length");
|
|
35
|
+
if (!Array.isArray(shards)) throw new Error("shard manifest is missing its shards array");
|
|
36
|
+
const manifest = {
|
|
37
|
+
"total-shards": totalShards,
|
|
38
|
+
threshold,
|
|
39
|
+
"content-type": contentType,
|
|
40
|
+
"original-length": originalLength,
|
|
41
|
+
shards: shards.map((raw) => {
|
|
42
|
+
if (!isStringKeyedRecord(raw)) throw new Error("shard manifest entry is malformed");
|
|
43
|
+
const device = raw.device;
|
|
44
|
+
const transferId = raw["transfer-id"];
|
|
45
|
+
if (!(device instanceof Uint8Array) || !(transferId instanceof Uint8Array)) throw new Error("shard manifest location is missing device/transfer-id bytes");
|
|
46
|
+
return {
|
|
47
|
+
device: Uint8Array.from(device),
|
|
48
|
+
"transfer-id": Uint8Array.from(transferId)
|
|
49
|
+
};
|
|
50
|
+
})
|
|
51
|
+
};
|
|
52
|
+
validateManifest(manifest);
|
|
53
|
+
return manifest;
|
|
54
|
+
}
|
|
55
|
+
/** The target for shard `index`, checked rather than asserted -- the length precondition above makes absence a caller bug worth a named error, not a silent undefined device. */
|
|
56
|
+
function targetFor(targets, index, shard) {
|
|
57
|
+
const target = targets[index];
|
|
58
|
+
if (target === void 0) throw new Error(`no target device for shard ${String(index)} (${String(shard.length)} bytes)`);
|
|
59
|
+
return target;
|
|
60
|
+
}
|
|
61
|
+
function isStringKeyedRecord(value) {
|
|
62
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
63
|
+
}
|
|
64
|
+
function validateManifest(manifest) {
|
|
65
|
+
if (manifest.shards.length !== manifest["total-shards"]) throw new Error(`shard manifest declares ${String(manifest["total-shards"])} shards but lists ${String(manifest.shards.length)}`);
|
|
66
|
+
if (manifest.threshold < 1 || manifest.threshold > manifest["total-shards"]) throw new Error(`shard manifest threshold ${String(manifest.threshold)} must be between 1 and ${String(manifest["total-shards"])}`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The sender half of the composition: split `payload` (already encrypted, when the content is secret -- encrypt-then-shard, so a single holder has neither enough pieces nor the key) into shards and build the manifest naming each shard's home. The bulk transfers themselves are the caller's policy (which sessions, when) -- this module owns the bytes and the manifest, the same primitive-vs-policy split data-sync itself draws.
|
|
70
|
+
*/
|
|
71
|
+
async function splitForShardedDelivery(payload, options) {
|
|
72
|
+
const { config, contentType, targets, nextTransferId } = options;
|
|
73
|
+
if (targets.length < config.totalShards) throw new Error(`every shard needs a home: ${String(config.totalShards)} shards but only ${String(targets.length)} targets`);
|
|
74
|
+
const shards = await require_domain_erasure_coding.encodeShards(payload, {
|
|
75
|
+
dataShards: config.dataShards,
|
|
76
|
+
totalShards: config.totalShards
|
|
77
|
+
});
|
|
78
|
+
const manifest = {
|
|
79
|
+
"total-shards": config.totalShards,
|
|
80
|
+
threshold: config.dataShards,
|
|
81
|
+
"content-type": contentType,
|
|
82
|
+
"original-length": payload.length,
|
|
83
|
+
shards: shards.map((shard, index) => ({
|
|
84
|
+
device: targetFor(targets, index, shard),
|
|
85
|
+
"transfer-id": nextTransferId(index)
|
|
86
|
+
}))
|
|
87
|
+
};
|
|
88
|
+
validateManifest(manifest);
|
|
89
|
+
return {
|
|
90
|
+
manifest,
|
|
91
|
+
shards,
|
|
92
|
+
manifestEntry: encodeShardManifest(manifest)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The reader half: any `threshold` shards plus the manifest reconstruct the payload -- and decrypt it, when a decryptor is given. Fails closed below the threshold (a mailbox holding fewer shards than the manifest names must not be able to read the content; that is the entire opacity property).
|
|
97
|
+
*/
|
|
98
|
+
async function reconstructFromShards(fetched, manifest, options = {}) {
|
|
99
|
+
const reconstructed = await require_domain_erasure_coding.decodeShards(fetched, {
|
|
100
|
+
dataShards: manifest.threshold,
|
|
101
|
+
totalShards: manifest["total-shards"]
|
|
102
|
+
}, manifest["original-length"]);
|
|
103
|
+
if (options.decrypt === void 0) return reconstructed;
|
|
104
|
+
return options.decrypt(reconstructed);
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
exports.SHARD_MANIFEST_CONTENT_TYPE = SHARD_MANIFEST_CONTENT_TYPE;
|
|
108
|
+
exports.decodeShardManifest = decodeShardManifest;
|
|
109
|
+
exports.encodeShardManifest = encodeShardManifest;
|
|
110
|
+
exports.reconstructFromShards = reconstructFromShards;
|
|
111
|
+
exports.splitForShardedDelivery = splitForShardedDelivery;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { D as DeviceId } from "../protocol-gbXtNeOc.cjs";
|
|
2
|
+
import { PresentedShard, ShardConfig } from "./erasure-coding.cjs";
|
|
3
|
+
//#region src/domain/shard-manifest.d.ts
|
|
4
|
+
/** The content-type a core/data entry carrying a shard manifest declares. */
|
|
5
|
+
export declare const SHARD_MANIFEST_CONTENT_TYPE = "application/x-wire-mesh-shard-manifest";
|
|
6
|
+
/** Where one shard lives: the device holding it, and the bulk transfer that delivered it. */
|
|
7
|
+
export interface ShardLocation {
|
|
8
|
+
device: DeviceId;
|
|
9
|
+
"transfer-id": Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
/** The manifest itself: N shards, any `threshold` of which reconstruct `original-length` bytes. */
|
|
12
|
+
export interface ShardManifest {
|
|
13
|
+
"total-shards": number;
|
|
14
|
+
threshold: number;
|
|
15
|
+
/** The reconstructed content's own content-type (already suffixed if encrypted). */
|
|
16
|
+
"content-type": string;
|
|
17
|
+
/** The byte length the K shards reconstruct to -- the fact the shards cannot carry themselves (zero-padding is indistinguishable from a trailing zero byte). */
|
|
18
|
+
"original-length": number;
|
|
19
|
+
shards: ShardLocation[];
|
|
20
|
+
}
|
|
21
|
+
export declare function encodeShardManifest(manifest: Readonly<ShardManifest>): Uint8Array<ArrayBuffer>;
|
|
22
|
+
export declare function decodeShardManifest(entry: Uint8Array): ShardManifest;
|
|
23
|
+
/** How a split names each transfer: the caller owns transfer-id allocation, same convention mintCapabilityToken applies to token-ids. */
|
|
24
|
+
export type NextTransferId = (shardIndex: number) => Uint8Array;
|
|
25
|
+
export interface SplitForShardedDeliveryOptions {
|
|
26
|
+
config: Readonly<ShardConfig>;
|
|
27
|
+
/** The content's own content-type -- already +aes256gcm-suffixed when the payload is encrypted (the usual case: encrypt first, then shard). */
|
|
28
|
+
contentType: string;
|
|
29
|
+
/** One target device per shard, in shard order; every shard needs a home. */
|
|
30
|
+
targets: readonly DeviceId[];
|
|
31
|
+
nextTransferId: NextTransferId;
|
|
32
|
+
}
|
|
33
|
+
export interface ShardedDelivery {
|
|
34
|
+
manifest: ShardManifest;
|
|
35
|
+
/** Shard i goes to targets[i] under transfer-id nextTransferId(i); the manifest records both. */
|
|
36
|
+
shards: Uint8Array[];
|
|
37
|
+
/** The manifest itself, encoded as a publishable core/data entry. */
|
|
38
|
+
manifestEntry: Uint8Array<ArrayBuffer>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The sender half of the composition: split `payload` (already encrypted, when the content is secret -- encrypt-then-shard, so a single holder has neither enough pieces nor the key) into shards and build the manifest naming each shard's home. The bulk transfers themselves are the caller's policy (which sessions, when) -- this module owns the bytes and the manifest, the same primitive-vs-policy split data-sync itself draws.
|
|
42
|
+
*/
|
|
43
|
+
export declare function splitForShardedDelivery(payload: Uint8Array, options: Readonly<SplitForShardedDeliveryOptions>): Promise<ShardedDelivery>;
|
|
44
|
+
export interface ReconstructOptions {
|
|
45
|
+
/** The decryption step, when the reconstructed bytes are ciphertext (the usual case). Applied AFTER reconstruction, never before -- decrypt-then-shard would expose plaintext to every shard holder. */
|
|
46
|
+
decrypt?: (ciphertext: Uint8Array) => Promise<Uint8Array>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The reader half: any `threshold` shards plus the manifest reconstruct the payload -- and decrypt it, when a decryptor is given. Fails closed below the threshold (a mailbox holding fewer shards than the manifest names must not be able to read the content; that is the entire opacity property).
|
|
50
|
+
*/
|
|
51
|
+
export declare function reconstructFromShards(fetched: readonly PresentedShard[], manifest: Readonly<ShardManifest>, options?: Readonly<ReconstructOptions>): Promise<Uint8Array>;
|
|
52
|
+
//#endregion
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { D as DeviceId } from "../protocol-gbXtNeOc.mjs";
|
|
2
|
+
import { PresentedShard, ShardConfig } from "./erasure-coding.mjs";
|
|
3
|
+
//#region src/domain/shard-manifest.d.ts
|
|
4
|
+
/** The content-type a core/data entry carrying a shard manifest declares. */
|
|
5
|
+
export declare const SHARD_MANIFEST_CONTENT_TYPE = "application/x-wire-mesh-shard-manifest";
|
|
6
|
+
/** Where one shard lives: the device holding it, and the bulk transfer that delivered it. */
|
|
7
|
+
export interface ShardLocation {
|
|
8
|
+
device: DeviceId;
|
|
9
|
+
"transfer-id": Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
/** The manifest itself: N shards, any `threshold` of which reconstruct `original-length` bytes. */
|
|
12
|
+
export interface ShardManifest {
|
|
13
|
+
"total-shards": number;
|
|
14
|
+
threshold: number;
|
|
15
|
+
/** The reconstructed content's own content-type (already suffixed if encrypted). */
|
|
16
|
+
"content-type": string;
|
|
17
|
+
/** The byte length the K shards reconstruct to -- the fact the shards cannot carry themselves (zero-padding is indistinguishable from a trailing zero byte). */
|
|
18
|
+
"original-length": number;
|
|
19
|
+
shards: ShardLocation[];
|
|
20
|
+
}
|
|
21
|
+
export declare function encodeShardManifest(manifest: Readonly<ShardManifest>): Uint8Array<ArrayBuffer>;
|
|
22
|
+
export declare function decodeShardManifest(entry: Uint8Array): ShardManifest;
|
|
23
|
+
/** How a split names each transfer: the caller owns transfer-id allocation, same convention mintCapabilityToken applies to token-ids. */
|
|
24
|
+
export type NextTransferId = (shardIndex: number) => Uint8Array;
|
|
25
|
+
export interface SplitForShardedDeliveryOptions {
|
|
26
|
+
config: Readonly<ShardConfig>;
|
|
27
|
+
/** The content's own content-type -- already +aes256gcm-suffixed when the payload is encrypted (the usual case: encrypt first, then shard). */
|
|
28
|
+
contentType: string;
|
|
29
|
+
/** One target device per shard, in shard order; every shard needs a home. */
|
|
30
|
+
targets: readonly DeviceId[];
|
|
31
|
+
nextTransferId: NextTransferId;
|
|
32
|
+
}
|
|
33
|
+
export interface ShardedDelivery {
|
|
34
|
+
manifest: ShardManifest;
|
|
35
|
+
/** Shard i goes to targets[i] under transfer-id nextTransferId(i); the manifest records both. */
|
|
36
|
+
shards: Uint8Array[];
|
|
37
|
+
/** The manifest itself, encoded as a publishable core/data entry. */
|
|
38
|
+
manifestEntry: Uint8Array<ArrayBuffer>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The sender half of the composition: split `payload` (already encrypted, when the content is secret -- encrypt-then-shard, so a single holder has neither enough pieces nor the key) into shards and build the manifest naming each shard's home. The bulk transfers themselves are the caller's policy (which sessions, when) -- this module owns the bytes and the manifest, the same primitive-vs-policy split data-sync itself draws.
|
|
42
|
+
*/
|
|
43
|
+
export declare function splitForShardedDelivery(payload: Uint8Array, options: Readonly<SplitForShardedDeliveryOptions>): Promise<ShardedDelivery>;
|
|
44
|
+
export interface ReconstructOptions {
|
|
45
|
+
/** The decryption step, when the reconstructed bytes are ciphertext (the usual case). Applied AFTER reconstruction, never before -- decrypt-then-shard would expose plaintext to every shard holder. */
|
|
46
|
+
decrypt?: (ciphertext: Uint8Array) => Promise<Uint8Array>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The reader half: any `threshold` shards plus the manifest reconstruct the payload -- and decrypt it, when a decryptor is given. Fails closed below the threshold (a mailbox holding fewer shards than the manifest names must not be able to read the content; that is the entire opacity property).
|
|
50
|
+
*/
|
|
51
|
+
export declare function reconstructFromShards(fetched: readonly PresentedShard[], manifest: Readonly<ShardManifest>, options?: Readonly<ReconstructOptions>): Promise<Uint8Array>;
|
|
52
|
+
//#endregion
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { decodeShards, encodeShards } from "./erasure-coding.mjs";
|
|
2
|
+
import { cdeDecodeOptions, cdeEncodeOptions, decode, encode } from "cbor2";
|
|
3
|
+
//#region src/domain/shard-manifest.ts
|
|
4
|
+
/**
|
|
5
|
+
* The shard manifest for wire-mesh#36's mailbox-opacity delivery (the composition half of #34's erasure-coded shard design): a small self-certifying-enough record published as an ordinary opaque core/data entry, naming which device holds which shard and how many are needed to reconstruct. The manifest deliberately carries NO key material and NO shard bytes -- a mailbox reading it learns only the distribution topology, and the encrypt-then- shard ordering means even a full set of shards without the epoch key is ciphertext.
|
|
6
|
+
*
|
|
7
|
+
* The entry encoding is plain canonical CDE CBOR (the same discipline every core/data application entry uses), with structural validation on both encode (self-check before publishing) and decode (a malformed or internally inconsistent manifest fails loudly rather than guiding a reader into reconstructing garbage).
|
|
8
|
+
*/
|
|
9
|
+
/** The content-type a core/data entry carrying a shard manifest declares. */
|
|
10
|
+
const SHARD_MANIFEST_CONTENT_TYPE = "application/x-wire-mesh-shard-manifest";
|
|
11
|
+
function encodeShardManifest(manifest) {
|
|
12
|
+
validateManifest(manifest);
|
|
13
|
+
return new Uint8Array(encode({
|
|
14
|
+
"total-shards": manifest["total-shards"],
|
|
15
|
+
threshold: manifest.threshold,
|
|
16
|
+
"content-type": manifest["content-type"],
|
|
17
|
+
"original-length": manifest["original-length"],
|
|
18
|
+
shards: manifest.shards.map((shard) => ({
|
|
19
|
+
device: shard.device,
|
|
20
|
+
"transfer-id": shard["transfer-id"]
|
|
21
|
+
}))
|
|
22
|
+
}, cdeEncodeOptions));
|
|
23
|
+
}
|
|
24
|
+
function decodeShardManifest(entry) {
|
|
25
|
+
const decoded = decode(entry, cdeDecodeOptions);
|
|
26
|
+
if (!isStringKeyedRecord(decoded)) throw new Error("shard manifest entry is not a CBOR map");
|
|
27
|
+
const totalShards = decoded["total-shards"];
|
|
28
|
+
const threshold = decoded.threshold;
|
|
29
|
+
const contentType = decoded["content-type"];
|
|
30
|
+
const originalLength = decoded["original-length"];
|
|
31
|
+
const shards = decoded.shards;
|
|
32
|
+
if (typeof totalShards !== "number" || typeof threshold !== "number") throw new Error("shard manifest is missing numeric total-shards/threshold");
|
|
33
|
+
if (typeof contentType !== "string" || typeof originalLength !== "number") throw new Error("shard manifest is missing content-type/original-length");
|
|
34
|
+
if (!Array.isArray(shards)) throw new Error("shard manifest is missing its shards array");
|
|
35
|
+
const manifest = {
|
|
36
|
+
"total-shards": totalShards,
|
|
37
|
+
threshold,
|
|
38
|
+
"content-type": contentType,
|
|
39
|
+
"original-length": originalLength,
|
|
40
|
+
shards: shards.map((raw) => {
|
|
41
|
+
if (!isStringKeyedRecord(raw)) throw new Error("shard manifest entry is malformed");
|
|
42
|
+
const device = raw.device;
|
|
43
|
+
const transferId = raw["transfer-id"];
|
|
44
|
+
if (!(device instanceof Uint8Array) || !(transferId instanceof Uint8Array)) throw new Error("shard manifest location is missing device/transfer-id bytes");
|
|
45
|
+
return {
|
|
46
|
+
device: Uint8Array.from(device),
|
|
47
|
+
"transfer-id": Uint8Array.from(transferId)
|
|
48
|
+
};
|
|
49
|
+
})
|
|
50
|
+
};
|
|
51
|
+
validateManifest(manifest);
|
|
52
|
+
return manifest;
|
|
53
|
+
}
|
|
54
|
+
/** The target for shard `index`, checked rather than asserted -- the length precondition above makes absence a caller bug worth a named error, not a silent undefined device. */
|
|
55
|
+
function targetFor(targets, index, shard) {
|
|
56
|
+
const target = targets[index];
|
|
57
|
+
if (target === void 0) throw new Error(`no target device for shard ${String(index)} (${String(shard.length)} bytes)`);
|
|
58
|
+
return target;
|
|
59
|
+
}
|
|
60
|
+
function isStringKeyedRecord(value) {
|
|
61
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
62
|
+
}
|
|
63
|
+
function validateManifest(manifest) {
|
|
64
|
+
if (manifest.shards.length !== manifest["total-shards"]) throw new Error(`shard manifest declares ${String(manifest["total-shards"])} shards but lists ${String(manifest.shards.length)}`);
|
|
65
|
+
if (manifest.threshold < 1 || manifest.threshold > manifest["total-shards"]) throw new Error(`shard manifest threshold ${String(manifest.threshold)} must be between 1 and ${String(manifest["total-shards"])}`);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The sender half of the composition: split `payload` (already encrypted, when the content is secret -- encrypt-then-shard, so a single holder has neither enough pieces nor the key) into shards and build the manifest naming each shard's home. The bulk transfers themselves are the caller's policy (which sessions, when) -- this module owns the bytes and the manifest, the same primitive-vs-policy split data-sync itself draws.
|
|
69
|
+
*/
|
|
70
|
+
async function splitForShardedDelivery(payload, options) {
|
|
71
|
+
const { config, contentType, targets, nextTransferId } = options;
|
|
72
|
+
if (targets.length < config.totalShards) throw new Error(`every shard needs a home: ${String(config.totalShards)} shards but only ${String(targets.length)} targets`);
|
|
73
|
+
const shards = await encodeShards(payload, {
|
|
74
|
+
dataShards: config.dataShards,
|
|
75
|
+
totalShards: config.totalShards
|
|
76
|
+
});
|
|
77
|
+
const manifest = {
|
|
78
|
+
"total-shards": config.totalShards,
|
|
79
|
+
threshold: config.dataShards,
|
|
80
|
+
"content-type": contentType,
|
|
81
|
+
"original-length": payload.length,
|
|
82
|
+
shards: shards.map((shard, index) => ({
|
|
83
|
+
device: targetFor(targets, index, shard),
|
|
84
|
+
"transfer-id": nextTransferId(index)
|
|
85
|
+
}))
|
|
86
|
+
};
|
|
87
|
+
validateManifest(manifest);
|
|
88
|
+
return {
|
|
89
|
+
manifest,
|
|
90
|
+
shards,
|
|
91
|
+
manifestEntry: encodeShardManifest(manifest)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The reader half: any `threshold` shards plus the manifest reconstruct the payload -- and decrypt it, when a decryptor is given. Fails closed below the threshold (a mailbox holding fewer shards than the manifest names must not be able to read the content; that is the entire opacity property).
|
|
96
|
+
*/
|
|
97
|
+
async function reconstructFromShards(fetched, manifest, options = {}) {
|
|
98
|
+
const reconstructed = await decodeShards(fetched, {
|
|
99
|
+
dataShards: manifest.threshold,
|
|
100
|
+
totalShards: manifest["total-shards"]
|
|
101
|
+
}, manifest["original-length"]);
|
|
102
|
+
if (options.decrypt === void 0) return reconstructed;
|
|
103
|
+
return options.decrypt(reconstructed);
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { SHARD_MANIFEST_CONTENT_TYPE, decodeShardManifest, encodeShardManifest, reconstructFromShards, splitForShardedDelivery };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wire-mesh-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
|
|
6
6
|
"repository": {
|
|
@@ -162,6 +162,10 @@
|
|
|
162
162
|
"import": "./dist/domain/room-token-verification.mjs",
|
|
163
163
|
"require": "./dist/domain/room-token-verification.cjs"
|
|
164
164
|
},
|
|
165
|
+
"./domain/shard-manifest": {
|
|
166
|
+
"import": "./dist/domain/shard-manifest.mjs",
|
|
167
|
+
"require": "./dist/domain/shard-manifest.cjs"
|
|
168
|
+
},
|
|
165
169
|
"./domain/tokens": {
|
|
166
170
|
"import": "./dist/domain/tokens.mjs",
|
|
167
171
|
"require": "./dist/domain/tokens.cjs"
|