@faremeter/sidecar
v0.20.1
Published
HTTP sidecar that evaluates pricing rules and delegates payment to configured facilitators. Designed to run alongside an nginx gateway generated by `@faremeter/gateway-nginx` -- the Lua code in the gateway calls the sidecar's `/request` and `/response` en
Downloads
102
Readme
@faremeter/sidecar
HTTP sidecar that evaluates pricing rules and delegates payment to configured facilitators. Designed to run alongside an nginx gateway generated by @faremeter/gateway-nginx -- the Lua code in the gateway calls the sidecar's /request and /response endpoints to enforce payment on every priced API operation.
Usage
The sidecar is intended to be embedded programmatically so you can configure payment handlers for your deployment. See Programmatic Usage below.
A standalone entrypoint is included for development and testing:
FAREMETER_BASE_URL=https://api.example.com \
FAREMETER_SPEC=./openapi.yaml \
pnpm tsx apps/sidecar/src/index.tsThe standalone entrypoint runs the sidecar with no payment handlers configured. It will advertise 402 pricing challenges but cannot settle payments. For production, embed the sidecar programmatically and provide your facilitator handlers.
Configuration (standalone entrypoint)
| Variable | Required | Default | Description |
| -------------------- | ---------------- | ------- | -------------------------------------------------------------- |
| FAREMETER_SPEC | yes (or CLI arg) | -- | Path to the OpenAPI spec with x-faremeter-pricing extensions |
| FAREMETER_BASE_URL | yes | -- | Public URL of the gateway (used as the x402 resource field) |
| PORT | no | 4002 | Port the sidecar listens on |
The spec path can also be passed as the first positional argument.
Endpoints
POST /request
Called by the nginx gateway's access_by_lua_block before proxying to the upstream. Evaluates pricing rules against the request, checks for a valid payment, and returns one of:
200with{"status": 200}-- payment verified (or settled for capture-only rules), proceed to upstream.200with{"status": 402, "headers": {...}, "body": {...}}-- payment required, return the 402 challenge to the client.
The transport status is always 200. The status field in the JSON body is the semantic status for the client. The nginx Lua code treats any non-200 transport status as a bad gateway.
POST /response
Called by the nginx gateway's log_by_lua_block after the upstream response has been sent to the client. Evaluates the capture expression against the full request + response context and settles the payment with the facilitator.
200with capture envelope -- settlement succeeded (or capture-only telemetry recorded). The Lua gateway deletes the capture buffer.422-- capture expression failed (missing field, negative coefficient). The Lua gateway retries with exponential backoff.500-- validation failure or unexpected error. The Lua gateway retries.
Programmatic Usage
The sidecar can be embedded in another process instead of running standalone:
import { createApp } from "@faremeter/sidecar/app";
import { loadSpec } from "@faremeter/middleware-openapi";
const spec = await loadSpec("./openapi.yaml");
const { app } = createApp({
spec,
baseURL: "https://api.example.com",
x402Handlers: [
/* facilitator handlers */
],
mppMethodHandlers: [
/* MPP handlers */
],
supportedVersions: { x402v1: true, x402v2: true },
onCapture: (operationKey, result) => {
// Called after each successful /response with the capture envelope.
// Fires for both settled and telemetry-only captures.
},
});The app is a Hono application. Serve it with any compatible HTTP server.
Capture Hook
The onCapture callback fires after every successful /response processing. The callback receives the operation key and the CaptureResponse envelope:
{
captured: boolean; // whether the capture expression produced an amount
settled: boolean; // whether the facilitator accepted the settlement
amount: Record<string, string>; // per-asset captured amounts
error?: unknown; // facilitator error payload on settlement failure
}The hook runs post-settlement and is awaited to catch async rejections. A throw or rejected promise is logged but does not corrupt the response to the gateway.
