swarm-deploy 0.0.0 → 0.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE.md +201 -0
  3. package/NOTICE.md +13 -0
  4. package/README.md +652 -0
  5. package/RELEASING.md +71 -0
  6. package/SECURITY.md +41 -0
  7. package/dist/abort.d.ts +16 -0
  8. package/dist/abort.js +52 -0
  9. package/dist/abort.js.map +1 -0
  10. package/dist/allowlist.d.ts +2 -0
  11. package/dist/allowlist.js +21 -0
  12. package/dist/allowlist.js.map +1 -0
  13. package/dist/bin/swarm-deploy.d.ts +2 -0
  14. package/dist/bin/swarm-deploy.js +41 -0
  15. package/dist/bin/swarm-deploy.js.map +1 -0
  16. package/dist/cli.d.ts +25 -0
  17. package/dist/cli.js +694 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/client.d.ts +121 -0
  20. package/dist/client.js +285 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/direct-dht.d.ts +81 -0
  23. package/dist/direct-dht.js +254 -0
  24. package/dist/direct-dht.js.map +1 -0
  25. package/dist/error-code.d.ts +15 -0
  26. package/dist/error-code.js +30 -0
  27. package/dist/error-code.js.map +1 -0
  28. package/dist/errors.d.ts +27 -0
  29. package/dist/errors.js +33 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/events-node.d.ts +2 -0
  32. package/dist/events-node.js +7 -0
  33. package/dist/events-node.js.map +1 -0
  34. package/dist/files.d.ts +47 -0
  35. package/dist/files.js +150 -0
  36. package/dist/files.js.map +1 -0
  37. package/dist/fs-node.d.ts +2 -0
  38. package/dist/fs-node.js +7 -0
  39. package/dist/fs-node.js.map +1 -0
  40. package/dist/identity.d.ts +7 -0
  41. package/dist/identity.js +50 -0
  42. package/dist/identity.js.map +1 -0
  43. package/dist/index.d.ts +8 -0
  44. package/dist/index.js +19 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/os-node.d.ts +2 -0
  47. package/dist/os-node.js +7 -0
  48. package/dist/os-node.js.map +1 -0
  49. package/dist/path-node.d.ts +2 -0
  50. package/dist/path-node.js +7 -0
  51. package/dist/path-node.js.map +1 -0
  52. package/dist/process-node.d.ts +2 -0
  53. package/dist/process-node.js +7 -0
  54. package/dist/process-node.js.map +1 -0
  55. package/dist/server.d.ts +153 -0
  56. package/dist/server.js +576 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/storage/atomic-file.d.ts +15 -0
  59. package/dist/storage/atomic-file.js +172 -0
  60. package/dist/storage/atomic-file.js.map +1 -0
  61. package/dist/storage/commit-journal.d.ts +83 -0
  62. package/dist/storage/commit-journal.js +279 -0
  63. package/dist/storage/commit-journal.js.map +1 -0
  64. package/dist/storage/commit-store.d.ts +246 -0
  65. package/dist/storage/commit-store.js +1339 -0
  66. package/dist/storage/commit-store.js.map +1 -0
  67. package/dist/storage/layout.d.ts +16 -0
  68. package/dist/storage/layout.js +403 -0
  69. package/dist/storage/layout.js.map +1 -0
  70. package/dist/storage/recovery.d.ts +99 -0
  71. package/dist/storage/recovery.js +253 -0
  72. package/dist/storage/recovery.js.map +1 -0
  73. package/dist/storage/retention.d.ts +163 -0
  74. package/dist/storage/retention.js +465 -0
  75. package/dist/storage/retention.js.map +1 -0
  76. package/dist/storage/root-coordinator.d.ts +7 -0
  77. package/dist/storage/root-coordinator.js +27 -0
  78. package/dist/storage/root-coordinator.js.map +1 -0
  79. package/dist/storage/session-store.d.ts +2 -0
  80. package/dist/storage/session-store.js +7 -0
  81. package/dist/storage/session-store.js.map +1 -0
  82. package/dist/storage/tar-session-store.d.ts +116 -0
  83. package/dist/storage/tar-session-store.js +816 -0
  84. package/dist/storage/tar-session-store.js.map +1 -0
  85. package/dist/storage/types.d.ts +62 -0
  86. package/dist/storage/types.js +3 -0
  87. package/dist/storage/types.js.map +1 -0
  88. package/dist/tar-protocol/controls.d.ts +48 -0
  89. package/dist/tar-protocol/controls.js +185 -0
  90. package/dist/tar-protocol/controls.js.map +1 -0
  91. package/dist/tar-protocol/direct-wire.d.ts +43 -0
  92. package/dist/tar-protocol/direct-wire.js +194 -0
  93. package/dist/tar-protocol/direct-wire.js.map +1 -0
  94. package/dist/tar-protocol/extract.d.ts +18 -0
  95. package/dist/tar-protocol/extract.js +124 -0
  96. package/dist/tar-protocol/extract.js.map +1 -0
  97. package/dist/tar-protocol/hash.d.ts +10 -0
  98. package/dist/tar-protocol/hash.js +44 -0
  99. package/dist/tar-protocol/hash.js.map +1 -0
  100. package/dist/tar-protocol/lifecycle.d.ts +13 -0
  101. package/dist/tar-protocol/lifecycle.js +90 -0
  102. package/dist/tar-protocol/lifecycle.js.map +1 -0
  103. package/dist/tar-protocol/manifest.d.ts +46 -0
  104. package/dist/tar-protocol/manifest.js +287 -0
  105. package/dist/tar-protocol/manifest.js.map +1 -0
  106. package/dist/tar-protocol/ustar.d.ts +22 -0
  107. package/dist/tar-protocol/ustar.js +87 -0
  108. package/dist/tar-protocol/ustar.js.map +1 -0
  109. package/dist/types.d.ts +58 -0
  110. package/dist/types.js +3 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/validation.d.ts +4 -0
  113. package/dist/validation.js +26 -0
  114. package/dist/validation.js.map +1 -0
  115. package/package.json +101 -1
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateAndExtractTar = validateAndExtractTar;
7
+ const b4a_1 = __importDefault(require("b4a"));
8
+ const abort_js_1 = require("../abort.js");
9
+ const errors_js_1 = require("../errors.js");
10
+ const controls_js_1 = require("./controls.js");
11
+ const hash_js_1 = require("./hash.js");
12
+ const ustar_js_1 = require("./ustar.js");
13
+ const EMPTY = b4a_1.default.alloc(0);
14
+ function invalid(message, cause = null) {
15
+ return new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, message, cause);
16
+ }
17
+ /**
18
+ * Validates a deterministic single-entry TAR stream and yields its payload.
19
+ *
20
+ * Because every byte outside the payload is fixed by the offered metadata, the
21
+ * whole stream can be checked by comparison: the header must equal the one
22
+ * canonical block, and everything after the payload must be zero. A stream that
23
+ * survives that is, by construction, one entry whose bytes are exactly
24
+ * `tar[512 .. 512 + fileSize)`, so no TAR parser is needed to extract it.
25
+ */
26
+ class CanonicalTarReader {
27
+ metadata;
28
+ header;
29
+ position = 0;
30
+ constructor(metadata) {
31
+ this.metadata = metadata;
32
+ this.header = (0, ustar_js_1.canonicalUstarHeader)(metadata.name, metadata.fileSize);
33
+ }
34
+ /** Checks `chunk` in place and returns the slice of it that is file payload. */
35
+ write(chunk) {
36
+ const base = this.position;
37
+ if (base > this.metadata.tarSize - chunk.byteLength) {
38
+ throw invalid('Trailing TAR payload');
39
+ }
40
+ const contentEnd = ustar_js_1.TAR_BLOCK_BYTES + this.metadata.fileSize;
41
+ for (let index = 0; index < chunk.byteLength; index++) {
42
+ const absolute = base + index;
43
+ if (absolute < ustar_js_1.TAR_BLOCK_BYTES) {
44
+ if (chunk[index] !== this.header[absolute])
45
+ throw invalid('Noncanonical TAR header');
46
+ continue;
47
+ }
48
+ if (absolute >= contentEnd && chunk[index] !== 0) {
49
+ throw invalid('Nonzero TAR padding or terminator');
50
+ }
51
+ }
52
+ this.position = base + chunk.byteLength;
53
+ // Payload occupies absolute offsets [512, 512 + fileSize); clip the chunk
54
+ // to that window and translate back to chunk-relative indices.
55
+ const start = Math.max(base, ustar_js_1.TAR_BLOCK_BYTES);
56
+ const end = Math.min(this.position, contentEnd);
57
+ return end <= start ? EMPTY : chunk.subarray(start - base, end - base);
58
+ }
59
+ finish() {
60
+ if (this.position !== this.metadata.tarSize)
61
+ throw invalid('Truncated TAR payload');
62
+ }
63
+ }
64
+ function assertStaging(staging) {
65
+ if (!staging ||
66
+ typeof staging !== 'object' ||
67
+ typeof staging.writeTar !== 'function' ||
68
+ typeof staging.writeFile !== 'function' ||
69
+ typeof staging.complete !== 'function' ||
70
+ typeof staging.abort !== 'function') {
71
+ throw invalid('Invalid TAR extraction staging interface');
72
+ }
73
+ }
74
+ async function validateAndExtractTar(source, offeredMetadata, staging, { signal = null } = {}) {
75
+ assertStaging(staging);
76
+ const metadata = (0, controls_js_1.decodeMetadataRecord)((0, controls_js_1.encodeMetadataRecord)(offeredMetadata));
77
+ const expectedFileDigest = b4a_1.default.from(metadata.fileSha256, 'hex');
78
+ const expectedTarDigest = b4a_1.default.from(metadata.tarSha256, 'hex');
79
+ const reader = new CanonicalTarReader(metadata);
80
+ const fileHash = new hash_js_1.SodiumSha256();
81
+ const tarHash = new hash_js_1.SodiumSha256();
82
+ let extractedBytes = 0;
83
+ try {
84
+ (0, abort_js_1.throwIfAborted)(signal);
85
+ for await (const value of source) {
86
+ (0, abort_js_1.throwIfAborted)(signal);
87
+ if (!b4a_1.default.isBuffer(value))
88
+ throw invalid('Invalid TAR source bytes');
89
+ const payload = reader.write(value);
90
+ tarHash.update(value);
91
+ await staging.writeTar(value);
92
+ if (payload.byteLength > 0) {
93
+ extractedBytes += payload.byteLength;
94
+ fileHash.update(payload);
95
+ await staging.writeFile(payload);
96
+ }
97
+ }
98
+ reader.finish();
99
+ if (extractedBytes !== metadata.fileSize)
100
+ throw invalid('Extracted file size mismatch');
101
+ const fileSha256 = fileHash.digest();
102
+ const tarSha256 = tarHash.digest();
103
+ if (!(0, hash_js_1.digestMatches)(fileSha256, expectedFileDigest)) {
104
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.CHECKSUM_MISMATCH, 'Extracted file digest mismatch');
105
+ }
106
+ if (!(0, hash_js_1.digestMatches)(tarSha256, expectedTarDigest)) {
107
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.CHECKSUM_MISMATCH, 'TAR digest mismatch');
108
+ }
109
+ await staging.complete();
110
+ return {
111
+ fileSize: extractedBytes,
112
+ fileSha256,
113
+ tarSize: metadata.tarSize,
114
+ tarSha256
115
+ };
116
+ }
117
+ catch (error) {
118
+ await Promise.resolve(staging.abort(error)).catch(() => { });
119
+ if (error instanceof errors_js_1.SwarmDeployError)
120
+ throw error;
121
+ throw invalid('TAR extraction failed', error);
122
+ }
123
+ }
124
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/tar-protocol/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare const SHA256_BYTES = 32;
2
+ export declare class SodiumSha256 {
3
+ private readonly state;
4
+ private finished;
5
+ constructor();
6
+ update(bytes: Uint8Array): this;
7
+ digest(): Buffer;
8
+ }
9
+ export declare function sodiumSha256(bytes: Uint8Array): Buffer;
10
+ export declare function digestMatches(actual: Uint8Array, expected: Uint8Array): boolean;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SodiumSha256 = exports.SHA256_BYTES = void 0;
7
+ exports.sodiumSha256 = sodiumSha256;
8
+ exports.digestMatches = digestMatches;
9
+ const b4a_1 = __importDefault(require("b4a"));
10
+ const sodium_native_1 = __importDefault(require("sodium-native"));
11
+ exports.SHA256_BYTES = 32;
12
+ class SodiumSha256 {
13
+ state = b4a_1.default.alloc(sodium_native_1.default.crypto_hash_sha256_STATEBYTES);
14
+ finished = false;
15
+ constructor() {
16
+ sodium_native_1.default.crypto_hash_sha256_init(this.state);
17
+ }
18
+ update(bytes) {
19
+ if (this.finished)
20
+ throw new Error('SHA-256 state is finalized');
21
+ sodium_native_1.default.crypto_hash_sha256_update(this.state, bytes);
22
+ return this;
23
+ }
24
+ digest() {
25
+ if (this.finished)
26
+ throw new Error('SHA-256 state is finalized');
27
+ this.finished = true;
28
+ const digest = b4a_1.default.alloc(exports.SHA256_BYTES);
29
+ sodium_native_1.default.crypto_hash_sha256_final(this.state, digest);
30
+ return digest;
31
+ }
32
+ }
33
+ exports.SodiumSha256 = SodiumSha256;
34
+ function sodiumSha256(bytes) {
35
+ const digest = b4a_1.default.alloc(exports.SHA256_BYTES);
36
+ sodium_native_1.default.crypto_hash_sha256(digest, bytes);
37
+ return digest;
38
+ }
39
+ function digestMatches(actual, expected) {
40
+ return (actual.byteLength === exports.SHA256_BYTES &&
41
+ expected.byteLength === exports.SHA256_BYTES &&
42
+ sodium_native_1.default.sodium_memcmp(actual, expected));
43
+ }
44
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/tar-protocol/hash.ts"],"names":[],"mappings":";;;;;;;;AAAA,8CAAqB;AACrB,kEAAkC;AAErB,QAAA,YAAY,GAAG,EAAE,CAAA;AAE9B;IACmB,KAAK,GAAG,aAAG,CAAC,KAAK,CAAC,uBAAM,CAAC,6BAA6B,CAAC,CAAA;IAChE,QAAQ,GAAG,KAAK,CAAA;IAExB;QACE,uBAAM,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,CAAC,KAAiB;QACtB,IAAI,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAA;QAChE,uBAAM,CAAC,yBAAyB,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QACnD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAA;QAChE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;QACpB,MAAM,MAAM,GAAG,aAAG,CAAC,KAAK,CAAC,QAAA,YAAY,CAAC,CAAA;QACtC,uBAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACnD,OAAO,MAAM,CAAA;IACf,CAAC;CACF;;AAED,sBAA6B,KAAiB;IAC5C,MAAM,MAAM,GAAG,aAAG,CAAC,KAAK,CAAC,QAAA,YAAY,CAAC,CAAA;IACtC,uBAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACxC,OAAO,MAAM,CAAA;AACf,CAAC;AAED,uBAA8B,MAAkB,EAAE,QAAoB;IACpE,OAAO,CACL,MAAM,CAAC,UAAU,KAAK,QAAA,YAAY;QAClC,QAAQ,CAAC,UAAU,KAAK,QAAA,YAAY;QACpC,uBAAM,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CACvC,CAAA;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type AbortSignalLike } from '../abort.js';
2
+ export interface ProtocolWritable {
3
+ write(bytes: Uint8Array): boolean;
4
+ on(event: string, listener: (...args: unknown[]) => void): this;
5
+ removeListener(event: string, listener: (...args: unknown[]) => void): this;
6
+ destroy(error?: unknown): void;
7
+ }
8
+ export interface ProtocolWriteOptions {
9
+ signal?: AbortSignalLike | null;
10
+ timeout?: number;
11
+ }
12
+ export declare function writeProtocolBytes(stream: ProtocolWritable, bytes: Uint8Array, { signal, timeout }?: ProtocolWriteOptions): Promise<void>;
13
+ export declare function writeFramedProtocolRecord(stream: ProtocolWritable, record: Uint8Array, options?: ProtocolWriteOptions): Promise<void>;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writeProtocolBytes = writeProtocolBytes;
7
+ exports.writeFramedProtocolRecord = writeFramedProtocolRecord;
8
+ const b4a_1 = __importDefault(require("b4a"));
9
+ const abort_js_1 = require("../abort.js");
10
+ const errors_js_1 = require("../errors.js");
11
+ const controls_js_1 = require("./controls.js");
12
+ const DEFAULT_WRITE_TIMEOUT = 60_000;
13
+ const MAX_TIMEOUT = 0x7fffffff;
14
+ function invalid(message, cause = null) {
15
+ return new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, message, cause);
16
+ }
17
+ function closedBeforeDrain() {
18
+ const error = invalid('Protocol stream closed before drain');
19
+ error.transport = true;
20
+ return error;
21
+ }
22
+ function assertTimeout(timeout) {
23
+ if (!Number.isSafeInteger(timeout) || timeout <= 0 || timeout > MAX_TIMEOUT) {
24
+ throw invalid('Invalid protocol write timeout');
25
+ }
26
+ }
27
+ function writeProtocolBytes(stream, bytes, { signal = null, timeout = DEFAULT_WRITE_TIMEOUT } = {}) {
28
+ if (!stream || typeof stream.write !== 'function' || typeof stream.destroy !== 'function') {
29
+ return Promise.reject(invalid('Invalid protocol stream'));
30
+ }
31
+ if (!b4a_1.default.isBuffer(bytes))
32
+ return Promise.reject(invalid('Invalid protocol bytes'));
33
+ try {
34
+ assertTimeout(timeout);
35
+ if (signal?.aborted)
36
+ throw (0, abort_js_1.abortError)();
37
+ if (stream.destroyed === true) {
38
+ throw closedBeforeDrain();
39
+ }
40
+ if (stream.write(bytes))
41
+ return Promise.resolve();
42
+ if (stream.destroyed === true) {
43
+ throw closedBeforeDrain();
44
+ }
45
+ }
46
+ catch (error) {
47
+ return Promise.reject(error);
48
+ }
49
+ return new Promise((resolve, reject) => {
50
+ let settled = false;
51
+ let removeAbort = () => { };
52
+ const timer = setTimeout(() => finish(new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.UPLOAD_IDLE_TIMEOUT, 'Protocol write drain timed out')), timeout);
53
+ const cleanup = () => {
54
+ clearTimeout(timer);
55
+ removeAbort();
56
+ stream.removeListener('drain', onDrain);
57
+ stream.removeListener('error', onError);
58
+ stream.removeListener('close', onClose);
59
+ };
60
+ const finish = (error = null) => {
61
+ if (settled)
62
+ return;
63
+ settled = true;
64
+ cleanup();
65
+ if (error === null) {
66
+ resolve();
67
+ return;
68
+ }
69
+ try {
70
+ stream.destroy(error);
71
+ }
72
+ catch { }
73
+ reject(error);
74
+ };
75
+ const onDrain = () => finish();
76
+ const onError = (error) => finish(error instanceof Error ? error : invalid('Protocol stream failed', error));
77
+ const onClose = () => finish(closedBeforeDrain());
78
+ stream.on('drain', onDrain);
79
+ stream.on('error', onError);
80
+ stream.on('close', onClose);
81
+ removeAbort = (0, abort_js_1.onAbort)(signal, () => finish((0, abort_js_1.abortError)()));
82
+ if (stream.destroyed === true) {
83
+ finish(closedBeforeDrain());
84
+ }
85
+ });
86
+ }
87
+ function writeFramedProtocolRecord(stream, record, options = {}) {
88
+ return writeProtocolBytes(stream, (0, controls_js_1.encodeControlFrame)(record), options);
89
+ }
90
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/tar-protocol/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { type AbortSignalLike } from '../abort.js';
2
+ import { type MetadataRecord } from './controls.js';
3
+ export { deterministicTarSize, TAR_BLOCK_BYTES, TAR_GID, TAR_GNAME, TAR_MODE, TAR_MTIME_MS, TAR_UID, TAR_UNAME } from './ustar.js';
4
+ export declare const TAR_TRANSFER_DOMAIN = "swarm-deploy/direct-tar/v1";
5
+ export interface TarSourceIdentity {
6
+ dev: number | bigint;
7
+ ino: number | bigint;
8
+ size: number;
9
+ mtimeMs: number;
10
+ }
11
+ export interface TarManifest {
12
+ path: string;
13
+ name: string;
14
+ fileSize: number;
15
+ fileSha256: Buffer;
16
+ tarSize: number;
17
+ tarSha256: Buffer;
18
+ transferId: Buffer;
19
+ source: TarSourceIdentity;
20
+ }
21
+ export interface TarOperationOptions {
22
+ signal?: AbortSignalLike | null;
23
+ }
24
+ export interface TarResumeOptions extends TarOperationOptions {
25
+ expectedPrefixSha256?: Uint8Array | null;
26
+ }
27
+ export type TarResumeResult = {
28
+ status: 'MATCH';
29
+ prefixSha256: Buffer;
30
+ bytesSent: number;
31
+ } | {
32
+ status: 'RESET_REQUIRED';
33
+ prefixSha256: Buffer;
34
+ bytesSent: 0;
35
+ };
36
+ export declare function computeTarTransferId(clientPublicKey: Uint8Array, immutable: {
37
+ name: string;
38
+ fileSize: number;
39
+ fileSha256: Uint8Array;
40
+ tarSize: number;
41
+ tarSha256: Uint8Array;
42
+ }): Buffer;
43
+ export declare function buildTarManifest(filePath: string, clientPublicKey: Uint8Array, { signal }?: TarOperationOptions): Promise<TarManifest>;
44
+ export declare function regenerateTarSuffix(manifest: TarManifest, offset: number, write: (chunk: Buffer) => void | Promise<void>, { signal, expectedPrefixSha256 }?: TarResumeOptions): Promise<TarResumeResult>;
45
+ export declare function metadataFromManifest(manifest: TarManifest, reset?: boolean): MetadataRecord;
46
+ export declare function assertMetadataTransferId(clientPublicKey: Uint8Array, offeredMetadata: MetadataRecord): void;
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TAR_TRANSFER_DOMAIN = exports.TAR_UNAME = exports.TAR_UID = exports.TAR_MTIME_MS = exports.TAR_MODE = exports.TAR_GNAME = exports.TAR_GID = exports.TAR_BLOCK_BYTES = exports.deterministicTarSize = void 0;
7
+ exports.computeTarTransferId = computeTarTransferId;
8
+ exports.buildTarManifest = buildTarManifest;
9
+ exports.regenerateTarSuffix = regenerateTarSuffix;
10
+ exports.metadataFromManifest = metadataFromManifest;
11
+ exports.assertMetadataTransferId = assertMetadataTransferId;
12
+ const b4a_1 = __importDefault(require("b4a"));
13
+ const _fs_1 = __importDefault(require("#fs"));
14
+ const _path_1 = __importDefault(require("#path"));
15
+ const sodium_native_1 = __importDefault(require("sodium-native"));
16
+ const abort_js_1 = require("../abort.js");
17
+ const errors_js_1 = require("../errors.js");
18
+ const files_js_1 = require("../files.js");
19
+ const layout_js_1 = require("../storage/layout.js");
20
+ const controls_js_1 = require("./controls.js");
21
+ const hash_js_1 = require("./hash.js");
22
+ const ustar_js_1 = require("./ustar.js");
23
+ var ustar_js_2 = require("./ustar.js");
24
+ Object.defineProperty(exports, "deterministicTarSize", { enumerable: true, get: function () { return ustar_js_2.deterministicTarSize; } });
25
+ Object.defineProperty(exports, "TAR_BLOCK_BYTES", { enumerable: true, get: function () { return ustar_js_2.TAR_BLOCK_BYTES; } });
26
+ Object.defineProperty(exports, "TAR_GID", { enumerable: true, get: function () { return ustar_js_2.TAR_GID; } });
27
+ Object.defineProperty(exports, "TAR_GNAME", { enumerable: true, get: function () { return ustar_js_2.TAR_GNAME; } });
28
+ Object.defineProperty(exports, "TAR_MODE", { enumerable: true, get: function () { return ustar_js_2.TAR_MODE; } });
29
+ Object.defineProperty(exports, "TAR_MTIME_MS", { enumerable: true, get: function () { return ustar_js_2.TAR_MTIME_MS; } });
30
+ Object.defineProperty(exports, "TAR_UID", { enumerable: true, get: function () { return ustar_js_2.TAR_UID; } });
31
+ Object.defineProperty(exports, "TAR_UNAME", { enumerable: true, get: function () { return ustar_js_2.TAR_UNAME; } });
32
+ exports.TAR_TRANSFER_DOMAIN = 'swarm-deploy/direct-tar/v1';
33
+ const READ_BYTES = 64 * 1024;
34
+ function invalid(message, cause = null) {
35
+ return new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, message, cause);
36
+ }
37
+ function assertSafeSize(size) {
38
+ (0, ustar_js_1.assertUstarFileSize)(size);
39
+ }
40
+ function assertClientKey(key) {
41
+ if (!b4a_1.default.isBuffer(key) || key.byteLength !== 32)
42
+ throw invalid('Invalid client public key');
43
+ }
44
+ function canonicalName(filePath) {
45
+ const name = (0, files_js_1.validateBasename)(_path_1.default.basename(filePath));
46
+ if ((0, files_js_1.isReservedHistoryName)(name)) {
47
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.INVALID_FILENAME, 'Filename cannot be encoded as USTAR');
48
+ }
49
+ return name;
50
+ }
51
+ function identity(stat) {
52
+ return { dev: stat.dev, ino: stat.ino, size: stat.size, mtimeMs: stat.mtimeMs };
53
+ }
54
+ function sameIdentity(expected, actual) {
55
+ return (actual.isFile() &&
56
+ actual.dev === expected.dev &&
57
+ actual.ino === expected.ino &&
58
+ actual.size === expected.size &&
59
+ actual.mtimeMs === expected.mtimeMs);
60
+ }
61
+ function assertIdentity(expected, actual, message) {
62
+ if (!sameIdentity(expected, actual))
63
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.FILE_BUSY, message);
64
+ }
65
+ async function openStableSource(filePath, expected, signal) {
66
+ (0, abort_js_1.throwIfAborted)(signal);
67
+ const pathStat = await _fs_1.default.promises.lstat(filePath);
68
+ (0, abort_js_1.throwIfAborted)(signal);
69
+ if (pathStat.isSymbolicLink() || !pathStat.isFile()) {
70
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.INVALID_FILENAME, 'Path must be a regular file');
71
+ }
72
+ const observed = identity(pathStat);
73
+ if (expected)
74
+ assertIdentity(expected, pathStat, 'Source file changed before TAR generation');
75
+ let handle = null;
76
+ try {
77
+ handle = await _fs_1.default.promises.open(filePath, (0, layout_js_1.safeFileOpenFlags)('read'));
78
+ const opened = await handle.stat();
79
+ assertIdentity(expected || observed, opened, 'Source identity changed while opening');
80
+ return { handle, source: expected || observed };
81
+ }
82
+ catch (error) {
83
+ if (handle)
84
+ await handle.close().catch(() => { });
85
+ if (error instanceof errors_js_1.SwarmDeployError)
86
+ throw error;
87
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.FILE_BUSY, 'Unable to safely open source file', error);
88
+ }
89
+ }
90
+ async function* generateTar(handle, name, size, fileHash, signal) {
91
+ (0, abort_js_1.throwIfAborted)(signal);
92
+ yield (0, ustar_js_1.canonicalUstarHeader)(name, size);
93
+ let position = 0;
94
+ while (position < size) {
95
+ (0, abort_js_1.throwIfAborted)(signal);
96
+ const chunk = b4a_1.default.alloc(Math.min(READ_BYTES, size - position));
97
+ const read = await handle.read(chunk, 0, chunk.byteLength, position);
98
+ const count = typeof read === 'number' ? read : read.bytesRead;
99
+ if (count !== chunk.byteLength) {
100
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.FILE_BUSY, 'Source file was truncated');
101
+ }
102
+ fileHash.update(chunk);
103
+ position += count;
104
+ yield chunk;
105
+ }
106
+ const padding = (ustar_js_1.TAR_BLOCK_BYTES - (size % ustar_js_1.TAR_BLOCK_BYTES)) % ustar_js_1.TAR_BLOCK_BYTES;
107
+ if (padding > 0)
108
+ yield b4a_1.default.alloc(padding);
109
+ yield b4a_1.default.alloc(2 * ustar_js_1.TAR_BLOCK_BYTES);
110
+ }
111
+ async function assertSourceFinal(filePath, handle, expected, signal) {
112
+ (0, abort_js_1.throwIfAborted)(signal);
113
+ assertIdentity(expected, await handle.stat(), 'Source file changed during TAR generation');
114
+ assertIdentity(expected, await _fs_1.default.promises.lstat(filePath), 'Source path changed during TAR generation');
115
+ (0, abort_js_1.throwIfAborted)(signal);
116
+ }
117
+ function transferField(hash, label, value) {
118
+ const labelBytes = b4a_1.default.from(`${label}\u0000`);
119
+ const valueBytes = typeof value === 'number' ? b4a_1.default.from(String(value)) : b4a_1.default.from(value);
120
+ hash
121
+ .update(labelBytes)
122
+ .update(b4a_1.default.from(`${valueBytes.byteLength}:`))
123
+ .update(valueBytes);
124
+ }
125
+ function computeTarTransferId(clientPublicKey, immutable) {
126
+ assertClientKey(clientPublicKey);
127
+ assertSafeSize(immutable.fileSize);
128
+ if (!b4a_1.default.isBuffer(immutable.fileSha256) || immutable.fileSha256.byteLength !== 32) {
129
+ throw invalid('Invalid file digest');
130
+ }
131
+ if (!b4a_1.default.isBuffer(immutable.tarSha256) || immutable.tarSha256.byteLength !== 32) {
132
+ throw invalid('Invalid TAR digest');
133
+ }
134
+ const hash = new hash_js_1.SodiumSha256();
135
+ transferField(hash, 'domain', exports.TAR_TRANSFER_DOMAIN);
136
+ transferField(hash, 'clientPublicKey', clientPublicKey);
137
+ transferField(hash, 'name', immutable.name);
138
+ transferField(hash, 'fileSize', immutable.fileSize);
139
+ transferField(hash, 'fileSha256', immutable.fileSha256);
140
+ transferField(hash, 'tarSize', immutable.tarSize);
141
+ transferField(hash, 'tarSha256', immutable.tarSha256);
142
+ transferField(hash, 'type', 'file');
143
+ transferField(hash, 'mode', ustar_js_1.TAR_MODE);
144
+ transferField(hash, 'uid', ustar_js_1.TAR_UID);
145
+ transferField(hash, 'gid', ustar_js_1.TAR_GID);
146
+ transferField(hash, 'mtimeMs', ustar_js_1.TAR_MTIME_MS);
147
+ transferField(hash, 'uname', ustar_js_1.TAR_UNAME);
148
+ transferField(hash, 'gname', ustar_js_1.TAR_GNAME);
149
+ transferField(hash, 'pax', 'none');
150
+ return hash.digest();
151
+ }
152
+ async function buildTarManifest(filePath, clientPublicKey, { signal = null } = {}) {
153
+ assertClientKey(clientPublicKey);
154
+ const name = canonicalName(filePath);
155
+ const opened = await openStableSource(filePath, null, signal);
156
+ assertSafeSize(opened.source.size);
157
+ const fileHash = new hash_js_1.SodiumSha256();
158
+ const tarHash = new hash_js_1.SodiumSha256();
159
+ let tarSize = 0;
160
+ try {
161
+ for await (const chunk of generateTar(opened.handle, name, opened.source.size, fileHash, signal)) {
162
+ tarHash.update(chunk);
163
+ tarSize += chunk.byteLength;
164
+ }
165
+ await assertSourceFinal(filePath, opened.handle, opened.source, signal);
166
+ }
167
+ finally {
168
+ await opened.handle.close().catch(() => { });
169
+ }
170
+ if (tarSize !== (0, ustar_js_1.deterministicTarSize)(opened.source.size)) {
171
+ throw invalid('Noncanonical deterministic TAR length');
172
+ }
173
+ const fileSha256 = fileHash.digest();
174
+ const tarSha256 = tarHash.digest();
175
+ const transferId = computeTarTransferId(clientPublicKey, {
176
+ name,
177
+ fileSize: opened.source.size,
178
+ fileSha256,
179
+ tarSize,
180
+ tarSha256
181
+ });
182
+ return {
183
+ path: filePath,
184
+ name,
185
+ fileSize: opened.source.size,
186
+ fileSha256,
187
+ tarSize,
188
+ tarSha256,
189
+ transferId,
190
+ source: opened.source
191
+ };
192
+ }
193
+ async function regenerateTarSuffix(manifest, offset, write, { signal = null, expectedPrefixSha256 = null } = {}) {
194
+ if (!manifest || typeof manifest !== 'object')
195
+ throw invalid('Invalid TAR manifest');
196
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset > manifest.tarSize) {
197
+ throw invalid('Invalid TAR resume offset');
198
+ }
199
+ if (typeof write !== 'function')
200
+ throw invalid('Invalid TAR suffix writer');
201
+ if (expectedPrefixSha256 !== null &&
202
+ (!b4a_1.default.isBuffer(expectedPrefixSha256) || expectedPrefixSha256.byteLength !== 32)) {
203
+ throw invalid('Invalid expected prefix digest');
204
+ }
205
+ const opened = await openStableSource(manifest.path, manifest.source, signal);
206
+ const fileHash = new hash_js_1.SodiumSha256();
207
+ const tarHash = new hash_js_1.SodiumSha256();
208
+ const prefixHash = new hash_js_1.SodiumSha256();
209
+ let position = 0;
210
+ let bytesSent = 0;
211
+ let prefixSha256 = offset === 0 ? prefixHash.digest() : null;
212
+ let resetRequired = prefixSha256 !== null &&
213
+ expectedPrefixSha256 !== null &&
214
+ !sodium_native_1.default.sodium_memcmp(prefixSha256, expectedPrefixSha256);
215
+ try {
216
+ for await (const chunk of generateTar(opened.handle, manifest.name, manifest.fileSize, fileHash, signal)) {
217
+ (0, abort_js_1.throwIfAborted)(signal);
218
+ tarHash.update(chunk);
219
+ const end = position + chunk.byteLength;
220
+ if (position < offset) {
221
+ const prefixEnd = Math.min(chunk.byteLength, offset - position);
222
+ prefixHash.update(chunk.subarray(0, prefixEnd));
223
+ if (end >= offset) {
224
+ prefixSha256 = prefixHash.digest();
225
+ if (expectedPrefixSha256 && !sodium_native_1.default.sodium_memcmp(prefixSha256, expectedPrefixSha256)) {
226
+ resetRequired = true;
227
+ }
228
+ if (!resetRequired && prefixEnd < chunk.byteLength) {
229
+ const suffix = chunk.subarray(prefixEnd);
230
+ await write(suffix);
231
+ bytesSent += suffix.byteLength;
232
+ }
233
+ }
234
+ }
235
+ else if (!resetRequired) {
236
+ await write(chunk);
237
+ bytesSent += chunk.byteLength;
238
+ }
239
+ position = end;
240
+ }
241
+ await assertSourceFinal(manifest.path, opened.handle, manifest.source, signal);
242
+ }
243
+ finally {
244
+ await opened.handle.close().catch(() => { });
245
+ }
246
+ if (position !== manifest.tarSize ||
247
+ (!resetRequired && bytesSent !== manifest.tarSize - offset)) {
248
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.FILE_BUSY, 'Regenerated TAR length changed');
249
+ }
250
+ const regeneratedFile = fileHash.digest();
251
+ const regeneratedTar = tarHash.digest();
252
+ if (!(0, hash_js_1.digestMatches)(regeneratedFile, manifest.fileSha256) ||
253
+ !(0, hash_js_1.digestMatches)(regeneratedTar, manifest.tarSha256)) {
254
+ throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.FILE_BUSY, 'Source file changed during TAR regeneration');
255
+ }
256
+ if (prefixSha256 === null)
257
+ throw invalid('Unable to hash TAR prefix');
258
+ if (resetRequired)
259
+ return { status: 'RESET_REQUIRED', prefixSha256, bytesSent: 0 };
260
+ return { status: 'MATCH', prefixSha256, bytesSent };
261
+ }
262
+ function metadataFromManifest(manifest, reset = false) {
263
+ return {
264
+ v: controls_js_1.CONTROL_VERSION,
265
+ name: manifest.name,
266
+ fileSize: manifest.fileSize,
267
+ fileSha256: b4a_1.default.toString(manifest.fileSha256, 'hex'),
268
+ tarSize: manifest.tarSize,
269
+ tarSha256: b4a_1.default.toString(manifest.tarSha256, 'hex'),
270
+ transferId: b4a_1.default.toString(manifest.transferId, 'hex'),
271
+ reset
272
+ };
273
+ }
274
+ function assertMetadataTransferId(clientPublicKey, offeredMetadata) {
275
+ const metadata = (0, controls_js_1.decodeMetadataRecord)((0, controls_js_1.encodeMetadataRecord)(offeredMetadata));
276
+ const expected = computeTarTransferId(clientPublicKey, {
277
+ name: metadata.name,
278
+ fileSize: metadata.fileSize,
279
+ fileSha256: b4a_1.default.from(metadata.fileSha256, 'hex'),
280
+ tarSize: metadata.tarSize,
281
+ tarSha256: b4a_1.default.from(metadata.tarSha256, 'hex')
282
+ });
283
+ const offered = b4a_1.default.from(metadata.transferId, 'hex');
284
+ if (!sodium_native_1.default.sodium_memcmp(expected, offered))
285
+ throw invalid('Noncanonical TAR transfer ID');
286
+ }
287
+ //# sourceMappingURL=manifest.js.map