---
name: learn-rat-stack
updated: "2026-10-06"
description: Trace a capability through Effect, XState, five projections, the CLI, and Alchemy. Try hosted search and read.
plain: "Trace one action from its contract through a handler, a service, a machine, five surfaces, the CLI and the cloud."
diagram: |-
  contract
    │ handler
    │ service + machine
    ▼
  one capability
  ├ CLI
  ├ HTTP
  ├ MCP
  ├ code mode
  └ RPC
---

# Learn the stack

> For agents: start with the [agent guide](https://ratstack.sh/llms.txt). Every page is Markdown by default; add `Accept: text/html` for HTML.

New on /news: [alchemy-turbo-cache](/news).

Start with `inspectFile`, a [capability](/systems/capabilities): one named action with a contract and a server-side handler. The [contract](/systems/capabilities) defines its name, input, output, failure, and metadata. The handler implements the action.

A service is a named interface for one job. A machine defines states and the events that permit movement between them. A [projection](/lore/one-capability-every-surface) builds a callable interface from capabilities.

Follow these parts, then try the hosted content tools.

Read [Effect basics](/lore/effect-basics) before this trace if the computation model is new. For implementation tasks, use [services](/lore/services-capture-dependencies), [Layer composition](/lore/layers-make-dependencies-explicit), and [testing](/lore/tests-that-earn-their-place).

## The pieces

- Effect describes work with typed failures and required dependencies.
- Schema defines accepted values and their runtime checks.
- A Layer builds services and supplies their construction dependencies.
- XState owns finite lifecycles. The Effect bridge runs their declared actors.
- TypeScript 7 and Effect diagnostics check types and Effect usage.
- Oxlint, Oxfmt, Vitest, and lefthook enforce [the fence](/lore/the-fence): checks and hooks that reject prohibited code and shortcuts.
- pnpm and Turborepo connect packages and cache checks.
- Alchemy declares the cloud. Follow [learn Alchemy](/skills/learn-alchemy) for the deployment graph.

## One action, five interfaces

```text
                     ┌─ CLI
                     ├─ HTTP
One Capability ──────┼─ MCP
                     ├─ code mode
                     └─ RPC
```

A [projection](/lore/one-capability-every-surface) exposes the same action through another interface. RPC serves browser clients. [Code mode](/lore/one-program-can-replace-several-tool-calls) lets agents compose calls in one program.

All five interfaces use one handler.

## Trace one action

The CLI uses `inspectFile`. The hosted site uses a separate content registry.

### 1. Define the contract

The [contract](/systems/capabilities) names the action and its input, output, and failure schemas.

packages/core/src/contracts.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 255-263; highlighted 255,258-263.

```typescript
export const inspectFileContract = defineContract("inspectFile", {
  annotations: { idempotent: true, readOnly: true },
  description: "Count bytes, characters, words, and lines in a file",
  failure: FileStatsError,
  input: Schema.Struct({
    path: Schema.String.annotate({ description: "File to inspect" }),
  }),
  output: FileStatsSchema,
});
```

`FileStatsError` belongs to the interface. The annotations mark a repeatable, read-only action.

### 2. Bind one handler

`implement` binds the contract to `runInspectMachine`. The registry includes the resulting [capability](/systems/capabilities).

packages/core/src/inspect-file.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 10-22; highlighted 10-12,14-15.

```typescript
export const inspectFile = implement(inspectFileContract, ({ path }) =>
  runInspectMachine(path)
);

export const capabilities = [
  inspectFile,
  ...flagCapabilities,
  joinInterest,
  listPrompts,
  getPrompt,
  listCafeNews,
  listCafeProjects,
] as const;
```

The handler starts a machine to read the file.

### 3. Put file work in a service

`FileInspector.make` captures the file system. Its `inspect` method reads bytes and maps file errors to `FileStatsError`.

packages/core/src/file-inspector.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 14-29; highlighted 15,20-24,28.

```typescript
  make: Effect.gen(function* makeFileInspector() {
    const fileSystem = yield* FileSystem.FileSystem;

    const inspect = Effect.fn("FileInspector.inspect")(function* inspect(
      path: string
    ) {
      const content = yield* fileSystem
        .readFile(path)
        .pipe(
          Effect.mapError(
            (error) => new FileStatsError({ path, reason: error.message })
          )
        );

      return summarizeBytes(path, content);
    });
```

The service exposes its [Layer](/lore/layer-constructor-pattern) beside the implementation. This follows [hexagonal architecture](/lore/hexagonal-architecture): composition supplies the file-system dependency.

packages/core/src/file-inspector.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 31-35; highlighted 34.

```typescript
    return { inspect } as const;
  }),
}) {
  static readonly layer = Layer.effect(this, this.make);
}
```

Callers ask the service to inspect a path. Composition chooses the file-system implementation.

### 4. Give the lifecycle named states

A declared `createEffectLogic` actor calls `FileInspector`.

packages/core/src/inspect-machine.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 26-30; highlighted 26,28.

```typescript
const readStats = createEffectLogic({
  effect: ({ input }) =>
    FileInspector.use((inspector) => inspector.inspect(input.path)),
  schemas: { input: Schema.Struct({ path: Schema.String }) },
});
```

The machine starts in `reading`. It finishes in `inspected` or `unreadable`.

packages/core/src/inspect-machine.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 40-64; highlighted 40,43,51,57,59,62.

```typescript
  initial: "reading",
  output: ({ context }) => context.outcome,
  states: {
    inspected: { type: "final" },
    reading: {
      invoke: {
        input: ({ context }) => ({ path: context.path }),
        onDone: {
          context: ({ event }) => ({
            outcome: inspectOutcome.Inspected({ stats: event.output }),
          }),
          target: "inspected",
        },
        onError: {
          context: ({ event }) => ({
            outcome: inspectOutcome.Unreadable({ error: event.error }),
          }),
          target: "unreadable",
        },
        src: "readStats",
      },
    },
    unreadable: { type: "final" },
  },
});
```

### 5. Project the registered actions

The command-line interface (CLI) projects capability lists for each server interface. HTTP uses stateless learning capabilities. MCP and code mode use their local learning counterparts. All three include `inspectFile`.

apps/cli/src/surfaces.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 25-33,40-51; highlighted 25-28,30-33,40,42,44,48.

```typescript
export const capabilities = [
  ...coreCapabilities,
  ...learnCapabilities,
] as const;

export const cliCapabilities = [
  ...coreCapabilities,
  ...localLearnCapabilities,
] as const;
export const http = toHttpApi("RatStack", capabilities);

export const tools = toToolkit(cliCapabilities);

export const codeMode = toCodeMode(cliCapabilities);

export const routes = Layer.merge(
  HttpApiBuilder.layer(http.api, { openapiPath: "/openapi.json" }).pipe(
    Layer.provide(http.layer)
  ),
  HttpApiScalar.layer(http.api, { path: "/docs" })
);
```

The RPC [projection](/lore/one-capability-every-surface) derives contracts from its supplied capabilities.

packages/capability/src/to-rpc.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 24-27; highlighted 24,27.

```typescript
export const toRpc = <const Caps extends readonly AnyCapability[]>(
  capabilities: Caps
): RpcProjection<Caps> => {
  const contracts = capabilities.map((capability) => capability.contract);
```

A projection takes a registry and reuses its domain handlers.

### 6. Connect the CLI

`toCommand` derives each command. `inspectFile` also gets the `stats` alias.

apps/cli/src/command.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 22-35; highlighted 25-28.

```typescript
const capabilityCommands = commandCapabilities.map((capability) => {
  const command =
    capability === inspectFile
      ? toCommand(capability, {
          positional: ["path"],
          render: formatFileStats,
        }).pipe(Command.withAlias("stats"))
      : toCommand(
          capability,
          capability === setFlag ? { promptForApproval: true } : undefined
        );

  return command;
});
```

The entry point supplies the service [Layer](/lore/layer-constructor-pattern) and Node services. It denies [approval](/systems/capabilities) by default.

apps/cli/src/cli.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 22-32; highlighted 24,30.

```typescript
  Effect.provide(
    Layer.mergeAll(
      Layer.provideMerge(FileInspector.layer, NodeServices.layer),
      remoteJoinInterestLayer,
      localPromptLibraryLayer.pipe(Layer.provide(NodeServices.layer)),
      localCafeDirectoryLayer.pipe(Layer.provide(NodeServices.layer)),
      localLearnLayer,
      Flags.defaults(eventFlags),
      Approval.denyAll
    )
  )
```

The command keeps the shared handler.

### 7. Compose the cloud

An Alchemy Stack groups one cloud infrastructure program and its outputs. Alchemy declares the Stack, Cloudflare providers, and state. The Stack includes `Mischief` and `Website`.

apps/infra/alchemy.run.ts at b4d6b8f499e57bc76914ad70df3a0c99751ae28c; lines 16-22,118-129; highlighted 16,19-20,118,120.

```typescript
export default Alchemy.Stack(
  "RatStack",
  {
    providers: Layer.mergeAll(Cloudflare.providers(), Drizzle.providers()),
    state: Cloudflare.state(),
  },
  Effect.gen(function* stack() {
    const website = yield* Website;

    const mischief = yield* Mischief;

    yield* mischief.bind`${website}`({
      bindings: [
        { name: "WEBSITE", service: website.workerName, type: "service" },
      ],
    });

    return { mischiefUrl: mischief.url, websiteUrl: website.url };
  }).pipe(
```

The Stack deploys the cloud applications. The Node file-inspection CLI runs separately.

## Try hosted search and read

Use [POST /api/execute](/llms.txt#run-code-one-program-instead-of-several-calls) for [code mode](/lore/one-program-can-replace-several-tool-calls). The [agent guide](/llms.txt) documents this search → read program:

```js
const found = await tools.search({ query: "cartridges", limit: 1 });
const page = await tools.read({ id: found.matches[0].id });
return { title: page.title, id: page.id };
```

Send it as a JSON `code` string:

```sh
curl --request POST 'https://ratstack.sh/api/execute' \
  --header 'content-type: application/json' \
  --data '{"code":"const found = await tools.search({ query: \"cartridges\", limit: 1 }); const page = await tools.read({ id: found.matches[0].id }); return { title: page.title, id: page.id };"}'
```

`read` takes the exact ID from `search`. This hosted program reads site content.

## Read before changing a piece

1. Read [`AGENTS.md`](/AGENTS.md) for pins, boundaries, and [the fence](/lore/the-fence).
2. Read [VISION.md](/VISION.md) for intent.
3. Read the package you plan to change.
4. Before Effect or XState work, read `node_modules/effect/AGENTS.md` and the pinned sources listed in repo law.

In a copied product repo, replace the template's product notes and rules. Keep the stack lessons that still serve the product.

Follow repo law and pinned code when an example is stale.

## Pick the next skill

- Add schemas and a shared action: [`add-a-capability`](/skills/add-a-capability).
- Model a lifecycle: [add a lifecycle machine](/skills/add-a-lifecycle-machine).
- Remove an interface: [keep or cut](/skills/keep-or-cut).
- Follow the cloud footprint: [learn Alchemy](/skills/learn-alchemy).

## Finish

Run the full gate:

```sh
pnpm turbo run check test build
```

Fix failures. Keep [the fence](/lore/the-fence) intact.

Check deployment separately from the gate.

## Lore on this page

- [Handle expected failures without hiding defects](/lore/error-model)
- [One program can replace several tool calls](/lore/one-program-can-replace-several-tool-calls)

## Unlinked mentions

- [add-a-capability](https://ratstack.sh/skills/add-a-capability) → The CLI, HTTP, MCP, RPC, and code-mode projections take implemented capabilities.

- [Agent rules for rat-stack (AGENTS.md)](https://ratstack.sh/AGENTS.md) → Role: Better Auth cartridge over the chosen DatabaseVendor: Auth, CurrentPerson, and the RPC middleware that provides it; only Unauthenticated crosses the wire.

- [Auth](https://ratstack.sh/systems/auth) → RPC means remote procedure call: a client calls a named operation on the server.

- [CAFE names the Cloudflare, Alchemy, Foldkit and Effect stack](https://ratstack.sh/lore/cafe-stack) → PR #6 moves chat events to Effect RPC over a Cloudflare.RpcDurableObject, one per chat.¶

- [Capabilities](https://ratstack.sh/systems/capabilities) → RPC calls.

- [Devtools](https://ratstack.sh/systems/devtools) → The local web development backend records calls and serves the overlay, RPC, and MCP tools under /\_\_rat.

- [Effect + Alchemy peers](https://ratstack.sh/resources/peers) → Why this tier: Upstream infrastructure reference; the study cites its RPC and Better Auth patterns.

- [Hexagonal architecture](https://ratstack.sh/lore/hexagonal-architecture) → One capability, every surface starts with a contract, binds a handler, and projects CLI, HTTP, MCP, RPC, and code mode.

- [Hosted agent interfaces](https://ratstack.sh/systems/agent-front-door) → RPC means remote procedure call: a client calls a named operation on the server.

- [Infrastructure as effects](https://ratstack.sh/lore/infrastructure-as-effects) → Projections turn registered capabilities into command line, HTTP, MCP tools, browser RPC, and code mode surfaces.

- [Lint and type escape ledger (debt.md)](https://ratstack.sh/debt.md) → Reason: Alchemy’s RPC Worker initialization returns its request Effect.

- [One capability, every surface](https://ratstack.sh/lore/one-capability-every-surface) → The interfaces are command line, HTTP, MCP tools, browser RPC, and code mode.

- [Schema projections: 2026-09-18 history](https://ratstack.sh/resources/schema-projections-and-code-mode) → Read One capability, every surface for today’s defineContract → implement pattern, including browser RPC, and current pins.

- [Tests prove behavior through supplied services](https://ratstack.sh/lore/tests-that-earn-their-place) → Our projection tests execute the produced CLI command, HTTP route, MCP tool, RPC call, or sandbox call.

- [Trace operations where they mean something](https://ratstack.sh/lore/trace-meaningful-operations) → RPC: Method or property path, target kind and correlation IDs¶

- [uncomplect](https://ratstack.sh/skills/uncomplect) → RPC lets a client call named server operations.¶

- [use-a-prompt](https://ratstack.sh/skills/use-a-prompt) → The same contracts project to CLI, HTTP, MCP tools, RPC, and code mode.
