@farthershore/backend 0.18.0 → 0.19.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/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to the runtime backend SDK are documented here. This SDK
4
4
  versions independently from the frontend and business SDKs. Pre-1.0: minor
5
5
  versions may include breaking changes.
6
6
 
7
+ ## [0.19.0]
8
+
9
+ ### Added
10
+
11
+ - One runtime token can now serve EVERY environment of a business. Bootstrap
12
+ reports `backendIds` — every backend the token may serve — and the SDK checks
13
+ the gateway's signed backend id for MEMBERSHIP of that set instead of equality
14
+ with a single id.
15
+
16
+ A process holds exactly one `FS_RUNTIME_TOKEN`, so an environment-scoped token
17
+ forced a SEPARATE deployment per environment: pointing a backend at a preview
18
+ environment meant repointing, and breaking, production. A business-scoped
19
+ token (`farthershore backend tokens create <biz>`, no `--env`) now serves them
20
+ all from one deployment; `--env <name>` still pins a token to one environment
21
+ when you want that guarantee.
22
+
23
+ Still fail-closed and still bounded by the business — this widens across
24
+ ENVIRONMENTS, never across businesses. Additive: an older core omits
25
+ `backendIds` and the single-id equality check still applies.
26
+
7
27
  ## [0.18.0]
8
28
 
9
29
  ### Added
package/README.md CHANGED
@@ -12,7 +12,7 @@ graceful lifecycle (health + shutdown). Everything else — your business, backe
12
12
  and environment ids, the verification keys, and the metering endpoint — is
13
13
  fetched automatically from the token at startup.
14
14
 
15
- > **Status: `0.18.0`.** Pre-1.0: minor releases may include breaking changes, so
15
+ > **Status: `0.19.0`.** Pre-1.0: minor releases may include breaking changes, so
16
16
  > pin this package to an exact version (or a patch-only range) and upgrade
17
17
  > deliberately.
18
18
 
package/dist/index.js CHANGED
@@ -1781,7 +1781,14 @@ async function verifyRequest(input, deps) {
1781
1781
  "signed business-id does not match this backend's business"
1782
1782
  );
1783
1783
  }
