@farthershore/backend 0.8.1 → 0.8.2

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/README.md CHANGED
@@ -4,7 +4,7 @@ Runtime metering and gateway-verification SDK for builder upstreams. Install one
4
4
  package, set one token (`FS_RUNTIME_TOKEN`), and Farther Shore handles signed
5
5
  gateway-to-upstream request verification plus response-bound usage reporting.
6
6
 
7
- > **Status: `0.8.1` (lockstep SDK family).** Published at the SAME version as
7
+ > **Status: `0.8.2` (lockstep SDK family).** Published at the SAME version as
8
8
  > `@farthershore/farthershore-js` and `@farthershore/product` — pin the three
9
9
  > together. Pre-1.0: minor bumps may break.
10
10
 
@@ -54,8 +54,8 @@ var RUNTIME_BOOTSTRAP_CONTRACT = {
54
54
  perEventMax: "number"
55
55
  },
56
56
  transport: {
57
- mode: "public_origin | mtls | cloudflare_tunnel",
58
- runner: "managed_cloudflared | sidecar | null",
57
+ mode: "direct | tunnel",
58
+ runner: "embedded | sidecar | null",
59
59
  originUrl: "string?",
60
60
  originHostname: "string?",
61
61
  localTarget: "string?",
@@ -247,17 +247,16 @@ var RUNTIME_HEALTH_CONTRACT = {
247
247
  };
248
248
  var RUNTIME_TRANSPORT_CONTRACT = {
249
249
  modes: {
250
- public_origin: "Gateway fetches the builder's public origin URL; the SDK middleware fail-closed-verifies every request. Provisions zero Cloudflare objects. Standard tier; also the dev path.",
251
- mtls: "Gateway presents a Farther-Shore-issued platform-wide client cert; the builder's ingress requires + verifies it. Channel-level mutual auth; provisions zero Cloudflare objects. Standard-plus / self-hosted tier.",
252
- cloudflare_tunnel: "Farther Shore provisions a private outbound Cloudflare Tunnel; no inbound port. The only Production-secure tier. Consumes Cloudflare tunnel/route slots."
250
+ direct: "Gateway fetches the builder's public origin URL; the SDK middleware fail-closed-verifies every request via Ed25519 request signing. Provisions zero Cloudflare objects. Available on all tiers; also the dev path.",
251
+ tunnel: "Farther Shore provisions a private outbound Cloudflare Tunnel; no inbound port. The Production-secure tier. Consumes Cloudflare tunnel/route slots."
253
252
  },
254
253
  runners: {
255
- managed_cloudflared: "fs.start() supervises cloudflared as a child process (default DX).",
254
+ embedded: "fs.start() supervises cloudflared as a child process (default DX).",
256
255
  sidecar: "Vanilla cloudflare/cloudflared container beside the app (production / non-Node)."
257
256
  },
258
- channelTrust: ["mtls", "cloudflare_tunnel"],
257
+ channelTrust: ["tunnel"],
259
258
  requestTrust: "x-fs-signature",
260
- invariant: "Channel trust (mtls/tunnel) and request trust (the X-FS-* signature) are distinct layers; both always apply. CF-Access-* headers are transport-layer only and are IGNORED by the SDK."
259
+ invariant: "Channel trust (tunnel) and request trust (the X-FS-* signature) are distinct layers; both always apply. CF-Access-* headers are transport-layer only and are IGNORED by the SDK."
261
260
  };
262
261
  export {
263
262
  RUNTIME_BODY_HASH_CONTRACT,
package/dist/index.js CHANGED
@@ -918,6 +918,7 @@ var ShutdownManager = class {
918
918
 
919
919
  // src/core/tunnel.ts
920
920
  import { spawn as nodeChildSpawn } from "node:child_process";
921
+ import { createRequire } from "node:module";
921
922
  var REDACTED_TOKEN = "***REDACTED***";
922
923
  var CLOUDFLARED_RUN_ARGS = [
923
924
  "tunnel",
@@ -925,12 +926,12 @@ var CLOUDFLARED_RUN_ARGS = [
925
926
  "run",
926
927
  "--token"
927
928
  ];
928
- var DEFAULT_BINARY_CANDIDATES = [
929
- "cloudflared",
930
- "/usr/local/bin/cloudflared",
931
- "/usr/bin/cloudflared",
932
- "/opt/cloudflared/cloudflared"
933
- ];
929
+ var CLOUDFLARED_BINARY_PACKAGES = {
930
+ "linux-x64": "@farthershore/cloudflared-linux-x64",
931
+ "linux-arm64": "@farthershore/cloudflared-linux-arm64",
932
+ "darwin-arm64": "@farthershore/cloudflared-darwin-arm64",
933
+ "darwin-x64": "@farthershore/cloudflared-darwin-x64"
934
+ };
934
935
  var DEFAULT_BASE_BACKOFF_MS = 1e3;
935
936
  var DEFAULT_MAX_BACKOFF_MS = 6e4;
936
937
  var DEFAULT_BINARY = "cloudflared";
@@ -1048,15 +1049,24 @@ var CloudflaredSupervisor = class {
1048
1049
  child.on("exit", (code, signal) => this.handleExit(code, signal));
1049
1050
  }
1050
1051
  /**
1051
- * Resolve the cloudflared binary path. Explicit `binaryPath` wins; otherwise
1052
- * the injected locator runs (Linux-first). Missing ⇒ a clear, redacted error.
1052
+ * Resolve the cloudflared binary path. Order:
1053
+ * 1. an installed `@farthershore/cloudflared-<platform>` optional-dependency
1054
+ * matching process.platform+arch (the injected locator), then
1055
+ * 2. an explicit `binaryPath` supplied by the host, then
1056
+ * 3. the bare `cloudflared` name on PATH (resolved at spawn time).
1057
+ *
1058
+ * On an unsupported arch (no optional dep, no binaryPath) AND no usable PATH
1059
+ * fallback, this raises a clear, redacted error pointing at the sidecar — it
1060
+ * NEVER downloads a binary at runtime. (Cross-platform binary management is the
1061
+ * optional-dep packages' job, populated at publish time.)
1053
1062
  */
1054
1063
  resolveBinary() {
1055
- if (this.binaryPath) return this.binaryPath;
1056
1064
  const located = this.locateBinary();
1057
1065
  if (located) return located;
1066
+ if (this.binaryPath) return this.binaryPath;
1067
+ if (currentBinaryPackageName() !== null) return DEFAULT_BINARY;
1058
1068
  throw new Error(
1059
- "cloudflared binary not found \u2014 install it in the image, supply binaryPath, or run the sidecar runner instead. (Linux container is the V1 supported target.)"
1069
+ "cloudflared binary not found for this platform/arch \u2014 install an @farthershore/cloudflared-<platform> package, supply binaryPath, or run the sidecar runner instead (no binary is downloaded at runtime)."
1060
1070
  );
1061
1071
  }
1062
1072
  /** Pipe stdout/stderr to the logger with the tunnel token redacted. */
@@ -1185,8 +1195,30 @@ function nodeProcess() {
1185
1195
  const proc = globalThis.process;
1186
1196
  return proc && typeof proc.on === "function" ? proc : null;
1187
1197
  }
1198
+ function currentBinaryPackageName() {
1199
+ const proc = globalThis.process;
1200
+ if (!proc?.platform || !proc.arch) return null;
1201
+ return CLOUDFLARED_BINARY_PACKAGES[`${proc.platform}-${proc.arch}`] ?? null;
1202
+ }
1188
1203
  function defaultLocateBinary() {
1189
- return DEFAULT_BINARY_CANDIDATES[0] ?? DEFAULT_BINARY;
1204
+ const pkg = currentBinaryPackageName();
1205
+ if (!pkg) return null;
1206
+ try {
1207
+ const require2 = createRequire(import.meta.url);
1208
+ const manifestPath = require2.resolve(`${pkg}/package.json`);
1209
+ return resolvePackageBinary(require2, pkg, manifestPath);
1210
+ } catch {
1211
+ return null;
1212
+ }
1213
+ }
1214
+ function resolvePackageBinary(require2, pkg, manifestPath) {
1215
+ const sep = manifestPath.includes("\\") ? "\\" : "/";
1216
+ const root = manifestPath.slice(0, manifestPath.lastIndexOf(sep));
1217
+ const manifest = require2(manifestPath);
1218
+ const binField = manifest.bin;
1219
+ const relative = typeof binField === "string" ? binField : binField?.cloudflared ?? `bin${sep}cloudflared`;
1220
+ const normalized = relative.replace(/^\.[\\/]/, "");
1221
+ return `${root}${sep}${normalized}`;
1190
1222
  }
1191
1223
 
1192
1224
  // src/core/verifyRequest.ts
@@ -1338,8 +1370,8 @@ function headerGetter(headers) {
1338
1370
 
1339
1371
  // src/core/runtime.ts
1340
1372
  var DEFAULT_CORE_URL = "https://core.farthershore.com";
1341
- var SDK_VERSION = "0.8.1".length > 0 ? "0.8.1" : "0.0.0-dev";
1342
- var CONTRACTS_FP = "3c1f8e3d44799bd4".length > 0 ? "3c1f8e3d44799bd4" : "0000000000000000";
1373
+ var SDK_VERSION = "0.8.2".length > 0 ? "0.8.2" : "0.0.0-dev";
1374
+ var CONTRACTS_FP = "5beebf042c7ce96d".length > 0 ? "5beebf042c7ce96d" : "0000000000000000";
1343
1375
  var FartherShore = class {
1344
1376
  bootstrapClient;
1345
1377
  fetchImpl;
@@ -1419,7 +1451,7 @@ var FartherShore = class {
1419
1451
  * The reflection code is dynamically imported so it stays OFF the per-request
1420
1452
  * verification hot path (the runtime stays route-unaware there). The report is
1421
1453
  * an OUTBOUND backend→core call (same channel as bootstrap/metering), so it
1422
- * works for every transport (public_origin / mTLS / cloudflare_tunnel).
1454
+ * works for every transport (direct / tunnel).
1423
1455
  *
1424
1456
  * Returns the reconcile result (or null if reflection is unavailable / boot
1425
1457
  * reporting failed). v1 reflects Express; `app` omitted → no-op.
@@ -1494,11 +1526,11 @@ var FartherShore = class {
1494
1526
  return config.verification.required;
1495
1527
  }
1496
1528
  /**
1497
- * Start the managed runner. For a `cloudflare_tunnel` backend whose runner is
1498
- * `managed_cloudflared`, this supervises `cloudflared` as a child process
1529
+ * Start the embedded runner. For a `tunnel` backend whose runner is
1530
+ * `embedded`, this supervises `cloudflared` as a child process
1499
1531
  * (spawned via the injected/default spawner) using the tunnel token from
1500
- * bootstrap. For every other transport (`public_origin`, `mtls`, or the
1501
- * `sidecar` runner) it is a no-op — there is no SDK-managed process to run.
1532
+ * bootstrap. For every other transport (`direct`, or the `sidecar` runner)
1533
+ * it is a no-op — there is no SDK-managed process to run.
1502
1534
  *
1503
1535
  * Fail-open by default: a tunnel that cannot start does NOT crash the host app
1504
1536
  * (request verification stays fail-closed regardless — a different axis).
@@ -1507,7 +1539,7 @@ var FartherShore = class {
1507
1539
  if (this.tunnelOptions.enabled === false) return;
1508
1540
  const config = await this.ensureBootstrapped();
1509
1541
  const transport = config.transport;
1510
- if (transport.mode !== "cloudflare_tunnel" || transport.runner !== "managed_cloudflared") {
1542
+ if (transport.mode !== "tunnel" || transport.runner !== "embedded") {
1511
1543
  return;
1512
1544
  }
1513
1545
  const tunnelToken = transport.cloudflared?.tunnelToken;
@@ -1515,7 +1547,7 @@ var FartherShore = class {
1515
1547
  if (this.tunnelOptions.failClosed) {
1516
1548
  throw new FartherShoreError(
1517
1549
  "invalid_token",
1518
- "managed cloudflared runner requires a tunnel token from bootstrap"
1550
+ "embedded cloudflared runner requires a tunnel token from bootstrap"
1519
1551
  );
1520
1552
  }
1521
1553
  return;
@@ -1552,8 +1584,8 @@ var FartherShore = class {
1552
1584
  return buildHealthReport({
1553
1585
  runtimeToken: this.runtimeToken.length > 0,
1554
1586
  bootstrap: this.bootstrapped && config !== null,
1555
- // Populated by the managed-cloudflared supervisor (Slice 3). Null until
1556
- // fs.start() launches a managed tunnel; otherwise the supervisor state.
1587
+ // Populated by the embedded-cloudflared supervisor (Slice 3). Null until
1588
+ // fs.start() launches an embedded tunnel; otherwise the supervisor state.
1557
1589
  tunnel: this.tunnel ? this.tunnel.healthString() : null,
1558
1590
  verification: this.verificationEnabled && config !== null,
1559
1591
  metering: this.meteringClient !== null
@@ -3,9 +3,9 @@ import type { ReconcileResult } from "../reflect/reconcile.js";
3
3
  import { type MeterOptions } from "./metering.js";
4
4
  import { type SpawnFn } from "./tunnel.js";
5
5
  import { type FartherShoreRequestContext, type VerifyRequestInput } from "./verifyRequest.js";
6
- /** Advanced opt-in tunnel config. The managed runner is the default DX. */
6
+ /** Advanced opt-in tunnel config. The embedded runner is the default DX. */
7
7
  export type FartherShoreTunnelOptions = {
8
- /** Opt out of the managed cloudflared runner (e.g. sidecar mode). */
8
+ /** Opt out of the embedded cloudflared runner (e.g. sidecar mode). */
9
9
  enabled?: boolean;
10
10
  /** Injected spawner (tests/non-default hosts). Defaults to node:child_process. */
11
11
  spawn?: SpawnFn;
@@ -35,7 +35,7 @@ export type FartherShoreInitOptions = {
35
35
  metering?: {
36
36
  enabled?: boolean;
37
37
  };
38
- /** Managed-cloudflared runner config (advanced opt-in; default DX is on). */
38
+ /** Embedded-cloudflared runner config (advanced opt-in; default DX is on). */
39
39
  tunnel?: FartherShoreTunnelOptions;
40
40
  /** SDK metadata forwarded to bootstrap. */
41
41
  instanceId?: string;
@@ -73,7 +73,7 @@ export declare class FartherShore {
73
73
  * The reflection code is dynamically imported so it stays OFF the per-request
74
74
  * verification hot path (the runtime stays route-unaware there). The report is
75
75
  * an OUTBOUND backend→core call (same channel as bootstrap/metering), so it
76
- * works for every transport (public_origin / mTLS / cloudflare_tunnel).
76
+ * works for every transport (direct / tunnel).
77
77
  *
78
78
  * Returns the reconcile result (or null if reflection is unavailable / boot
79
79
  * reporting failed). v1 reflects Express; `app` omitted → no-op.
@@ -89,11 +89,11 @@ export declare class FartherShore {
89
89
  /** Whether verification is required (bootstrap × opt-out). */
90
90
  verificationRequired(): Promise<boolean>;
91
91
  /**
92
- * Start the managed runner. For a `cloudflare_tunnel` backend whose runner is
93
- * `managed_cloudflared`, this supervises `cloudflared` as a child process
92
+ * Start the embedded runner. For a `tunnel` backend whose runner is
93
+ * `embedded`, this supervises `cloudflared` as a child process
94
94
  * (spawned via the injected/default spawner) using the tunnel token from
95
- * bootstrap. For every other transport (`public_origin`, `mtls`, or the
96
- * `sidecar` runner) it is a no-op — there is no SDK-managed process to run.
95
+ * bootstrap. For every other transport (`direct`, or the `sidecar` runner)
96
+ * it is a no-op — there is no SDK-managed process to run.
97
97
  *
98
98
  * Fail-open by default: a tunnel that cannot start does NOT crash the host app
99
99
  * (request verification stays fail-closed regardless — a different axis).
@@ -121,8 +121,16 @@ export declare class CloudflaredSupervisor {
121
121
  healthString(): string;
122
122
  private spawnChild;
123
123
  /**
124
- * Resolve the cloudflared binary path. Explicit `binaryPath` wins; otherwise
125
- * the injected locator runs (Linux-first). Missing ⇒ a clear, redacted error.
124
+ * Resolve the cloudflared binary path. Order:
125
+ * 1. an installed `@farthershore/cloudflared-<platform>` optional-dependency
126
+ * matching process.platform+arch (the injected locator), then
127
+ * 2. an explicit `binaryPath` supplied by the host, then
128
+ * 3. the bare `cloudflared` name on PATH (resolved at spawn time).
129
+ *
130
+ * On an unsupported arch (no optional dep, no binaryPath) AND no usable PATH
131
+ * fallback, this raises a clear, redacted error pointing at the sidecar — it
132
+ * NEVER downloads a binary at runtime. (Cross-platform binary management is the
133
+ * optional-dep packages' job, populated at publish time.)
126
134
  */
127
135
  private resolveBinary;
128
136
  /** Pipe stdout/stderr to the logger with the tunnel token redacted. */
@@ -51,8 +51,8 @@ export declare const RUNTIME_BOOTSTRAP_CONTRACT: {
51
51
  readonly perEventMax: "number";
52
52
  };
53
53
  readonly transport: {
54
- readonly mode: "public_origin | mtls | cloudflare_tunnel";
55
- readonly runner: "managed_cloudflared | sidecar | null";
54
+ readonly mode: "direct | tunnel";
55
+ readonly runner: "embedded | sidecar | null";
56
56
  readonly originUrl: "string?";
57
57
  readonly originHostname: "string?";
58
58
  readonly localTarget: "string?";
@@ -214,15 +214,14 @@ export declare const RUNTIME_HEALTH_CONTRACT: {
214
214
  };
215
215
  export declare const RUNTIME_TRANSPORT_CONTRACT: {
216
216
  readonly modes: {
217
- readonly public_origin: "Gateway fetches the builder's public origin URL; the SDK middleware fail-closed-verifies every request. Provisions zero Cloudflare objects. Standard tier; also the dev path.";
218
- readonly mtls: "Gateway presents a Farther-Shore-issued platform-wide client cert; the builder's ingress requires + verifies it. Channel-level mutual auth; provisions zero Cloudflare objects. Standard-plus / self-hosted tier.";
219
- readonly cloudflare_tunnel: "Farther Shore provisions a private outbound Cloudflare Tunnel; no inbound port. The only Production-secure tier. Consumes Cloudflare tunnel/route slots.";
217
+ readonly direct: "Gateway fetches the builder's public origin URL; the SDK middleware fail-closed-verifies every request via Ed25519 request signing. Provisions zero Cloudflare objects. Available on all tiers; also the dev path.";
218
+ readonly tunnel: "Farther Shore provisions a private outbound Cloudflare Tunnel; no inbound port. The Production-secure tier. Consumes Cloudflare tunnel/route slots.";
220
219
  };
221
220
  readonly runners: {
222
- readonly managed_cloudflared: "fs.start() supervises cloudflared as a child process (default DX).";
221
+ readonly embedded: "fs.start() supervises cloudflared as a child process (default DX).";
223
222
  readonly sidecar: "Vanilla cloudflare/cloudflared container beside the app (production / non-Node).";
224
223
  };
225
- readonly channelTrust: readonly ["mtls", "cloudflare_tunnel"];
224
+ readonly channelTrust: readonly ["tunnel"];
226
225
  readonly requestTrust: "x-fs-signature";
227
- readonly invariant: "Channel trust (mtls/tunnel) and request trust (the X-FS-* signature) are distinct layers; both always apply. CF-Access-* headers are transport-layer only and are IGNORED by the SDK.";
226
+ readonly invariant: "Channel trust (tunnel) and request trust (the X-FS-* signature) are distinct layers; both always apply. CF-Access-* headers are transport-layer only and are IGNORED by the SDK.";
228
227
  };
@@ -103,8 +103,8 @@ export type CanonicalSigningInput = {
103
103
  policyVersion: string;
104
104
  };
105
105
  export type RuntimeEnvironmentKind = RuntimeTokenKind;
106
- export type TransportMode = "public_origin" | "mtls" | "cloudflare_tunnel";
107
- export type TransportRunner = "managed_cloudflared" | "sidecar";
106
+ export type TransportMode = "direct" | "tunnel";
107
+ export type TransportRunner = "embedded" | "sidecar";
108
108
  export type RuntimeBootstrapRequest = {
109
109
  instanceId?: string;
110
110
  sdkVersion?: string;
@@ -135,7 +135,6 @@ export type RuntimeTransportConfig = {
135
135
  tunnelToken: string;
136
136
  version: string;
137
137
  };
138
- mtlsClientCertRef?: string;
139
138
  };
140
139
  export type RuntimeRouteDescriptor = {
141
140
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farthershore/backend",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Farther Shore backend SDK for builder upstreams: signed response usage, fail-closed gateway request verification, health, and lifecycle from FS_RUNTIME_TOKEN",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,6 +31,12 @@
31
31
  "publishConfig": {
32
32
  "access": "public"
33
33
  },
34
+ "optionalDependencies": {
35
+ "@farthershore/cloudflared-linux-x64": "0.0.0",
36
+ "@farthershore/cloudflared-linux-arm64": "0.0.0",
37
+ "@farthershore/cloudflared-darwin-arm64": "0.0.0",
38
+ "@farthershore/cloudflared-darwin-x64": "0.0.0"
39
+ },
34
40
  "peerDependencies": {
35
41
  "express": "^4.0.0 || ^5.0.0"
36
42
  },