Farther ShoreDocs
Go to Farther Shore
The fs.business() program
Meters & measures
Counted resources
Routes & access groups
Route groups & grants
Plans & pricing
The build output
Team RBAC
Tenancy & identity
Frontend integrations
@farthershore/business
RulesRoutes and metering familiesDeclarationsChoose an authoring workflow
Add metered routes
Add a resource limit
Add team RBAC
@farthershore/business exports
@farthershore/business/codegen exports
Status
Docs/Reference/@farthershore/business

@farthershore/business

Functional value-model API for authoring a software business in TypeScript.

@farthershore/business is the Business-as-Code SDK. This guide explains the 3.2 authoring model; the generated export reference contains the complete signatures and types. Use a single namespace import and declaration functions that return immutable branded refs:

ts
import * as fs from "@farthershore/business";

fs.backend("api");
const requests = fs.requests();
const tokens = fs.measure("tokens");
const tokenUsage = fs.meter("token_usage", { measures: [tokens] });
const tokenPricing = fs.pricing("token_usage", {
  meter: tokenUsage,
  catalog: [fs.rate.perMillion(fs.money.usd(2))],
});
const seats = fs.resource("seats", { cap: fs.scope.subscription });

const chat = fs.route("/v1/chat", {
  post: { costs: [requests.fixed(1)], reports: [tokenUsage] },
});
const admin = fs.route("/v1/admin", {
  post: { surfaces: [fs.surfaces.api] },
});
fs.meterRoutes("chat-tokens", chat, { reports: [tokenUsage] });

fs.plan("pro", {
  kind: fs.plan.kind.hybrid,
  price: fs.money.usd(49).monthly(),
  usagePricing: tokenPricing.current(),
  funding: { buckets: [fs.included(fs.money.usd(10))] },
  spendPolicy: {
    onExhaustion: fs.exhaustion.overage(tokenPricing.current()),
  },
  grants: [chat, admin],
  limits: [requests.perMinute(60), seats.max(25)],
});

export default fs.business();

Rules

  • Plans grant route refs directly. Use fs.group() to reuse a route bundle.
  • Cross-references use refs, never declaration-name strings.
  • Every plan declares kind: fs.plan.kind.*; the compiler validates the five economic controls (price, usagePricing, funding, lifecycle, spendPolicy) against it.
  • Money constructors accept human major units and lower to exact minor units; catalog rates lower to exact rationals.
  • Platform vocabulary is grouped under fs.surfaces, fs.scope, fs.money, fs.rate, fs.modifier, fs.plan.kind, fs.exhaustion, fs.disclosure, fs.display, and fs.rail.
  • Managed RBAC enablement is platform-owned — toggle it in the dashboard or with farthershore business rbac enable, not in code.
  • The SDK declares the raw product-permission vocabulary through route operations and permission-bearing groups. Subscribing organizations compose that vocabulary into their own roles, defaults, assignments, and direct grants at runtime; no customer role is authored in business/ or seeded by RBAC enablement.
  • Exactly one module under business/ default-exports fs.business().

The compiler imports business modules in canonical order inside a fresh, compile-scoped worker. Ordinary TypeScript values and extra exports are inert; only registered SDK declarations affect Manifest IR. An import that throws is reported with its source file.

Routes and metering families

Routes follow the OpenAPI Path Item model: a concrete or {parameter} path lists the HTTP operations that exist on that path. Wildcards do not declare routes. fs.meterRoutes(key, route, options) binds a measurement meter to a declared route under an author-supplied stable key.

ts
const tokens = fs.measure("tokens");
const tokenUsage = fs.meter("token_usage", { measures: [tokens] });

const tokenPricing = fs.pricing("token_usage", {
  meter: tokenUsage,
  catalog: [fs.rate.perMillion(fs.money.usd(2))],
});

const createChat = fs.route("/v1/chat", {
  post: { surfaces: [fs.surfaces.api] },
});

