@amalgm/live 0.2.44 → 0.2.45

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.
@@ -10,7 +10,7 @@
10
10
  * host effects; this file owns the shape and its laws.
11
11
  */
12
12
  import { type Checked } from './validate.js';
13
- export declare const CONTENT_CONTRACT = "amalgm-entity-content@3";
13
+ export declare const CONTENT_CONTRACT = "amalgm-entity-content@4";
14
14
  export type ContentContract = typeof CONTENT_CONTRACT;
15
15
  export declare const MIN_CONTENT_BLOCK_BYTES: number;
16
16
  export declare const MAX_CONTENT_BLOCK_BYTES: number;
@@ -10,7 +10,7 @@
10
10
  * host effects; this file owns the shape and its laws.
11
11
  */
12
12
  import { pass, fail, isRecord } from './validate.js';
13
- export const CONTENT_CONTRACT = 'amalgm-entity-content@3';
13
+ export const CONTENT_CONTRACT = 'amalgm-entity-content@4';
14
14
  export const MIN_CONTENT_BLOCK_BYTES = 4 * 1024 * 1024;
15
15
  export const MAX_CONTENT_BLOCK_BYTES = 16 * 1024 * 1024;
16
16
  /** Maximum accepted block size. Kept as the public manifest-bound name. */
@@ -12,6 +12,7 @@ export * from './binding.js';
12
12
  export * from './presence.js';
13
13
  export * from './docs.js';
14
14
  export * from './content.js';
15
+ export * from './protection.js';
15
16
  export * from './frames/stream.js';
16
17
  export * from './frames/gateway.js';
17
18
  export * from './frames/tunnel.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
@@ -12,6 +12,7 @@ export * from './binding.js';
12
12
  export * from './presence.js';
13
13
  export * from './docs.js';
14
14
  export * from './content.js';
15
+ export * from './protection.js';
15
16
  export * from './frames/stream.js';
16
17
  export * from './frames/gateway.js';
17
18
  export * from './frames/tunnel.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Opening sealed private-resource envelopes.
