npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@farthershore/backend

v0.21.2

Published

Farther Shore backend SDK for builder upstreams: signed response usage, fail-closed gateway request verification, first-class webhook consumption, health, and lifecycle from FS_RUNTIME_TOKEN

Downloads

307

Readme

@farthershore/backend

The runtime SDK for your own backend. When you run a software business on Farther Shore with a bring-your-own-backend, the platform's edge gateway sits in front of your service. This package lets your backend trust the gateway (verify that each request really came from it) and report usage back for metering and billing — from a single token, FS_RUNTIME_TOKEN.

Install one package, set one environment variable, and you get fail-closed gateway-to-upstream request verification, response-bound usage reporting, and graceful lifecycle (health + shutdown). Everything else — your business, backend, and environment ids, the verification keys, and the metering endpoint — is fetched automatically from the token at startup.

Status: 0.21.2. Pre-1.0: minor releases may include breaking changes, so pin this package to an exact version (or a patch-only range) and upgrade deliberately.

Install

npm install @farthershore/backend

Requires Node 22+. The Express adapter has an optional express peer dependency (v4 or v5); the core verification primitive is framework-neutral.

Quick start (any Fetch-compatible handler)

import { fartherShore } from "@farthershore/backend";

const fs = fartherShore.initFromEnv(); // derives everything from FS_RUNTIME_TOKEN

export async function POST(request: Request) {
  const url = new URL(request.url);
  const body = new Uint8Array(await request.clone().arrayBuffer());

  // Fail-closed: throws a FartherShoreError if the request is not a genuine,
  // unmodified request signed by the gateway.
  const ctx = await fs.verifyRequest({
    method: request.method,
    path: url.pathname,
    query: url.search,
    headers: request.headers,
    body,
  });

  const result = await runWorkflow(await request.json());

  // ONE reporting verb. No identity argument (the verified context carries the
  // served identity) and no transport argument (the SDK picks one).
  await ctx.report({
    meter: "model_usage",
    values: { tokens_used: result.tokensUsed },
    dims: { model: result.model },
  });

  return Response.json(result);
}

Quick start (Express)

import { fartherShore } from "@farthershore/backend";

const fs = fartherShore.initFromEnv();

app.use(fs.middleware()); // fail-closed verify -> req.fartherShore

app.post("/v1/runs", async (req, res) => {
  const result = await runWorkflow(req.body);

  await req.fartherShore.report({
    meter: "model_usage",
    values: { tokens_used: result.tokensUsed },
  });

  res.json(result);
});

await fs.ready(app); // bootstrap, reconcile routes, report this replica ready
app.listen(3000);
process.on("SIGTERM", () => void fs.shutdown());

What initFromEnv() derives

You configure exactly one thing: FS_RUNTIME_TOKEN (mint it for your backend with the Farther Shore CLI or dashboard). Everything else — business / backend / environment ids, the JWKS url used to verify signatures, the metering endpoint and credential, and verification settings — is fetched from the platform at startup and cached in memory. The token is validated eagerly, so a missing or malformed token fails fast.

You can override the core URL via FS_CORE_URL (or pass options to initFromEnv()), but in normal use no other configuration is needed.

Request verification (fail-closed, always)

fs.middleware() (Express) and the framework-neutral fs.verifyRequest({ method, path, query, headers, body }) recompute a canonical signing string from the actual request and verify the gateway's Ed25519 signature against a JWKS-resolved public key. The plaintext X-FS-* headers are untrusted — identity comes only from a signature whose claims match the real request, so a forged or replayed request cannot impersonate the gateway.

Every failure (missing / malformed / bad-signature / stale / clock-skew / wrong-route / body-hash-mismatch / replayed-nonce / unknown-key / keys-unavailable) throws a typed FartherShoreError that maps to HTTP 401 (413 for oversized bodies). There is no fail-open path.

Replay protection (nothing to configure)

A signed request is one-time-use, and two things enforce that:

  • A time window, always on. The gateway signs a timestamp into the request; anything older than ~305s (replay window + clock skew) is rejected outright. This holds for every deployment shape and needs nothing from you.
  • A seen-id list. Inside that window, the SDK remembers each X-Fs-Request-Id it has accepted and rejects a second sighting. The default list is in-memory and per-process.

