Farther ShoreDocs
Go to Farther Shore
What is FartherShore
Install the CLI
Quickstart
Core concepts
The @Product class
Meters & resources
Features & routes
Capabilities & entitlements
Plans & pricing
The Manifest IR
Bring your own backend
Transport modes
Metering & verification
Runtime tokens
Frontend SDK
Root & data components
Auth & sessions
Entitlement gates
Connect Stripe
Subscriptions & usage
Plan changes & grandfathering
Billing strategies
Apply & deploy
Environments
Migrations
Docs versions & archive
Operate with an agent
Operation classes
MCP server
End-to-end via CLI/MCP
CLI reference
@farthershore/product
@farthershore/backend
Quick start (Fetch handlers)ExpressVerification (fail-closed, always)MeteringThe fartherShore instancePublic exportsDeclaring a backend
@farthershore/farthershore-js
Environment variables
Response & deny codes
Add a metered capability
Gate a feature
Change a price
Prepaid credits
Meter AI tokens
Operate via an agent
Prepare for launch
Status
Docs/Reference/@farthershore/backend

@farthershore/backend

Runtime gateway-verification and usage metering for builder upstreams.

Previous@farthershore/productNext@farthershore/farthershore-js

@farthershore/backend is the runtime SDK for your upstream. Install one package, set one token (FS_RUNTIME_TOKEN), and Farther Shore handles signed gateway-to-upstream request verification plus response-bound usage reporting. Pin it in lockstep with @farthershore/product and @farthershore/farthershore-js.

npm install @farthershore/backend

Mint the token with the CLI — it's returned once:

farthershore backend tokens create croncloud --backend <backendId> --format json

fartherShore.initFromEnv() derives everything — product/backend ids, the JWKS URL, the metering endpoint, verification config, transport — from FS_RUNTIME_TOKEN via POST /v1/runtime/bootstrap (cached in memory, refreshed lazily). The builder configures exactly one thing. See environment variables.

Quick start (Fetch handlers)

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

const fs = fartherShore.initFromEnv();

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

  // Fail-closed: throws FartherShoreError (→ 401) on any verification failure.
  await fs.verifyRequest({
    method: request.method,
    path: url.pathname,
    query: url.search,
    headers: request.headers,
    body,
  });

  const result = await runWorkflow(await request.json());
  return withUsage(request, Response.json(result), {
    tokens_used: result.tokensUsed, // matches a dynamic @Meter key in the product
  });
}

Express

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

const fs = fartherShore.initFromEnv();
app.use(fs.middleware());           // fail-closed verify → req.fartherShore
await fs.ready(app);                // boot-time route reconciliation (fail-open)
app.post("/v1/cron-jobs", handler);
app.listen(3000);
process.on("SIGTERM", () => void fs.shutdown());

Verification (fail-closed, always)

The gateway signs each request with Ed25519; the SDK recomputes the canonical signing string from the actual request and verifies the signature against a JWKS-resolved key. The plaintext X-FS-* headers are untrusted — identity comes only from a signature whose claims match the real request. Every failure (missing / malformed / bad-signature / stale / clock-skew / wrong-route / body-hash-mismatch / replayed-nonce / unknown-kid / jwks-unavailable) throws and maps to HTTP 401 (413 for oversized bodies). There is no fail-open branch.

Metering

Three ways to report usage. The meter key is not hardcoded by the SDK — it must match a dynamic @Meter declared in the product.

HelperWhenNetwork call?
withUsage(request, response, usage, options?)Usage known while returning a gateway-handled response.No — signs usage into response headers; the gateway verifies, settles, and strips them.
createUsage(request, options?)Same, but build usage incrementally (.report(meter, value).wrap(response)).No.
fs.meter(meter, qty, { requestId, routeId })Async / background usage not tied to a response.Yes — POSTs an idempotent event to /v1/metering/events. At-least-once.
return withUsage(request, Response.json(result), { tokens_used: result.tokensUsed }, {
  measureContext: { model: result.model },          // free-form pricing/analytics context
  creditUnitsConsumed: { credits: result.creditsUsed }, // credit-wallet products
});

