Farther ShoreDocs
Go to Farther Shore
The fs.business() program
Meters & measures
Counted resources
Routes & access groups
Paths and methodsAuthentication and subjectCallability and visibilityMetering and limitsTimeout and retryBackend and resource effectsImporting OpenAPI
Route groups & grants
Plans & pricing
The build output
Team RBAC
Tenancy & identity
Frontend integrations
@farthershore/business
Add metered routes
Add a resource limit
Add team RBAC
@farthershore/business exports
@farthershore/business/codegen exports
Status
Docs/Build your product/Routes & access groups

Routes & access groups

Declare each HTTP operation's access, metering, policy, and backend binding.

fs.route(path, operations) declares concrete HTTP operations and returns one grantable ref for the path.

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

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

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

fs.plan("pro", {
  kind: fs.plan.kind.flat,
  price: fs.money.usd(49).monthly(),
  grants: [chat],
  limits: [requests.perMinute(60)],
});

export default fs.business();

Paths and methods

Paths start with / and may use named parameters such as {id} or :id. Declare the exact methods that exist: get, post, put, patch, delete, head, or options.

Wildcards do not declare callable routes. Use them only as non-granting fs.meterRoutes() selectors over separately declared operations.

Calling fs.route() more than once for the same normalized path merges different methods. Declaring the same method twice with different options is a conflict.

Authentication and subject

Routes require customer authentication by default. public: true removes that requirement and cannot be combined with a member/service subject requirement.

  • requireMember: true admits only a verified person or personal key.
  • requireService: true admits only an organization-owned service credential.
  • omit both when either verified subject is valid.

Subject requirements are useful when the backend's data model is inherently per-person or machine-only.

Callability and visibility

surfaces is a callability allowlist. Use typed fs.surfaces.api and fs.surfaces.ui values. Omitting the field permits all supported authenticated surfaces.

An empty list is invalid, not deny-all. Surface sets are deduplicated and canonically ordered. public: true cannot be combined with a surface restriction because there is no credential to classify; hidden: true is still allowed. A route that does not admit the API surface is omitted from generated API discovery. Do not confuse hiding a route with denying an otherwise eligible caller.

hidden: true removes an otherwise API-callable operation from generated API discovery and returns a not-found response to wrong-surface callers. It is a visibility control, not an authorization replacement.

Metering and limits

  • costs records a fixed, gateway-known structural amount (requests.fixed(1)).
  • fs.meterRoutes(key, route, { reports, ... }) binds the measurement meters the backend reports on this route, plus admission bounds (maxOutputUnits, caps, chunkPolicy, postStream).
  • onStatusCodes narrows which response outcomes count.
  • rateLimit and quota create route-scoped bounds.

Every bounded dimension must be attached to the operation. For the default requests dimension, combine the bound with a request cost:

ts
const requests = fs.requests();
fs.route("/v1/ping", {
  get: {
    costs: [requests.fixed(1)],
    rateLimit: "10/min",
  },
});

Timeout and retry

timeout and idleTimeout accept milliseconds or values such as "500ms", "30s", or "2m", up to ten minutes. retry: true expands to the platform's bounded safe default. Use retries only for operations whose upstream behavior is idempotent, or supply your own idempotency mechanism.

The exact retry: true expansion is two total attempts (one retry), on network errors and 5xx responses, with { base: 100, max: 1000, jitter: 0.2 } backoff and budgetRatio: 0.1. These are bounds, not a guarantee that every request is retried. An explicit retry policy may use one to three total attempts; its retryOn must contain network and/or 5xx, backoff base must be nonnegative, maximum must be at least base, and jitter and budget ratio must be between zero and one.

Duration numbers are whole milliseconds from 1 to 600,000. Strings use ms, s, or m and must resolve to whole milliseconds: "0.5s" is valid, "0.5ms" is rejected rather than rounded.

Raw policy and shorthand precedence

The advanced policy object is the base; top-level shorthand options override it. For example, retry: false removes a retry configured inside policy, and public: false overrides policy.authMode: "public". Do not specify conflicting forms casually: the compiler resolves them deterministically, not by object-key order. Equivalent shorthand and canonical policy normalize to the same IR.

Backend and resource effects

backend binds an operation to a logical fs.backend() ref. The concrete origin is environment-owned and configured after deployment.

Every business that declares gateway routes must declare at least one logical backend. With exactly one backend, it is the implicit default and route entries may omit backend. A backendless business is valid only when it declares no gateway routes, such as a hosted frontend-only product. This makes an unroutable route a compile error (BACKEND_REQUIRED_FOR_ROUTE) instead of a product that appears ready but returns Unknown project at runtime.

creates and deletes associate a successful operation with one counted resource effect. Use reported counts for batch or asynchronous changes.

Importing OpenAPI

The business program remains authoritative. farthershore import openapi is a local scaffold tool that generates route declarations for review; it does not publish an OpenAPI document or create a second source of truth. Inspect and edit the generated refs, policies, grants, and metering before committing.

PreviousCounted resourcesNextRoute groups & grants

On this page

Paths and methodsAuthentication and subjectCallability and visibilityMetering and limitsTimeout and retryRaw policy and shorthand precedenceBackend and resource effectsImporting OpenAPI