@amalgm/shell 0.1.122 → 0.1.123

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.
@@ -3,12 +3,14 @@
3
3
  *
4
4
  * Downloads no longer relay bytes through the gateway. This plane asks the
5
5
  * gateway for two control facts only — the per-resource content key, handed
6
- * once per resource over the authenticated wire, and short-lived storage GET
7
- * URLs, batched by Live's presign broker — then fetches sealed envelopes
8
- * straight from content storage and opens them locally. Bytes travel
9
- * storage → this machine; the gateway signs and revokes, nothing more.
6
+ * once per resource over the authenticated wire, and content refs, batched by
7
+ * Live's refs broker: a short-lived storage URL per object plus, for objects
8
+ * packed inside one immutable pack, the exact byte range holding their sealed
9
+ * envelope. Same-turn range requests against one pack merge into minimal
10
+ * spans — one ranged GET each — and every envelope is opened locally. Bytes
11
+ * travel storage → this machine; the gateway signs and revokes, nothing more.
10
12
  */
11
- import { type PresignObject } from "@amalgm/live";
13
+ import { type ContentObjectName } from "@amalgm/live";
12
14
  interface DirectContentWire {
13
15
  request(frame: Record<string, unknown> & {
14
16
  type: string;
@@ -19,12 +21,13 @@ export declare class DirectContentPlane {
19
21
  * process is enough; it lives only in memory, never at rest. */
20
22
  private readonly keys;
21
23
  private readonly brokers;
24
+ private readonly spans;
22
25
  private readonly wire;
23
26
  constructor(wire: DirectContentWire);
24
27
  /** Fetch one sealed storage object and open it. Every attempt asks for a
25
- * fresh URL — expiry is the gateway's revocation lever, so URLs are used
26
- * immediately and never reused. */
27
- fetchObject(resourceId: string, object: PresignObject): Promise<Buffer>;
28
+ * fresh ref — URL expiry is the gateway's revocation lever, so URLs are
29
+ * used immediately and never reused. */
30
+ fetchObject(resourceId: string, object: ContentObjectName): Promise<Buffer>;
28
31
  private contentKey;
29
32
  private broker;
30
33
  }
@@ -3,47 +3,75 @@
3
3
  *
4
4
  * Downloads no longer relay bytes through the gateway. This plane asks the
5
5
  * gateway for two control facts only — the per-resource content key, handed
6
- * once per resource over the authenticated wire, and short-lived storage GET
7
- * URLs, batched by Live's presign broker — then fetches sealed envelopes
8
- * straight from content storage and opens them locally. Bytes travel
9
- * storage → this machine; the gateway signs and revokes, nothing more.
6
+ * once per resource over the authenticated wire, and content refs, batched by
7
+ * Live's refs broker: a short-lived storage URL per object plus, for objects
8
+ * packed inside one immutable pack, the exact byte range holding their sealed
9
+ * envelope. Same-turn range requests against one pack merge into minimal
10
+ * spans — one ranged GET each — and every envelope is opened locally. Bytes
11
+ * travel storage → this machine; the gateway signs and revokes, nothing more.
10
12
  */
11
- import { createPresignBroker, openProtectedEnvelopeBytes, } from "@amalgm/live";
13
+ import { createRefsBroker, createSpanFetcher, openProtectedEnvelopeBytes, } from "@amalgm/live";
12
14
  import { WireRequestError } from "./wire-client.js";
15
+ function decodeRefs(frame) {
16
+ const urls = frame.urls;
17
+ const refs = frame.refs;
18
+ if (!Array.isArray(urls) || !urls.every((url) => typeof url === "string")
19
+ || !Array.isArray(refs)) {
20
+ throw new Error("content authority answered an invalid refs reply");
21
+ }
22
+ return refs.map((raw) => {
23
+ const ref = raw;
24
+ if (!Number.isSafeInteger(ref.url) || Number(ref.url) < 0 || Number(ref.url) >= urls.length) {
25
+ throw new Error("content authority answered an invalid refs reply");
26
+ }
27
+ const url = urls[Number(ref.url)];
28
+ if (ref.offset == null && ref.length == null)
29
+ return { url };
30
+ if (!Number.isSafeInteger(ref.offset) || Number(ref.offset) < 0
31
+ || !Number.isSafeInteger(ref.length) || Number(ref.length) < 1
32
+ || !Number.isSafeInteger(Number(ref.offset) + Number(ref.length))) {
33
+ throw new Error("content authority answered an invalid refs reply");
34
+ }
35
+ return { url, offset: Number(ref.offset), length: Number(ref.length) };
36
+ });
37
+ }
13
38
  export class DirectContentPlane {
14
39
  /** Key material is stable for a resource's lifetime, so one handoff per
15
40
  * process is enough; it lives only in memory, never at rest. */
16
41
  keys = new Map();
17
42
  brokers = new Map();
43
+ spans;
18
44
  wire;
19
45
  constructor(wire) {
20
46
  this.wire = wire;
47
+ this.spans = createSpanFetcher({ fetchSpan: fetchStorageSpan });
21
48
  }
22
49
  /** Fetch one sealed storage object and open it. Every attempt asks for a
23
- * fresh URL — expiry is the gateway's revocation lever, so URLs are used
24
- * immediately and never reused. */
50
+ * fresh ref — URL expiry is the gateway's revocation lever, so URLs are
51
+ * used immediately and never reused. */
25
52
  async fetchObject(resourceId, object) {
26
- const [{ key, contract }, url] = await Promise.all([
53
+ const [{ key, contract }, ref] = await Promise.all([
27
54
  this.contentKey(resourceId),
28
- this.broker(resourceId).url(object),
55
+ this.broker(resourceId).ref(object),
29
56
  ]);
30
- const response = await fetch(url);
31
- if (response.status === 404) {
32
- // Preserve the authority's established missing-content code. Callers
33
- // that are immediately following a successful immutable put may opt
34
- // into their existing bounded not-found retry; other callers fail.
35
- throw new WireRequestError({
36
- type: "shared.error",
37
- code: "entity_content_not_found",
38
- message: `content storage has no ${object.kind} ${object.sha256}`,
39
- status: 404,
40
- retryable: false,
41
- });
57
+ let sealed;
58
+ try {
59
+ sealed = await this.spans.fetch(ref);
42
60
  }
43
- if (!response.ok) {
44
- throw new Error(`content storage answered ${response.status} for a ${object.kind}`);
61
+ catch (error) {
62
+ // The span fetch cannot name the object it served; restore the
63
+ // caller-facing identity on the authority's established missing code.
64
+ if (error instanceof WireRequestError && error.code === "entity_content_not_found") {
65
+ throw new WireRequestError({
66
+ type: "shared.error",
67
+ code: "entity_content_not_found",
68
+ message: `content storage has no ${object.kind} ${object.sha256}`,
69
+ status: 404,
70
+ retryable: false,
71
+ });
72
+ }
73
+ throw error;
45
74
  }
46
- const sealed = new Uint8Array(await response.arrayBuffer());
47
75
  const opened = await openProtectedEnvelopeBytes(key, sealed, {
48
76
  resourceId,
49
77
  contract,
@@ -79,18 +107,14 @@ export class DirectContentPlane {
79
107
  broker(resourceId) {
80
108
  let broker = this.brokers.get(resourceId);
81
109
  if (!broker) {
82
- broker = createPresignBroker({
83
- requestUrls: async (objects) => {
110
+ broker = createRefsBroker({
111
+ requestRefs: async (objects) => {
84
112
  const frame = await this.wire.request({
85
- type: "private.entity-content.presign",
113
+ type: "private.entity-content.refs",
86
114
  resource_id: resourceId,
87
115
  objects: objects.map((object) => ({ kind: object.kind, sha256: object.sha256 })),
88
- }, ["private.entity-content.presigned"]);
89
- const urls = frame.urls;
90
- if (!Array.isArray(urls) || !urls.every((url) => typeof url === "string")) {
91
- throw new Error("content authority answered an invalid presign reply");
92
- }
93
- return urls;
116
+ }, ["private.entity-content.refs-result"]);
117
+ return decodeRefs(frame);
94
118
  },
95
119
  });
96
120
  this.brokers.set(resourceId, broker);
@@ -98,4 +122,29 @@ export class DirectContentPlane {
98
122
  return broker;
99
123
  }
100
124
  }
125
+ /** One HTTP GET against content storage: the whole object, or exactly one
126
+ * pack span. A server that ignores the Range header and answers the full
127
+ * object is sliced here — the span fetcher's length law rejects anything
128
+ * short or shifted. */
129
+ async function fetchStorageSpan(url, span) {
130
+ const response = await fetch(url, span ? {
131
+ headers: { range: `bytes=${span.offset}-${span.offset + span.length - 1}` },
132
+ } : undefined);
133
+ if (response.status === 404) {
134
+ throw new WireRequestError({
135
+ type: "shared.error",
136
+ code: "entity_content_not_found",
137
+ message: "content storage has no object at a referenced location",
138
+ status: 404,
139
+ retryable: false,
140
+ });
141
+ }
142
+ if (!response.ok) {
143
+ throw new Error(`content storage answered ${response.status} for a content read`);
144
+ }
145
+ const body = new Uint8Array(await response.arrayBuffer());
146
+ if (!span || response.status === 206)
147
+ return body;
148
+ return body.subarray(span.offset, span.offset + span.length);
149
+ }
101
150
  //# sourceMappingURL=content-direct.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-direct.js","sourceRoot":"","sources":["../src/content-direct.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content-direct.js","sourceRoot":"","sources":["../src/content-direct.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { existsSync, lstatSync, realpathSync, readFileSync, readdirSync, readlin
3
3
  import { open, readFile } from "node:fs/promises";
4
4
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import { buildUserHomeManifest, buildUserManifest, liveMachineStateDir, scopedAmalgmDir, shippedUserHomeDeclaration, } from "@amalgm/core/identity";
6
- import { CHUNK_BYTES, CONTENT_CONTRACT, INLINE_CARGO_MAX_BYTES, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, decodeAcceptedEntityRecord, deriveUploadManifest, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, downloadAll as downloadAllArtifacts, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, pathIsSuspect, PRESIGN_BATCH_OBJECTS, privateEntityResourceId, repositoryIdentityHash, sameRecords, snapshotFromRecords, stableJson, streamCargo, travelingRecords, upload as uploadArtifact, userGroundRecords, GRAPH_HEAD_OPERATION_KIND, } from "@amalgm/live";
6
+ import { CHUNK_BYTES, CONTENT_CONTRACT, INLINE_CARGO_MAX_BYTES, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, decodeAcceptedEntityRecord, deriveUploadManifest, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, downloadAll as downloadAllArtifacts, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, pathIsSuspect, CONTENT_REFS_BATCH_OBJECTS, privateEntityResourceId, repositoryIdentityHash, sameRecords, snapshotFromRecords, stableJson, streamCargo, travelingRecords, upload as uploadArtifact, userGroundRecords, GRAPH_HEAD_OPERATION_KIND, } from "@amalgm/live";
7
7
  import Database from "better-sqlite3";
8
8
  import { atomicCopy, atomicWrite, ensurePrivateDir, ensureUserDir } from "./filesystem.js";
9
9
  import { emitFilesStage, } from "./files-observability.js";
@@ -833,10 +833,10 @@ export class UserGroundHost {
833
833
  throw new Error("entity Record rail already started");
834
834
  await this.wire.open();
835
835
  // One floor, no per-feature gates: this shell and its gateway release
836
- // together, and the gateway ships first. 13 = immutable registry graph
837
- // pages plus one graph-head commit; an older gateway cannot publish or
838
- // resolve that representation.
839
- if (this.wire.protocolVersion < 13) {
836
+ // together, and the gateway ships first. 14 = immutable content packs
837
+ // with a refs frame answering locations; an older gateway still speaks
838
+ // presign and cannot resolve a packed object at all.
839
+ if (this.wire.protocolVersion < 14) {
840
840
  throw new Error("gateway speaks an older wire protocol than this shell");
841
841
  }
842
842
  this.adoptCloudDeliveryBaseline(identity, this.cloudState.deliveryWatermark);
@@ -1637,8 +1637,9 @@ export class UserGroundHost {
1637
1637
  };
1638
1638
  // Every storage object — the manifest and each block — is fetched
1639
1639
  // straight from content storage over a presigned URL and opened locally;
1640
- // the wire carries only the key handoff and signing requests. A retry
1641
- // asks for a fresh URL every attempt.
1640
+ // packed objects arrive as merged byte ranges of their immutable pack.
1641
+ // The wire carries only the key handoff and refs requests. A retry asks
1642
+ // for a fresh ref every attempt.
1642
1643
  const fetchObject = async (object) => {
1643
1644
  requests += 1;
1644
1645
  const bytes = await retryContent(() => this.direct.fetchObject(resourceId, object), true, retryAttempts);
@@ -2275,8 +2276,8 @@ export class UserGroundHost {
2275
2276
  * frames; the answer is the only permission to skip an upload. */
2276
2277
  async sealedContentHashes(resourceId, hashes) {
2277
2278
  const batches = [];
2278
- for (let start = 0; start < hashes.length; start += PRESIGN_BATCH_OBJECTS) {
2279
- batches.push(hashes.slice(start, start + PRESIGN_BATCH_OBJECTS));
2279
+ for (let start = 0; start < hashes.length; start += CONTENT_REFS_BATCH_OBJECTS) {
2280
+ batches.push(hashes.slice(start, start + CONTENT_REFS_BATCH_OBJECTS));
2280
2281
  }
2281
2282
  const sealed = new Set();
2282
2283
  const answers = await Promise.all(batches.map((batch) => retryContent(() => this.wire.request({