> ## Documentation Index
> Fetch the complete documentation index at: https://forst-lang.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Call Forst over HTTP

> Call Forst functions from JavaScript and TypeScript applications over HTTP.

Forst generates either Promise-based calls or Effect values. Both modes use the same package subpaths and `$`-prefixed handles.

<Note>
  Install `@forst/cli` first. Add a `postinstall` script that runs `forst generate .`.
  Run generate once before you import `@forst/gen`.
  Details are on the [Installation](/docs/installation#generated-typescript-client) page.
</Note>

Promise mode is the default. To generate Effect values, install `effect` and enable Effect mode in `ftconfig.json`:

<Columns cols={2}>
  <Card title="Promise mode">
    No extra dependency or generate setting is required.
  </Card>

  <Card title="Effect mode">
    ```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    npm install effect
    ```

    ```json theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    {
      "generate": {
        "effect": true
      }
    }
    ```
  </Card>
</Columns>

Point the client at a server:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
export FORST_BASE_URL=http://127.0.0.1:6321
```

Call the same generated function in either style:

<CodeGroup>
  ```typescript Promise theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { $auth } from "@forst/gen/auth";

  const { valid } = await $auth.VerifyPassword({
    plainPassword: "secret",
    passwordHash: "$2a$...",
  });
  ```

  ```typescript Effect theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { Effect } from "effect";
  import { ForstClientLive } from "@forst/gen";
  import { $auth } from "@forst/gen/auth";

  const program = Effect.gen(function* () {
    const { valid } = yield* $auth.VerifyPassword({
      plainPassword: "secret",
      passwordHash: "$2a$...",
    });
    return valid;
  });

  const valid = await Effect.runPromise(
    program.pipe(Effect.provide(ForstClientLive))
  );
  ```
</CodeGroup>

Import the package handle from `@forst/gen/<package>` (for example `$auth` from `@forst/gen/auth`) and call through it in both modes. See [Generate a TypeScript client](/docs/interop/invoke/generate-types) and [Effect mode](/docs/interop/invoke/effect).

## Quick start

<Steps>
  <Step title="Write a public Forst function">
    Only **exported** functions (capitalized names) are callable from JavaScript.

    ```ft theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    package auth

    import "golang.org/x/crypto/bcrypt"

    func VerifyPassword(input {
      plainPassword: String,
      passwordHash: String
    }) {
      hashBytes := []byte(input.passwordHash)
      plainBytes := []byte(input.plainPassword)

      compareErr := bcrypt.CompareHashAndPassword(hashBytes, plainBytes)
      if compareErr is Nil() {
        return { valid: true }
      }
      return { valid: false }
    }
    ```
  </Step>

  <Step title="Install and generate">
    Follow [Installation § Generated TypeScript client](/docs/installation#generated-typescript-client).

    Promise mode is enabled by default. For Effect return types, set `"generate": { "effect": true }` in `ftconfig.json` before generating.
  </Step>

  <Step title="Run a server">
    **Development** — hot reload while you edit `.ft` files:

    ```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    npx forst dev -root . -port 6320
    ```

    **Production** — linked program binary for slim container images. Enable embedded invoke in `ftconfig.json` before building:

    ```json theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    {
      "server": { "embedded": true }
    }
    ```

    ```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    npx forst generate .
    npx forst build -root . -o .forst/build -- ./main.ft
    FORST_ROOT=/app /app/.forst/build/bin/main
    ```

    With `bridge.hostMode`, the same binary runs your Forst entry and starts the Node app as a child. Set `FORST_SKIP_NODE_HOST=1` on the same binary when Node runs separately and the binary should not spawn the Node host.

    For Go source inspection or hand `go build`, emit sources with `generate.go` flags:

    ```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    npx forst generate --go-entry=./main.ft --go-out=./out/main.go --skip-client .
    go build -o ./bin/api ./out/*.go
    ```
  </Step>

  <Step title="Set the URL and call">
    ```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
    export FORST_BASE_URL=http://127.0.0.1:6321   # built-in server
    # or
    export FORST_BASE_URL=http://localhost:6320    # forst dev
    ```

    <CodeGroup>
      ```typescript Promise theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
      import { $auth } from "@forst/gen/auth";

      const result = await $auth.VerifyPassword({
        plainPassword: "secret",
        passwordHash: "$2a$...",
      });
      ```

      ```typescript Effect theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
      import { Effect } from "effect";
      import { ForstClientLive } from "@forst/gen";
      import { $auth } from "@forst/gen/auth";

      const result = await Effect.runPromise(
        $auth.VerifyPassword({
          plainPassword: "secret",
          passwordHash: "$2a$...",
        }).pipe(Effect.provide(ForstClientLive))
      );
      ```
    </CodeGroup>
  </Step>
</Steps>

Repository example: [`examples/in/rfc/embedded-invoke`](https://github.com/forst-lang/forst/tree/main/examples/in/rfc/embedded-invoke).

## Configure the client

Promise mode creates a configured client object. Effect mode creates a layer that provides the generated package services:

<CodeGroup>
  ```typescript Promise theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { createForstClient } from "@forst/gen";

  const forst = createForstClient({
    baseUrl: process.env.FORST_BASE_URL,
    headers: {
      Authorization: `Bearer ${process.env.API_TOKEN}`,
    },
  });

  await forst.auth.VerifyPassword({
    plainPassword: "secret",
    passwordHash: "$2a$...",
  });
  ```

  ```typescript Effect theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { Effect } from "effect";
  import { ForstClientLayer } from "@forst/gen";
  import { $auth } from "@forst/gen/auth";

  const ForstLive = ForstClientLayer({
    baseUrl: process.env.FORST_BASE_URL,
    headers: {
      Authorization: `Bearer ${process.env.API_TOKEN}`,
    },
  });

  await Effect.runPromise(
    $auth.VerifyPassword({
      plainPassword: "secret",
      passwordHash: "$2a$...",
    }).pipe(Effect.provide(ForstLive))
  );
  ```
</CodeGroup>

### Custom HTTP headers

Pass default headers on the client config. They merge into every invoke and stream request. Per-call headers in the second argument override defaults for that request only.

<CodeGroup>
  ```typescript Promise theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { createForstClient } from "@forst/gen";
  import { $auth } from "@forst/gen/auth";

  const forst = createForstClient({
    baseUrl: process.env.FORST_BASE_URL,
    headers: {
      Authorization: `Bearer ${process.env.API_TOKEN}`,
      "X-Tenant-Id": process.env.TENANT_ID ?? "",
    },
  });

  await forst.auth.VerifyPassword(input);

  await $auth.VerifyPassword(input, {
    headers: { "X-Request-Id": crypto.randomUUID() },
  });
  ```

  ```typescript Effect theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { ForstClientLayer } from "@forst/gen";
  import { $auth } from "@forst/gen/auth";

  const ForstLive = ForstClientLayer({
    baseUrl: process.env.FORST_BASE_URL,
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
  });
  ```
</CodeGroup>

`Content-Type: application/json` is always set. The invoke authentication headers are reserved and cannot be replaced by your defaults or per-call headers. See [Invoke security](/docs/interop/invoke/security#reserved-request-headers).

Connect-mode workers need the invoke HMAC secret from `FORST_INVOKE_TOKEN` (base64url). The `forst dev` process sets this when it starts. Sidecar spawn mode uses an inherited `FORST_INVOKE_AUTH_FD` handoff instead. See [Invoke security](/docs/interop/invoke/security#authentication-flow).

## How the client picks a server

| You run                                        | Set env                                | Client behavior                                 |
| ---------------------------------------------- | -------------------------------------- | ----------------------------------------------- |
| Compiled Go binary                             | `FORST_BASE_URL=http://127.0.0.1:6321` | HTTP connect                                    |
| `forst dev`                                    | `FORST_BASE_URL=http://localhost:6320` | HTTP connect                                    |
| Nothing in development                         | —                                      | Connects to the default invoke port from config |
| Production (`NODE_ENV=production`) with no URL | —                                      | Fails with `InvokeBaseUrlMissing`               |

**Production never spawns a server.** Set `FORST_BASE_URL` (or `FORST_INVOKE_URL` / `FORST_DEV_URL`) explicitly. Local spawn of `forst dev` is opt-in for development only.

The **`forst dev`** HTTP contract (`/invoke`, `/version`, …) is documented in [Dev server](/docs/workflow/dev-server). Saving a `.ft` file regenerates the client so the editor stays in step. See [Dev server § Generated client](/docs/workflow/dev-server#generated-client).

## Failures

Every invoke failure has a stable `_tag`. Promise mode throws the tagged value. Effect mode exposes the same tags through its typed error channel:

<CodeGroup>
  ```typescript Promise theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { isInvokeFailure } from "@forst/errors";
  import { $auth } from "@forst/gen/auth";

  try {
    await $auth.VerifyPassword(input);
  } catch (error) {
    if (!isInvokeFailure(error)) throw error;
    switch (error._tag) {
      case "@forst/errors/InvokeRejected":
        return badRequest(error.serverError);
      case "@forst/errors/InvokeBaseUrlMissing":
      case "@forst/errors/InvokeUnreachable":
        return serviceUnavailable();
      default:
        throw error;
    }
  }
  ```

  ```typescript Effect theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { Effect } from "effect";
  import { $auth } from "@forst/gen/auth";

  const program = $auth.VerifyPassword(input).pipe(
    Effect.catchTag("@forst/errors/InvokeRejected", (error) =>
      Effect.succeed(badRequest(error.serverError))
    ),
    Effect.catchTag("@forst/errors/InvokeUnreachable", () =>
      Effect.succeed(serviceUnavailable())
    )
  );
  ```
</CodeGroup>

| Tag                                     | Meaning                                            | What to do                                             |
| --------------------------------------- | -------------------------------------------------- | ------------------------------------------------------ |
| `@forst/errors/InvokeRejected`          | Server returned `success: false`                   | Map `serverError` to a client response or retry policy |
| `@forst/errors/InvokeHttpFailure`       | Non-2xx HTTP status                                | Check `status` and `responseText`, fix URL or server   |
| `@forst/errors/InvokeTimedOut`          | Exceeded `timeoutMs` or abort signal               | Raise timeout, fix slow handler, or surface a 504      |
| `@forst/errors/InvokeUnreachable`       | Connection refused or DNS failure                  | Confirm the binary or `forst dev` is listening         |
| `@forst/errors/InvokeBaseUrlMissing`    | No base URL and spawn is forbidden                 | Set `FORST_BASE_URL` (required in production)          |
| `@forst/errors/InvokeStreamAborted`     | NDJSON stream ended early or a row failed to parse | Retry stream, or fix the streaming handler             |
| `@forst/errors/ContractVersionMismatch` | Client and server contract versions disagree       | Upgrade client and server together                     |

Import invoke helpers from `@forst/errors` in Promise mode or `@forst/errors/effect` in Effect mode (`InvokeRejected`, `isInvokeFailure`, …). Class names stay short. Built-in invoke, harness, and unknown-failure `_tag` strings use the `@forst/errors/` prefix. Domain error tags are namespaced with your npm package name (for example `@forst/tictactoe/CellTaken`). Optional domain namespaces are also available from `@forst/gen/$errors` (for example `errors.auth.NotFound`).

### Domain errors

When a Forst function raises a nominal `error X { ... }`, the server includes structured `errorValue` on the invoke envelope (HTTP contract version `"2"`). The generated client decodes it into a tagged class with the same name as the Forst error type.

```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
import { $main } from "@forst/tictactoe/main";
import { $CellTaken } from "@forst/tictactoe/main/errors";

try {
  await $main.PlayMove({ state, row: 1, col: 2 });
} catch (error) {
  if (error instanceof $CellTaken) {
    console.log(error.row, error.col);
  }
}
```

In Effect mode the function error channel includes each inferred domain error, `ForstUnknownFailure`, and transport failures:

```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
import { Effect } from "effect";
import { $main } from "@forst/tictactoe/main";

const program = $main.PlayMove(req).pipe(
  Effect.catchTag("@forst/tictactoe/CellTaken", (e) => Effect.succeed({ row: e.row, col: e.col }))
);
```

Unmapped server failures (generic Go errors, panics) surface as `ForstUnknownFailure`.

### `.safe()`

Promise mode offers `.safe()`. Effect mode can move the typed failure into an `Either` value:

<CodeGroup>
  ```typescript Promise theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { InvokeRejected } from "@forst/errors";
  import { $auth } from "@forst/gen/auth";

  const result = await $auth.VerifyPassword.safe(input);
  if (!result.ok) {
    switch (result.error._tag) {
      case "@forst/errors/InvokeRejected":
        return badRequest(result.error.serverError);
      default:
        throw result.error;
    }
  }
  const { valid } = result.value;
  ```

  ```typescript Effect theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { Effect } from "effect";
  import { $auth } from "@forst/gen/auth";

  const program = Effect.gen(function* () {
    const result = yield* Effect.either($auth.VerifyPassword(input));
    if (result._tag === "Left") {
      return handleFailure(result.left);
    }
    return result.right.valid;
  });
  ```
</CodeGroup>

## Per-call options

<CodeGroup>
  ```typescript Promise theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  await $auth.VerifyPassword(input, {
    signal: AbortSignal.timeout(2_000),
    timeoutMs: 2_000,
    retries: 2,
  });
  ```

  ```typescript Effect theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
  import { Effect, Schedule } from "effect";
  import { $auth } from "@forst/gen/auth";

  const program = $auth.VerifyPassword(input).pipe(
    Effect.timeout("2 seconds"),
    Effect.retry(Schedule.recurs(2))
  );
  ```
</CodeGroup>

| Option      | Purpose                                                                                                    |
| ----------- | ---------------------------------------------------------------------------------------------------------- |
| `signal`    | Cancel the in-flight request                                                                               |
| `timeoutMs` | Fail with `InvokeTimedOut` after this many milliseconds                                                    |
| `retries`   | Retry on transient failures (`InvokeTimedOut`, `InvokeUnreachable`, …) with exponential backoff and jitter |

In Effect mode, fiber interruption cancels the HTTP request. Use `Effect.timeout` and `Effect.retry` for timeouts and retry schedules.

## Runtime support

The generated client targets **Node.js 20.19+** server-side use (SSR, API routes, scripts). It assumes `fetch`, `process.env`, and optional `AbortSignal`.

| Environment                             | Support                                                                                                               |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Node SSR (Remix, Vite SSR, Express)     | Supported. See [Generate a TypeScript client § Bundlers](/docs/interop/invoke/generate-types#bundlers-vite-remix-webpack). |
| Cloudflare Workers / Vercel Edge / Deno | Not supported today. Transport reads `process.env` and defaults assume Node.                                          |
| Browser bundles                         | Not supported. Invoke from your backend instead.                                                                      |

For edge deployments, keep Forst behind a Node or Go invoke server and call it from the edge with `fetch` and your own thin wrapper if needed.

## Built-in HTTP server

Your compiled Go binary starts a local invoke server. On Unix it defaults to a Unix domain socket at `.forst/invoke.sock`. On Windows, or with `FORST_INVOKE_TRANSPORT=tcp`, it uses loopback TCP (default port `6321`).

Enable with `server.embedded` in `ftconfig.json`:

```json theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
{
  "server": {
    "embedded": true,
    "host": "127.0.0.1",
    "port": "6321"
  }
}
```

Then `forst build -o <dir>` links a native invoke binary (requires `server.embedded`). It writes `.forst/invoke.ready` and a separate local token file. HMAC auth is on by default. Use the Forst client, sidecar, or CLI helpers for `POST /invoke`, because authenticated RPC needs a fresh challenge and HMAC proof. For a manual smoke test over TCP:

```bash theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/languages/forst.json"]}}
FORST_INVOKE_TRANSPORT=tcp curl -s http://127.0.0.1:6321/health
```

See [Invoke security](/docs/interop/invoke/security) for the local transport, token storage, reserved headers, and the test-only auth escape hatch.

### Environment variables

| Variable                                  | Purpose                                                    |
| ----------------------------------------- | ---------------------------------------------------------- |
| `FORST_BASE_URL` / `FORST_DEV_URL`        | Invoke server URL (aliases)                                |
| `FORST_INVOKE_URL`                        | Explicit URL override                                      |
| `FORST_INVOKE_ENABLED`                    | Enable built-in server at runtime without ftconfig         |
| `FORST_INVOKE_HOST` / `FORST_INVOKE_PORT` | Override bind address for TCP mode                         |
| `FORST_INVOKE_TRANSPORT`                  | Set to `tcp` to force loopback TCP instead of Unix sockets |
| `FORST_INVOKE_AUTH`                       | Set to `off` to disable HMAC auth (local debugging only)   |
| `FORST_ROOT`                              | Project root for `.forst/invoke.ready`                     |
| `FORST_SKIP_SPAWN`                        | Force HTTP connect (never spawn `forst dev`)               |

When the built-in server starts, Forst writes `.forst/invoke.ready` with `socketPath` (Unix default) or `url` (TCP) for tooling auto-discovery.

## Host mode (Remix and similar)

Incremental migration often runs three channels in one deployment:

| Channel              | Direction                 | Typical endpoint                                    |
| -------------------- | ------------------------- | --------------------------------------------------- |
| App HTTP             | Browser → Node app        | `:6322` (e.g. remix-serve in local forst host mode) |
| Built-in HTTP server | Node → Forst              | `http://127.0.0.1:6321/invoke`                      |
| Bridgert host socket | Forst → legacy JavaScript | `.forst/node.sock`                                  |

Enable both `server.embedded` and `bridge.hostMode` in `ftconfig.json`. Remix loaders call generated client functions over `:6321`; Forst `main` calls legacy JS over the nodert socket.

Full combined demo: [`examples/in/rfc/bridge-interop/remix-serve`](https://github.com/forst-lang/forst/tree/main/examples/in/rfc/bridge-interop/remix-serve).

Bridgert setup is covered in [Run in host mode](/docs/interop/bridge/build-and-runtime#run-in-host-mode).

## Export rules

Only **public** functions with no unsatisfied [Providers](/docs/language/providers) appear in the generated client. Functions that need wired dependencies belong in Forst `main` startup, not in `/invoke` from Node.

## Troubleshooting

<Accordion title="Cannot find module '@forst/gen/...'">
  Follow [Installation § Generated TypeScript client](/docs/installation#generated-typescript-client). Fresh checkouts need `postinstall` because `.forst/client` is gitignored and `npm ci` deletes the `node_modules` link.
</Accordion>

<Accordion title="Connection refused / InvokeUnreachable">
  The binary or `forst dev` is not listening. Check `FORST_BASE_URL` and the port in `ftconfig.json` (`server.port`, default `6321` for embedded).
</Accordion>

<Accordion title="InvokeBaseUrlMissing in production">
  Set `FORST_BASE_URL`. Production never auto-spawns `forst dev`.
</Accordion>

<Accordion title="function not found in invoke response">
  Check the `POST /invoke` body: `package` must match the Forst package name, and `function` must match the exported name exactly. List available functions with `GET /functions`.
</Accordion>

<Accordion title="Types do not match the server">
  Re-run `npx forst generate` after changing `.ft` files. In development, regenerate-on-save keeps `@forst/gen` aligned. See [Dev server](/docs/workflow/dev-server#generated-client).
</Accordion>

<Accordion title="Compile error: function has unsatisfied providers">
  Wire providers in Go/`main` before the server starts. Provider-dependent functions are excluded from the generated client surface.
</Accordion>

## Caveats

Generated client invoke and the built-in HTTP server are **experimental**. Pin compiler versions in production.

### Check `contractVersion`

After upgrades, verify **`GET /version`** **`contractVersion`** matches what your client expects.

### Spawn is local dev only

Auto-spawn of **`forst dev`** is for local development. Production must set **`FORST_BASE_URL`** and use the built-in HTTP server or another explicit connect target.

### Exported functions only

Only **capitalized** function names appear in discovery and the invoke registry.

### Providers before expose

Wire **`with`** blocks in Go **`main`** before the server starts. See [Providers § Caveats](/docs/language/providers#caveats).

### Dev vs production URL

**Executor profile** (`forst dev` without embedded/host mode) often listens on **`6320`**. **Runtime profile** (embedded or host mode) uses **`server.port`** (default **`6321`**). A wrong **`FORST_BASE_URL`** looks like "works in dev, fails in prod".

## Related

<CardGroup cols={2}>
  <Card title="Installation" icon="download" href="/docs/installation#generated-typescript-client">
    `@forst/cli`, `postinstall`, and first generate.
  </Card>

  <Card title="Generate a TypeScript client" icon="https://mintcdn.com/forst/r5GJChnfkgCSJa-b/icons/typescript.svg?fit=max&auto=format&n=r5GJChnfkgCSJa-b&q=85&s=8a8c0cd7b4bf60c264d51f66d0f52e91" href="/docs/interop/invoke/generate-types" width="512" height="512" data-path="icons/typescript.svg">
    Subpaths, types, and `@forst/gen` layout.
  </Card>

  <Card title="Testing" icon="flask" href="/docs/interop/invoke/testing">
    Stub calls with `withForstTestScope`.
  </Card>

  <Card title="Effect mode" icon="bolt" href="/docs/interop/invoke/effect">
    Tagged errors and `generate.effect`.
  </Card>

  <Card title="Dev server" icon="server" href="/docs/workflow/dev-server">
    `forst dev` HTTP contract and regenerate on save.
  </Card>

  <Card title="CLI reference" icon="terminal" href="/docs/workflow/cli#generate-configuration">
    Full `generate` configuration table.
  </Card>
</CardGroup>
