Farther ShoreDocs
Go to Farther Shore
Bring your own backend
Scaffold a backend service
Transport modes
Infrastructure with OpenTofu
What OpenTofu owns, and what it does notMap platform environments to infrastructure environmentsKeep state where a second machine can read itA concrete example: RailwayOrder of operationsWhat to re-run when something changesVerifyNext steps
Deploy on Railway
Deploy on Render
Deploy on AWS
Deploy on Google Cloud
Metering & verification
Runtime tokens
Storing per-user data
@farthershore/backend
Add a backend
Connect a direct backend
Add a webhook consumer
@farthershore/backend exports
@farthershore/backend/express exports
@farthershore/backend/reflect exports
@farthershore/backend/runtime exports
@farthershore/backend/testing exports
@farthershore/backend/webhooks exports
backend-sdk HTTP contracts
Status
Docs/Connect your application/Infrastructure with OpenTofu

Infrastructure with OpenTofu

Provision the backend host, its secrets, and one deployment per environment with tofu, then bind each origin to a Farther Shore environment.

Farther Shore is the gateway, billing, and entitlement plane in front of an HTTP service that you run. It does not host that service. Creating it with a host's CLI is the fastest way to a first working preview; describing it with OpenTofu is what makes a second environment, a rebuild, and a handover reproducible.

Use this page once the product is real enough to need more than one environment. For the first hour, Scaffold a backend is the shorter path.

What OpenTofu owns, and what it does not

ThingOwner
The host project, service, deployment, and public hostnameOpenTofu
FS_RUNTIME_TOKEN and other process secrets in the hostOpenTofu (as a sensitive variable it does not mint)
Plans, pricing, routes, meters, limits, fs.backend()The business/ program in the managed repository
The Farther Shore environment row and its origin bindingThe farthershore CLI

OpenTofu never authors contract state, and the CLI never provisions hosting. Runtime tokens are minted by the CLI and delivered by OpenTofu.

Map platform environments to infrastructure environments

Keep the mapping one-to-one and name both sides identically.

Farther ShoreGit branchInfrastructure
preview environment previewenv/previewworkspace preview
productiondefault branchworkspace production

Each side stays independent: the contract comes from the branch, the origin comes from the infrastructure, and farthershore backend create --env / farthershore backend bind --env is the single join between them.

Give every environment its own deployment and its own FS_RUNTIME_TOKEN. A token minted with --env preview cannot bootstrap production, which is the point: a compromised preview deployment cannot serve live customers.

Keep state where a second machine can read it

Shared infrastructure must not use local state. Configure a remote backend with locking before the first tofu apply, and keep one state file per environment — either separate workspaces or separate backend keys.

hcl
terraform {
  required_version = ">= 1.10"

  backend "s3" {
    bucket = "acme-tofu-state"
    key    = "farthershore/api.tfstate"
    region = "us-east-1"
    # Native S3 conditional-write locking. On older OpenTofu, lock with
    # `dynamodb_table` instead; both mechanisms remain supported.
    use_lockfile = true
  }
}

State contains secret values in plaintext. Encrypt the bucket, restrict who can read it, and never commit a .tfstate file. OpenTofu's built-in state encryption is worth enabling on top of the backend's own encryption.

A concrete example: Railway

The community Railway provider models a project, its environments, one service per environment, that service's domain, and its variables. Substitute your own provider if you deploy elsewhere — the shape below is what matters, not the vendor.

hcl
terraform {
  required_providers {
    railway = {
      source  = "terraform-community-providers/railway"
      version = "~> 0.5"
    }
  }
}

# RAILWAY_TOKEN comes from the environment; never write it into a .tf file.
provider "railway" {}

variable "environment_name" {
  type        = string
  description = "preview or production; matches the Farther Shore environment"
}

variable "fs_runtime_token" {
  type        = string
  sensitive   = true
  description = "Minted by farthershore backend tokens create for THIS environment"
}

resource "railway_project" "api" {
  name    = "acme-api"
  private = true
}

resource "railway_environment" "this" {
  name       = var.environment_name
  project_id = railway_project.api.id
}

