@sanoma-ai/connector-cloudflare 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/registry.js CHANGED
@@ -2,10 +2,11 @@ import { createHash } from "node:crypto";
2
2
  import { DriverError, errorMessage, retryableStatus } from "@sanoma-ai/workflows";
3
3
  /*
4
4
  * The runtime image copied into the account's registry, registry to registry over the OCI
5
- * distribution API with `fetch`, no Docker: a container application runs an image of its own
6
- * account's registry, which Cloudflare pulls without a limit and caches. The source is pulled
7
- * anonymously (`ghcr.io/sanoma-ai/runtime`, public), the target pushed to with the temporary
8
- * credentials the driver gets from Cloudflare.
5
+ * distribution API with `fetch`, no Docker: a container application of the `durable_object`
6
+ * scheduling policy runs only an image of its own account's registry, pinned by digest, which
7
+ * Cloudflare pulls without a limit and caches. The source is pulled anonymously
8
+ * (`ghcr.io/sanoma-ai/runtime`, public), the target pushed to with the temporary credentials the
9
+ * driver gets from Cloudflare.
9
10
  */
10
11
  /** Cloudflare's registry. An image already in the account's runs as declared; any other is copied in. */
11
12
  export const CLOUDFLARE_REGISTRY = "registry.cloudflare.com";
@@ -44,20 +45,38 @@ export function accountImage(image, account, registry = CLOUDFLARE_REGISTRY) {
44
45
  const { name, sep, reference } = parse(image);
45
46
  return `${registry}/${account}/${name.replaceAll("/", "-")}${sep}${reference}`;
46
47
  }
48
+ /**
49
+ * True when `image` is in the account's registry and pinned by digest: what the application runs
50
+ * as it is, with nothing to copy or ask.
51
+ */
52
+ export function accountPinned(image, account, registry = CLOUDFLARE_REGISTRY) {
53
+ return image.startsWith(`${registry}/${account}/`) && parse(image).sep === "@";
54
+ }
55
+ /** `image`'s repository at `digest`: `<registry>/<path>@sha256:…`, the one reference the application takes. */
56
+ export function pinImage(image, digest) {
57
+ const { host, name } = parse(image);
58
+ return `${host}/${name}@${digest}`;
59
+ }
47
60
  /** A registry on this machine is spoken to over HTTP, as Docker does; any other over HTTPS. */
48
61
  const origin = (host) => `${/^(localhost|127\.0\.0\.1)(:\d+)?$/.test(host) ? "http" : "https"}://${host}`;
49
62
  /**
50
63
  * Copies `image` into the account's registry as `accountImage(image)`, unless the target's
51
- * manifest by that tag has the source's digest already. Each blob the target lacks is streamed
52
- * from the source; the manifest goes last, its bytes and media type unchanged, so its digest is
53
- * the source's. Each request may take `timeoutMs` (default ten minutes: a layer streams through).
54
- * A failure is a `DriverError`, retryable as its status is; the credentials are in no message.
64
+ * manifest by that tag has the source's digest already, and returns the copy pinned by that
65
+ * digest (`pinImage`). Each blob the target lacks is streamed from the source; the manifest goes
66
+ * last, its bytes and media type unchanged, so its digest is the source's. An image in the
67
+ * account's registry already is not copied: pinned by digest it is returned as it is, by a tag
68
+ * its manifest's digest is asked for. Each request may take `timeoutMs` (default ten minutes: a
69
+ * layer streams through). A failure is a `DriverError`, retryable as its status is; the
70
+ * credentials are in no message.
55
71
  */
