Metering & verification
Verify gateway requests and report usage from your backend with @farthershore/backend.
Verify gateway requests and report usage from your backend with @farthershore/backend.
@farthershore/backend is the SDK your origin imports. It does two jobs:
verify that an inbound request really came from the Farther Shore gateway
(fail-closed Ed25519), and report how much each request consumed so the
platform can meter and bill it. You configure exactly one thing — the
FS_RUNTIME_TOKEN — and everything else (product and
backend ids, the JWKS URL, the metering endpoint, verification config,
transport) is fetched from bootstrap and cached in memory.
npm install @farthershore/backend
import { fartherShore } from "@farthershore/backend";
// Derives everything from FS_RUNTIME_TOKEN via POST /v1/runtime/bootstrap.
const fs = fartherShore.initFromEnv();
The plaintext X-FS-* headers on a forwarded request are untrusted. Identity
comes only from a gateway signature whose claims match the actual request. The
SDK recomputes the canonical signing string from the real method, path, query,
headers, and body, then verifies the gateway's Ed25519 signature against a
JWKS-resolved public key.
fs.middleware() runs the verification and attaches req.fartherShore. Any
failure responds 401 (or 413 for an oversized body) — there is no fail-open
branch.
import { fartherShore } from "@farthershore/backend";
const fs = fartherShore.initFromEnv();
app.use(fs.middleware()); // fail-closed verify → req.fartherShore
app.post("/v1/cron-jobs", async (req, res) => {
const job = await createCronJob(req.body);
res.json(job);
});
app.listen(3000);
process.on("SIGTERM", () => void fs.shutdown());
fs.verifyRequest({ method, path, query, headers, body }) is the
framework-neutral primitive. It throws a typed FartherShoreError on any
failure; otherwise it returns the verified context.
export async function POST(request: Request) {
const url = new URL(request.url);
const body = new Uint8Array(await request.clone().arrayBuffer());
await fs.verifyRequest({
method: request.method,
path: url.pathname,
query: url.search,
headers: request.headers,
body,
});
// ... handle the verified request ...
}
Every failure mode — missing, malformed, bad-signature, stale, clock-skew,
wrong-route, body-hash-mismatch, replayed-nonce, unknown-kid, jwks-unavailable —
returns a typed FartherShoreError mapped to HTTP 401 (413 for oversized
bodies). Verification is required only when your @Backend
sets verification: { required: true }; until then the gateway still forwards,
but turning it on is the secure default once your token is provisioned.
When you know usage by the time you return the response — tokens consumed,
credits spent — sign it into the response with withUsage(). This makes no
backend→core network call. It signs the usage into internal x-fs-metering
response headers with your FS_RUNTIME_TOKEN; the gateway verifies, settles, and
strips those headers before the subscriber receives the response.
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());
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 }, // raw usage, keyed by meter
{
measureContext: { model: result.model }, // free-form context
creditUnitsConsumed: { credits: result.creditsUsed }, // credit-wallet map
},
);
}
The meter keys (tokens_used above) are not hardcoded by the SDK — each must
match a dynamic meter declared in your product contract via @Meter(...) and
listed in a route's reports. Keys must be lowercase alphanumeric with underscores
(^[a-z0-9_]{1,64}$) and values non-negative finite numbers, validated locally
before signing — a bad key or value throws MeteringError. Platform-managed
defaults like request counts need no upstream code at all.
createUsage() for incremental reportingWhen usage accrues across a handler, build it up with createUsage() and wrap
the response at the end. withUsage() is just sugar over this.
import { createUsage } from "@farthershore/backend";
export async function POST(request: Request) {
const usage = createUsage(request);
const step1 = await embed(/* ... */);
usage.report("tokens_used", step1.tokens);
const step2 = await generate(/* ... */);
usage.report("tokens_used", step2.tokens);
return usage.wrap(Response.json({ ok: true }), {
measureContext: { model: step2.model },
});
}
fs.meter(meter, qty, { requestId, routeId }) is for usage that is not tied
to a gateway response — background jobs, deferred tallies. Unlike withUsage(),
this DOES make a network call: it enqueues an idempotent event and POSTs it to
/v1/metering/events with a reusable bearer credential. Delivery is
at-least-once and the event_id keeps core's ingest safe; values are tallied and
billed post-cycle, not enforced in real time.
// Background reconciliation, hours after the original request.
await fs.meter("tokens_used", 1280, {
requestId: job.requestId,
routeId: "cron-job.run",
});
Reach for fs.meter() only when usage is genuinely decoupled from a response.
For request-bound usage, prefer withUsage() / createUsage() — they are
cheaper (no network call) and settle in the same request the subscriber made.
The instance bootstraps lazily on first use. On shutdown it flushes any buffered
metering and sends a stopping heartbeat.
fs.health(); // { runtimeToken, bootstrap, tunnel, verification, metering }
await fs.shutdown(); // flush metering + stopping heartbeat (call on SIGTERM)
Response-bound reports only settle for meters your product actually declares.
Declare the meter and bind it as a route report in the same @Product class:
@Product({ name: "croncloud", origin: "https://api.example.com" })
class CronCloud {
@Requests()
requests!: unknown;
@Meter("tokens_used", { unit: "token", estimate: 500 })
tokens!: unknown;
@Feature("runs", {
plans: ["starter"],
routes: {
// The upstream may report `tokens_used` on this route.
"POST /v1/runs": { reports: "tokens_used" },
},
})
runs!: unknown;
@Plan("starter", {
name: "Starter",
limits: {
requests: { rate: 600, interval: "minute", enforcement: "enforce" },
},
})
starter!: unknown;
}
See usage metering for meter shapes,
runtime tokens for provisioning the token this SDK
reads, and the @farthershore/backend reference for
the full export surface.