resource "railway_service" "api" {
  name               = "api"
  project_id         = railway_project.api.id
  source_repo        = "acme/acme-api"
  source_repo_branch = var.environment_name == "production" ? "main" : "env/preview"
  root_directory     = "/api"
}

resource "railway_service_domain" "api" {
  subdomain      = "acme-api-${var.environment_name}"
  environment_id = railway_environment.this.id
  service_id     = railway_service.api.id
}

resource "railway_variable" "runtime_token" {
  name           = "FS_RUNTIME_TOKEN"
  value          = var.fs_runtime_token
  environment_id = railway_environment.this.id
  service_id     = railway_service.api.id
}

output "origin_url" {
  value = "https://${railway_service_domain.api.domain}"
}

Pass the secret from your own secret store rather than a variable file:

bash
tofu workspace select preview
TF_VAR_fs_runtime_token="$(read-from-your-secret-store)" \
  tofu apply -var environment_name=preview

If you use a different host, the four resources to look for are the same: a project or account scope, one service per environment, a public HTTPS hostname, and a secret variable bound to that service and environment.

Order of operations

The runtime token is scoped to a backend row, so the row must exist first.

  1. Declare the logical backend in business/ and push the branch, so the environment has an accepted contract:

    ts
    const api = fs.backend("api", {
      transport: { mode: "direct" },
      default: true,
    });
    
  2. Provision the hosting for that environment and read its hostname:

    bash
    tofu workspace select preview
    tofu apply -var environment_name=preview
    tofu output -raw origin_url
    
  3. Register the origin for that environment:

    bash
    farthershore backend create acme \
      --env preview \
      --name api --slug api \
      --transport direct \
      --origin-url "$(tofu output -raw origin_url)" \
      --default \
      --idempotency-key <persisted-backend-create-attempt-key> \
      --format json
    
  4. Mint the environment's token after that row exists, then deliver it:

    bash
    farthershore backend tokens create acme \
      --env preview \
      --idempotency-key <persisted-backend-tokens-create-attempt-key> \
      --format json
    

    Pass --backend <backend-id> as well when the environment has more than one backend, so the token resolves to the intended row. Store the one-time value in your secret store and re-apply so the host receives it.

  5. Repeat for production with --env production omitted (production is the default target) and a production-scoped token.

Steps 3 and 4 cannot be inverted, and they cannot be moved into OpenTofu: the token is a one-time secret returned by a CLI write, not a declarable resource.

What to re-run when something changes

ChangeRe-run
Plans, pricing, routes, meters, limits — anything in business/git push only. No tofu run.
Backend application code in api/Your host's deploy (often automatic from the branch).
A new fs.backend() slugtofu apply, then backend create + tokens create.
A new environmentfarthershore env create, then tofu apply in a new workspace, then backend create + tokens create.
Hostname, region, replica count, resource sizingtofu plan then tofu apply, then backend bind --env <name> --origin-url <new-url> if the hostname moved.
Token rotationbackend tokens create, update the secret, re-apply, verify, then revoke the predecessor.

Always read tofu plan before tofu apply. A plan that proposes replacing the service or its domain will change the origin URL, which requires a matching farthershore backend bind or the gateway will return origin_unavailable.

Verify

  • tofu output -raw origin_url returns an HTTPS URL that serves /healthz unauthenticated.
  • farthershore backend list acme --format json shows the row for each environment with a concrete target. This command is business-wide and takes no --env; read the environment off each returned row.
  • A signed gateway request reaches the backend, and a direct call to the origin without a platform signature fails with missing_signature.
  • Publishing production succeeds. If it fails with BACKEND_TARGET_REQUIRED, a declared backend has no production binding — see Production releases.

Next steps

  • Runtime tokens — scope, delivery, and rotation.
  • Preview environments — branches, applies, and teardown.
  • Scaffold a backend — the single-service starting point.
PreviousTransport modesNextDeploy on Railway

On this page

What OpenTofu owns, and what it does notMap platform environments to infrastructure environmentsKeep state where a second machine can read itA concrete example: RailwayOrder of operationsWhat to re-run when something changesVerifyNext steps