# What localhost2137 is (/)



localhost2137 runs stateful emulator plugins for external developer services. An application talks
to a plugin through its normal provider SDK or HTTP boundary. Tests, scripts, and developers control
the same local world through typed operations or the CLI.

```ts title="localhost.config.ts"
import { slack } from "@localhost2137/slack";
import { defineConfig } from "localhost2137";

export default defineConfig({
	clock: { mode: "pinned", startAt: "2026-01-01T00:00:00.000Z" },
	services: {
		slack: slack({
			config: {
				botToken: "xoxb-local-crash-course",
				eventsUrl: null,
				signingSecret: "local-crash-course-signing-secret",
				workspaceName: "Local workspace",
			},
			seed: {
				users: [{ id: "U_ADA", name: "Ada" }],
				channels: [{ id: "C_GENERAL", name: "general", members: ["U_ADA"] }],
			},
		}),
	},
});
```

```sh title="terminal 1"
pnpm exec localhost dev
```

```sh title="terminal 2"
pnpm exec localhost run -- pnpm dev
```

The config mounts emulator code and optional local seed data. `localhost dev` hosts the configured
services; `localhost run --` starts the application with that instance's local URLs and fake
credentials. Neither the developer nor a coding agent needs provider API keys for this local path.
[Getting started](/getting-started.md) runs this exact config and shows what each command changes.

It is not a generic request/response mock server, and a plugin is not a local copy of an entire
provider. Each plugin implements a bounded service contract: provider-shaped HTTP behavior, state,
control operations, persistence, and any callback delivery it supports.

## Two interfaces, one local world [#two-interfaces-one-local-world]

Your application uses the plugin's provider-compatible HTTP API. Tests, scripts, developers, and
coding agents use operations to arrange and inspect the same state.

```text
application ── provider SDK / HTTP ──> emulated API
                                         │
test or agent ── typed operation / CLI ──┘
```

Operations can also trigger external actors and events. The interface a test should use depends on
the behavior it claims to verify. [Operations and emulated APIs](/operations-and-apis.md) follows that
boundary through complete Slack and Stripe scenarios.
[Callbacks and webhooks](/callbacks.md) explains the opposite HTTP direction: plugin delivery into the
application, including signatures, retries, tracked work, and shared receiver constraints.

The service key in `localhost.config.ts` is the route, storage, CLI, and typed-handle identity. The
`dev` Slack URL, for example, begins at `http://127.0.0.1:2137/dev/slack`.

## When it fits [#when-it-fits]

Use localhost2137 when the behavior under test depends on a service world changing over a sequence:
an SDK call creates state, another call reads it, time advances, or an event reaches the application.
A function fake or HTTP stub is usually simpler when the claim concerns one call and no meaningful
service lifecycle. A provider sandbox or real service is still needed for claims about live
infrastructure and unimplemented provider behavior.

[Choose the right test boundary](/test-boundaries.md) develops that decision from the defect a test
must catch. [Provider compatibility](/compatibility.md) explains how to assess the narrower claims an
installed plugin actually proves.

## Ownership boundary [#ownership-boundary]

The runtime owns explicit instances, route mounting, lifecycle timing, isolated storage locations,
operation adapters, generated connection values, virtual time, tracked async work, and control-plane
authentication.

Each plugin owns service behavior, compatible responses, control operations, configuration, data
format, migrations, retry rules, and persistence. This boundary is why localhost2137 can host a
general plugin ecosystem without learning each provider's rules in its kernel.

## The normal loop [#the-normal-loop]

1. Configure installed plugins in ordinary TypeScript.
2. Start `localhost dev`; it creates the persistent `dev` world empty only when it is absent. Later
   starts preserve that world until you reset it.
3. Pass generated connection values to the application's existing SDK or service adapter.
4. Arrange and inspect state through plugin operations.
5. Exercise the application through its normal provider-facing boundary.
6. Await tracked asynchronous work before asserting.
7. Reset or destroy the world when it no longer matters.

[Instances and isolation](/instances.md) explains which resources belong to a world and which remain
shared. In particular, instances do not create separate plugin configuration or callback URLs;
before parallel event or webhook tests, read
[Callbacks and webhooks](/callbacks.md#parallel-receivers-require-a-routing-rule).

## Continue by intent [#continue-by-intent]

* **See one complete loop:** [Getting started](/getting-started.md) runs, changes, and repeats the Slack
  example.
* **Adopt it in a project:** [Run an existing application](/existing-application.md) preserves the
  application's normal service boundary, then [Integration testing](/testing.md) turns that setup into
  an owned test harness.
* **Evaluate the testing model:** [Choose the right test boundary](/test-boundaries.md), then read
  [Operations and emulated APIs](/operations-and-apis.md), [Instances and isolation](/instances.md), and
  [What determinism means](/determinism.md).
* **Add service behavior:** [Using plugins](/plugins/using.md) starts with an installed package;
  [Build your first plugin](/plugins/first-plugin.md) starts with a small executable contract.

The reference section is for exact behavior once a task raises a question: start with
[Configuration](/configuration.md), [CLI workflow and reference](/cli.md), or
[Runtime boundaries](/limitations.md).