That default is deliberate: running more than one replica means each has its own list, so a captured request could be replayed once per replica within the ~305s window — and closing that would mean asking you to provision and operate a distributed cache. We would rather keep your setup to one environment variable and let the time window bound the exposure.

If you do want one-time-use enforced across replicas, inject a shared store:

initFromEnv({ nonceStore: myStore }); // checkAndRemember(id) => boolean | Promise<boolean>

Make it TTL-bound to the signature validity window. If that store goes down, requests fail closed — it never degrades to "not a replay". fs.replayProtection() reports which mode is active ("shared" | "single-instance") if you want it in your boot logs.

Authorization — the permission grammar & in-handler checks

The edge permission constraint is the route-level security boundary: the gateway resolves the acting user's effective permissions at token mint and carries them in the signed X-Fs-Context claim. These SDK helpers exist for finer-grained, in-handler checks the route layer can't express (field- or record-level gating).

The grammar

A permission is a plain string, checked with three rungs:

  • * — the global wildcard. Grants every key (org OWNER, RBAC disabled, personal orgs — the gateway stamps an explicit ["*"]).
  • <subject>:* — the subject wildcard, e.g. widgets:* grants widgets:read, widgets:write, and any other widgets:<verb>. (A literal * subject never takes this rung — only the bare * grant is global.)
  • exact keys — e.g. widgets:write. Custom permission strings work: any <subject>:<verb> you invent is checked verbatim; there is no fixed verb vocabulary at this layer.

Route-shaped keys follow routePermission(subject, method)<subject>:read for safe verbs (GET / HEAD / OPTIONS, any casing) and <subject>:write for everything else — the SAME helper the platform uses to derive a route's required permission, exported here so you never re-spell the suffix.

Fail-closed at the carrier: an absent permission set (ctx.permissions === undefined) always denies — absence never means grant-all, even on a fully verified request. [] (authenticated, no grants) also denies. A route you don't want gated simply doesn't call a check.

Checking permissions

// Namespace form — dev (`rt.authz`, traced) and prod (`fs.authz`) match:
app.post(
  "/v1/widgets",
  fs.middleware(),
  fs.handler((ctx, req, res) => {
    fs.authz.requirePermission(ctx, "widgets:write"); // throws 403 permission_denied
    // or: if (fs.authz.hasPermission(ctx, "widgets:publish")) { ... }
    res.json({ ok: true });
  }),
);

// Declarative form — the handler options overload runs the same fail-closed
// check BEFORE your callback:
app.post(
  "/v1/widgets",
  fs.middleware(),
  fs.handler({ permission: "widgets:write" }, (ctx, req, res) => {
    res.json({ ok: true });
  }),
);

A failed check responds 403 { "error": "permission_denied" } (a thrown FartherShorePermissionError is mapped by fs.handler). The standalone hasPermission / requirePermission / permissionSatisfies / routePermission exports are available for non-Express frameworks. In the dev runtime, use rt.authz.* — the same shape, with every decision recorded into the per-request trace (see templates/3-simulated-authz.test.ts).

Usage reporting — one verb

ctx.report({ meter, values, dims?, quote? }) on the verified context is the ONLY reporting surface. Backends report measurements, never money: values are observed facts (tokens, jobs, rows), dims name the catalog tuple they were produced under, and the platform owns what they cost.

await req.fartherShore.report({
  meter: "model_usage",
  values: { input_tokens: 1200, output_tokens: 850 },
  dims: { model: "acme-4", cache_status: "hit" },
});

No identity ceremony. The subscription and served release ride the signed context the gateway already sent, so there is no subscriptionId to forget — the "unbilled because the handler omitted an id" failure mode is unreachable. Hand the same FartherShoreContext to a background job and it keeps reporting against that same served identity:

import type { FartherShoreContext } from "@farthershore/backend";

export async function runJob(job, fartherShore: FartherShoreContext) {
  const result = await perform(job);
  await fartherShore.report({
    meter: "jobs",
    values: { jobs: 1 },
    dims: { queue: job.queue },
  });
  return result.output;
}

Transport is an implementation detail. Reported before the response is sent, the measurement rides signed x-fs-metering response headers — no extra network call; the gateway verifies, settles, and strips them before the subscriber sees the response. Reported after res.end() (a stream) or from a background job, it goes over the attested post-stream channel with the same served identity. The builder never picks; report() resolves { ok, transport } if you want to know.