1784
- if (deps.backendId !== void 0 && signedBackendId !== deps.backendId) {
1784
+ if (deps.backendIds !== void 0 && deps.backendIds.size > 0) {
1785
+ if (!deps.backendIds.has(signedBackendId)) {
1786
+ throw new FartherShoreError(
1787
+ "route_mismatch",
1788
+ "signed backend-id is not one this deployment serves"
1789
+ );
1790
+ }
1791
+ } else if (deps.backendId !== void 0 && signedBackendId !== deps.backendId) {
1785
1792
  throw new FartherShoreError(
1786
1793
  "route_mismatch",
1787
1794
  "signed backend-id does not match this backend"
@@ -1918,7 +1925,7 @@ function headerGetter(headers) {
1918
1925
 
1919
1926
  // src/core/runtime.ts
1920
1927
  var DEFAULT_CORE_URL = "https://core.farthershore.com";
1921
- var SDK_VERSION = "0.18.0".length > 0 ? "0.18.0" : "0.0.0-dev";
1928
+ var SDK_VERSION = "0.19.0".length > 0 ? "0.19.0" : "0.0.0-dev";
1922
1929
  var FartherShore = class {
1923
1930
  bootstrapClient;
1924
1931
  fetchImpl;
@@ -2066,11 +2073,13 @@ var FartherShore = class {
2066
2073
  );
2067
2074
  }
2068
2075
  const knownRouteIds = new Set(config.routes.map((r) => r.id));
2076
+ const backendIds = config.backendIds?.length ? new Set(config.backendIds) : void 0;
2069
2077
  const context = await verifyRequest(input, {
2070
2078
  jwks: this.jwks,
2071
2079
  nonceCache: this.nonceCache,
2072
2080
  businessId: config.business.id,
2073
2081
  backendId: config.backend.id,
2082
+ ...backendIds ? { backendIds } : {},
2074
2083
  knownRouteIds,
2075
2084
  clockSkewSeconds: config.verification.clockSkewSeconds,
2076
2085
  replayWindowSeconds: config.verification.replayWindowSeconds,
@@ -2012,7 +2012,14 @@ async function verifyRequest(input, deps) {
2012
2012
  "signed business-id does not match this backend's business"
2013
2013
  );
2014
2014
  }
2015
- if (deps.backendId !== void 0 && signedBackendId !== deps.backendId) {
2015
+ if (deps.backendIds !== void 0 && deps.backendIds.size > 0) {
2016
+ if (!deps.backendIds.has(signedBackendId)) {
2017
+ throw new FartherShoreError(
2018
+ "route_mismatch",
2019
+ "signed backend-id is not one this deployment serves"
2020
+ );
2021
+ }
2022
+ } else if (deps.backendId !== void 0 && signedBackendId !== deps.backendId) {
2016
2023
  throw new FartherShoreError(
2017
2024
  "route_mismatch",
2018
2025
  "signed backend-id does not match this backend"
@@ -2149,7 +2156,7 @@ function headerGetter(headers) {
2149
2156
 
2150
2157
  // src/core/runtime.ts
2151
2158
  var DEFAULT_CORE_URL = "https://core.farthershore.com";
2152
- var SDK_VERSION = "0.18.0".length > 0 ? "0.18.0" : "0.0.0-dev";
2159
+ var SDK_VERSION = "0.19.0".length > 0 ? "0.19.0" : "0.0.0-dev";
2153
2160
  var FartherShore = class {
2154
2161
  bootstrapClient;
2155
2162
  fetchImpl;
@@ -2297,11 +2304,13 @@ var FartherShore = class {
2297
2304
  );
2298
2305
  }
2299
2306
  const knownRouteIds = new Set(config.routes.map((r) => r.id));
2307
+ const backendIds = config.backendIds?.length ? new Set(config.backendIds) : void 0;
2300
2308
  const context = await verifyRequest(input, {
2301
2309
  jwks: this.jwks,
2302
2310
  nonceCache: this.nonceCache,
2303
2311
  businessId: config.business.id,
2304
2312
  backendId: config.backend.id,
2313
+ ...backendIds ? { backendIds } : {},
2305
2314
  knownRouteIds,
2306
2315
  clockSkewSeconds: config.verification.clockSkewSeconds,
2307
2316
  replayWindowSeconds: config.verification.replayWindowSeconds,
@@ -68,8 +68,21 @@ export type VerifyRequestDeps = {
68
68
  nonceCache: NonceStore;
69
69
  /** Expected business id (from bootstrap). When set, must match the signed claim. */
70
70
  businessId?: string;
71
- /** Expected backend id (from bootstrap). When set, must match. */
71
+ /** Expected backend id (from bootstrap). When set, must match — unless
72
+ * `backendIds` is provided, which supersedes it with a membership check. */
72
73
  backendId?: string;
74
+ /**
75
+ * Every backend id this deployment may serve (from bootstrap), across all of
76
+ * the business's environments. When provided, the signed backend id must be a
77
+ * MEMBER of this set — superseding the single-id equality check above.
78
+ *
79
+ * A process holds exactly one `FS_RUNTIME_TOKEN`, so pinning to one backend id
80
+ * forced a separate deployment per environment: serving a preview env meant
81
+ * repointing (and breaking) production. A business-scoped token serves them
82
+ * all from one deployment. Still fail-closed, still bound to this business —
83
+ * just no longer bound to a single environment.
84
+ */
85
+ backendIds?: ReadonlySet<string>;
73
86
  /**
74
87
  * Set of route ids this backend serves (from bootstrap). When provided AND
75
88
  * the signed route-id is non-empty, the signed route must be a member —
@@ -233,6 +233,21 @@ export type RuntimeBootstrapResponse = {
233
233
  slug: string;
234
234
  name: string;
235
235
  };
236
+ /**
237
+ * Every backend id this token may serve, across ALL of the business's
238
+ * environments — `backend.id` is always a member.
239
+ *
240
+ * One deployment holds exactly one `FS_RUNTIME_TOKEN`, so an
241
+ * environment-scoped token forced a SEPARATE deployment per environment:
242
+ * serving a preview env meant repointing (and breaking) production. A
243
+ * business-scoped token serves every environment from one deployment, and the
244
+ * SDK checks the gateway's signed backend id for MEMBERSHIP of this set
245
+ * rather than equality with a single id.
246
+ *
247
+ * Optional and additive: an older core omits it and the SDK falls back to the
248
+ * single-id equality check.
249
+ */
250
+ backendIds?: string[];
236
251
  environment: {
237
252
  id: string | null;
238
253
  kind: RuntimeEnvironmentKind;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farthershore/backend",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
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",
@@ -37,8 +37,8 @@
37
37
  "access": "public"
38
38
  },
39
39
  "optionalDependencies": {
40
- "@farthershore/cloudflared-linux-x64": "0.0.0",
41
40
  "@farthershore/cloudflared-linux-arm64": "0.0.0",
41
+ "@farthershore/cloudflared-linux-x64": "0.0.0",
42
42
  "@farthershore/cloudflared-darwin-arm64": "0.0.0",
43
43
  "@farthershore/cloudflared-darwin-x64": "0.0.0"
44
44
  },