56
72
  export async function copyImage(image, target, timeoutMs = 10 * 60_000) {
57
73
  const source = parse(image);
58
- const to = parse(accountImage(image, target.account, target.registry));
74
+ const copy = accountImage(image, target.account, target.registry);
75
+ const to = parse(copy);
59
76
  const ref = source.reference;
60
77
  const refuse = (why) => new DriverError(`cloudflare: copying ${image}: ${why}`, { retryable: false });
78
+ if (accountPinned(image, target.account, target.registry))
79
+ return image;
61
80
  // Docker Hub is not copied from: its API is on another host, and its pulls are limited.
62
81
  if (!/[.:]/.test(source.host) || source.host === "docker.io") {
63
82
  throw refuse("name the registry it is in, not Docker Hub: ghcr.io/sanoma-ai/runtime:<version>");
@@ -115,6 +134,20 @@ export async function copyImage(image, target, timeoutMs = 10 * 60_000) {
115
134
  throw refuse(`${url.origin}${url.pathname} gave no token`);
116
135
  return { authorization: `Bearer ${token ?? access_token}` };
117
136
  };
137
+ const basic = `Basic ${Buffer.from(`${target.username}:${target.password}`).toString("base64")}`;
138
+ const into = `${origin(to.host)}/v2/${to.name}`;
139
+ if (copy === image) {
140
+ // In the account by a tag (pushed by hand): only its digest is asked for.
141
+ const auth = await authorize(to.host, `repository:${to.name}:pull`, basic);
142
+ const there = await ask(`${into}/manifests/${ref}`, {
143
+ method: "HEAD",
144
+ headers: { ...auth, accept: MANIFESTS.join(", ") },
145
+ });
146
+ const digest = there.headers.get("docker-content-digest");
147
+ if (!digest?.startsWith("sha256:"))
148
+ throw refuse("the account's registry named no digest for it");
149
+ return pinImage(image, digest);
150
+ }
118
151
  const pull = await authorize(source.host, `repository:${source.name}:pull`);
119
152
  const from = `${origin(source.host)}/v2/${source.name}`;
120
153
  const got = await send(`${from}/manifests/${ref}`, { headers: { ...pull, accept: MANIFESTS.join(", ") } });
@@ -125,12 +158,10 @@ export async function copyImage(image, target, timeoutMs = 10 * 60_000) {
125
158
  const digest = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
126
159
  if (source.sep === "@" && digest !== ref)
127
160
  throw refuse(`its registry answered a manifest of digest ${digest}`);
128
- const basic = `Basic ${Buffer.from(`${target.username}:${target.password}`).toString("base64")}`;
129
161
  const push = await authorize(to.host, `repository:${to.name}:pull,push`, basic);
130
- const into = `${origin(to.host)}/v2/${to.name}`;
131
162
  const there = await ask(`${into}/manifests/${ref}`, { method: "HEAD", headers: { ...push, accept: mediaType } }, [404]);
132
163
  if (there.ok && there.headers.get("docker-content-digest") === digest)
133
- return;
164
+ return pinImage(copy, digest);
134
165
  const manifest = JSON.parse(new TextDecoder().decode(bytes));
135
166
  for (const { digest: blob, size } of [manifest.config, ...manifest.layers]) {
136
167
  const head = await ask(`${into}/blobs/${blob}`, { method: "HEAD", headers: push }, [404]);
@@ -156,5 +187,6 @@ export async function copyImage(image, target, timeoutMs = 10 * 60_000) {
156
187
  headers: { ...push, "content-type": mediaType },
157
188
  body: bytes,
158
189
  });
190
+ return pinImage(copy, digest);
159
191
  }
160
192
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,7 @@ export declare const cloudflare: {
27
27
  at: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
28
28
  rang: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
29
29
  healthy: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodBoolean>>;
30
+ token: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
30
31
  }, import("zod/v4/core").$strip>, "cloudflare.alarm", {
31
32
  readonly host: {
32
33
  readonly type: readonly ["cloudflare.container"];
package/dist/steps.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { defineSteps } from "@sanoma-ai/workflows";
2
2
  import { cloudflareOps } from "./connector.js";
3
3
  const { create } = cloudflareOps.container;
4
- /** The runtime image the steps' containers name: one already in Cloudflare's registry, so nothing copies it. */
5
- const IMAGE = "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime:0.7.0";
4
+ /** The runtime image the steps' containers name: one already in the account's registry, pinned by digest, so nothing copies or asks for it. */
5
+ const IMAGE = "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime@sha256:7777777777777777777777777777777777777777777777777777777777777777";
6
6
  /**
7
7
  * Cloudflare's steps for Gherkin tests: a company's container before the run (named `host`, so
8
8
  * an operation's own test can use `$host.state.url`), its secrets carrying the wakeup token.
package/dist/steps.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC;AAE3C,gHAAgH;AAChH,MAAM,KAAK,GAAG,kFAAkF,CAAC;AAEjG;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC,YAAY,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;IACrE,KAAK,CAAC,0DAA0D,EAAE,CAAC,IAAY,EAAE,QAAgB,EAAE,EAAE,CAAC,CAAC;QACrG,EAAE,EAAE,MAAM;QACV,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,IAAI;gBACJ,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,2BAA2B,IAAI,MAAM;gBAC7C,GAAG,EAAE,MAAM;gBACX,QAAQ;gBACR,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,EAAE,mBAAmB,EAAE,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;aACrF;SACF;QACD,EAAE,EAAE,MAAM;KACX,CAAC,CAAC,CAAC;AACN,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC;AAE3C,+IAA+I;AAC/I,MAAM,KAAK,GACT,oJAAoJ,CAAC;AAEvJ;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC,YAAY,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;IACrE,KAAK,CAAC,0DAA0D,EAAE,CAAC,IAAY,EAAE,QAAgB,EAAE,EAAE,CAAC,CAAC;QACrG,EAAE,EAAE,MAAM;QACV,KAAK,EAAE;YACL,OAAO,EAAE;gBACP,IAAI;gBACJ,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,2BAA2B,IAAI,MAAM;gBAC7C,GAAG,EAAE,MAAM;gBACX,QAAQ;gBACR,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,EAAE,mBAAmB,EAAE,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;aACrF;SACF;QACD,EAAE,EAAE,MAAM;KACX,CAAC,CAAC,CAAC;AACN,CAAC,CAAC,CAAC"}
package/dist/worker.d.ts CHANGED
@@ -9,4 +9,4 @@ export interface WorkerModule {
9
9
  }
10
10
  /** The Worker's modules, read from the package once. */
11
11
  export declare function workerModules(): Promise<WorkerModule[]>;
12
- export { HEALTH_PATH, INACTIVITY_MS, Instance, NAMESPACE, type ObjectState, WAKE_PATH, worker, } from "../worker/instance.js";
12
+ export { HEALTH_PATH, IMAGE, INACTIVITY_MS, Instance, NAMESPACE, type ObjectState, worker, } from "../worker/instance.js";
package/dist/worker.js CHANGED
@@ -17,5 +17,5 @@ export function workerModules() {
17
17
  });
18
18
  return modules;
19
19
  }
20
- export { HEALTH_PATH, INACTIVITY_MS, Instance, NAMESPACE, WAKE_PATH, worker, } from "../worker/instance.js";
20
+ export { HEALTH_PATH, IMAGE, INACTIVITY_MS, Instance, NAMESPACE, worker, } from "../worker/instance.js";
21
21
  //# sourceMappingURL=worker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,+FAA+F;AAC/F,uEAAuE;AAEvE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAEtD,uFAAuF;AACvF,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AACtC,6CAA6C;AAC7C,MAAM,OAAO,GAAG,CAAC,WAAW,EAAE,aAAa,CAAU,CAAC;AAEtD,gGAAgG;AAChG,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAQ7C,IAAI,OAA4C,CAAC;AAEjD,wDAAwD;AACxD,MAAM,UAAU,aAAa;IAC3B,OAAO,KAAK,OAAO,CAAC,GAAG,CACrB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAChG,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACvB,OAAO,GAAG,SAAS,CAAC;QACpB,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,OAAO,EACL,WAAW,EACX,aAAa,EACb,QAAQ,EACR,SAAS,EAET,SAAS,EACT,MAAM,GACP,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,+FAA+F;AAC/F,uEAAuE;AAEvE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAEtD,uFAAuF;AACvF,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AACtC,6CAA6C;AAC7C,MAAM,OAAO,GAAG,CAAC,WAAW,EAAE,aAAa,CAAU,CAAC;AAEtD,gGAAgG;AAChG,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAQ7C,IAAI,OAA4C,CAAC;AAEjD,wDAAwD;AACxD,MAAM,UAAU,aAAa;IAC3B,OAAO,KAAK,OAAO,CAAC,GAAG,CACrB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAChG,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACvB,OAAO,GAAG,SAAS,CAAC;QACpB,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,OAAO,EACL,WAAW,EACX,KAAK,EACL,aAAa,EACb,QAAQ,EACR,SAAS,EAET,MAAM,GACP,MAAM,uBAAuB,CAAC"}
@@ -10,7 +10,7 @@ Feature: Cloudflare containers and alarms
10
10
  {
11
11
  "desired": {
12
12
  "name": "company",
13
- "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime:0.7.0",
13
+ "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime@sha256:7777777777777777777777777777777777777777777777777777777777777777",
14
14
  "source": "https://github.com/acme/company.git",
15
15
  "ref": "main",
16
16
  "revision": "abc123",
@@ -33,7 +33,7 @@ Feature: Cloudflare containers and alarms
33
33
  "gone": false,
34
34
  "state": {
35
35
  "name": "company",
36
- "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime:0.7.0",
36
+ "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime@sha256:7777777777777777777777777777777777777777777777777777777777777777",
37
37
  "source": "https://github.com/acme/company.git",
38
38
  "ref": "main",
39
39
  "revision": "abc123",
@@ -51,7 +51,7 @@ Feature: Cloudflare containers and alarms
51
51
  "id": "company",
52
52
  "desired": {
53
53
  "name": "company",
54
- "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime:0.8.0",
54
+ "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime@sha256:8888888888888888888888888888888888888888888888888888888888888888",
55
55
  "source": "https://github.com/acme/company.git",
56
56
  "ref": "main",
57
57
  "revision": "abc123",
@@ -62,7 +62,7 @@ Feature: Cloudflare containers and alarms
62
62
  """
63
63
  Then it returns
64
64
  """
65
- { "action": "update", "replace": [], "planned": { "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime:0.8.0" } }
65
+ { "action": "update", "replace": [], "planned": { "image": "registry.cloudflare.com/0123456789abcdef0123456789abcdef/sanoma-ai-runtime@sha256:8888888888888888888888888888888888888888888888888888888888888888" } }
66
66
  """
67
67
 
68
68
  Scenario: Delete
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanoma-ai/connector-cloudflare",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "Cloudflare for Sanoma: a Container host that stops when quiet, and a Durable Object alarm that wakes it, from one Worker",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -61,12 +61,12 @@
61
61
  },
62
62
  "devDependencies": {
63
63
  "@sanoma-ai/testing": "0.1.0",
64
- "@sanoma-ai/workflows": "0.9.1",
64
+ "@sanoma-ai/workflows": "0.10.3",
65
65
  "msw": "^3.0.3",
66
66
  "zod": "^4.6.5"
67
67
  },
68
68
  "peerDependencies": {
69
- "@sanoma-ai/workflows": "^0.9.1",
69
+ "@sanoma-ai/workflows": "^0.10.3",
70
70
  "zod": "^4"
71
71
  },
72
72
  "engines": {
@@ -8,6 +8,8 @@ export declare const HEALTH_PATH: "/__sanoma/health";
8
8
  export declare const INACTIVITY_MS: number;
9
9
  /** The binding that names the object, in the Worker's env. */
10
10
  export declare const NAMESPACE: "SANOMA_INSTANCE";
11
+ /** The name the upload gives the runtime image (`containers[].images`), which the object starts. */
12
+ export declare const IMAGE: "runtime";
11
13
 
12
14
  /** The label naming an image: a digest of it. */
13
15
  export declare function imageLabel(image: string): Promise<string>;
@@ -16,11 +18,21 @@ export declare function imageLabel(image: string): Promise<string>;
16
18
  export interface ObjectState {
17
19
  container: {
18
20
  readonly running: boolean;
19
- start(options: { env: Record<string, string>; enableInternet: boolean; labels: Record<string, string> }): void;
20
- inspect(): Promise<{ labels: Record<string, string> } | null>;
21
+ /** The images the Worker's upload names (`containers[].images`), by name. */
22
+ readonly images: Record<string, string>;
23
+ start(options: {
24
+ image: string;
25
+ instance: string;
26
+ env: Record<string, string>;
27
+ enableInternet: boolean;
28
+ labels: Record<string, string>;
29
+ }): void;
30
+ inspect(): Promise<{ image?: string; labels: Record<string, string> } | null>;
21
31
  destroy(reason?: string): Promise<void>;
22
32
  getTcpPort(port: number): { fetch(input: string | Request, init?: RequestInit): Promise<Response> };
23
33
  setInactivityTimeout(ms: number): Promise<void>;
34
+ /** Settles when the container started last ends: resolved on exit 0, rejected on any other. */
35
+ monitor?(): Promise<void>;
24
36
  };
25
37
  storage: {
26
38
  get<T>(key: string): Promise<T | undefined>;
@@ -3,8 +3,9 @@
3
3
  // a stubbed `ctx`, so both kits check this code. No imports, so Node loads it as it is.
4
4
  //
5
5
  // The object runs the company's container (`ctx.container`): it starts it on a request, at the
6
- // revision and image the Worker's `SANOMA_REVISION` and `SANOMA_IMAGE` name (a running one at
7
- // another is destroyed and started again: a deploy), waits for `/healthz`, and proxies every
6
+ // Worker's `SANOMA_REVISION` and the image its upload names (`ctx.container.images.runtime`, as
7
+ // the `durable_object` scheduling policy has it: the object says what runs), a running one at
8
+ // another destroyed and started again (a deploy); it waits for `/healthz`, and proxies every
8
9
  // request to it. It answers `/__sanoma/health` without starting it (with `?start=1`, once it
9
10
  // runs), and holds the company's one wake (`/__sanoma/wake`) as an alarm, which starts the
10
11
  // container at `at` and calls its `/wake`.
@@ -27,11 +28,16 @@ const MAX_POLL_MS = 1_000;
27
28
  const WAKE_KEY = "wake";
28
29
  /** The binding that names the object, in the Worker's env. */
29
30
  export const NAMESPACE = "SANOMA_INSTANCE";
31
+ /** The name the upload gives the runtime image (`containers[].images`), which the object starts. */
32
+ export const IMAGE = "runtime";
33
+ /** The container's size, one of the named types `start` takes: ½ vCPU, 4 GiB (`basic`, ¼ vCPU, is refused under this policy). */
34
+ const INSTANCE_TYPE = "standard-1";
30
35
 
31
36
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
32
37
  const json = (status, body) =>
33
38
  new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
34
- const problem = (status, error) => json(status, { error });
39
+ /** A refusal; with `exit`, how the container ended: a deploy stops waiting on it. */
40
+ const problem = (status, error, exit) => json(status, { error, ...(exit !== undefined && { exit }) });
35
41
 
36
42
  /** The Worker's text bindings (plain text and secrets): the container's environment. */
37
43
  function containerEnv(env) {
@@ -64,6 +70,12 @@ export class Instance {
64
70
  #starting;
65
71
  /** True once a container this object started (or found) answered `/healthz`. */
66
72
  #ready = false;
73
+ /** What the last try of a start saw, for the health route while the container is not ready. */
74
+ #last;
75
+ /** How the container this object started ended, when it did: what `monitor` told. */
76
+ #exit;
77
+ /** The start whose `monitor` may set `#exit`: an earlier container's end is not this one's. */
78
+ #started;
67
79
 
68
80
  constructor(ctx, env) {
69
81
  this.ctx = ctx;
@@ -86,13 +98,15 @@ export class Instance {
86
98
  try {
87
99
  await this.ensureRunning();
88
100
  } catch (error) {
89
- return problem(503, `the company's container did not start: ${error?.message ?? error}`);
101
+ return problem(503, `the company's container did not start: ${error?.message ?? error}`, this.#exit);
90
102
  }
91
103
  if (health) return this.health();
92
104
  const headers = new Headers(request.headers);
93
- headers.delete("host");
105
+ // The port's fetch reaches the container whatever the URL's host, which becomes the request's
106
+ // Host header: the one asked for, so the company serves the public site declared on it.
107
+ headers.set("x-forwarded-proto", url.protocol.slice(0, -1));
94
108
  const body = request.method === "GET" || request.method === "HEAD" ? undefined : request.body;
95
- const forwarded = new Request(new URL(url.pathname + url.search, "http://container"), {
109
+ const forwarded = new Request(new URL(url.pathname + url.search, `http://${url.host}`), {
96
110
  method: request.method,
97
111
  headers,
98
112
  body,
@@ -110,7 +124,13 @@ export class Instance {
110
124
 
111
125
  /** What the Worker runs, and whether its container does. */
112
126
  health() {
113
- return json(200, { revision: this.env.SANOMA_REVISION ?? "", running: this.ctx.container.running });
127
+ return json(200, {
128
+ revision: this.env.SANOMA_REVISION ?? "",
129
+ running: this.ctx.container.running,
130
+ ready: this.#ready,
131
+ ...(this.#last !== undefined && !this.#ready && { last: this.#last }),
132
+ ...(this.#exit !== undefined && { exit: this.#exit }),
133
+ });
114
134
  }
115
135
 
116
136
  /** Starts the container at the Worker's revision and image unless it runs them and answered; one start at a time. */
@@ -122,13 +142,29 @@ export class Instance {
122
142
 
123
143
  async #start() {
124
144
  this.#ready = false;
145
+ this.#exit = undefined;
125
146
  const container = this.ctx.container;
126
- const labels = { revision: this.env.SANOMA_REVISION ?? "", image: await imageLabel(this.env.SANOMA_IMAGE ?? "") };
147
+ const image = container.images?.[IMAGE];
148
+ // Not one to retry: the Worker was uploaded without its image.
149
+ if (!image) throw new Error(`the Worker names no "${IMAGE}" image among its containers' images`);
150
+ const labels = { revision: this.env.SANOMA_REVISION ?? "", image: await imageLabel(image) };
127
151
  const until = Date.now() + START_TIMEOUT_MS;
128
152
  let last;
153
+ const saw = (what) => (last = this.#last = what);
129
154
  let wait = POLL_MS;
130
155
  let checked = false;
131
156
  while (Date.now() < until) {
157
+ if (this.#exit) {
158
+ // Right after the application is made, the platform may not have assigned it to the
159
+ // namespace yet: that start fails and is tried again. Any other end is the answer, as a
160
+ // container that ended is not started again and again. Outside the try: a try's failure
161
+ // is what one try saw, and would otherwise swallow this.
162
+ if (!this.#exit.includes("no container application assigned")) {
163
+ throw new Error(`${this.#exit} before answering /healthz on port ${this.port} (${last})`);
164
+ }
165
+ saw(this.#exit);
166
+ this.#exit = undefined;
167
+ }
132
168
  try {
133
169
  if (!checked && container.running) {
134
170
  // One running from before: kept only if it says it started at these (a deploy otherwise).
@@ -138,7 +174,25 @@ export class Instance {
138
174
  }
139
175
  checked = true;
140
176
  if (!container.running) {
141
- container.start({ env: containerEnv(this.env), enableInternet: true, labels });
177
+ container.start({
178
+ image,
179
+ instance: INSTANCE_TYPE,
180
+ env: containerEnv(this.env),
181
+ enableInternet: true,
182
+ labels,
183
+ });
184
+ // `start` returns once its options are checked: a start that fails later, or a container
185
+ // that exits, is what `monitor` tells, and the next try reports it. Only this start's, and
186
+ // only until it answered: an earlier container's monitor may settle after this one started,
187
+ // and one that answered and then stopped (Cloudflare's own stop, a crash) is started again
188
+ // by the next request, not reported as a start that failed.
189
+ this.#exit = undefined;
190
+ const started = (this.#started = {});
191
+ const ended = (how) => void (this.#started === started && !this.#ready && (this.#exit = how));
192
+ container.monitor?.().then(
193
+ () => ended("the container exited"),
194
+ (error) => ended(`the container failed: ${error?.message ?? error}`),
195
+ );
142
196
  }
143
197
  // A try of its own time: a hung one would hold every request past the deadline.
144
198
  const res = await container
@@ -150,16 +204,17 @@ export class Instance {
150
204
  this.#ready = true;
151
205
  return;
152
206
  }
153
- last = `/healthz answered ${res.status}`;
207
+ saw(`/healthz answered ${res.status}`);
154
208
  } catch (error) {
155
209
  // Not listening yet, or not yet free to start again after a destroy.
156
- last = error?.message ?? String(error);
210
+ saw(error?.message ?? String(error));
157
211
  }
158
- // Doubling to a second: a container that crashes as it starts is not started four times a second.
212
+ // Doubling to a second: a container that is slow to listen is not asked four times a second.
159
213
  await sleep(wait);
160
214
  wait = Math.min(wait * 2, MAX_POLL_MS);
161
215
  }
162
- throw new Error(`no answer from /healthz on port ${this.port} in ${START_TIMEOUT_MS / 1000} s (${last})`);
216
+ const ended = this.#exit ? `; ${this.#exit}` : "";
217
+ throw new Error(`no answer from /healthz on port ${this.port} in ${START_TIMEOUT_MS / 1000} s (${last}${ended})`);
163
218
  }
164
219
 
165
220
  /** `/__sanoma/wake`, with the company's `SANOMA_WAKEUP_TOKEN`: the wake this object holds. */