# Concepts
URL: /docs/concepts
LLM index: /llms.txt
Description: Learn the model every Limrun guide builds on: instances, credentials, lifecycle, labels, placement, and assets.

# Concepts

Limrun has a small model: you create *instances*, each instance gets its own endpoints and token, and instances live until you delete them or a timeout fires. Read this page once and the guides will make sense in any order.

## Instances

An instance is anything you create through Limrun. There are four kinds, each with its own CLI topic and SDK resource:

| Instance | CLI | TypeScript SDK resource |
|---|---|---|
| iOS simulator | `lim ios` | `iosInstances` |
| Android emulator | `lim android` | `androidInstances` |
| Xcode sandbox | `lim xcode` | `xcodeInstances` |
| Gradle sandbox | `lim gradle` | `gradleInstances` |

Each instance has an ID, a state, and a `status` block with the URLs you use to reach it. Instances are single-tenant: only one client should drive a simulator or emulator at a time, so scope them per user, per pull request, or per agent session.

Build instances and device instances are separate on purpose. You create an Xcode sandbox, build on it, and *attach* a simulator when you want to run the result. Attaching installs the latest successful build, and every later successful build installs and relaunches on the attached simulator. You pay only for what is running: drop the simulator between test runs and keep the sandbox, or the other way around. Building before the simulator exists also keeps an idle simulator from reaching its inactivity timeout during a long build.

## Control plane and instance endpoints

Limrun has one control plane and a set of endpoints per instance.

- The **control plane** at `https://api.limrun.com` creates, lists, gets, and deletes instances and manages assets. It authenticates with your organization's API key.
- Each **instance** exposes its own endpoints once it is ready: a device or build API (`status.apiUrl`), a WebSocket for streaming (`status.endpointWebSocketUrl`), an MCP server on simulators and emulators (`status.mcpUrl`), and an ADB endpoint on Android emulators. They authenticate with that instance's token.

The full list of status fields is in [Instance spec and status](/docs/reference/instances#status-fields).

## Credentials

Five credentials show up in Limrun integrations. Hand each one only to the party that needs it.

| Credential | What it can do | Who should hold it |
|---|---|---|
| API key (`LIM_API_KEY`) | Create, list, and delete every instance in the organization, and manage assets. | Your machine, your CI secrets, your backend. Never a browser. |
| Instance token (`status.token`) | Drive one instance through its endpoints, only while it is running. Returned only to credentials that can control the instance. | Whoever drives that instance: a device client, `<RemoteControl />`, an MCP client, or a [sandboxed agent](/docs/agents/sandboxed-agents). |
| Stream token (`status.streamToken`) | Open the instance's streaming WebSocket (`status.endpointWebSocketUrl`) and nothing else. | A browser that only streams the device, while the instance token stays on your backend. |
| Signed stream URL (`status.signedStreamUrl`) | Open the instance in the Limrun console's streaming view, with the instance token in the URL fragment. | Anyone you want to watch or use the device: a reviewer, a teammate, a Slack thread. |
| Scoped token | Reach specific registry endpoints, such as device installs or the Apple relay, for a limited time. | A browser session started by your backend. See [scoped tokens](/docs/reference/sdk#scoped-tokens). |

Create API keys in the console under **Settings**, **API Keys**. The CLI reads `LIM_API_KEY` from the environment or a `.env` file in the working directory, or uses the key stored in `~/.lim/config.yaml`.

## Lifecycle and timeouts

Every instance moves through the same states:

```text
creating ──► assigned ──► ready ──► terminated
```

`creating` means hardware is being provisioned, `assigned` means it is booting, and `ready` means every URL in `status` works. An instance ends in `terminated` when you delete it, when a timeout fires, or when it fails; a failure sets `status.errorMessage`. Pass `wait: true` in the SDK (the CLI always waits) to get the instance back only once it is ready.

Two timeouts bound an instance's life:

- **Inactivity timeout** deletes the instance after a period without activity. Simulators, emulators, and Xcode sandboxes default to your organization's setting for that platform, and Gradle sandboxes to 10 minutes. Override it per instance with a duration such as `10m` or `3h`.
- **Hard timeout** deletes the instance after a fixed wall-clock time regardless of activity. The default `0` means no cap.

Delete instances explicitly when the work is done. The timeouts are the safety net for crashed jobs and forgotten sessions.

## Labels and reuse

Labels are free-form `key=value` pairs you attach at create time. They do three jobs:

- **Reuse.** With `--reuse-if-exists` (`reuseIfExists: true`), a create call returns an existing instance whose labels match exactly instead of provisioning a new one. Retries and repeated CI runs land on the same warm instance.
- **Listing.** `--label-selector` (`labelSelector`) filters lists with a comma-separated `key=value` list.
- **Cleanup.** Delete everything tagged with a pull request number or a user ID in one loop.

Reuse matching runs in the region that handles the create call. Placement follows the caller's location and capacity, so a create from a different network can land in another region, miss the match, and start a fresh instance. Give each task its own label, such as `--label issue=<id>`. Reuse needs at least one label: without labels, `--reuse-if-exists` is ignored and every call creates a new instance.

Useful label keys are `tenant`, `user`, `session`, `pr`, `repo`, `agent`, and `managed_by`.

## The CLI's default instance

When you omit `--id`, CLI commands target the instance of that type you created last. The CLI records it per *workspace* in `~/.lim/last-instances.json`. A workspace is the current git repository or worktree by default, so agents working in separate worktrees never drive each other's instances. Assign any other directory its own workspace with `lim set-workspace-dir`, or set one explicitly with `--workspace` or `LIM_WORKSPACE`. On `lim xcode build` and `lim xcode test`, `--workspace` names the `.xcworkspace` instead, so use `LIM_WORKSPACE`.

Scripts and agents that create several instances should pass `--id` every time.

## Placement

Limrun schedules each instance into a region. You influence placement three ways:

- **Jurisdiction** (`--jurisdiction`, `spec.jurisdiction`: `us`, `eu`, or `as`) is a hard constraint. Creation fails when no region in the jurisdiction has capacity.
- **Clues** (`spec.clues`) express a preference. Pass an end user's IP (`ClientIP`) or coordinates (`ClientLocation`) so an embedded device runs close to the person using it, or an Android OS version (`OSVersion`).
- **Region** (`--region`, `spec.region`) is deprecated. It is a preference, not a pin: the request overflows to other regions when the preferred one is full.

Details and payloads are in [Instance spec and status](/docs/reference/instances#placement).

## Assets

[Asset Storage](/docs/platform/asset-storage) holds the binaries instances install: iOS simulator builds (`.app` folders zipped or tarballed), signed IPAs, Android APKs and AABs, and encrypted keychain snapshots. Upload once, then install an asset by name when an instance boots, push it to a running instance, or share it as a browser preview. Build commands upload to Asset Storage with `--upload`.

## Next steps

<Columns cols={2}>
  <Card title="Set up a coding agent" icon="bot" href="/docs/agents/cli">
    Give an agent the CLI and skills to build and run your app.
  </Card>
  <Card title="Run a simulator" icon="smartphone" href="/docs/ios/run-simulator">
    Create a simulator and drive it from the CLI or SDK.
  </Card>
  <Card title="Embed a device" icon="monitor-play" href="/docs/platform/embed-simulator">
    Stream a live device into your own product.
  </Card>
  <Card title="Instance spec and status" icon="book-open" href="/docs/reference/instances">
    Every create field, status field, and state.
  </Card>
</Columns>