Multiple meters after the response is sent → ONE batched call. A served request owns exactly ONE post-stream callback identity, so sequential awaited single-meter calls after the response cannot all be delivered — the first call flushes the callback and every later call resolves { ok: false }. Pass an ARRAY to report several meters atomically through that single callback:

await fartherShore.report([
  { meter: "model_usage", values: { output_tokens: 512 } },
  { meter: "jobs", values: { jobs: 1 } },
]);

All entries of a batch share one quote (supplying two different quotes throws) and one dims tuple — the request receipt rates under (route, dims), so report each dims tuple on its own request. The same one-quote / one-dims rule applies to in-band accumulation before the response is sent. Before the response is sent this constraint does not exist — sequential in-band reports accumulate into the same signed response payload automatically.

Malformed input (a bad meter/measure/dimension key, a negative or non-finite value, a malformed quote) throws — a dropped measurement is unbilled revenue. Delivery failures resolve { ok: false, reason } instead of rejecting, so a metering hiccup never breaks your endpoint. Calling report() on a context that did not come from the runtime (the bare verifyRequest() primitive) throws an error naming the fix.

The meter keys you report must match meters declared in your business; the gateway validates them against the served release's measurement-emission schema. Request-count style limits are enforced by the gateway and need no backend code.

Quotes: the one bounded money channel

quote is the sole exception to "never money": a proposed rate input for a pricing policy that declared the backendQuoted rule with repo-authored {min,max} bounds (dynamic upstream resale, bespoke jobs).

await fartherShore.report({
  meter: "jobs",
  values: { jobs: 1 },
  quote: { currency: "usd", amountNanos: "250000000" }, // $0.25
});

Core clamps it to the declared bounds and flags an out-of-range proposal for dispute; contract modifiers and funding still apply on top, and the ledger only ever records core-rated charges. The SDK does not validate the bounds (it cannot know them) — it rejects only structurally malformed quotes.

Non-JS backends

The wire recipe is language-neutral: any backend can stamp the same signed headers with a stdlib HMAC. See docs/response-metering-wire.md, or use computeMeteringHeaders() directly from a non-Express JS host.

Consuming platform webhooks

Endpoints are created in the dashboard or CLI (farthershore webhook create); the SDK consumes what they deliver. @farthershore/backend/webhooks is standalone — a receiver needs only its fswh_ signing secret, not a runtime token.

import { createWebhookHandler } from "@farthershore/backend/webhooks";

const webhooks = createWebhookHandler({
  secret: process.env.FS_WEBHOOK_SECRET!,
  on: {
    "subscription.created": async (event) => {
      await provision(event.data.subscriptionId, event.businessId);
    },
    "payment.failed": async (event) => {
      await flagAccount(event.data.subscriptionId);
    },
  },
});

// Express — mount after a RAW body parser so the signature can be checked:
app.post(
  "/webhooks/farthershore",
  express.raw({ type: "*/*" }),
  webhooks.express(),
);
// Fetch-style runtimes (Next.js route handlers, Hono, Workers):
export const POST = webhooks.fetch;