3
+ *
4
+ * The gateway is the format's producer: it derives a per-resource key from
5
+ * its root secret and seals every object it puts in content storage as an
6
+ * AES-256-GCM envelope whose AAD binds resource, contract, and purpose.
7
+ * A runtime holding its resource key — handed once over the authenticated
8
+ * tunnel — opens those objects after fetching them directly from storage.
9
+ * This module is that opener: portable (WebCrypto, no platform imports) so
10
+ * any host the SDK supports can consume protected content.
11
+ */
12
+ export declare const PROTECTED_ENVELOPE_FORMAT = "amalgm.private.entity-content.aes-256-gcm@2";
13
+ export declare const PROTECTED_ENVELOPE_MAGIC = "AMGEC002";
14
+ /** The binding an envelope was sealed under; opening must present the same. */
15
+ export interface ProtectionBinding {
16
+ readonly resourceId: string;
17
+ readonly contract: string;
18
+ readonly purpose: string;
19
+ }
20
+ export declare class ProtectionError extends Error {
21
+ constructor(message: string);
22
+ }
23
+ /** Open one binary storage envelope to verified plaintext. The fixed layout is
24
+ * `magic | iv | tag | ciphertext`; content never pays JSON/base64 expansion.
25
+ * Refuses anything malformed, tampered with, or sealed under a different key
26
+ * or binding. */
27
+ export declare function openProtectedEnvelopeBytes(key: Uint8Array, bytes: Uint8Array, binding: ProtectionBinding): Promise<Uint8Array>;
28
+ //# sourceMappingURL=protection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protection.d.ts","sourceRoot":"","sources":["../../src/contracts/protection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,yBAAyB,gDAAgD,CAAC;AACvF,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAOD;;;iBAGiB;AACjB,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,UAAU,CAAC,CAyBrB"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Opening sealed private-resource envelopes.
3
+ *
4
+ * The gateway is the format's producer: it derives a per-resource key from
5
+ * its root secret and seals every object it puts in content storage as an
6
+ * AES-256-GCM envelope whose AAD binds resource, contract, and purpose.
7
+ * A runtime holding its resource key — handed once over the authenticated
8
+ * tunnel — opens those objects after fetching them directly from storage.
9
+ * This module is that opener: portable (WebCrypto, no platform imports) so
10
+ * any host the SDK supports can consume protected content.
11
+ */
12
+ export const PROTECTED_ENVELOPE_FORMAT = 'amalgm.private.entity-content.aes-256-gcm@2';
13
+ export const PROTECTED_ENVELOPE_MAGIC = 'AMGEC002';
14
+ export class ProtectionError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'ProtectionError';
18
+ }
19
+ }
20
+ const MAGIC_BYTES = new TextEncoder().encode(PROTECTED_ENVELOPE_MAGIC);
21
+ const IV_BYTES = 12;
22
+ const TAG_BYTES = 16;
23
+ const HEADER_BYTES = MAGIC_BYTES.byteLength + IV_BYTES + TAG_BYTES;
24
+ /** Open one binary storage envelope to verified plaintext. The fixed layout is
25
+ * `magic | iv | tag | ciphertext`; content never pays JSON/base64 expansion.
26
+ * Refuses anything malformed, tampered with, or sealed under a different key
27
+ * or binding. */
28
+ export async function openProtectedEnvelopeBytes(key, bytes, binding) {
29
+ if (key.byteLength !== 32)
30
+ throw new ProtectionError('protection key must be exactly 32 bytes');
31
+ if (bytes.byteLength < HEADER_BYTES
32
+ || MAGIC_BYTES.some((value, index) => bytes[index] !== value)) {
33
+ throw new ProtectionError('payload is not a protected envelope');
34
+ }
35
+ try {
36
+ const ivStart = MAGIC_BYTES.byteLength;
37
+ const tagStart = ivStart + IV_BYTES;
38
+ const dataStart = tagStart + TAG_BYTES;
39
+ const iv = bytes.slice(ivStart, tagStart);
40
+ const tag = bytes.slice(tagStart, dataStart);
41
+ const data = bytes.slice(dataStart);
42
+ const sealed = new Uint8Array(data.length + tag.length);
43
+ sealed.set(data, 0);
44
+ sealed.set(tag, data.length);
45
+ const additionalData = new TextEncoder().encode(`${PROTECTED_ENVELOPE_FORMAT}:${binding.resourceId}:${binding.contract}:${binding.purpose}`);
46
+ const secret = await crypto.subtle.importKey('raw', key.slice(), 'AES-GCM', false, ['decrypt']);
47
+ const plain = await crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData }, secret, sealed);
48
+ return new Uint8Array(plain);
49
+ }
50
+ catch {
51
+ throw new ProtectionError('protected envelope failed authentication');
52
+ }
53
+ }
54
+ //# sourceMappingURL=protection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protection.js","sourceRoot":"","sources":["../../src/contracts/protection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,6CAA6C,CAAC;AACvF,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;AASnD,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;AACvE,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,YAAY,GAAG,WAAW,CAAC,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnE;;;iBAGiB;AACjB,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,GAAe,EACf,KAAiB,EACjB,OAA0B;IAE1B,IAAI,GAAG,CAAC,UAAU,KAAK,EAAE;QAAE,MAAM,IAAI,eAAe,CAAC,yCAAyC,CAAC,CAAC;IAChG,IAAI,KAAK,CAAC,UAAU,GAAG,YAAY;WAC9B,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,eAAe,CAAC,qCAAqC,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC;QACvC,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;QACpC,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACpB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,MAAM,cAAc,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAC7C,GAAG,yBAAyB,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,CAC5F,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAChG,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACnG,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,eAAe,CAAC,0CAA0C,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
@@ -5,6 +5,10 @@
5
5
  * only the gaps, verifies every fetched chunk, and then asks the cache to seal
6
6
  * the complete artifact. Sealing must verify the whole byte count and content
7
7
  * hash before returning a local handle.
8
+ *
9
+ * Every chunk is one content-addressed storage object, so the host's ports
10
+ * fetch chunks independently — parallelism, transport, and decryption are the
11
+ * host's concern; the verification laws here are not.
8
12
  */
9
13
  import { type ContentManifest } from '../contracts/content.js';
10
14
  import type { Sha256Hex } from '../entities/types.js';
