---
title: Executors
description: "Pilot executor reference — Browser, Terraform, Git, Persist, Shell, HTTP, and Composite."
section: Pilot
order: 3
updatedAt: 2026-06-01
slug: pilot/executors
---
# Executors

Pilot routes each playbook step to an **executor** based on the step's `executor` field or its action prefix. The executor registry resolves the target executor in this order:

1. Explicit `executor` field on the step
2. Action prefix inference (`tf_` -> terraform, `git_` -> git, etc.)
3. Default: `browser`

## Browser (default)

The browser executor drives a Playwright browser instance. It handles all 14 built-in actions: `navigate`, `click`, `fill`, `select`, `set_toggle`, `wait_for`, `assert`, `capture`, `capture_secret`, `ai_decide`, `scroll`, `hover`, `press`, `upload`.

When `ai_fallback: true`, a failing selector triggers the AI healing engine, which compares the stored DOM snapshot to the current page and proposes a replacement selector.

### Configuration

No explicit configuration required. The browser executor starts automatically.

| Env var | Purpose | Default |
|---------|---------|---------|
| `CUITTY_AI_KEY` | AI model key for selector healing | (local model) |

## Terraform

Wraps the Architect API for infrastructure provisioning. Steps are prefixed with `tf_`.

| Action | Description | Key fields |
|--------|-------------|------------|
| `tf_init` | Create or ensure a target | `target_id`, `provider`, `kind` |
| `tf_validate` | Validate target config | `target_id`, `resource_types` |
| `tf_plan` | Generate a runtime plan | `target_id`, `resource_types` |
| `tf_apply` | Apply runtime state | `target_id`, `resource_types` |
| `tf_destroy` | Destroy runtime resources | `target_id`, `resource_types` |
| `tf_output` | Read a specific output value | `target_id`, `output_name` |
| `tf_import` | Import an existing resource | `target_id`, `resource_type`, `resource_id` |
| `tf_state` | Read full runtime state | `target_id` |

All Terraform steps accept an optional `capture_as` field and `allow_remote` boolean.

### Configuration

| Env var | Purpose | Default |
|---------|---------|---------|
| `PILOT_ARCHITECT_URL` | Architect API base URL | `http://localhost:4470` |
| `PILOT_EXECUTOR_TERRAFORM` | Enable/disable | `true` |

## Git

Wraps the Code API for repository and pull request operations. Steps are prefixed with `git_`.

| Action | Description | Key fields |
|--------|-------------|------------|
| `git_clone` | Create a repository | `name`, `visibility`, `auto_init` |
| `git_branch` | Create a branch | `owner`, `repo`, `name`, `sha` |
| `git_commit` | Create a commit | `owner`, `repo`, `message` |
| `git_push` | Push a branch | `owner`, `repo`, `branch` |
| `git_pr_create` | Open a pull request | `owner`, `repo`, `title`, `head`, `base` |
| `git_pr_merge` | Merge a pull request | `owner`, `repo`, `number`, `strategy` |
| `git_tag` | Create a tag | `owner`, `repo`, `tag_name`, `name` |
| `git_release` | Create a release | `owner`, `repo`, `tag_name`, `name`, `body` |

Merge strategies: `merge`, `squash`, `rebase`, `fast_forward`.

### Configuration

| Env var | Purpose | Default |
|---------|---------|---------|
| `PILOT_CODE_URL` | Code API base URL | `http://localhost:4351` |
| `PILOT_EXECUTOR_GIT` | Enable/disable | `true` |

## Persist

Wraps the Persist API for storage provisioning and data management. Steps are prefixed with `persist_`.

| Action | Description | Key fields |
|--------|-------------|------------|
| `persist_provision` | Provision storage stores | `profile`, `stores` |
| `persist_migrate` | Run database migrations | `dry_run` |
| `persist_sync` | Sync data between stores | |
| `persist_backup` | Create a backup | |
| `persist_restore` | Restore from backup | `archive_path` |
| `persist_proxy_start` | Start the proxy | |
| `persist_proxy_stop` | Stop the proxy | |

### Configuration

| Env var | Purpose | Default |
|---------|---------|---------|
| `PILOT_PERSIST_URL` | Persist API base URL | `http://localhost:4290` |
| `PILOT_EXECUTOR_PERSIST` | Enable/disable | `true` |

## Shell

Executes shell commands with an allowlist for security. Steps are prefixed with `shell_`.

| Action | Description | Key fields |
|--------|-------------|------------|
| `shell_exec` | Run a single command | `command`, `args`, `env` |
| `shell_script` | Run a multi-line script | `command` |
| `shell_assert` | Run a command and assert output | `command`, `assert` |

Shell assertions support: `exit_code`, `stdout_contains`, `stderr_empty`.

### Configuration

| Env var | Purpose | Default |
|---------|---------|---------|
| `PILOT_EXECUTOR_SHELL` | Enable/disable | `false` (disabled by default) |

The shell executor uses an `allowedCommands` allowlist in `executor_config`:

```yaml
executor_config:
  shell:
    allowedCommands: ["curl", "dig", "nslookup", "jq"]
    timeout: 30000
```

## HTTP

Makes API requests, polls endpoints, and asserts response contents. Steps are prefixed with `http_`.

| Action | Description | Key fields |
|--------|-------------|------------|
| `http_request` | Send an HTTP request | `url`, `method`, `headers`, `body` |
| `http_assert` | Request + assert response | `url`, `assert` |
| `http_poll` | Poll until condition met | `url`, `until`, `interval`, `max_attempts` |

HTTP assertions support: `status`, `body_contains`, `header`.

### Configuration

| Env var | Purpose | Default |
|---------|---------|---------|
| `PILOT_EXECUTOR_HTTP` | Enable/disable | `true` |

The HTTP executor supports an `allowedHosts` restriction in `executor_config`:

```yaml
executor_config:
  http:
    allowedHosts: ["api.cloudflare.com", "api.github.com"]
    timeout: 30000
```

## Composite

The composite executor orchestrates other steps. It supports four meta-actions:

| Action | Description | Key fields |
|--------|-------------|------------|
| `run_playbook` | Execute another playbook inline | `playbook`, `inputs` |
| `parallel` | Run step branches concurrently | `branches` |
| `conditional` | Execute steps if condition is true | `condition`, `then` |
| `loop` | Repeat steps until condition or max iterations | `until`, `max_iterations`, `steps` |

Conditions use the syntax `captures.key == value`, `captures.key != value`, or `captures.key` (truthy check).

### Example: Conditional + parallel

```yaml
steps:
  - id: check-provider
    action: conditional
    executor: composite
    condition: "captures.provider == cloudflare"
    then:
      - id: setup-dns
        action: run_playbook
        playbook: cloudflare.dns.add-a-record
        inputs:
          zone: "{{zone}}"
          record_name: "{{subdomain}}"
          ip_address: "{{ip}}"
  - id: deploy-both
    action: parallel
    executor: composite
    branches:
      - steps:
          - id: provision-storage
            action: persist_provision
            profile: production
      - steps:
          - id: apply-infra
            action: tf_apply
            target_id: "{{target}}"
            resource_types: ["aws_s3_bucket"]
```

## See also

- [Playbook reference](/docs/pilot/playbook-reference) — full YAML schema
- [JavaScript SDK](/docs/pilot/sdk/javascript) — run playbooks programmatically
- [Quickstart](/docs/pilot/quickstart) — record and replay your first workflow