Plain request counting (from @Requests()) is platform-managed and needs no upstream code.

The fartherShore instance

MemberDescription
fartherShore.initFromEnv(options?)Construct an instance; derive everything from FS_RUNTIME_TOKEN. Throws missing_token / invalid_token eagerly.
fs.middleware(options?)Express middleware: fail-closed verify → req.fartherShore.
fs.verifyRequest(input)Framework-neutral verification primitive ({ method, path, query, headers, body }).
fs.meter(meter, qty, options?)Record billing-only usage (POST /v1/metering/events).
fs.ready(app?)Boot-time route reconciliation against the declared lock; reports drift. Fail-open — never blocks boot.
fs.start()Start the embedded cloudflared runner for a tunnel backend; no-op otherwise.
fs.health()Local runtime health report.
fs.shutdown()Graceful: flush metering + send a stopping heartbeat.
fs.onShutdown(hook)Register an additional shutdown hook.

initFromEnv(options) accepts runtimeToken, coreUrl, env, fetchImpl, verification: { enabled }, metering: { enabled }, tunnel, and instanceId for tests and advanced opt-outs — but the default DX is everything on, token only.

Public exports

ExportWhat it is
fartherShore, initFromEnvThe conceptual entrypoint and its top-level convenience twin.
FartherShore, FartherShoreInstanceThe runtime class and its augmented type.
withUsage, createUsageResponse-bound metering signers.
MeteringError, UsageMap, UsageReporter, MeteringOptionsResponse-metering error + types.
FartherShoreError, statusForCodeThe typed verification error and its HTTP-status mapper.
verifyRequest, FartherShoreRequestContext, VerifyRequestInputThe standalone verification primitive + types.
createExpressMiddleware, ExpressMiddleware, MiddlewareOptionsExpress adapter.
JwksClient, NonceCache, BootstrapClient, MeteringClientThe lower-level clients initFromEnv composes.
CloudflaredSupervisor, nodeSpawn, FartherShoreTunnelOptionsThe embedded tunnel runner (BYO-backend).
buildHealthReport, reportHealth, ShutdownManagerHealth + shutdown helpers.
FS_RUNTIME_TOKEN_ENV, RUNTIME_TOKEN_CAPABILITIES, RUNTIME_HEADER_NAMES, MAX_BODY_BYTES, RUNTIME_CLOCK_SKEW_SECONDS, RUNTIME_REPLAY_WINDOW_SECONDS, RUNTIME_ERROR_CODESShared contract constants (mirrors @farthershore/contracts/runtime).
hashBody, buildCanonicalSigningString, signCanonicalString, verifyCanonicalSignature, canonicalizeQuerySigning primitives (one source of truth across SDKs and the gateway).
METERING_PAYLOAD_HEADER, METERING_SIGNATURE_HEADER, METERING_TOKEN_HEADER, DEFAULT_TOKEN_ENVResponse-metering header-contract constants.

Declaring a backend

A backend is declared in the product via @Backend. Bind routes to it with a route's backend; a single backend is the default, otherwise mark one default: true.

import { Backend, Feature } from "@farthershore/product";

@Backend("prod-origin", {
  transport: { mode: "direct" },
  verification: { required: true },
  default: true,
})
prodOrigin!: unknown;

@Feature("cron-jobs", {
  backend: "prod-origin",                       // feature-wide default
  routes: { "POST /v1/cron-jobs": { backend: "prod-origin" } }, // per-route override
})
cronJobs!: unknown;

@Backend options: name, slug, transport: { mode: "direct" | "tunnel", runner }, verification: { required }, meters (allow-list), default, originUrl, originHostname. A route that meters a dimension the backend's meters allow-list excludes is rejected at build time.