@@ -15,12 +19,7 @@ export interface DownloadPorts<LocalArtifact> {
15
19
  /** True only when this exact content-addressed chunk is locally verified. */
16
20
  hasChunk(artifact: TransferArtifact, manifest: ContentManifest, index: number): Promise<boolean>;
17
21
  getChunk(artifact: TransferArtifact, manifest: ContentManifest, index: number): Promise<Uint8Array>;
18
- /** Fetch adjacent logical file blocks in one binary response, concatenated
19
- * in `indexes` order. */
20
- getChunkBatch?(artifact: TransferArtifact, manifest: ContentManifest, indexes: readonly number[]): Promise<Uint8Array>;
21
22
  putChunk(artifact: TransferArtifact, manifest: ContentManifest, index: number, bytes: Uint8Array): Promise<void>;
22
- /** Persist one already-verified contiguous file batch. */
23
- putChunkBatch?(artifact: TransferArtifact, manifest: ContentManifest, indexes: readonly number[], bytes: Uint8Array): Promise<void>;
24
23
  /** Verify and expose the complete immutable artifact assembled from cache. */
25
24
  sealArtifact(artifact: TransferArtifact, manifest: ContentManifest): Promise<SealedArtifact<LocalArtifact>>;
26
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/transfer/download.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAI/E,MAAM,WAAW,aAAa,CAAC,aAAa;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,6EAA6E;IAC7E,QAAQ,CACN,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,QAAQ,CACN,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB;6BACyB;IACzB,aAAa,CAAC,CACZ,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,QAAQ,CACN,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,0DAA0D;IAC1D,aAAa,CAAC,CACZ,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,8EAA8E;IAC9E,YAAY,CACV,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc,CAAC,aAAa;IAC3C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe,CAAC,aAAa;IAC5C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAwBD,yEAAyE;AACzE,wBAAsB,QAAQ,CAAC,aAAa,EAC1C,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,EACnC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAwDzC"}
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/transfer/download.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG/E,MAAM,WAAW,aAAa,CAAC,aAAa;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,6EAA6E;IAC7E,QAAQ,CACN,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,QAAQ,CACN,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,QAAQ,CACN,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,8EAA8E;IAC9E,YAAY,CACV,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc,CAAC,aAAa;IAC3C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe,CAAC,aAAa;IAC5C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,yEAAyE;AACzE,wBAAsB,QAAQ,CAAC,aAAa,EAC1C,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,EACnC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAqCzC"}
@@ -5,29 +5,14 @@
5
5
  * only the gaps, verifies every fetched chunk, and then asks the cache to seal
6
6
  * the complete artifact. Sealing must verify the whole byte count and content
7
7
  * hash before returning a local handle.
8
+ *
9
+ * Every chunk is one content-addressed storage object, so the host's ports
10
+ * fetch chunks independently — parallelism, transport, and decryption are the
11
+ * host's concern; the verification laws here are not.
8
12
  */
9
13
  import { checkContentManifest } from '../contracts/content.js';
10
14
  import { assertTransferArtifact } from './artifacts.js';
11
- import { planChunkBatches } from './batches.js';
12
15
  import { DEFAULT_TRANSFER_CONCURRENCY, parallelForEach } from './parallel.js';
13
- const checkedBatchChunks = (bytes, indexes, manifest, sha256Hex) => {
14
- const chunks = [];
15
- let offset = 0;
16
- for (const index of indexes) {
17
- const expected = manifest.chunks[index];
18
- if (!expected)
19
- throw new Error(`download manifest named an unknown chunk index`);
20
- const chunk = bytes.subarray(offset, offset + expected.bytes);
21
- if (chunk.byteLength !== expected.bytes || sha256Hex(chunk) !== expected.sha256) {
22
- throw new Error(`downloaded content chunk ${index} failed verification`);
23
- }
24
- chunks.push(chunk);
25
- offset += expected.bytes;
26
- }
27
- if (offset !== bytes.byteLength)
28
- throw new Error('downloaded file batch has the wrong byte count');
29
- return chunks;
30
- };
31
16
  /** Download one immutable artifact, fetching only machine-cache gaps. */
32
17
  export async function download(artifact, ports, options = {}) {
33
18
  assertTransferArtifact(artifact);
@@ -40,36 +25,18 @@ export async function download(artifact, ports, options = {}) {
40
25
  if (!await ports.hasChunk(artifact, manifest, index))
41
26
  missing.push(index);
42
27
  }
43
- if (artifact.kind === 'file' && ports.getChunkBatch) {
44
- await parallelForEach(planChunkBatches(missing, manifest), options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY, async (batch) => {
45
- const bytes = await ports.getChunkBatch(artifact, manifest, batch.indexes);
46
- if (!(bytes instanceof Uint8Array))
47
- throw new Error('downloaded file batch is not binary bytes');
48
- const chunks = checkedBatchChunks(bytes, batch.indexes, manifest, ports.sha256Hex);
49
- if (ports.putChunkBatch) {
50
- await ports.putChunkBatch(artifact, manifest, batch.indexes, bytes);
51
- }
52
- else {
53
- for (let offset = 0; offset < batch.indexes.length; offset += 1) {
54
- await ports.putChunk(artifact, manifest, batch.indexes[offset], chunks[offset]);
55
- }
56
- }
57
- });
58
- }
59
- else {
60
- await parallelForEach(missing, options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY, async (index) => {
61
- const expected = manifest.chunks[index];
62
- if (expected === undefined)
63
- throw new Error('download manifest named an unknown chunk index');
64
- const bytes = await ports.getChunk(artifact, manifest, index);
65
- if (!(bytes instanceof Uint8Array)
66
- || bytes.byteLength !== expected.bytes
67
- || ports.sha256Hex(bytes) !== expected.sha256) {
68
- throw new Error(`downloaded content chunk ${index} failed verification`);
69
- }
70
- await ports.putChunk(artifact, manifest, index, bytes);
71
- });
72
- }
28
+ await parallelForEach(missing, options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY, async (index) => {
29
+ const expected = manifest.chunks[index];
30
+ if (expected === undefined)
31
+ throw new Error('download manifest named an unknown chunk index');
32
+ const bytes = await ports.getChunk(artifact, manifest, index);
33
+ if (!(bytes instanceof Uint8Array)
34
+ || bytes.byteLength !== expected.bytes
35
+ || ports.sha256Hex(bytes) !== expected.sha256) {
36
+ throw new Error(`downloaded content chunk ${index} failed verification`);
37
+ }
38
+ await ports.putChunk(artifact, manifest, index, bytes);
39
+ });
73
40
  const sealed = await ports.sealArtifact(artifact, manifest);
74
41
  if (sealed.bytes !== manifest.bytes || sealed.contentHash !== manifest.contentHash) {
75
42
  throw new Error(`downloaded artifact ${artifact.contentHash} failed verification`);
@@ -1 +1 @@
1
- {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/transfer/download.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/transfer/download.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,oBAAoB,EAAwB,MAAM,yBAAyB,CAAC;AAErF,OAAO,EAAE,sBAAsB,EAAyB,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,4BAA4B,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AA+C9E,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAA0B,EAC1B,KAAmC,EACnC,UAA2B,EAAE;IAE7B,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,oBAAoB,CAClC,MAAM,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,EACjC,QAAQ,CAAC,WAAW,CACrB,CAAC;IACF,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC;IAE/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,IAAI,4BAA4B,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAClG,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC9F,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU,CAAC;eAC7B,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,KAAK;eACnC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,sBAAsB,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5D,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;QACnF,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,WAAW,sBAAsB,CAAC,CAAC;IACrF,CAAC;IACD,OAAO;QACL,QAAQ;QACR,QAAQ;QACR,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,gBAAgB,EAAE,OAAO,CAAC,MAAM;QAChC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM;KACtD,CAAC;AACJ,CAAC"}
@@ -2,4 +2,5 @@
2
2
  export * from './artifacts.js';
3
3
  export * from './upload.js';
4
4
  export * from './download.js';
5
+ export * from './presign.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transfer/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transfer/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
@@ -2,4 +2,5 @@
2
2
  export * from './artifacts.js';
3
3
  export * from './upload.js';
4
4
  export * from './download.js';
5
+ export * from './presign.js';
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transfer/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transfer/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Presigned-URL brokering for direct content-storage fetches.
3
+ *
4
+ * Downloads fetch every storage object straight from content storage using
5
+ * short-lived URLs the gateway signs. Callers ask for one object's URL at a
6
+ * time; the broker coalesces every request made in the same event-loop turn
7
+ * into one batched signing call, so thousands of tiny fetches cost a handful
8
+ * of control-plane round trips. URLs are used immediately and never cached —
9
+ * their expiry is the gateway's revocation lever.
10
+ */
11
+ /** One law shared with the gateway: the most objects one signing call may name. */
12
+ export declare const PRESIGN_BATCH_OBJECTS = 2048;
13
+ export interface PresignObject {
14
+ /** 'manifest' names a whole artifact by content hash; 'block' one chunk by its sha256. */
15
+ readonly kind: 'manifest' | 'block';
16
+ readonly sha256: string;
17
+ }
18
+ export interface PresignBrokerPorts {
19
+ /** Sign every named object, returning URLs aligned by index. */
20
+ requestUrls(objects: readonly PresignObject[]): Promise<readonly string[]>;
21
+ }
22
+ export interface PresignBroker {
23
+ url(object: PresignObject): Promise<string>;
24
+ }
25
+ export declare function createPresignBroker(ports: PresignBrokerPorts): PresignBroker;
26
+ //# sourceMappingURL=presign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presign.d.ts","sourceRoot":"","sources":["../../src/transfer/presign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,WAAW,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C;AAQD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CA+B5E"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Presigned-URL brokering for direct content-storage fetches.
3
+ *
4
+ * Downloads fetch every storage object straight from content storage using
5
+ * short-lived URLs the gateway signs. Callers ask for one object's URL at a
6
+ * time; the broker coalesces every request made in the same event-loop turn
7
+ * into one batched signing call, so thousands of tiny fetches cost a handful
8
+ * of control-plane round trips. URLs are used immediately and never cached —
9
+ * their expiry is the gateway's revocation lever.
10
+ */
11
+ /** One law shared with the gateway: the most objects one signing call may name. */
12
+ export const PRESIGN_BATCH_OBJECTS = 2048;
13
+ export function createPresignBroker(ports) {
14
+ let pending = [];
15
+ let scheduled = false;
16
+ const flush = async () => {
17
+ scheduled = false;
18
+ while (pending.length > 0) {
19
+ const batch = pending.splice(0, PRESIGN_BATCH_OBJECTS);
20
+ try {
21
+ const urls = await ports.requestUrls(batch.map((waiter) => waiter.object));
22
+ if (urls.length !== batch.length) {
23
+ throw new Error('presign returned the wrong number of urls');
24
+ }
25
+ batch.forEach((waiter, index) => waiter.resolve(urls[index]));
26
+ }
27
+ catch (error) {
28
+ batch.forEach((waiter) => waiter.reject(error));
29
+ }
30
+ }
31
+ };
32
+ return {
33
+ url(object) {
34
+ return new Promise((resolve, reject) => {
35
+ pending.push({ object, resolve, reject });
36
+ if (!scheduled) {
37
+ scheduled = true;
38
+ setTimeout(() => { void flush(); }, 0);
39
+ }
40
+ });
41
+ },
42
+ };
43
+ }
44
+ //# sourceMappingURL=presign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presign.js","sourceRoot":"","sources":["../../src/transfer/presign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAuB1C,MAAM,UAAU,mBAAmB,CAAC,KAAyB;IAC3D,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,SAAS,GAAG,KAAK,CAAC;QAClB,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC3E,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;gBAC/D,CAAC;gBACD,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAE,CAAC,CAAC,CAAC;YACjE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,CAAC,MAAqB;YACvB,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC7C,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC1C,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,SAAS,GAAG,IAAI,CAAC;oBACjB,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -17,7 +17,7 @@ exports.manifestFromChunks = manifestFromChunks;
17
17
  exports.contentBlockSize = contentBlockSize;
18
18
  exports.chunkSizes = chunkSizes;
19
19
  const validate_js_1 = require("./validate.js");
20
- exports.CONTENT_CONTRACT = 'amalgm-entity-content@3';
20
+ exports.CONTENT_CONTRACT = 'amalgm-entity-content@4';
21
21
  exports.MIN_CONTENT_BLOCK_BYTES = 4 * 1024 * 1024;
22
22
  exports.MAX_CONTENT_BLOCK_BYTES = 16 * 1024 * 1024;
23
23
  /** Maximum accepted block size. Kept as the public manifest-bound name. */
@@ -28,6 +28,7 @@ __exportStar(require("./binding.js"), exports);
28
28
  __exportStar(require("./presence.js"), exports);
29
29
  __exportStar(require("./docs.js"), exports);
30
30
  __exportStar(require("./content.js"), exports);
31
+ __exportStar(require("./protection.js"), exports);
31
32
  __exportStar(require("./frames/stream.js"), exports);
32
33
  __exportStar(require("./frames/gateway.js"), exports);
33
34
  __exportStar(require("./frames/tunnel.js"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;AAEH,gDAA8B;AAC9B,iDAA+B;AAC/B,8CAA4B;AAC5B,gDAA8B;AAC9B,+CAA6B;AAC7B,gDAA8B;AAC9B,4CAA0B;AAC1B,+CAA6B;AAC7B,qDAAmC;AACnC,sDAAoC;AACpC,qDAAmC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;AAEH,gDAA8B;AAC9B,iDAA+B;AAC/B,8CAA4B;AAC5B,gDAA8B;AAC9B,+CAA6B;AAC7B,gDAA8B;AAC9B,4CAA0B;AAC1B,+CAA6B;AAC7B,kDAAgC;AAChC,qDAAmC;AACnC,sDAAoC;AACpC,qDAAmC"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * Opening sealed private-resource envelopes.
4
+ *
5
+ * The gateway is the format's producer: it derives a per-resource key from
6
+ * its root secret and seals every object it puts in content storage as an
7
+ * AES-256-GCM envelope whose AAD binds resource, contract, and purpose.
8
+ * A runtime holding its resource key — handed once over the authenticated
9
+ * tunnel — opens those objects after fetching them directly from storage.
10
+ * This module is that opener: portable (WebCrypto, no platform imports) so
11
+ * any host the SDK supports can consume protected content.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.ProtectionError = exports.PROTECTED_ENVELOPE_MAGIC = exports.PROTECTED_ENVELOPE_FORMAT = void 0;
15
+ exports.openProtectedEnvelopeBytes = openProtectedEnvelopeBytes;
16
+ exports.PROTECTED_ENVELOPE_FORMAT = 'amalgm.private.entity-content.aes-256-gcm@2';
17
+ exports.PROTECTED_ENVELOPE_MAGIC = 'AMGEC002';
18
+ class ProtectionError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = 'ProtectionError';
22
+ }
23
+ }
24
+ exports.ProtectionError = ProtectionError;
25
+ const MAGIC_BYTES = new TextEncoder().encode(exports.PROTECTED_ENVELOPE_MAGIC);
26
+ const IV_BYTES = 12;
27
+ const TAG_BYTES = 16;
28
+ const HEADER_BYTES = MAGIC_BYTES.byteLength + IV_BYTES + TAG_BYTES;
29
+ /** Open one binary storage envelope to verified plaintext. The fixed layout is
30
+ * `magic | iv | tag | ciphertext`; content never pays JSON/base64 expansion.
31
+ * Refuses anything malformed, tampered with, or sealed under a different key
32
+ * or binding. */
33
+ async function openProtectedEnvelopeBytes(key, bytes, binding) {
34
+ if (key.byteLength !== 32)
35
+ throw new ProtectionError('protection key must be exactly 32 bytes');
36
+ if (bytes.byteLength < HEADER_BYTES
37
+ || MAGIC_BYTES.some((value, index) => bytes[index] !== value)) {
38
+ throw new ProtectionError('payload is not a protected envelope');
39
+ }
40
+ try {
41
+ const ivStart = MAGIC_BYTES.byteLength;
42
+ const tagStart = ivStart + IV_BYTES;
43
+ const dataStart = tagStart + TAG_BYTES;
44
+ const iv = bytes.slice(ivStart, tagStart);
45
+ const tag = bytes.slice(tagStart, dataStart);
46
+ const data = bytes.slice(dataStart);
47
+ const sealed = new Uint8Array(data.length + tag.length);
48
+ sealed.set(data, 0);
49
+ sealed.set(tag, data.length);
50
+ const additionalData = new TextEncoder().encode(`${exports.PROTECTED_ENVELOPE_FORMAT}:${binding.resourceId}:${binding.contract}:${binding.purpose}`);
51
+ const secret = await crypto.subtle.importKey('raw', key.slice(), 'AES-GCM', false, ['decrypt']);
52
+ const plain = await crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData }, secret, sealed);
53
+ return new Uint8Array(plain);
54
+ }
55
+ catch {
56
+ throw new ProtectionError('protected envelope failed authentication');
57
+ }
58
+ }
59
+ //# sourceMappingURL=protection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protection.js","sourceRoot":"","sources":["../../src/contracts/protection.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AA4BH,gEA6BC;AAvDY,QAAA,yBAAyB,GAAG,6CAA6C,CAAC;AAC1E,QAAA,wBAAwB,GAAG,UAAU,CAAC;AASnD,MAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AALD,0CAKC;AAED,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,gCAAwB,CAAC,CAAC;AACvE,MAAM,QAAQ,GAAG,EAAE,CAAC;AACpB,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,YAAY,GAAG,WAAW,CAAC,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnE;;;iBAGiB;AACV,KAAK,UAAU,0BAA0B,CAC9C,GAAe,EACf,KAAiB,EACjB,OAA0B;IAE1B,IAAI,GAAG,CAAC,UAAU,KAAK,EAAE;QAAE,MAAM,IAAI,eAAe,CAAC,yCAAyC,CAAC,CAAC;IAChG,IAAI,KAAK,CAAC,UAAU,GAAG,YAAY;WAC9B,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,eAAe,CAAC,qCAAqC,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC;QACvC,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;QACpC,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;QACvC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACpB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,MAAM,cAAc,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAC7C,GAAG,iCAAyB,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,CAC5F,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAChG,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACnG,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,eAAe,CAAC,0CAA0C,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
@@ -6,31 +6,16 @@
6
6
  * only the gaps, verifies every fetched chunk, and then asks the cache to seal
7
7
  * the complete artifact. Sealing must verify the whole byte count and content
8
8
  * hash before returning a local handle.
9
+ *
10
+ * Every chunk is one content-addressed storage object, so the host's ports
11
+ * fetch chunks independently — parallelism, transport, and decryption are the
12
+ * host's concern; the verification laws here are not.
9
13
  */
10
14
  Object.defineProperty(exports, "__esModule", { value: true });
11
15
  exports.download = download;
12
16
  const content_js_1 = require("../contracts/content.js");
13
17
  const artifacts_js_1 = require("./artifacts.js");
14
- const batches_js_1 = require("./batches.js");
15
18
  const parallel_js_1 = require("./parallel.js");
16
- const checkedBatchChunks = (bytes, indexes, manifest, sha256Hex) => {
17
- const chunks = [];
18
- let offset = 0;
19
- for (const index of indexes) {
20
- const expected = manifest.chunks[index];
21
- if (!expected)
22
- throw new Error(`download manifest named an unknown chunk index`);
23
- const chunk = bytes.subarray(offset, offset + expected.bytes);
24
- if (chunk.byteLength !== expected.bytes || sha256Hex(chunk) !== expected.sha256) {
25
- throw new Error(`downloaded content chunk ${index} failed verification`);
26
- }
27
- chunks.push(chunk);
28
- offset += expected.bytes;
29
- }
30
- if (offset !== bytes.byteLength)
31
- throw new Error('downloaded file batch has the wrong byte count');
32
- return chunks;
33
- };
34
19
  /** Download one immutable artifact, fetching only machine-cache gaps. */
35
20
  async function download(artifact, ports, options = {}) {
36
21
  (0, artifacts_js_1.assertTransferArtifact)(artifact);
@@ -43,36 +28,18 @@ async function download(artifact, ports, options = {}) {
43
28
  if (!await ports.hasChunk(artifact, manifest, index))
44
29
  missing.push(index);
45
30
  }
46
- if (artifact.kind === 'file' && ports.getChunkBatch) {
47
- await (0, parallel_js_1.parallelForEach)((0, batches_js_1.planChunkBatches)(missing, manifest), options.concurrency ?? parallel_js_1.DEFAULT_TRANSFER_CONCURRENCY, async (batch) => {
48
- const bytes = await ports.getChunkBatch(artifact, manifest, batch.indexes);
49
- if (!(bytes instanceof Uint8Array))
50
- throw new Error('downloaded file batch is not binary bytes');
51
- const chunks = checkedBatchChunks(bytes, batch.indexes, manifest, ports.sha256Hex);
52
- if (ports.putChunkBatch) {
53
- await ports.putChunkBatch(artifact, manifest, batch.indexes, bytes);
54
- }
55
- else {
56
- for (let offset = 0; offset < batch.indexes.length; offset += 1) {
57
- await ports.putChunk(artifact, manifest, batch.indexes[offset], chunks[offset]);
58
- }
59
- }
60
- });
61
- }
62
- else {
63
- await (0, parallel_js_1.parallelForEach)(missing, options.concurrency ?? parallel_js_1.DEFAULT_TRANSFER_CONCURRENCY, async (index) => {
64
- const expected = manifest.chunks[index];
65
- if (expected === undefined)
66
- throw new Error('download manifest named an unknown chunk index');
67
- const bytes = await ports.getChunk(artifact, manifest, index);
68
- if (!(bytes instanceof Uint8Array)
69
- || bytes.byteLength !== expected.bytes
70
- || ports.sha256Hex(bytes) !== expected.sha256) {
71
- throw new Error(`downloaded content chunk ${index} failed verification`);
72
- }
73
- await ports.putChunk(artifact, manifest, index, bytes);
74
- });
75
- }
31
+ await (0, parallel_js_1.parallelForEach)(missing, options.concurrency ?? parallel_js_1.DEFAULT_TRANSFER_CONCURRENCY, async (index) => {
32
+ const expected = manifest.chunks[index];
33
+ if (expected === undefined)
34
+ throw new Error('download manifest named an unknown chunk index');
35
+ const bytes = await ports.getChunk(artifact, manifest, index);
36
+ if (!(bytes instanceof Uint8Array)
37
+ || bytes.byteLength !== expected.bytes
38
+ || ports.sha256Hex(bytes) !== expected.sha256) {
39
+ throw new Error(`downloaded content chunk ${index} failed verification`);
40
+ }
41
+ await ports.putChunk(artifact, manifest, index, bytes);
42
+ });
76
43
  const sealed = await ports.sealArtifact(artifact, manifest);
77
44
  if (sealed.bytes !== manifest.bytes || sealed.contentHash !== manifest.contentHash) {
78
45
  throw new Error(`downloaded artifact ${artifact.contentHash} failed verification`);
@@ -1 +1 @@
1
- {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/transfer/download.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/transfer/download.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAqDH,4BAyCC;AA5FD,wDAAqF;AAErF,iDAA+E;AAC/E,+CAA8E;AA+C9E,yEAAyE;AAClE,KAAK,UAAU,QAAQ,CAC5B,QAA0B,EAC1B,KAAmC,EACnC,UAA2B,EAAE;IAE7B,IAAA,qCAAsB,EAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,IAAA,iCAAoB,EAClC,MAAM,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,EACjC,QAAQ,CAAC,WAAW,CACrB,CAAC;IACF,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC;IAE/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,IAAA,6BAAe,EAAC,OAAO,EAAE,OAAO,CAAC,WAAW,IAAI,0CAA4B,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAClG,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC9F,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,CAAC,KAAK,YAAY,UAAU,CAAC;eAC7B,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,KAAK;eACnC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,sBAAsB,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5D,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;QACnF,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,WAAW,sBAAsB,CAAC,CAAC;IACrF,CAAC;IACD,OAAO;QACL,QAAQ;QACR,QAAQ;QACR,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,gBAAgB,EAAE,OAAO,CAAC,MAAM;QAChC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM;KACtD,CAAC;AACJ,CAAC"}
@@ -18,4 +18,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  __exportStar(require("./artifacts.js"), exports);
19
19
  __exportStar(require("./upload.js"), exports);
20
20
  __exportStar(require("./download.js"), exports);
21
+ __exportStar(require("./presign.js"), exports);
21
22
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transfer/index.ts"],"names":[],"mappings":";AAAA,wDAAwD;;;;;;;;;;;;;;;;AAExD,iDAA+B;AAC/B,8CAA4B;AAC5B,gDAA8B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transfer/index.ts"],"names":[],"mappings":";AAAA,wDAAwD;;;;;;;;;;;;;;;;AAExD,iDAA+B;AAC/B,8CAA4B;AAC5B,gDAA8B;AAC9B,+CAA6B"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * Presigned-URL brokering for direct content-storage fetches.
4
+ *
5
+ * Downloads fetch every storage object straight from content storage using
6
+ * short-lived URLs the gateway signs. Callers ask for one object's URL at a
7
+ * time; the broker coalesces every request made in the same event-loop turn
8
+ * into one batched signing call, so thousands of tiny fetches cost a handful
9
+ * of control-plane round trips. URLs are used immediately and never cached —
10
+ * their expiry is the gateway's revocation lever.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.PRESIGN_BATCH_OBJECTS = void 0;
14
+ exports.createPresignBroker = createPresignBroker;
15
+ /** One law shared with the gateway: the most objects one signing call may name. */
16
+ exports.PRESIGN_BATCH_OBJECTS = 2048;
17
+ function createPresignBroker(ports) {
18
+ let pending = [];
19
+ let scheduled = false;
20
+ const flush = async () => {
21
+ scheduled = false;
22
+ while (pending.length > 0) {
23
+ const batch = pending.splice(0, exports.PRESIGN_BATCH_OBJECTS);
24
+ try {
25
+ const urls = await ports.requestUrls(batch.map((waiter) => waiter.object));
26
+ if (urls.length !== batch.length) {
27
+ throw new Error('presign returned the wrong number of urls');
28
+ }
29
+ batch.forEach((waiter, index) => waiter.resolve(urls[index]));
30
+ }
31
+ catch (error) {
32
+ batch.forEach((waiter) => waiter.reject(error));
33
+ }
34
+ }
35
+ };
36
+ return {
37
+ url(object) {
38
+ return new Promise((resolve, reject) => {
39
+ pending.push({ object, resolve, reject });
40
+ if (!scheduled) {
41
+ scheduled = true;
42
+ setTimeout(() => { void flush(); }, 0);
43
+ }
44
+ });
45
+ },
46
+ };
47
+ }
48
+ //# sourceMappingURL=presign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presign.js","sourceRoot":"","sources":["../../src/transfer/presign.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AA0BH,kDA+BC;AAvDD,mFAAmF;AACtE,QAAA,qBAAqB,GAAG,IAAI,CAAC;AAuB1C,SAAgB,mBAAmB,CAAC,KAAyB;IAC3D,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,SAAS,GAAG,KAAK,CAAC;QAClB,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,6BAAqB,CAAC,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC3E,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;gBAC/D,CAAC;gBACD,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAE,CAAC,CAAC,CAAC;YACjE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,CAAC,MAAqB;YACvB,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC7C,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC1C,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,SAAS,GAAG,IAAI,CAAC;oBACjB,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/live",
3
- "version": "0.2.44",
3
+ "version": "0.2.45",
4
4
  "description": "Portable realtime SDK for Amalgm — wire contracts, mutation discipline, and delivery state machines behind host-injected adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist-cjs/index.js",
@@ -91,6 +91,9 @@
91
91
  },
92
92
  "scripts": {
93
93
  "build": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true }); rmSync('dist-cjs', { recursive: true, force: true })\" && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node --input-type=module -e \"import { writeFileSync } from 'node:fs'; writeFileSync('dist-cjs/package.json', JSON.stringify({ type: 'commonjs' }) + '\\n')\"",
94
+ "certification:smoke": "node certification/src/release.mjs prepare --profile smoke --amalgm-version latest",
95
+ "certification:release": "node certification/src/release.mjs prepare --profile certification",
96
+ "certification:preflight": "node certification/src/release.mjs preflight",
94
97
  "test": "npm run build && node --test tests/",
95
98
  "test:parity": "npm run build && node --test tests/parity/"
96
99
  },