const getChat = fs.route("/v1/chat/{id}", {
  get: { surfaces: [fs.surfaces.api, fs.surfaces.ui] },
});

const createEmbedding = fs.route("/v1/embeddings", {
  post: { surfaces: [fs.surfaces.api] },
});

fs.meterRoutes("chat-tokens", createChat, {
  reports: [tokenUsage],
  maxOutputUnits: tokens.atMost(8192),
});
fs.meterRoutes("embedding-tokens", createEmbedding, { reports: [tokenUsage] });

fs.plan("pro", {
  kind: fs.plan.kind.usage,
  usagePricing: tokenPricing.current(),
  grants: [createChat, getChat, createEmbedding],
});

The binding key ("chat-tokens") is what economic agreements and admission bounds reference; keep it stable across releases. maxOutputUnits, chunkPolicy, caps, and postStream declare the admission bounds described in Meters and measures. A route with no binding reports no rated usage.

Declarations

FunctionPurpose
fs.requests()Platform request counter for structural bounds and gateway-known fixed costs.
fs.measure(key)One observed quantity a backend reports.
fs.dimension(key, options?)A selector axis; .value(v) mints a catalog selector, .is(v) a modifier condition.
fs.provider(key)An owned provider namespace; .model(key) mints a catalog item.
fs.meter(key, { measures, dimensions? })A measurement meter — what the backend reports together and under which dimensions.
fs.pricing(key, { meter, catalog })A versioned catalog of exact rates for one meter; .current() / .withContractTerms() / .fixedVersion(n) bind it.
fs.meterRoutes(key, route, options)Bind a meter to a declared route under a stable key, with admission bounds.
fs.route(path, operations)Path-first HTTP operations and their policy, surface, action, and backend bindings.
fs.plan(key, options)Declared kind plus price, usagePricing, funding, lifecycle, spendPolicy, grants, limits, and archival behavior.
fs.resource(name, options)Counted resource; its ref exposes max(count) for typed plan limits.
fs.backend(id, options)Backend declaration returning a route-bindable ref.
fs.frontendIntegration(id, options)Browser-to-provider integration whose credential is held and injected by the platform.
fs.group(key, routes)Reusable route bundle.
fs.business(options?)Seal the registry; use as the sole default export.

Value constructors: fs.money.usd(n).monthly() / .yearly(), fs.rate.perUnit / per / perMillion / rational / graduated / volume / backendQuoted, fs.modifier.multiplier(n, d).when(...), fs.included / prepaid / promo / referral, fs.display.multiplier({ factor }), fs.disclosure.opaque | transparent, fs.exhaustion.block | overage(binding), fs.rail.x402, and fs.plan.kind.*.

The namespace values fs.surfaces and fs.scope supply typed platform vocabulary. Use the generated reference for the exhaustive export inventory; this guide concentrates on the declarations used in the change loop.

Business identity, origin, and presentation are platform-owned operating state. Frontend page routes and navigation are authored in the editable frontend application, not in business.ts. Route surfaces control credential callability only. The source program must be deterministic; Farther Shore compiles it twice and rejects differing hashes.

Choose an authoring workflow

GoalWorkflow
Create or split a programBusiness program, build output
Make an operation callableRoutes, groups and grants
Sell measured usageMeters, plans, metered route recipe
Bound stored inventoryResources, resource-limit recipe
Model customer accessTenancy, team RBAC
Connect code and providersBackend, frontend integrations

The /codegen entrypoint is tooling for source generation. It does not replace the authored business/ program or activate a contract. In particular, reconstructing source from structural IR cannot recover commerce data that is absent from that IR; unsupported shapes fail rather than inventing prices. Review generated source, run the local build, and verify apply in the selected environment before testing traffic.

PreviousFrontend integrationsNextAdd metered routes

On this page

RulesRoutes and metering familiesDeclarationsChoose an authoring workflow