Farther ShoreDocs
Go to Farther Shore
What is FartherShore
Install the CLI
Quickstart
1. Install and authenticate2. Create the product3. Define the product4. Build it5. Connect billing and add pricing6. Publish7. Confirm it's liveExercise the gatewayIterateNext
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
@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/Get started/Quickstart

Quickstart

Create, define, build, and publish your first product.

PreviousInstall the CLINextCore concepts

This walks the real happy path: install and authenticate, create a product, author a minimal @Product class, build it, connect billing, add a plan, publish, and confirm it's live. We'll build CronCloud, a managed cron-jobs product, the same example used throughout the docs.

Every step is a copy-pasteable command. Pass --format json on any command for machine-readable output.

1. Install and authenticate

npm install -g @farthershore/cli
farthershore auth login --token mk_xxx
farthershore auth whoami

See Install the CLI for tokens, env vars, and CI usage.

2. Create the product

Creation provisions a managed GitHub repo containing the editable frontend/ starter and the product/product.config.ts entrypoint, so you need a connected GitHub account and a repo target. Check connection status first:

farthershore connect github --format json

Then create:

farthershore product create \
  --name croncloud \
  --display-name "CronCloud" \
  --origin https://api.example.com \
  --repo-owner your-gh-org \
  --repo-name croncloud \
  --format json
  • --name is the product slug (it becomes the subdomain).
  • --origin is the business-logic URL FartherShore calls for customer-facing actions — the API the gateway forwards to.
  • --repo-owner / --repo-name target the managed GitHub repo to create.

The response includes the clone URL. A new product starts as a DRAFT. Clone the repo and install the product workspace:

git clone <clone-url> croncloud
npm install --prefix croncloud/product

3. Define the product

Open product/product.config.ts and replace it with the CronCloud manifest. One class declares the meters, the gateway routes, the capability they require, and two priced plans that grant it:

import {
  Product,
  Requests,
  Meter,
  Resource,
  Capability,
  Feature,
  Plan,
  capabilityGrant,
} from "@farthershore/product";

@Product({
  name: "croncloud",
  origin: "https://api.example.com",
  displayName: "CronCloud",
  description: "Managed cron jobs",
})
export default class CronCloud {
  // Platform-managed request meter — applies `requests = 1` to every route.
  @Requests()
  requests!: unknown;

  // A variable usage meter your backend reports (ms of compute).
  @Meter("compute", { display: "Compute", unit: "ms" })
  compute!: unknown;

  // A counted resource you can cap per plan.
  @Resource("cron_jobs", { display: "Cron jobs", countSource: "action_inferred" })
  cronJobs!: unknown;

  // The capability a plan must grant to reach the feature's routes.
  @Capability("managed-cron", {
    title: "Managed Cron Jobs",
    includesFeatures: ["cron-jobs"],
  })
  managedCron!: unknown;

  // The feature: gateway routes keyed by "METHOD /path".
  @Feature("cron-jobs", {
    description: "Cron job CRUD",
    plans: ["starter", "pro"],
    routes: {
      "GET /v1/cron-jobs": {},
      "POST /v1/cron-jobs": {},
      "DELETE /v1/cron-jobs/{id}": {},
    },
  })
  cronJobsFeature!: unknown;

  @Plan("starter", {
    name: "Starter",
    price: { amount: 2900, currency: "usd", interval: "month" },
    grants: [capabilityGrant("managed-cron", { limits: { cron_jobs: 10 } })],
    limits: { requests: { rate: 600, interval: "minute", enforcement: "enforce" } },
  })
  starter!: unknown;

  @Plan("pro", {
    name: "Pro",
    price: { amount: 19900, currency: "usd", interval: "month" },
    grants: [capabilityGrant("managed-cron", { limits: { cron_jobs: 100 } })],
    limits: { requests: { rate: 6000, interval: "minute", enforcement: "enforce" } },
  })
  pro!: unknown;
}

Cross-references are by string key: the plan grants the "managed-cron" capability, which includesFeatures: ["cron-jobs"], which owns the routes. price.amount is integer cents ($29.00 = 2900). See Core concepts for the full vocabulary.

product/product.config.ts and everything it imports must be deterministic — no dates, randomness, or network calls. The build runs twice and rejects the push if the two generated hashes differ.

4. Build it

Compile the class to Manifest IR locally. This is the same compiler the platform runs server-side, so a green build means the manifest is valid:

farthershore build --format json

This emits manifest-ir.json. Fix any reported errors (a missing capability reference, a malformed route key, a plan without a rate limit) and rebuild until it's clean.

5. Connect billing and add pricing

Publishing requires a verified Stripe connection. The connect flow is browser-only; the CLI reports status so you can poll until it's done:

farthershore connect stripe croncloud --format json

The two plans above are declared in code and apply when the manifest is published. List what's currently on the product at any time:

farthershore plan list croncloud --format json

You can also create a plan directly through the API — for example a free tier to round out the lineup — without editing the manifest. A free plan needs a hard-enforced rate limit:

farthershore plan create croncloud \
  --key free \
  --name "Free" \
  --free \
  --rate-limit 60 \
  --rate-window minute \
  --format json

6. Publish

Take the product live. Publish enforces the same gates as the dashboard: at least one plan, an origin, and a verified Stripe connection. It auto-derives a semver bump from the change and cuts a vX.Y.Z release:

farthershore product publish croncloud --format json

Preview the computed version and reasons without cutting a release:

farthershore product publish croncloud --dry-run --format json

If a gate fails, the CLI prints an actionable code — STRIPE_NOT_CONNECTED, STRIPE_NOT_VERIFIED, or BILLING_TAX_NOT_ENROLLED — with remediation.

7. Confirm it's live

Verify the product is actually serving — lifecycle status, the latest release version, the latest deployment, and a derived live boolean:

farthershore product status croncloud --format json

When status is ACTIVE and live is true, the loop is connected end to end: the gateway is enforcing your plans in front of https://api.example.com, admitted requests are metered, and Stripe is billing them.

Exercise the gateway

To send a real authenticated request before you have live customers, mint a test persona key in a preview environment. It returns an fsk_test_* key once (store it) you can use to call the gateway and watch usage land:

farthershore env create croncloud --name preview --branch env/preview --format json
farthershore persona bootstrap croncloud --env preview --plan starter --format json

Iterate

To change the product later, edit product/product.config.ts, rebuild, and publish again — or just push accepted product/** changes and the GitHub bot compiles and publishes. The repo is the source of truth either way.

farthershore build --format json
farthershore product publish croncloud --format json

Next

  • Core concepts — the @Product class, the Manifest IR, and how the gateway enforces it.
  • Install the CLI — full command and flag reference.