# One capability, every surface

> 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: [tla-ts 0.0.9 adds TLC's -continue](/news).

**Goal:** add one action and get a command, an HTTP route, an [MCP](/lore/mcp-is-another-surface) tool, and a code-mode function from it. Browser [RPC](/skills/learn-rat-stack) uses the same contracts; `search` and `read` show that side.

A capability is one named action with a [contract](/systems/capabilities) and a server-side [handler](/systems/capabilities).
A contract defines the shared input, output, failures, and metadata.
[Effect](/lore/effect-basics) [Schema](/lore/schemas-define-the-boundary) provides types and runtime checks for these values. `defineContract` owns the name, Effect schemas, annotations, and approval setting. `implement` binds that contract to its handler.

Here, a surface is an [interface](/lore/one-schema-three-surfaces) for calling the action.
A projection is code that converts registered capabilities into an interface.
The interfaces are command line, HTTP, MCP tools, browser RPC, and [code mode](/lore/one-program-can-replace-several-tool-calls).
Each interface derives its input and failure definitions from the same schemas.

## Worked example: `inspectFile`

### 1. The contract

packages/core/src/contracts.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 292-300; highlighted none. Title: Name, schemas, failure, and annotations.

```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,
});
```

The contract holds no handler. A browser can import it without server code.

### 2. The handler

packages/core/src/inspect-file.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 14-19; highlighted 14-16,19. Title: implement binds the handler; capabilities registers it.

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

export const capabilities = [
  inspectFile,
```

The handler runs [the inspection machine](/lore/lifecycles-are-machines).

### 3. The projections

apps/cli/src/surfaces.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 40-44; highlighted none. Title: HTTP, MCP tools, and code mode from the same lists.

```typescript
export const http = toHttpApi("RatStack", capabilities);

export const tools = toToolkit(cliCapabilities);

export const codeMode = toCodeMode(cliCapabilities);
```

apps/cli/src/command.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 22-28; highlighted 25-28. Title: The CLI command, with a stats alias.

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

No projection repeats the schema. Each one reads the contract it was given.

## Why one contract

A second hand-written route would need its own schema, and the two could drift.
Here each surface derives its input and failure from one place.

The split replaced `defineCapability`, which bundled contract data and its handler in one definition. The RPC [client](/skills/uncomplect) needed the contract without importing server implementation. `toRpcGroup` now projects contracts alone; `toRpc` connects those same contracts to implemented capabilities. The browser docs client imports only the shared contracts and RPC group. [Commit 9a7bea5](https://github.com/joelhooks/rat-stack/commit/9a7bea5) records the split.

Michael Arnaldi demonstrates Schema decoding request input and encoding a frontend-safe result while preserving its output type. [Effect Meetup SF, 27:17](https://youtu.be/BqUnsDnMnMo?t=1637). In another conversation, the speakers discuss generated schemas and migrations as work agents can maintain, and a rule that rejects ad hoc JSON parsing. [Cause & Effect 10, 63:49](https://youtu.be/irmz5X3c7Do?t=3829) [67:53](https://youtu.be/irmz5X3c7Do?t=4073).

## Next

To add a capability, define its contract and implement it beside the data or infrastructure it owns. Register it in `capabilities`, then check each interface you keep. `packages/capability/test/` exercises projections; `apps/site/src/capabilities/index.ts` shows the hosted registry.

Use [the add-a-capability skill](/skills/add-a-capability) for the steps.

Read [A browser send follows one path to the capability](/lore/a-browser-send-follows-one-path) to see `search` called over RPC.

[Analytics](/systems/analytics) shows a separate infrastructure [cartridge](/lore/cartridges); [interest signup](/systems/interest) shows core ports with a delivery [adapter](/lore/hexagonal-architecture) outside core. Neither turns static pages or protocol discovery into domain capabilities.

See [lifecycles are machines](/lore/lifecycles-are-machines) for stateful handlers and [cartridges](/lore/cartridges) for the infrastructure boundary. [HATEOAS](/lore/hateoas) covers links that guide an agent's next action; [hexagonal architecture](/lore/hexagonal-architecture) names the ports-and-adapters principle.

## Sources

1. [rat-stack/AGENTS.md at main · joelhooks/rat-stack · GitHub](https://github.com/joelhooks/rat-stack/blob/main/AGENTS.md)
   GitHub joelhooks/rat-stack. Projection law; used for one contract across every interface. Accessed 2026-10-01.

2. [Shared contracts at a3f5a14](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/packages/core/src/contracts.ts)
   GitHub joelhooks/rat-stack. inspectFileContract declares the name, schemas, failure, and annotations. Accessed 2026-11-08.

3. [inspectFile capability at a3f5a14](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/packages/core/src/inspect-file.ts)
   GitHub joelhooks/rat-stack. implement binds inspectFileContract to runInspectMachine and registers it in capabilities. Accessed 2026-11-08.

4. [CLI projections at a3f5a14](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/apps/cli/src/surfaces.ts)
   GitHub joelhooks/rat-stack. toHttpApi, toToolkit, and toCodeMode project the same capability lists. Accessed 2026-11-08.

5. [CLI commands at a3f5a14](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/apps/cli/src/command.ts)
   GitHub joelhooks/rat-stack. toCommand projects inspectFile with a positional path and the stats alias. Accessed 2026-11-08.

6. [rat-stack/packages/capability/src/implement.ts at main · joelhooks/rat-stack · GitHub](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/implement.ts)
   GitHub joelhooks/rat-stack. Handler binding; used for separating contracts from implementations. Accessed 2026-10-01.

7. [rat-stack/packages/capability/src/to-rpc-group.ts at main · joelhooks/rat-stack · GitHub](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/to-rpc-group.ts)
   GitHub joelhooks/rat-stack. Client RPC projection; used for contracts without server handlers. Accessed 2026-10-01.

## Lore on this page

- [One schema, three surfaces](/lore/one-schema-three-surfaces)
- [Michael Arnaldi](/lore/michael-arnaldi)
- [Schemas define the boundary](/lore/schemas-define-the-boundary)

## Linked from

- A browser send follows one path to the capability → In the Foldkit docs browser, a search is a Message, then an update, then a named Command that calls the search capability over RPC on a cache miss and returns a result Message. → [Read page](https://ratstack.sh/lore/a-browser-send-follows-one-path)

- Agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms.txt)

- An MCP your users want → Rhys Sullivan's practical guide gives rat-stack a checklist for useful MCP tools. → [Read page](https://ratstack.sh/lore/an-mcp-your-users-want)

- CAFE names the Cloudflare, Alchemy, Foldkit and Effect stack → CAFE is a named stack: Cloudflare runs the app, Alchemy declares it, Foldkit draws the UI, and Effect does the typed work. → [Read page](https://ratstack.sh/lore/cafe-stack)

- Capabilities → Named application actions exposed through generated interfaces share one definition and one server-side implementation. → [Read page](https://ratstack.sh/systems/capabilities)

- Cartridges → A cartridge owns one job and brings the infrastructure its service needs. → [Read page](https://ratstack.sh/lore/cartridges)

- Change log → What changed in the files served here, newest first. → [Read page](https://ratstack.sh/log)

- Effect keeps the surfaces on one contract → Schema owns values, projections build interfaces, and Layers supply implementations. → [Read page](https://ratstack.sh/lore/how-we-do-it-with-effect)

- Full agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms-full.txt)

- HATEOAS → Links in a representation tell its reader what to do next; rat-stack applies that idea to agent-facing pages and responses. → [Read page](https://ratstack.sh/lore/hateoas)

- Hexagonal architecture → Ports name the application's jobs; adapters connect them to the outside world. → [Read page](https://ratstack.sh/lore/hexagonal-architecture)

- Infrastructure as effects → Sam Goodwin relates application operations to infrastructure requirements in one typed program. → [Read page](https://ratstack.sh/lore/infrastructure-as-effects)

- learn-rat-stack → Trace a capability through Effect, XState, five projections, the CLI, and Alchemy. Try hosted search and read. → [Read page](https://ratstack.sh/skills/learn-rat-stack)

- Lifecycles are machines → XState owns finite lifecycle states while declared Effect actors own side effects. → [Read page](https://ratstack.sh/lore/lifecycles-are-machines)

- Rat Stack lore | rat-stack → Short, source-grounded notes on the ideas and decisions behind rat-stack. → [Read page](https://ratstack.sh/lore)

- rat-stack → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/)

- Run Effect at the integration boundary → Build the runtime where a calling framework enters, reuse its services, and dispose it when that integration ends. → [Read page](https://ratstack.sh/lore/run-effect-at-the-boundary)

- Schema projections: 2026-09-18 history → Historical Effect rc.115 proposal and implementation receipts from 2026-09-18; use the one-capability-every-surface lore page for the current pattern. → [Read page](https://ratstack.sh/resources/schema-projections-and-code-mode)

- Source change history (log.md) → The generated change log as Markdown. → [Read page](https://ratstack.sh/log.md)

- The war stories → Seven public repo incidents: a deploy undoes itself, failure says 200, sessions forget their guests, and the rat tests the demolition instructions. → [Read page](https://ratstack.sh/lore/shitrat-war-stories)

- Turbo remote cache → A separate Worker stores streamed Turbo artifacts in R2, with one credential per team and lifecycle expiry. → [Read page](https://ratstack.sh/systems/turbo-cache)

## Unlinked mentions

- [add-a-capability](https://ratstack.sh/skills/add-a-capability) → A projection builds a callable interface from capabilities.¶

- [add-a-feature-flag](https://ratstack.sh/skills/add-a-feature-flag) → A projection is a copy derived from a committed value.¶

- [Agent rules for rat-stack (AGENTS.md)](https://ratstack.sh/AGENTS.md) → The DO commits values, receipts, and projection markers together; alarms project to KV.

- [Analytics](https://ratstack.sh/systems/analytics) → Local tests prove projection and archive writes; they do not prove Cloudflare delivery.¶

- [Build and run rat-stack (README.md)](https://ratstack.sh/README.md) → Code mode is another projection.

- [Capabilities](https://ratstack.sh/systems/capabilities) → A projection builds a callable interface from a capability.

- [Durability](https://ratstack.sh/systems/durability) → The default operator adapter commits position, receipt and projection marker in one Durable Object transaction.¶

- [Effect + Alchemy peer patterns](https://ratstack.sh/resources/same-version-repos) → Keep capability schemas in packages/core, the toRpc projection in packages/capability, and UI atoms in the web app.

- [Effect keeps the surfaces on one contract](https://ratstack.sh/lore/how-we-do-it-with-effect) → A projection builds an interface from a capability, a named action with a contract and handler.

- [Feature flags](https://ratstack.sh/systems/flags) → A projection is a copy derived from a committed value.¶

- [Flags climb on evidence](https://ratstack.sh/lore/flags-climb-on-evidence) → A projection is a copy derived from a committed value.¶

- [Hexagonal architecture](https://ratstack.sh/lore/hexagonal-architecture) → The no-hand-rolled-surface rule keeps interface construction in the projection package.¶

- [keep-or-cut](https://ratstack.sh/skills/keep-or-cut) → A projection builds a callable interface from capabilities.¶

- [learn-rat-stack](https://ratstack.sh/skills/learn-rat-stack) → A projection takes a registry and reuses its domain handlers.¶

- [Lint and type escape ledger (debt.md)](https://ratstack.sh/debt.md) → ts: a projection over a heterogeneous list erases error and requirement types at the boundary and recovers them for callers.

- [One prompt serves people and agents](https://ratstack.sh/systems/prompts) → A projection builds a callable interface from capabilities.¶

- [One schema, three surfaces](https://ratstack.sh/lore/one-schema-three-surfaces) → A projection builds an interface from a capability.¶

- [OpenAPI describes the refusal too](https://ratstack.sh/lore/openapi-describes-the-refusal-too) → ts builds the /api projection.

- [rat-stack](https://ratstack.sh/) → Each projection calls the same handler through the same contract.

- [Schema projections: 2026-09-18 history](https://ratstack.sh/resources/schema-projections-and-code-mode) → Code mode is a fourth projection: serialize the catalog (Executor IR shape: JSON Schema per type, path per capability), emit .d.ts from the JSON Schema, expose search and execute as two ordinary Tools whose execute handler runs model code in a sandbox that can only call capabilities by path.

- [Schemas define the boundary](https://ratstack.sh/lore/schemas-define-the-boundary) → In rat-stack, one capability contract supplies schemas to every interface projection.¶

- [security-audit](https://ratstack.sh/skills/security-audit) → The no-hand-rolled-surface rule in the boundary plugin restricts projection construction to the capability package.¶

- [Tests prove behavior through supplied services](https://ratstack.sh/lore/tests-that-earn-their-place) → A capability is a named action; a projection converts it into an interface.¶

- [uncomplect](https://ratstack.sh/skills/uncomplect) → Projection: An interface built from capabilities: a command, API, or agent tool.¶
