@farthershore/backend 0.12.0 → 0.13.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/README.md CHANGED
@@ -12,7 +12,7 @@ graceful lifecycle (health + shutdown). Everything else — your product, backen
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.12.0`.** Pre-1.0: minor releases may include breaking changes, so
15
+ > **Status: `0.13.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
 
@@ -149,6 +149,58 @@ real time.
149
149
  - `fs.shutdown()` flushes any buffered metering and sends a `stopping` heartbeat.
150
150
  Call it on `SIGTERM` / `SIGINT` for graceful shutdown.
151
151
 
152
+ ## Local development & testing — the mode ladder
153
+
154
+ You do not need the platform to test a backend that runs behind it. Pick the
155
+ lowest tier that answers your question — each is a superset of the one below.
156
+
157
+ **Tier 0 — off (needs NOTHING from the SDK).** Unit-test your business logic
158
+ directly. The gateway sits in front of you in production; your pure handlers
159
+ don't import Farther Shore to be tested. There is no SDK step at this tier — see
160
+ `templates/1-unit.test.ts`.
161
+
162
+ **Tier 1 — passthrough.** Run your real app over HTTP with `fs.middleware()`
163
+ mounted but verification OFF, matching the pre-keystone deploy order: the
164
+ middleware passes requests through without attaching a context, so your routes
165
+ run normally. Activate with `FS_DEV_MODE=passthrough`. See
166
+ `templates/2-passthrough-http.test.ts`.
167
+
168
+ **Tier 2 — simulated.** A real runtime wired to an in-process gateway with
169
+ fail-closed verification ON and SIGNED personas driving requests. Assert the
170
+ fail-closed boundary (a persona without a permission gets 403) and that usage is
171
+ metered. Activate with `FS_DEV_MODE=simulated`, or construct explicitly:
172
+
173
+ ```ts
174
+ import { createDevRuntime, definePersona } from "@farthershore/backend/testing";
175
+
176
+ const rt = createDevRuntime({
177
+ mode: "simulated",
178
+ personas: {
179
+ creator: definePersona({
180
+ name: "creator",
181
+ permissions: ["widgets:create"],
182
+ }),
183
+ },
184
+ });
185
+
186
+ // Sign a request as a persona and drive your app (supertest, fetch, or inject):
187
+ const headers = await rt.asPersona("creator").headers({ path: "/v1/widgets" });
188
+ // rt.usage.byMeter() → assert reported usage
189
+ // rt.trace.forRequest(id) → why a request verified / was denied
190
+ ```
191
+
192
+ `@farthershore/backend/testing` gives you signed personas (owner / admin /
193
+ member / anonymous, plus your own), an in-process gateway fixture, and
194
+ assertable `usage` + per-request `trace` side channels. It is dev/test tooling
195
+ only — **it throws when `NODE_ENV=production`**, and `FS_DEV_MODE` is never a
196
+ required-at-boot variable. When `FS_DEV_MODE` is set, `initFromEnv()`
197
+ self-constructs the simulator (ephemeral keys, a loud banner, JSONL usage/trace
198
+ logs under `.farthershore/`, and a mode-600 `.farthershore/dev-keys.json` so a
199
+ separate test-runner process can sign against a running service via
200
+ `personaClientFromKeysFile`).
201
+
202
+ See `templates/3-simulated-authz.test.ts` for the full fail-closed + usage flow.
203
+
152
204
  ## Key exports
153
205
 
154
206
  | Export | Purpose |
@@ -157,13 +209,26 @@ real time.
157
209
  | `fs.middleware()` | Express fail-closed verify → `req.fartherShore`. |
158
210
  | `fs.verifyRequest({...})` | Framework-neutral request verification. |
159
211
  | `withUsage()` / `createUsage()` | Response-bound usage reporting (no network call). |
212
+ | `computeMeteringHeaders()` | Metering headers as a plain map — never throws. |
160
213
  | `fs.meter(meter, qty, opts)` | Async/background usage event. |
161
214
  | `fs.health()` / `fs.shutdown()` | Health report and graceful shutdown. |
162
215
  | `FartherShoreError`, `MeteringError` | Typed errors. |
216
+ | `@farthershore/backend/testing` | Dev-mode + persona test harness (dev/test only). |
163
217
 
164
218
  A subpath export, `@farthershore/backend/express`, exposes the Express adapter
165
219
  types directly if you prefer to wire the middleware yourself.
166
220
 
221
+ ## Metering channels
222
+
223
+ Two usage channels exist and are **not** interchangeable:
224
+
225
+ - **Response-bound** (`withUsage` / `createUsage` / `computeMeteringHeaders`) is
226
+ the attested, request-bound settlement channel: the gateway verifies the HMAC
227
+ and settles the reported units against the request's lease in the same
228
+ lifecycle. Wire recipe (any language): [`docs/response-metering-wire.md`](docs/response-metering-wire.md).
229
+ - **Background** (`fs.meter`) is a billing-only, unattested, post-cycle tally for
230
+ usage not tied to a gateway response. It never settles a lease.
231
+
167
232
  ## Learn more
168
233
 
169
234
  - Platform documentation: https://docs.farthershore.com