What the handler does for you, in order: verifies the Standard Webhooks signature over the raw body (webhook-id.webhook-timestamp.body, HMAC-SHA256, any v1, entry — so a platform-side rotation's dual signature just works, and you can pass secrets: [current, previous] while you roll your own copy); rejects timestamps outside ±5 minutes; deduplicates on webhook-id (a retry after a lost 2xx is acknowledged without re-running your code; pass a shared nonceStore on multi-instance receivers); parses the typed envelope { id, type, createdAt, businessId, environmentId, data }; acknowledges unknown event types with 2xx (onUnknown to log them) so a newer platform never causes a 500 storm; and turns a thrown handler into a 500 so the platform retries (30 s / 5 min / 30 min) — the delivery id is released so that retry runs your handler again.

verifyWebhook({ body, headers, secrets }) is the bare primitive if you want to wire routing yourself, and signWebhookForTesting() from @farthershore/backend/testing produces a platform-identical signed delivery for your receiver tests.

Lifecycle

  • await fs.ready(app) bootstraps, reconciles the registered route surface, and sends the replacement replica's ready heartbeat. Call it after registering routes and before accepting traffic.
  • fs.health() returns the current local health report (token present, bootstrap loaded, verification + metering status).
  • fs.shutdown() flushes any buffered metering and sends a stopping heartbeat. Call it on SIGTERM / SIGINT for graceful shutdown.

Local development & testing — the mode ladder

You do not need the platform to test a backend that runs behind it. Pick the lowest tier that answers your question — each is a superset of the one below.

Tier 0 — off (needs NOTHING from the SDK). Unit-test your business logic directly. The gateway sits in front of you in production; your pure handlers don't import Farther Shore to be tested. There is no SDK step at this tier — see templates/1-unit.test.ts.

Tier 1 — passthrough. Run your real app over HTTP with fs.middleware() mounted but verification OFF for local development only: the middleware passes requests through without attaching a context, so your routes run normally. Never use this mode in a deployed environment. Activate locally with FS_DEV_MODE=passthrough. See templates/2-passthrough-http.test.ts.

Tier 2 — simulated. A real runtime wired to an in-process gateway with fail-closed verification ON and SIGNED personas driving requests. Assert the fail-closed boundary (a persona without a permission gets 403) and that usage is metered. Activate with FS_DEV_MODE=simulated, or construct explicitly:

import { createDevRuntime, definePersona } from "@farthershore/backend/testing";

const rt = createDevRuntime({
  mode: "simulated",
  personas: {
    creator: definePersona({
      name: "creator",
      permissions: ["widgets:create"],
    }),
  },
});

// Sign a request as a persona and drive your app (supertest, fetch, or inject):
const headers = await rt.asPersona("creator").headers({ path: "/v1/widgets" });
// rt.usage.byMeter()   → assert reported usage
// rt.trace.forRequest(id) → why a request verified / was denied

@farthershore/backend/testing gives you signed personas (owner / admin / member / anonymous, plus your own), an in-process gateway fixture, and assertable usage + per-request trace side channels. It is dev/test tooling only — it throws when NODE_ENV=production, and FS_DEV_MODE is never a required-at-boot variable. When FS_DEV_MODE is set, initFromEnv() self-constructs the simulator (ephemeral keys, a loud banner, JSONL usage/trace logs under .farthershore/, and a mode-600 .farthershore/dev-keys.json so a separate test-runner process can sign against a running service via personaClientFromKeysFile).

See templates/3-simulated-authz.test.ts for the full fail-closed + usage flow.

Key exports

| Export | Purpose | | ------------------------------------ | ----------------------------------------------------- | | fartherShore.initFromEnv() | Create the runtime instance from FS_RUNTIME_TOKEN. | | fs.middleware() | Express fail-closed verify → req.fartherShore. | | fs.verifyRequest({...}) | Framework-neutral request verification. | | fs.handler({ permission? }, cb) | Verified-principal handler (+ declarative gate). | | fs.authz.requirePermission(ctx, k) | In-handler authz (fail-closed; also hasPermission). | | routePermission(subject, method) | Route-derived permission key (:read/:write). | | ctx.report({meter, values, …}) | THE reporting verb (SDK picks the transport). | | computeMeteringHeaders() | Metering headers as a plain map — never throws. | | fs.health() / fs.shutdown() | Health report and graceful shutdown. | | FartherShoreError, MeteringError | Typed errors. | | @farthershore/backend/webhooks | createWebhookHandler / verifyWebhook (receivers). | | @farthershore/backend/testing | Dev-mode + persona test harness (dev/test only). |

A subpath export, @farthershore/backend/express, exposes the Express adapter types directly if you prefer to wire the middleware yourself.

Metering transports

One verb, two transports — ctx.report() chooses; you never do:

  • In-band (signed x-fs-metering response headers) while the response is still open: the attested, request-bound settlement channel. The gateway verifies the HMAC and settles the reported units against the request's lease in the same lifecycle, then strips the headers. Wire recipe (any language): docs/response-metering-wire.md.
  • Post-stream (the attested POST /v1/metering/events callback) once the response is on the wire, or from a background job holding the context. It is HMAC-attested and carries the same served identity, writes the sole billable row for the reported units, and never mutates real-time enforcement windows — so units unknown at admission cannot be hard-enforced.

Learn more

  • Platform documentation: https://docs.farthershore.com
  • Provisioning a backend and minting a runtime token is done through the Farther Shore CLI or dashboard.