> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# walkerOS CLI

The walkerOS CLI (`@walkeros/cli`) is a command-line tool for building, testing, and running event collection flows. It handles the complete workflow from configuration to deployment. It bundles flows into optimized JavaScript, testing with simulated events, and running collection servers locally or in Docker.

## Installation[​](#installation "Direct link to Installation")

### Global Installation (Recommended)[​](#global-installation-recommended "Direct link to Global Installation (Recommended)")

Install globally to use the `walkeros` command anywhere:

```
npm install -g @walkeros/cli

# Verify installation
walkeros --version
```

### Local Installation[​](#local-installation "Direct link to Local Installation")

Install in your project for team consistency:

```
npm install --save-dev @walkeros/cli

# Use with npx
npx walkeros --version
```

## Commands overview[​](#commands-overview "Direct link to Commands overview")

| Command    | Purpose                                                        | Use Case                                                                                               |
| ---------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `bundle`   | Build production-ready bundle from flow config                 | Create deployable JavaScript from configuration                                                        |
| `push`     | Execute event with real API calls (or `--simulate` for mocked) | Testing and production validation                                                                      |
| `setup`    | Run the optional `setup()` lifecycle on one component          | Provision external resources (BigQuery datasets, Pub/Sub topics, SQLite tables, webhook registrations) |
| `validate` | Validate events, flows, mappings, contracts, or entries        | Check configuration before bundling                                                                    |
| `run`      | Start HTTP event collection server                             | Accept incoming events via HTTP POST                                                                   |
| `cache`    | Manage CLI package and build caches                            | Clear stale caches, view cache statistics                                                              |
| `auth`     | Authentication and identity                                    | Log in, log out, check identity                                                                        |
| `projects` | Manage walkerOS projects                                       | Create, list, update, delete projects                                                                  |
| `flows`    | Manage walkerOS flows                                          | Create, list, update, delete, duplicate flows                                                          |
| `deploy`   | Create and manage deployments                                  | Deploy flows to walkerOS cloud or self-hosted                                                          |
| `previews` | Manage preview bundles                                         | Test flow changes on live sites before deploying                                                       |
| `observe`  | Open an Observe session on a flow                              | Watch a running flow's live events from the terminal                                                   |

The `push` command accepts either a config JSON or a pre-built bundle as input.

## Configuration types[​](#configuration-types "Direct link to Configuration types")

The CLI uses types from `@walkeros/core`:

* **`Flow.Json`** - Root config file format (`version`, `flows`)
* **`Flow`** - Single flow (has `config`, `sources`, `destinations`, `transformers`, `collector`)
* **`Flow.Config`** - Per-flow config block (`platform`, `url`, `settings`, `bundle`)
* **`Collector.InitConfig`** - Runtime type passed to `startFlow()`

The CLI transforms `Flow.Json` → `Flow` (per flow) → bundled code that uses `Collector.InitConfig` at runtime.

## Packages and overrides[​](#packages-and-overrides "Direct link to Packages and overrides")

Build-time concerns live under each flow's `config.bundle` block. Three fields are supported:

* **`packages`**: the list of npm packages (or local paths) the bundler should install for this flow. Pacote downloads them based on this list. You do **not** need to run `npm install` yourself for step packages; the CLI handles it transparently.
* **`overrides`**: pin transitive dependency versions, matching npm's `overrides` semantics. Useful when an upstream package declares an overly narrow version range that conflicts with a newer transitive version in the same tree.
* **`traceInclude`** (server flows only): an escape hatch for the file tracer. See below.

```
"flows": {
  "default": {
    "config": {
      "platform": "server",
      "bundle": {
        "packages": {
          "@walkeros/collector": {}
        },
        "overrides": {
          "@amplitude/analytics-types": "2.11.1"
        }
      }
    }
  }
}
```

### Server bundles use nft tracing[​](#server-bundles-use-nft-tracing "Direct link to Server bundles use nft tracing")

Server flows (`platform: "server"`) are traced automatically with [`@vercel/nft`](https://github.com/vercel/nft). The bundler externalizes every step package, then nft walks the entry to figure out which files are actually reachable at runtime, and copies only those files into `dist/node_modules/`. Native add-ons, `.proto` files, and `__dirname`-loaded assets are picked up.

There is no `walkerOS.bundle.external` annotation: nft figures out the externalization automatically.

### Escape hatch: `traceInclude`[​](#escape-hatch-traceinclude "Direct link to escape-hatch-traceinclude")

If nft cannot statically reach a file (rare: dynamic `require(somePath)` constructed from a runtime variable, dynamically loaded data files), declare it explicitly with `flow.<name>.config.bundle.traceInclude`. Paths and globs are both supported (matched via picomatch). Paths resolve against the install root, not your project directory.

```
"flows": {
  "default": {
    "config": {
      "platform": "server",
      "bundle": {
        "packages": { "@walkeros/server-destination-gcp": {} },
        "traceInclude": [
          "node_modules/some-pkg/data/*.json",
          "node_modules/another-pkg/lib/runtime-loaded.js"
        ]
      }
    }
  }
}
```

### Range conflict resolution[​](#range-conflict-resolution "Direct link to Range conflict resolution")

When two transitive consumers declare incompatible ranges for the same dependency (e.g., `arrify@^3.0.0` from one package, `arrify@^2.0.0` from another), the bundler now resolves the chosen range to a concrete version via the npm registry and nests any non-satisfying spec under its consumer's `node_modules/`. This kills classes of upstream chaos (like `arrify is not a function` from `@google-cloud/common`) before they can reach runtime. After install, a defense-in-depth check warns when any installed package's declared deps don't match what's reachable on its resolution path; if you see those warnings, add an `overrides` entry to your flow's `config.bundle.overrides` to pin the offending dep. If the npm registry is unreachable during validation, set `BUNDLER_STRICT_RANGES=0` to bypass strict validation with a warning instead of failing the build.

### Schema version[​](#schema-version "Direct link to Schema version")

The flow\.json schema stays at `version: 4`. Build-time fields live under `flow.<name>.config.bundle.{packages, overrides, traceInclude}`. The `flow.<name>.config.bundle.external` sub-field is no longer supported in @walkeros/cli\@4.x.

## Package imports[​](#package-imports "Direct link to Package imports")

### Implicit collector[​](#implicit-collector "Direct link to Implicit collector")

The collector is added automatically. To pin a specific version (recommended for production), add the collector explicitly:

```
"packages": {
  "@walkeros/collector": { "version": "1.0.0" },
  "@walkeros/web-source-browser": {},
  "@walkeros/destination-demo": {}
}
```

### Default exports (sources & destinations)[​](#default-exports-sources--destinations "Direct link to Default exports (sources & destinations)")

Sources and destinations automatically use their **default export** - no `imports` needed:

```
"packages": {
  "@walkeros/web-source-browser": {},        // Uses default export
  "@walkeros/destination-demo": {}           // Uses default export
}
```

### Utility imports[​](#utility-imports "Direct link to Utility imports")

Use `imports` when you need specific utility functions from a package:

```
"packages": {
  "@walkeros/core": { "imports": ["getId", "clone"] },     // Optional utilities
  "@walkeros/web-source-browser": {
    "imports": ["createTagger"]                            // Additional utility
  }
}
```

### Named-export selection with `import`[​](#named-export-selection-with-import "Direct link to named-export-selection-with-import")

For packages without a default export, or to pick a specific named export, set `import` on the step alongside `package`. The bundler adds the name to the `imports` list automatically.

```
"sources": {
  "custom": {
    "package": "@my/custom-package",
    "import": "namedExport"
  }
}
```

The legacy `code: "namedExport"` form is no longer accepted. Validation raises `OBSOLETE_CODE_STRING` with a rename hint pointing to `import`.

## Local packages[​](#local-packages "Direct link to Local packages")

By default, the CLI downloads packages from npm. For development or testing unpublished packages, you can use local packages instead by specifying a `path` property.

### Configuration[​](#configuration "Direct link to Configuration")

Add a `path` property to any package to use a local directory instead of npm:

```
{
  "packages": {
    "@my/custom-destination": {
      "path": "./my-custom-destination"
    }
  }
}
```

### Resolution Rules[​](#resolution-rules "Direct link to Resolution Rules")

* **`path` takes precedence** - When both `path` and `version` are specified, `path` is used
* **Relative paths** - Resolved relative to the config file's directory
* **Absolute paths** - Used as-is
* **dist folder** - If a `dist/` folder exists, it's used; otherwise the package root is used

### Dependency Resolution[​](#dependency-resolution "Direct link to Dependency Resolution")

When a local package has dependencies on other packages that are also specified with local paths, the CLI will use the local versions for those dependencies too. This prevents npm versions from overwriting your local packages.

```
{
  "packages": {
    "@walkeros/core": {
      "path": "../packages/core"
    },
    "@walkeros/collector": {
      "path": "../packages/collector"
    }
  }
}
```

In this example, even though `@walkeros/collector` depends on `@walkeros/core`, the local version of core will be used (not downloaded from npm). This is essential when testing changes across multiple interdependent packages.

### Use Cases[​](#use-cases "Direct link to Use Cases")

**Development of custom packages:**

```
{
  "packages": {
    "@my-org/destination-custom": {
      "path": "../my-destination"
    }
  }
}
```

**Testing local changes to walkerOS packages:**

```
{
  "packages": {
    "@walkeros/collector": {
      "path": "../../packages/collector"
    }
  }
}
```

When ready for production, simply remove the `path` property to use the published npm version.

## Getting started[​](#getting-started "Direct link to Getting started")

Before using the CLI, you need a [flow configuration file](https://www.walkeros.io/docs/getting-started/modes/bundled.md). Here's a minimal example:

```
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "server",
        "bundle": {
          "packages": {
            "@walkeros/server-source-express": {},
            "@walkeros/destination-demo": {}
          }
        }
      },
      "sources": {
        "http": {
          "package": "@walkeros/server-source-express",
          "config": {
            "settings": { "path": "/collect", "port": 8080 }
          }
        }
      },
      "destinations": {
        "console": {
          "package": "@walkeros/destination-demo",
          "config": {
            "settings": { "name": "Console Logger" }
          }
        }
      },
      "collector": { "run": true }
    }
  }
}
```

Implicit collector

The `@walkeros/collector` package is automatically added when your flow has sources or destinations.

Save this as `flow.json`.

## Bundle command[​](#bundle-command "Direct link to Bundle command")

The `bundle` command builds production-ready JavaScript bundles from flow configurations.

### Use Case[​](#use-case "Direct link to Use Case")

You've defined your sources, destinations, and transformations in a flow configuration file. Now you need to:

* Download the required npm packages
* Bundle everything into a single optimized JavaScript file
* Deploy it to production (Docker, Cloud Run, serverless functions)

The bundle command handles all of this.

### Basic Usage[​](#basic-usage "Direct link to Basic Usage")

```
walkeros bundle flow.json
```

This writes the optimized bundle to stdout (web flows only). Use `-o` to write to a file or directory (e.g., `-o ./dist/walker.js` for web, `-o ./dist/` for server, which produces `dist/{flow.mjs, package.json, node_modules/}`). For server flows you can also write a `.tar.gz`/`.tgz` archive (e.g., `-o ./flow.tar.gz`), which packs the server bundle directory (`flow.mjs`, `package.json`, `node_modules/`) into a single gzip file. Web single-file bundles do not support archive output.

### Step-by-Step Guide[​](#step-by-step-guide "Direct link to Step-by-Step Guide")

**1. Create a flow configuration**

Create `server-collect.json`:

```
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "server",
        "bundle": {
          "packages": {
            "@walkeros/server-source-express": {},
            "@walkeros/destination-demo": {}
          }
        }
      },
      "sources": {
        "http": {
          "package": "@walkeros/server-source-express",
          "config": {
            "settings": {
              "path": "/collect",
              "port": 8080,
              "cors": true
            }
          }
        }
      },
      "destinations": {
        "console": {
          "package": "@walkeros/destination-demo",
          "config": {
            "settings": {
              "name": "Event Logger",
              "values": ["name", "data.title", "timestamp"]
            }
          }
        }
      },
      "collector": { "run": true }
    }
  }
}
```

**2. Bundle the flow**

```
walkeros bundle server-collect.json --stats
```

Output:

```
📦 Downloading packages from npm...

✓ @walkeros/collector@latest

✓ @walkeros/server-source-express@latest

✓ @walkeros/destination-demo@latest



🔨 Bundling...

✓ Bundle created: ./dist/flow.mjs



📊 Bundle Statistics:

   Size: 45.2 KB (minified)

   Packages: 3

   Format: ESM
```

**3. Review the bundle**

```
ls -lh dist/
# -rw-r--r--  1 user  staff   45K  flow.mjs
# -rw-r--r--  1 user  staff  152B  package.json
# drwxr-xr-x 12 user  staff  384B  node_modules

file dist/flow.mjs
# dist/flow.mjs: JavaScript source, UTF-8 Unicode text
```

The bundle is now ready to deploy!

### Options[​](#options "Direct link to Options")

```
walkeros bundle <config-file> [options]
```

| Option                | Description                                       |
| --------------------- | ------------------------------------------------- |
| `-o, --output <path>` | Write bundle to file, directory, or presigned URL |
| `-f, --flow <name>`   | Build specific flow (for multi-flow configs)      |
| `--all`               | Build all flows                                   |
| `-s, --stats`         | Show bundle statistics                            |
| `--json`              | Output statistics as JSON (for CI/CD)             |
| `--no-cache`          | Skip package cache, download fresh                |
| `-v, --verbose`       | Detailed logging                                  |

When `--output` is a URL (e.g., a presigned S3 URL), the CLI bundles to a temp file and uploads via HTTP PUT. This is used by the walkerOS cloud service for remote builds.

For server flows, `-o` resolves to a directory: `dist/{flow.mjs, package.json, node_modules/}`. When `-o` ends in `.tar.gz` or `.tgz`, that same directory is packed into a single gzip archive instead (server flows only). For multi-flow `--all`, each flow gets its own subdirectory: `dist/<flowName>/...`.

### Multi-Flow Example[​](#multi-flow-example "Direct link to Multi-Flow Example")

```
# Bundle development flow
walkeros bundle config.json --flow development

# Bundle production with stats
walkeros bundle config.json --flow production --stats

# Bundle all flows
walkeros bundle config.json --all
```

## Push command[​](#push-command "Direct link to Push command")

The `push` command executes your flow with a real event. By default it makes actual API calls to your configured destinations. Use `--simulate` to mock specific steps for safe testing. It accepts either a config JSON (which gets bundled) or a pre-built bundle.

### Use Case[​](#use-case-1 "Direct link to Use Case")

You want to:

* Test event processing with mocked destinations (`--simulate`)
* Test with real third-party APIs (GA4, Meta, BigQuery, etc.)
* Verify production credentials and endpoints work
* Debug actual API responses and errors
* Perform integration testing before deployment
* Execute a pre-built bundle without rebuilding

Push handles both safe local testing (with `--simulate`) and real integration testing.

### Basic Usage[​](#basic-usage-1 "Direct link to Basic Usage")

```
# With config JSON (auto-bundled)
walkeros push flow.json --event '{"name":"page view","data":{"title":"Home"}}'

# With pre-built bundle
walkeros push dist/flow.mjs --event '{"name":"page view"}'
```

### Step-by-Step Guide[​](#step-by-step-guide-1 "Direct link to Step-by-Step Guide")

**1. Create a flow configuration**

Create `api-flow.json` with a destination that makes real HTTP calls:

```
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "server",
        "bundle": {
          "packages": {
            "@walkeros/web-destination-api": {}
          }
        }
      },
      "destinations": {
        "api": {
          "package": "@walkeros/web-destination-api",
          "config": {
            "settings": {
              "url": "https://your-endpoint.com/events",
              "method": "POST"
            }
          }
        }
      },
      "collector": { "run": true }
    }
  }
}
```

**2. Create an event file**

Create `event.json`:

```
{
  "name": "order complete",
  "data": {
    "id": "ORD-12345",
    "total": 299.99,
    "currency": "USD"
  },
  "user": {
    "id": "user_abc123"
  }
}
```

**3. Push the event**

```
walkeros push api-flow.json --event event.json --verbose
```

**4. Review the output**

```
📥 Loading event...

📦 Loading flow configuration...

🔨 Bundling flow configuration...

🖥️  Executing in server environment (Node.js)...

Pushing event: order complete

✅ Event pushed successfully

   Event ID: 1701234567890-abc12-1

   Entity: order

   Action: complete

   Duration: 1234ms
```

The event was sent to your real API endpoint!

### Options[​](#options-1 "Direct link to Options")

```
walkeros push <input> [options]
```

| Option                      | Description                                                                                                    |
| --------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `-e, --event <source>`      | **Required.** Event to push (JSON string, file path, or URL)                                                   |
| `-f, --flow <name>`         | Flow name (for multi-flow configs)                                                                             |
| `-p, --platform <platform>` | Platform override (`web` or `server`)                                                                          |
| `--simulate <step>`         | Simulate a step (repeatable). Mocks the step's push, captures result. Use `destination.NAME` or `source.NAME`. |
| `--mock <step=value>`       | Mock a step with a specific return value (repeatable). Use `destination.NAME=VALUE`.                           |
| `--snapshot <source>`       | JS file to eval before execution. Sets global state (`window.dataLayer`, `process.env`, etc.).                 |
| `-o, --output <path>`       | Write result to file                                                                                           |
| `--json`                    | Output results as JSON                                                                                         |
| `-v, --verbose`             | Verbose output with debug information                                                                          |
| `-s, --silent`              | Suppress output (for CI/CD)                                                                                    |

### Input Types[​](#input-types "Direct link to Input Types")

The CLI auto-detects the input type by attempting to parse as JSON:

* **Config JSON** - Bundled and executed
* **Pre-built bundle** (`.js`/`.mjs`) - Executed directly

When using pre-built bundles, platform is detected from file extension:

* `.mjs` → server (ESM, Node.js)
* `.js` → web (IIFE, JSDOM)

Use `--platform` to override if extension doesn't match intended runtime.

### Event Input Formats[​](#event-input-formats "Direct link to Event Input Formats")

The `--event` parameter accepts three formats:

**Inline JSON string:**

```
walkeros push flow.json --event '{"name":"page view","data":{"title":"Home"}}'
```

**File path:**

```
walkeros push flow.json --event ./events/order.json
```

**URL:**

```
walkeros push flow.json --event https://example.com/sample-event.json
```

### Push modes[​](#push-modes "Direct link to Push modes")

| Mode     | Flag         | API Calls          | Use Case                                   |
| -------- | ------------ | ------------------ | ------------------------------------------ |
| Real     | (none)       | Real HTTP requests | Integration testing, production validation |
| Simulate | `--simulate` | Mocked (captured)  | Safe local testing                         |
| Mock     | `--mock`     | Returns mock value | Controlled testing                         |

**Recommended workflow:**

* Use `push --simulate` first to validate configuration without side effects
* Use `push` (without flags) to verify real integrations work before deployment

### JSON Output[​](#json-output "Direct link to JSON Output")

For CI/CD pipelines, use `--json` for machine-readable output:

```
walkeros push flow.json --event '{"name":"page view"}' --json --silent
```

Output:

```
{

  "success": true,

  "event": {

    "id": "1701234567890-abc12-1",

    "name": "page view",

    "entity": "page",

    "action": "view"

  },

  "duration": 1234

}
```

On error:

```
{

  "success": false,

  "error": "Connection refused: https://your-endpoint.com/events",

  "duration": 5023

}
```

### Multi-Flow[​](#multi-flow "Direct link to Multi-Flow")

Push to specific flows:

```
# Push to staging
walkeros push flow.json --event event.json --flow staging

# Push to production
walkeros push flow.json --event event.json --flow production
```

## Setup command[​](#setup-command "Direct link to Setup command")

The `setup` command runs the optional `setup()` lifecycle on a single component to provision external resources before traffic flows. Use it for one-time provisioning steps like creating a BigQuery dataset and table, declaring a Pub/Sub topic and subscription, creating SQLite tables, or registering a webhook with a third-party API.

For the conceptual overview of the lifecycle (when to implement `setup()` in a component, how it relates to the runtime hot path, and the operator separation rationale), see [Setup lifecycle](https://www.walkeros.io/docs/destinations/create-your-own.md#setup-lifecycle-optional).

### Use case[​](#use-case-2 "Direct link to Use case")

Setup is for the operator-time, "do this once before traffic" step that sits between writing config and accepting events:

* A BigQuery destination needs its dataset and table to exist with the right schema before rows can be streamed in.
* A Pub/Sub source needs a subscription against a topic before it can pull messages.
* A SQLite store needs its tables created before reads and writes work.
* A webhook integration needs to register its callback URL with the upstream service.

Setup is **explicit only**. It is never triggered by `push`, `simulate`, `deploy`, or the long-running runtime. You run it once when provisioning, then again only when external resources need to be re-provisioned (for example, after changing the table schema in config). The runtime never tries to "fix" missing resources on its own.

### Basic usage[​](#basic-usage-2 "Direct link to Basic usage")

```
# Provision the BigQuery dataset and table for the destination named "bigquery"
walkeros setup destination.bigquery
```

### Target syntax[​](#target-syntax "Direct link to Target syntax")

The target uses the same `<kind>.<name>` syntax as `walkeros push --simulate`. Valid kinds are `source`, `destination`, and `store`. Transformers are pure functions and have no setup.

| Target                 | Resolves to                                           |
| ---------------------- | ----------------------------------------------------- |
| `destination.bigquery` | The destination named `bigquery` in the resolved flow |
| `source.events-in`     | The source named `events-in`                          |
| `store.session`        | The store named `session`                             |

### Package resolution[​](#package-resolution "Direct link to Package resolution")

Setup resolves the component's package exactly like `walkeros bundle`: the version pinned in `config.bundle.packages` (or inline on the step as `"@scope/pkg@x.y.z"`, with the bundle pin winning on disagreement) is downloaded from the npm registry into a temporary tree and imported from there.

```
{
  "config": {
    "platform": "server",
    "bundle": {
      "packages": {
        "@walkeros/server-destination-gcp": { "version": "4.4.0" }
      }
    }
  },
  "destinations": {
    "bigquery": {
      "package": "@walkeros/server-destination-gcp",
      "config": { "setup": true }
    }
  }
}
```

What this means in practice:

* **Works from any install.** npx, devDependency, or global: no local `npm install` of the component package is needed or consulted.
* **Shared cache with bundle.** Exact version pins keep working offline once cached; `latest` and ranges re-resolve against the registry.
* **Local packages via `path`.** A `path` entry in `config.bundle.packages` is supported for local development; the target must be a built package directory (package.json plus dist output), since setup imports with Node at runtime.
* **No version drift with exact pins.** Setup and bundle resolve through the same pipeline and cache, so an exact version pin guarantees both act on the same package release and provisioning (for example a BigQuery schema) cannot drift from what the bundle writes. Mutable specifications (`latest`, ranges, `path` targets) can resolve differently between separate runs, so pin exact versions when drift matters.

### Options[​](#options-2 "Direct link to Options")

```
walkeros setup <target> [options]
```

| Option                | Description                                           |
| --------------------- | ----------------------------------------------------- |
| `-c, --config <path>` | Flow config file (default: `./flow.json`)             |
| `-f, --flow <name>`   | Flow name for multi-flow configs                      |
| `--json`              | Output as JSON (passed to the package's setup logger) |
| `-v, --verbose`       | Verbose output                                        |
| `-s, --silent`        | Suppress output                                       |

### What "skip" means[​](#what-skip-means "Direct link to What \"skip\" means")

The framework narrates one of three reasons when nothing runs and exits successfully:

| Reason                  | Trigger                                                              |
| ----------------------- | -------------------------------------------------------------------- |
| `no setup function`     | The package does not export a `setup` function on its default export |
| `config.setup is false` | The flow config explicitly opts out with `"setup": false`            |
| `config.setup is unset` | The flow config does not declare `setup` at all                      |

In all three cases the exit code is `0`. Setup is a single-purpose command, not a tiered status check.

### Result output[​](#result-output "Direct link to Result output")

When the package's `setup()` returns a non-undefined value, the CLI emits it as JSON on stdout for `jq`-style scripting. This lets you script around what was provisioned:

```
walkeros setup destination.bigquery --json | jq .datasetCreated
```

If the package returns nothing, no JSON is emitted; you only see the framework narration.

### Exit codes[​](#exit-codes "Direct link to Exit codes")

| Code     | Meaning                                                           |
| -------- | ----------------------------------------------------------------- |
| 0        | Success or skip (any of the three skip reasons above)             |
| non-zero | Failure (resolution error, missing default export, package threw) |

### IAM note[​](#iam-note "Direct link to IAM note")

Setup typically needs higher permissions than runtime push: "create dataset" or "create subscription" vs "write rows" or "pull messages". Operators commonly run setup with a separate service account, or different credentials, than the runtime uses. Plan your IAM accordingly: grant the runtime only what it needs to push, and grant setup the broader provisioning scope.

### Examples[​](#examples "Direct link to Examples")

```
# Provision the BigQuery dataset and table for the destination named "bigquery"
# in the default flow file.
walkeros setup destination.bigquery

# Same but pointing to a specific flow in a multi-flow config.
walkeros setup destination.bigquery --flow analytics

# Custom config path.
walkeros setup destination.bigquery --config ./flows/prod.json

# Pipe the structured result to jq.
walkeros setup destination.bigquery --json | jq .datasetCreated

# Provision a Pub/Sub source's subscription.
walkeros setup source.events-in
```

## Validate command[​](#validate-command "Direct link to Validate command")

The `validate` command checks the structure and correctness of events, flow configurations, mapping configurations, contracts, or individual flow entries before bundling or deployment.

### Use case[​](#use-case-3 "Direct link to Use case")

Before bundling or deploying your flow, you want to:

* Catch configuration errors early
* Verify event structure follows walkerOS conventions
* Check mapping patterns are valid
* Validate contracts follow the named-entry shape, with entity-action schemas nested under `events`
* Validate a specific destination, source, or transformer entry against its package's published JSON Schema
* Integrate validation into CI/CD pipelines

Validate gives you fast feedback without the overhead of bundling.

### Basic usage[​](#basic-usage-3 "Direct link to Basic usage")

```
# Validate a flow configuration (schema, references, cross-step examples)
walkeros validate flow.json

# Validate an event structure
walkeros validate event.json --type event

# Validate a mapping configuration
walkeros validate mapping.json --type mapping

# Validate a contract
walkeros validate contract.json --type contract

# Validate a specific entry against its package schema
walkeros validate flow.json --path destinations.snowplow
```

### Validation types[​](#validation-types "Direct link to Validation types")

The `--type` option accepts four validation types. Default is `flow`:

| Type             | What it validates                                                                                                 |
| ---------------- | ----------------------------------------------------------------------------------------------------------------- |
| `event`          | Event structure: `name` field exists, follows "entity action" format with space, valid data types                 |
| `flow` (default) | Flow\.Json config: schema, references, packages, and cross-step example compatibility                             |
| `mapping`        | Mapping rules: event patterns use "entity action" or wildcard format, rule structures are valid                   |
| `contract`       | Contract structure: named entries with optional `extend`, `tagging`, `description`, `events`, and `schema` fields |

Use `--path` for entry validation against package schemas (e.g., `--path destinations.snowplow`).

### Step-by-step guide[​](#step-by-step-guide-2 "Direct link to Step-by-step guide")

**1. Validate a flow configuration**

```
walkeros validate flow.json
```

Output (valid):

```
Validating flow...



Validation Results:

  ✓ All checks passed



Summary: 0 error(s), 0 warning(s)
```

**2. Validate with errors**

```
# Create an invalid event
echo '{"name":"pageview"}' > bad-event.json

walkeros validate bad-event.json --type event
```

Output:

```
Validating event...



Validation Results:

  ✗ name: Event name must be "entity action" format with space (e.g., "page view")



Summary: 1 error(s), 0 warning(s)
```

**3. Validate with warnings**

```
# Event without consent object
echo '{"name":"page view","data":{"title":"Home"}}' > event.json

walkeros validate event.json --type event
```

Output:

```
Validating event...



Validation Results:

  ✓ All checks passed

  ⚠ consent: No consent object provided

    → Consider adding a consent object for GDPR/privacy compliance



Summary: 0 error(s), 1 warning(s)
```

### Options[​](#options-3 "Direct link to Options")

```
walkeros validate [input] [options]
```

| Option                | Description                                                                          |
| --------------------- | ------------------------------------------------------------------------------------ |
| `-t, --type <type>`   | Validation type (default: `flow`). Also: `event`, `mapping`, `contract`              |
| `--path <path>`       | Validate a specific entry against its package schema (e.g., `destinations.snowplow`) |
| `-f, --flow <name>`   | Flow name to validate (for multi-flow configs)                                       |
| `-o, --output <path>` | Write result to file                                                                 |
| `--strict`            | Treat warnings as errors (exit code 2)                                               |
| `--json`              | Output results as JSON                                                               |
| `-v, --verbose`       | Show detailed validation information                                                 |
| `-s, --silent`        | Suppress banner output                                                               |

### Exit codes[​](#exit-codes-1 "Direct link to Exit codes")

| Code | Meaning                                          |
| ---- | ------------------------------------------------ |
| 0    | Valid (no errors)                                |
| 1    | Validation errors found                          |
| 2    | Warnings found (with `--strict`)                 |
| 3    | Input error (file not found, invalid JSON, etc.) |

### CI/CD integration[​](#cicd-integration "Direct link to CI/CD integration")

Use `--json` and exit codes for automated pipelines:

```
# Fail on any error
walkeros validate flow.json --json || exit 1

# Fail on warnings too (strict mode)
walkeros validate flow.json --strict --json || exit 1
```

JSON output format:

```
{

  "valid": true,

  "type": "flow",

  "errors": [],

  "warnings": [

    {

      "path": "packages.@walkeros/destination-demo",

      "message": "Package \"@walkeros/destination-demo\" has no version specified",

      "suggestion": "Consider specifying a version for reproducible builds"

    }

  ],

  "details": {

    "flowNames": ["default"],

    "flowCount": 1,

    "packageCount": 2

  }

}
```

### Multi-flow validation[​](#multi-flow-validation "Direct link to Multi-flow validation")

Validate a specific flow in a multi-flow configuration:

```
# Validate only the "production" flow
walkeros validate flow.json --flow production
```

If the flow doesn't exist:

```
Validation Results:

  ✗ flows: Flow "production" not found. Available: default, staging



Summary: 1 error(s), 0 warning(s)
```

### Event validation details[​](#event-validation-details "Direct link to Event validation details")

Event validation checks (see [event.ts](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/src/commands/validate/validators/event.ts)):

1. **Name field exists** - Required field
2. **Name is non-empty** - Cannot be empty string
3. **Entity-action format** - Must contain space (e.g., `"page view"` not `"pageview"`)
4. **Schema validation** - Data types match expected structure
5. **Best practices** - Warns if consent object is missing

```
# Valid event
walkeros validate '{"name":"product view","data":{"id":"P123"}}' --type event

# Invalid: missing space in name
walkeros validate '{"name":"productview"}' --type event
```

### Mapping validation details[​](#mapping-validation-details "Direct link to Mapping validation details")

Mapping validation checks (see [mapping.ts](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/src/commands/validate/validators/mapping.ts)):

1. **Object structure** - Must be an object with event patterns as keys
2. **Event patterns** - Must be `"entity action"` format or contain wildcard (`*`)
3. **Rule structure** - Each rule must be an object or array of objects
4. **Catch-all position** - Warns if `*` is not the last pattern

```
# Valid mapping
walkeros validate '{
  "page view": {"name": "pageview"},
  "*": {"name": "generic_event"}
}' --type mapping
```

### Contract validation details[​](#contract-validation-details "Direct link to Contract validation details")

Contract validation checks (see [contract.ts](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/src/commands/validate/validators/contract.ts)):

1. **Root structure** - Must be an object of named contract entries, not an array or primitive
2. **Named-entry shape enforced** - A flat entity-action map, or a `$`-prefixed root key, is rejected as the flat shape
3. **Unknown keys** - A key on a contract entry outside `extend`, `tagging`, `description`, `events`, `schema` is a warning, not an error
4. **`tagging`** - If present, must be a number
5. **`schema`** - If present, must be a JSON Schema object
6. **`extend`** - Must reference an existing contract entry; circular chains are rejected
7. **Entity and action keys** - Cannot be empty strings
8. **Event entries** - Each `events.<entity>.<action>` value must be a JSON Schema object

```
# Valid contract
walkeros validate '{
  "web": {
    "tagging": 1,
    "events": {
      "page": {
        "view": {
          "properties": {
            "title": { "type": "string" }
          }
        }
      }
    }
  }
}' --type contract
```

### Entry validation (`--path`)[​](#entry-validation---path "Direct link to entry-validation---path")

Entry validation checks a specific destination, source, or transformer in your flow config against the package's published JSON Schema. The CLI fetches the schema from the package on the CDN and validates the `config.settings` object using AJV.

```
# Validate a specific destination entry
walkeros validate flow.json --path destinations.snowplow

# Validate a specific source entry
walkeros validate flow.json --path sources.browser

# Short form (works if the key is unique across sections)
walkeros validate flow.json --path snowplow
```

The entry validator:

1. Resolves the entry from the flow config (first flow is used)
2. Reads the `package` field to identify the npm package
3. Fetches the package's JSON Schema from the CDN
4. Validates the entry's `config.settings` against the schema

## Auth commands[​](#auth-commands "Direct link to Auth commands")

The `auth` command group manages authentication with the walkerOS cloud service. Authentication is required for cloud commands (`projects`, `flows`, `deploy`).

### Login[​](#login "Direct link to Login")

Log in to walkerOS via an OAuth browser flow. The CLI requests a device code, opens your browser for authorization, and polls for the resulting token.

```
walkeros auth login
```

Output:

```
! Your one-time code: ABCD-1234

  Authorize here: https://app.walkeros.io/auth/device?code=ABCD-1234



  Opening browser...

  Waiting for authorization... (press Ctrl+C to cancel)



✓ Logged in as user@example.com

  Token stored in ~/.config/walkeros/config.json
```

The token is stored in `~/.config/walkeros/config.json` with `0600` permissions. You can also set the `WALKEROS_TOKEN` environment variable instead of using `auth login`.

| Option          | Description                                         |
| --------------- | --------------------------------------------------- |
| `--url <url>`   | Custom app URL (default: `https://app.walkeros.io`) |
| `--json`        | Output as JSON                                      |
| `-v, --verbose` | Verbose output                                      |
| `-s, --silent`  | Suppress output                                     |

### Logout[​](#logout "Direct link to Logout")

Remove stored credentials from disk.

```
walkeros auth logout
```

| Option          | Description     |
| --------------- | --------------- |
| `--json`        | Output as JSON  |
| `-v, --verbose` | Verbose output  |
| `-s, --silent`  | Suppress output |

### Whoami[​](#whoami "Direct link to Whoami")

Show the current authenticated user's identity, including email, user ID, and project ID (if the token is project-scoped).

```
walkeros auth whoami
```

Output:

```
user@example.com

User: usr_abc123

Project: proj_def456
```

| Option                | Description          |
| --------------------- | -------------------- |
| `-o, --output <path>` | Write result to file |
| `--json`              | Output as JSON       |
| `-v, --verbose`       | Verbose output       |
| `-s, --silent`        | Suppress output      |

### Token resolution[​](#token-resolution "Direct link to Token resolution")

The CLI resolves authentication tokens in this order:

1. `WALKEROS_TOKEN` environment variable
2. Config file (`~/.config/walkeros/config.json`, written by `auth login`)
3. Not authenticated (cloud commands will fail)

## Projects commands[​](#projects-commands "Direct link to Projects commands")

The `projects` command group manages walkerOS cloud projects. All commands require authentication (see [Auth commands](#auth-commands)).

### List projects[​](#list-projects "Direct link to List projects")

```
walkeros projects list
```

| Option         | Description                                                      |
| -------------- | ---------------------------------------------------------------- |
| `--cursor <c>` | Resume from a pagination cursor returned by a previous list call |
| `--limit <n>`  | Maximum number of projects to return                             |

### Get project details[​](#get-project-details "Direct link to Get project details")

```
# Get by explicit ID
walkeros projects get proj_abc123

# Uses WALKEROS_PROJECT_ID if omitted
walkeros projects get
```

### Create a project[​](#create-a-project "Direct link to Create a project")

```
walkeros projects create "My Analytics Project"
```

### Update a project[​](#update-a-project "Direct link to Update a project")

```
walkeros projects update proj_abc123 --name "New Name"
```

### Delete a project[​](#delete-a-project "Direct link to Delete a project")

```
walkeros projects delete proj_abc123
```

### Common options[​](#common-options "Direct link to Common options")

All `projects` subcommands support:

| Option                | Description          |
| --------------------- | -------------------- |
| `-o, --output <path>` | Write result to file |
| `--json`              | Output as JSON       |
| `-v, --verbose`       | Verbose output       |
| `-s, --silent`        | Suppress output      |

## Flows commands[​](#flows-commands "Direct link to Flows commands")

The `flows` command group manages flow configurations within a project. All commands require authentication and a project context (either `--project` or `WALKEROS_PROJECT_ID`).

### List flows[​](#list-flows "Direct link to List flows")

```
walkeros flows list
```

| Option              | Description                                                      |
| ------------------- | ---------------------------------------------------------------- |
| `--project <id>`    | Project ID (defaults to `WALKEROS_PROJECT_ID`)                   |
| `--cursor <c>`      | Resume from a pagination cursor returned by a previous list call |
| `--limit <n>`       | Maximum number of flows to return                                |
| `--sort <field>`    | Sort by: `name`, `updated_at`, `created_at`                      |
| `--order <dir>`     | Sort order: `asc`, `desc`                                        |
| `--include-deleted` | Include soft-deleted flows                                       |

### Get a flow[​](#get-a-flow "Direct link to Get a flow")

Retrieves a flow with its full `Flow.Json` content.

```
walkeros flows get cfg_abc123
```

| Option           | Description                                    |
| ---------------- | ---------------------------------------------- |
| `--project <id>` | Project ID (defaults to `WALKEROS_PROJECT_ID`) |

### Create a flow[​](#create-a-flow "Direct link to Create a flow")

```
# With inline JSON
walkeros flows create "My Flow" --content '{"version":4,"flows":{"default":{"config":{"platform":"server"}}}}'

# From a file via stdin
cat flow.json | walkeros flows create "My Flow"
```

| Option                 | Description                                    |
| ---------------------- | ---------------------------------------------- |
| `--project <id>`       | Project ID (defaults to `WALKEROS_PROJECT_ID`) |
| `-c, --content <json>` | Flow\.Json string or file path                 |

### Update a flow[​](#update-a-flow "Direct link to Update a flow")

```
# Update name only
walkeros flows update cfg_abc123 --name "Updated Flow"

# Update content
walkeros flows update cfg_abc123 --content '{"version":4,"flows":{"default":{"config":{"platform":"server"}}}}'
```

| Option                 | Description                                    |
| ---------------------- | ---------------------------------------------- |
| `--project <id>`       | Project ID (defaults to `WALKEROS_PROJECT_ID`) |
| `--name <name>`        | New flow name                                  |
| `-c, --content <json>` | New Flow\.Config JSON string or file path      |

### Delete a flow[​](#delete-a-flow "Direct link to Delete a flow")

Soft-deletes a flow configuration.

```
walkeros flows delete cfg_abc123
```

### Duplicate a flow[​](#duplicate-a-flow "Direct link to Duplicate a flow")

Creates a copy of an existing flow configuration.

```
walkeros flows duplicate cfg_abc123 --name "Flow Copy"
```

| Option           | Description                                    |
| ---------------- | ---------------------------------------------- |
| `--project <id>` | Project ID (defaults to `WALKEROS_PROJECT_ID`) |
| `--name <name>`  | Name for the copy (defaults to "Copy of ...")  |

### Common options[​](#common-options-1 "Direct link to Common options")

All `flows` subcommands support:

| Option                | Description          |
| --------------------- | -------------------- |
| `-o, --output <path>` | Write result to file |
| `--json`              | Output as JSON       |
| `-v, --verbose`       | Verbose output       |
| `-s, --silent`        | Suppress output      |

## Deploy commands[​](#deploy-commands "Direct link to Deploy commands")

The `deploy` command group handles deploying flows to the walkerOS cloud or managing self-hosted deployments with heartbeat registration. All commands require authentication.

### deploy create[​](#deploy-create "Direct link to deploy create")

Create a new deployment. The CLI infers the deployment type (`web` or `server`) from the flow configuration.

```
# From a local config file
walkeros deploy create flow.json

# From a remote flow ID
walkeros deploy create cfg_abc123

# With a specific flow name (multi-config flows)
walkeros deploy create flow.json --flow production
```

On success, the CLI displays the deployment ID, slug, and type. It also shows example commands for running the deployment locally or via Docker. The create response does not include a deploy token: deploy tokens are minted separately, so the Docker instructions tell you to create one in the app (Settings, Self-hosted deploy token) and set it as `WALKEROS_DEPLOY_TOKEN`.

| Option                | Description                                    |
| --------------------- | ---------------------------------------------- |
| `--label <string>`    | Human-readable label for the deployment        |
| `-f, --flow <name>`   | Flow name for multi-flow configs               |
| `--project <id>`      | Project ID (defaults to `WALKEROS_PROJECT_ID`) |
| `-o, --output <path>` | Write result to file                           |
| `--json`              | Output as JSON                                 |
| `-v, --verbose`       | Verbose output                                 |
| `-s, --silent`        | Suppress output                                |

### deploy start[​](#deploy-start "Direct link to deploy start")

Deploy a remote flow to walkerOS cloud infrastructure. Auto-detects whether to use web (script hosting) or server (container) deployment based on the flow content. Streams deployment progress via SSE.

```
# Deploy a flow
walkeros deploy start cfg_abc123

# Deploy a specific config in a multi-config flow
walkeros deploy start cfg_abc123 --flow production

# Deploy without waiting for completion
walkeros deploy start cfg_abc123 --no-wait
```

Output (web deployment):

```
Building bundle...

Publishing to web...

✓ Published: https://cdn.walkeros.io/proj_xxx/walker.js
```

Output (server deployment):

```
Building bundle...

Deploying container...

Starting container...

✓ Active: https://collect-abc123.walkeros.io
```

| Option                | Description                                                                                                 |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| `--project <id>`      | Project ID (defaults to `WALKEROS_PROJECT_ID`)                                                              |
| `-f, --flow <name>`   | Flow name for multi-config flows                                                                            |
| `--no-wait`           | Return immediately after triggering (do not stream progress)                                                |
| `--timeout <seconds>` | Override the client-side wait budget (defaults to 12 minutes, which covers the server's full deploy budget) |
| `-o, --output <path>` | Write result to file                                                                                        |
| `--json`              | Output as JSON                                                                                              |
| `-v, --verbose`       | Verbose output                                                                                              |
| `-s, --silent`        | Suppress output                                                                                             |

By default `deploy start` streams progress until the deploy completes. The client-side wait budget defaults to 12 minutes so a slow but healthy long deploy is never aborted early and misread as a failure in CI; `--timeout` overrides it. Each invocation sends a fresh idempotency key, so retrying after a failed attempt starts a new deploy instead of replaying the previous result.

When a deploy fails, the CLI prints a stable, machine-readable `error.code` alongside the message so CI can branch on the code without matching prose. A rate-limited trigger (`429`) is reported as retryable with the `Retry-After` hint; under `--wait` the CLI honors one bounded retry automatically.

### deploy list[​](#deploy-list "Direct link to deploy list")

List all deployments in a project.

```
walkeros deploy list

# Filter by type
walkeros deploy list --type server

# Filter by status
walkeros deploy list --status active
```

| Option              | Description                                                      |
| ------------------- | ---------------------------------------------------------------- |
| `--project <id>`    | Project ID (defaults to `WALKEROS_PROJECT_ID`)                   |
| `--cursor <c>`      | Resume from a pagination cursor returned by a previous list call |
| `--limit <n>`       | Maximum number of deployments to return                          |
| `--type <type>`     | Filter by type: `web`, `server`                                  |
| `--status <status>` | Filter by status                                                 |

### deploy status[​](#deploy-status "Direct link to deploy status")

Get deployment details by ID or slug.

```
walkeros deploy status dep_abc123

# Or by slug
walkeros deploy status my-collector
```

| Option           | Description |
| ---------------- | ----------- |
| `--project <id>` | Project ID  |

### deploy delete[​](#deploy-delete "Direct link to deploy delete")

Delete a deployment by ID or slug.

```
walkeros deploy delete dep_abc123
```

| Option           | Description    |
| ---------------- | -------------- |
| `--project <id>` | Project ID     |
| `--json`         | Output as JSON |

### Common options[​](#common-options-2 "Direct link to Common options")

All `deploy` subcommands support:

| Option                | Description          |
| --------------------- | -------------------- |
| `-o, --output <path>` | Write result to file |
| `--json`              | Output as JSON       |
| `-v, --verbose`       | Verbose output       |
| `-s, --silent`        | Suppress output      |

## Previews command[​](#previews-command "Direct link to Previews command")

The `previews` command group manages preview bundles: short-lived flow bundles used to test configuration changes on a real production site before deploying. Creating a preview mints an app-signed, origin-bound activation grant; opening `?elbPreview={grant}` on your site verifies the grant locally, with no network round trip, and swaps in the preview bundle for that browser for the life of the preview session. Activation links minted for an Observe session additionally carry an `elbPreviewSession` companion grant, which the previewed page uses to forward server-bound events to that session's isolated container.

All commands require authentication and a project (set via `WALKEROS_PROJECT_ID` or `--project`).

### How activation works[​](#how-activation-works "Direct link to How activation works")

A managed web bundle that supports preview activation is produced by the wrap step's `preview` option, which bakes in a public key keyring, the issuing environment, and an opaque per-project binding (never a project ID or secret). The emitted bundle imports `browserSwapActivator` from `@walkeros/core` to verify an activation grant and swap in the preview artifact. `preview` is the current wrap option for this; it replaces the earlier `previewOrigin` / `previewScope` options. A companion `previewGrantTargets` option exists for the preview artifact itself: it forwards the stored grant as an `X-Walkeros-Preview` header to named server-bound destinations, so a server flow can be previewed too. `preview` and `previewGrantTargets` are mutually exclusive on a single wrap invocation: a host bundle activates a preview, a preview artifact injects its grant, not both.

### previews list[​](#previews-list "Direct link to previews list")

List all previews for a flow.

```
walkeros previews list <flowId> [--project <projectId>]
```

| Option           | Description                    |
| ---------------- | ------------------------------ |
| `--project <id>` | Project ID (overrides default) |

### previews get[​](#previews-get "Direct link to previews get")

Get details for a single preview.

```
walkeros previews get <flowId> <previewId> [--project <projectId>]
```

| Option           | Description                    |
| ---------------- | ------------------------------ |
| `--project <id>` | Project ID (overrides default) |

### previews create[​](#previews-create "Direct link to previews create")

Create a preview bundle for a specific flow settings entry.

```
walkeros previews create <flowId> [options]
```

| Option                   | Description                                                    |
| ------------------------ | -------------------------------------------------------------- |
| `-f, --flow <name>`      | Flow settings name (resolved to an ID)                         |
| `-s, --settings-id <id>` | Flow settings ID (alternative to `--flow`)                     |
| `-u, --url <siteUrl>`    | Your site URL; prints a ready-to-open activation URL on stdout |
| `--project <id>`         | Project ID (overrides default)                                 |

Without `--url`, the CLI prints the activation fragment (`?elbPreview=...`) to stdout: append it to any URL on your site to activate preview mode. With `--url`, it prints an activation URL for your site on stdout, plus a deactivation URL on stderr. Most modern terminals make the URL clickable.

You must provide either `--flow` or `--settings-id` to pick which flow settings the preview should bundle.

### previews delete[​](#previews-delete "Direct link to previews delete")

Delete a preview. Removes both the DB row and the S3 bundle immediately; the production walker on visitors' browsers self-heals on the next page load by clearing the stored `elbPreview` grant and loading the production flow.

```
walkeros previews delete <flowId> <previewId> [options]
```

| Option           | Description                                           |
| ---------------- | ----------------------------------------------------- |
| `-y, --yes`      | Skip confirmation (required to run non-interactively) |
| `--project <id>` | Project ID (overrides default)                        |

### Example[​](#example "Direct link to Example")

```
# Create a preview for the `demo` flow settings with a ready-to-open URL
walkeros previews create flow_abc123 \
  --flow demo \
  --url https://example.com

# List previews for a flow
walkeros previews list flow_abc123

# Delete when you're done
walkeros previews delete flow_abc123 prv_xyz456 --yes
```

## Observe command[​](#observe-command "Direct link to Observe command")

The `observe` command group opens an Observe session: a time-boxed window on one flow that runtimes attach to as arms. A preview arm streams from a browser, a container arm runs server-side, and both feed one shared journeys feed. See [Observe](https://www.walkeros.io/docs/getting-started/observe.md) for how a flow connects to an observer in the first place.

Requires authentication and a project (set via `WALKEROS_PROJECT_ID` or `--project`).

### observe start[​](#observe-start "Direct link to observe start")

Mint an Observe session for a flow and print the attach info for each arm.

```
walkeros observe start <flowId> [options]
```

| Option                | Description                                                                                 |
| --------------------- | ------------------------------------------------------------------------------------------- |
| `-f, --flow <name>`   | Flow settings name, which also picks the arms (auto-resolved when the flow has exactly one) |
| `--project <id>`      | Project ID (overrides default)                                                              |
| `--level <level>`     | Observation detail level: `off`, `standard`, or `trace`                                     |
| `--replace`           | Replace an existing active session for this flow                                            |
| `--no-wait`           | Do not wait for the session to settle                                                       |
| `--timeout <seconds>` | Seconds to wait for the session to settle (default: 300)                                    |
| `--json`              | Output the session as JSON                                                                  |

`--level` steers the provisioned container arm; the web arm's observation detail does not follow it.

The observed settings picks the arms: a web settings attaches the preview arm, plus the container arm of any server flow it references; a server settings attaches the container arm alone.

A settled session renders both arms. An arm this session did not attach renders as `Web: no web part` or `Server: no server part` in place of its block, so the output below is the both-arms case:

```
Observe session ses_abc123 (live)

  Web
    Bundle URL: https://cdn.test/preview/proj_test/walker.k9x2m4p7abcd.js
    Credential: obsw_pb_x.ses_abc123.sec_1
    Activate:   https://shop.example/?elbPreview=header.payload.signature&elbObserve=obsw_pb_x.ses_abc123.sec_1

  Server
    Flow:       server
    Endpoint:   https://obs-ses-abc123.containers.test
    Env:
      WALKEROS_OBSERVER_URL=https://observer.test
      WALKEROS_DEPLOYMENT_ID=ses_abc123
      WALKEROS_INGEST_TOKEN=srv_ingest_tok

  Expires:  <ISO timestamp>
  Records:  42

  The session lives server-side; closing this terminal does not end it.
  Sessions idle out server-side when no tab or traffic keeps them alive.
```

Open the `Activate:` URL on your site to attach the web arm: the link carries the session credential, so the page starts posting records without any further setup. Export the three `WALKEROS_*` variables into a server runtime to attach a runtime the app did not provision; both arms then feed the same journeys feed.

That block goes to stderr. Stdout carries the live server endpoint alone, so it pipes cleanly:

```
ENDPOINT=$(walkeros observe start flow_abc123 --flow web)
```

`--json` prints the whole session to stdout instead, for scripts that need the credential or the expiry.

### Exit codes[​](#exit-codes-2 "Direct link to Exit codes")

| Code | Meaning                                                                 |
| ---- | ----------------------------------------------------------------------- |
| `0`  | Session settled (or `--no-wait` returned the mint immediately)          |
| `1`  | Session failed to arm, or `--timeout` elapsed while it was still arming |

A timeout is not a teardown: the session keeps arming server-side, and it is the app or a re-run of `observe start` that reports where it landed.

## Run command[​](#run-command "Direct link to Run command")

The `run` command starts an HTTP server that accepts events and processes them through your flow.

### Use Case[​](#use-case-4 "Direct link to Use Case")

You need an HTTP endpoint to:

* Receive events from browser clients, mobile apps, or server-side sources
* Process events through your collector and destinations
* Test the full event pipeline locally before deploying to production

This is similar to running a Segment or Jitsu collection endpoint.

### Basic Usage[​](#basic-usage-4 "Direct link to Basic Usage")

```
walkeros run flow.json --port 8080
```

### Step-by-Step Guide[​](#step-by-step-guide-3 "Direct link to Step-by-Step Guide")

**1. Create a collection flow**

Create `collect.json`:

```
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "server",
        "bundle": {
          "packages": {
            "@walkeros/server-source-express": {},
            "@walkeros/destination-demo": {}
          }
        }
      },
      "sources": {
        "http": {
          "package": "@walkeros/server-source-express",
          "config": {
            "settings": {
              "path": "/collect",
              "port": 8080,
              "cors": true
            }
          }
        }
      },
      "destinations": {
        "console": {
          "package": "@walkeros/destination-demo",
          "config": {
            "settings": {
              "name": "Event Collector",
              "values": ["name", "data", "user.id", "timestamp"]
            }
          }
        }
      },
      "collector": {
        "run": true,
        "globals": {
          "environment": "development"
        }
      }
    }
  }
}
```

**2. Start the collector**

```
walkeros run flow.json
```

Output:

```
📦 Bundling flow...

✓ Bundle ready



🚀 Starting collection server...

✓ Server running on http://localhost:8080

✓ Endpoint: POST http://localhost:8080/collect

✓ Health check: GET http://localhost:8080/health
```

**3. Send test events**

Open a new terminal and send events:

```
# Page view event
curl -X POST http://localhost:8080/collect \
  -H "Content-Type: application/json" \
  -d '{
    "name": "page view",
    "data": {
      "title": "Home Page",
      "path": "/"
    },
    "user": {
      "id": "user123"
    }
  }'

# Product view event
curl -X POST http://localhost:8080/collect \
  -H "Content-Type: application/json" \
  -d '{
    "name": "product view",
    "data": {
      "id": "P123",
      "name": "Laptop",
      "price": 999
    }
  }'
```

**4. See events in console**

The collector terminal shows:

```
[Event Collector] page view

  data: {"title":"Home Page","path":"/"}

  user.id: user123

  timestamp: 1701234567890



[Event Collector] product view

  data: {"id":"P123","name":"Laptop","price":999}

  timestamp: 1701234567891
```

### Options[​](#options-4 "Direct link to Options")

```
walkeros run <config|bundle|archive> [options]
```

The input can be a flow config, a pre-built bundle, or a `.tar.gz`/`.tgz` flow archive (local file or URL).

| Option                           | Description                                                 |
| -------------------------------- | ----------------------------------------------------------- |
| `-p, --port <number>`            | Server port (default: 8080)                                 |
| `-h, --host <address>`           | Host address (default: 0.0.0.0)                             |
| `--deploy <id-or-slug>`          | Deployment ID or slug (enables heartbeat to walkerOS cloud) |
| `--project <id>`                 | Project ID (used with `--deploy`)                           |
| `--url <url>`                    | Public URL of this server (used with `--deploy`)            |
| `--health-endpoint <path>`       | Health check path (default: `/health`)                      |
| `--heartbeat-interval <seconds>` | Heartbeat interval in seconds (default: 60)                 |
| `--json`                         | Output as JSON                                              |
| `-v, --verbose`                  | Detailed logging                                            |
| `-s, --silent`                   | Suppress output                                             |

Heartbeat registration

When `--deploy` is provided, the collector registers itself with the walkerOS cloud via periodic heartbeats. This makes the running instance visible in the project dashboard and enables remote management.

```
walkeros run flow.json \
  --deploy dep_abc123 \
  --project proj_def456 \
  --url https://collect.example.com \
  --heartbeat-interval 30
```

The heartbeat sends instance ID, uptime, CLI version, and mode. The server can respond with `update` (triggering a bundle refresh) or `stop` (graceful shutdown).

### Running Pre-Built Bundles[​](#running-pre-built-bundles "Direct link to Running Pre-Built Bundles")

You can also run pre-built bundles directly:

```
# First, bundle
walkeros bundle collect.json

# Then run the bundle
walkeros run dist/flow.mjs --port 8080
```

### Running flow archives[​](#running-flow-archives "Direct link to Running flow archives")

`run` also accepts a `.tar.gz`/`.tgz` flow archive, either a local file or a URL. The CLI fetches or reads the gzip, extracts the bundle and its sibling `node_modules/`, and runs the entry. This lets server flows whose step packages are external resolve those packages at runtime from the extracted `node_modules/`.

```
# Bundle to an archive
walkeros bundle collect.json -o flow.tar.gz

# Run a local archive
walkeros run flow.tar.gz --port 8080

# Run an archive from a URL
walkeros run https://example.com/flow.tar.gz
```

## Complete example: Web → Server flow[​](#complete-example-web--server-flow "Direct link to Complete example: Web → Server flow")

This example demonstrates a complete analytics pipeline:

* Browser events captured by web flow
* Sent to server collection endpoint
* Logged to console (swap for BigQuery in production)

### 1. Create Server Collection Flow[​](#1-create-server-collection-flow "Direct link to 1. Create Server Collection Flow")

Create `server-collect.json`:

```
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "server",
        "bundle": {
          "packages": {
            "@walkeros/server-source-express": {},
            "@walkeros/destination-demo": {}
          }
        }
      },
      "sources": {
        "http": {
          "package": "@walkeros/server-source-express",
          "config": {
            "settings": { "path": "/collect", "port": 8080, "cors": true }
          }
        }
      },
      "destinations": {
        "console": {
          "package": "@walkeros/destination-demo",
          "config": {
            "settings": { "name": "Server Logger" }
          }
        }
      },
      "collector": { "run": true }
    }
  }
}
```

### 2. Create Web Tracking Flow[​](#2-create-web-tracking-flow "Direct link to 2. Create Web Tracking Flow")

Create `web-track.json`:

```
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "web",
        "bundle": {
          "packages": {
            "@walkeros/web-source-browser": {},
            "@walkeros/web-destination-api": {}
          }
        }
      },
      "sources": {
        "browser": {
          "package": "@walkeros/web-source-browser",
          "config": {
            "settings": { "pageview": true, "elb": "track" }
          }
        }
      },
      "destinations": {
        "api": {
          "package": "@walkeros/web-destination-api",
          "config": {
            "settings": {
              "url": "http://localhost:8080/collect",
              "method": "POST"
            }
          }
        }
      },
      "collector": { "run": true }
    }
  }
}
```

### 3. Start Collection Server[​](#3-start-collection-server "Direct link to 3. Start Collection Server")

Terminal 1:

```
walkeros run server-collect.json
```

### 4. Start Web Server[​](#4-start-web-server "Direct link to 4. Start Web Server")

Terminal 2:

```
walkeros run web-track.json --port 3000
```

### 5. Test in Browser[​](#5-test-in-browser "Direct link to 5. Test in Browser")

Create `demo.html`:

```
<!DOCTYPE html>
<html>
<head>
  <script src="http://localhost:3000/walker.js"></script>
</head>
<body>
  <h1 data-elb="promotion" data-elb-promotion="name:Get Started">
    Welcome to walkerOS
  </h1>
  <button data-elbaction="click:cta">Start Tracking</button>

  <script>
    // Manual tracking
    track('custom event', { action: 'demo', value: 42 });
  </script>
</body>
</html>
```

Open in browser. Terminal 1 shows:

```
[Server Logger] page view

[Server Logger] promotion view

[Server Logger] promotion cta

[Server Logger] custom event
```

## Cache command[​](#cache-command "Direct link to Cache command")

The `cache` command manages the CLI's package and build caches.

### Use Case[​](#use-case-5 "Direct link to Use Case")

The CLI caches downloaded npm packages and compiled builds to speed up repeated operations. You may need to:

* Clear stale cached packages when debugging version issues
* Free up disk space by removing old cached builds
* View cache statistics to understand cache usage

### How Caching Works[​](#how-caching-works "Direct link to How Caching Works")

**Package Cache** (`.tmp/cache/packages/`):

* Mutable versions (`latest`, `^`, `~`) are re-checked daily
* Exact versions (`0.4.1`) are cached indefinitely
* Saves network time on repeated builds

**Build Cache** (`.tmp/cache/builds/`):

* Caches compiled bundles based on flow\.json content + date
* Identical configs reuse cached builds within the same day
* Dramatically speeds up repeated builds (\~100x faster)

### Basic Usage[​](#basic-usage-5 "Direct link to Basic Usage")

```
# View cache statistics
walkeros cache info

# Clear all caches
walkeros cache clear
```

### Commands[​](#commands "Direct link to Commands")

**View cache info:**

```
walkeros cache info
```

Output:

```
Cache directory: .tmp/cache

Cached packages: 12

Cached builds: 5
```

**Clear all caches:**

```
walkeros cache clear
```

**Clear only package cache:**

```
walkeros cache clear --packages
```

**Clear only build cache:**

```
walkeros cache clear --builds
```

### Bypassing Cache[​](#bypassing-cache "Direct link to Bypassing Cache")

To skip the cache for a single build operation:

```
walkeros bundle flow.json --no-cache
```

This downloads fresh packages and rebuilds without using or updating the cache.

### Options[​](#options-5 "Direct link to Options")

| Option       | Description                  |
| ------------ | ---------------------------- |
| `--packages` | Clear only the package cache |
| `--builds`   | Clear only the build cache   |

## Global options[​](#global-options "Direct link to Global options")

These options work with all commands:

```
walkeros [command] [options]
```

| Option      | Description                |
| ----------- | -------------------------- |
| `--verbose` | Show detailed logs         |
| `--silent`  | Suppress output            |
| `--json`    | Output as JSON (for CI/CD) |
| `--help`    | Show help for command      |
| `--version` | Show CLI version           |

## Environment variables[​](#environment-variables "Direct link to Environment variables")

| Variable                 | Purpose                                                                                                                                                                    |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WALKEROS_TOKEN`         | API token for cloud commands (overrides `auth login` config)                                                                                                               |
| `WALKEROS_PROJECT_ID`    | Default project ID for `projects`, `flows`, and `deploy` commands                                                                                                          |
| `WALKEROS_APP_URL`       | Base URL override (default: `https://app.walkeros.io`)                                                                                                                     |
| `WALKEROS_DEPLOY_TOKEN`  | Deploy token for container heartbeat authentication                                                                                                                        |
| `WALKEROS_CLIENT_TYPE`   | Override client identity sent to the app. Defaults to `cli`; set to `runner` for long-lived flow runners. Used by the runtime image.                                       |
| `WALKEROS_OBSERVE_LEVEL` | Baseline telemetry level for `walkeros run` (`off`, `standard`, or `trace`). Invalid values are warned about and ignored.                                                  |
| `WALKEROS_CONFIG_FROZEN` | Set to `1` or `true` to make `walkeros run` serve the bundle as an immutable snapshot: secrets are still injected at boot, but config hot-swap and heartbeat are disabled. |

## API version compatibility[​](#api-version-compatibility "Direct link to API version compatibility")

Official clients (CLI, MCP, runner image) send three headers on every request to the walkerOS app so the server can tell them apart and enforce minimum versions per endpoint:

* `User-Agent`: e.g. `walkeros-cli/1.4.0 (node/18.19.0; linux)`
* `X-WalkerOS-Client`: `cli`, `mcp`, or `runner`
* `X-WalkerOS-Client-Version`: the installed package version

If you call an endpoint that has been updated in a way that requires a newer client, the app responds with `426 Upgrade Required`. The CLI prints the required minimum version and the upgrade command, then exits with code 2:

```
Error: This walkerOS app endpoint requires @walkeros/cli >= 1.5.0

(you have 1.4.0). Upgrade with: npm install -g @walkeros/cli@latest

See https://walkeros.io/docs/upgrading for details.
```

The MCP server surfaces the same information in tool error responses so AI assistants can tell the user to upgrade. See [Upgrading](https://www.walkeros.io/docs/upgrading.md) for the full version-negotiation rules.

Set `WALKEROS_CLIENT_TYPE=runner` to identify the CLI binary as a long-lived flow runner instead of an interactive CLI session. The official runner Docker image sets this automatically.

## CI/CD integration[​](#cicd-integration-1 "Direct link to CI/CD integration")

### GitHub Actions[​](#github-actions "Direct link to GitHub Actions")

```
name: Build Flows
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install CLI
        run: npm install -g @walkeros/cli

      - name: Bundle flows
        run: walkeros bundle flow.json --all --stats --json > stats.json

      - name: Upload bundles
        uses: actions/upload-artifact@v3
        with:
          name: flows
          path: dist/
```

### Docker Build[​](#docker-build "Direct link to Docker Build")

The canonical multi-stage Dockerfile installs the CLI in the build stage, bundles the flow into a `dist/` directory, then copies the directory into the runtime image. You do not need to declare step packages in `package.json`; pacote installs them transparently from `flow.json`.

```
FROM node:22.23.0-alpine AS builder
WORKDIR /build
RUN npm init -y && npm install --save-dev @walkeros/cli
COPY flow.json ./
RUN npx walkeros bundle flow.json -o dist/

FROM walkeros/flow:4
WORKDIR /app/flow
COPY --from=builder /build/dist/ ./
ENV PORT=8080
EXPOSE 8080
```

## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting")

### Package Download Issues[​](#package-download-issues "Direct link to Package Download Issues")

If packages fail to download:

```
# Clear cache and retry
walkeros bundle flow.json --no-cache

# Check npm registry access
npm ping
```

### Build Issues[​](#build-issues "Direct link to Build Issues")

If you encounter build issues:

```
# Clear cache and retry
walkeros cache clear

# Retry with verbose output
walkeros bundle flow.json --verbose
```

### Port Already in Use[​](#port-already-in-use "Direct link to Port Already in Use")

If the port is already in use:

```
# Use a different port
walkeros run flow.json --port 8081

# Or kill the process using the port
lsof -ti:8080 | xargs kill
```

### Authentication Issues[​](#authentication-issues "Direct link to Authentication Issues")

If cloud commands fail with authentication errors:

```
# Check current identity
walkeros auth whoami

# Re-authenticate
walkeros auth login

# Or set token directly
export WALKEROS_TOKEN=sk-walkeros-xxx
```

### nft cannot trace a runtime asset[​](#nft-cannot-trace-a-runtime-asset "Direct link to nft cannot trace a runtime asset")

If a runtime asset is missing in `dist/node_modules/` (typically because the dep loads it via a dynamically constructed path), declare it explicitly under the flow's `config.bundle.traceInclude`. Paths and globs both work, resolved against the install root:

```
"flows": {
  "default": {
    "config": {
      "platform": "server",
      "bundle": {
        "packages": { "@walkeros/server-destination-gcp": {} },
        "traceInclude": [
          "node_modules/some-pkg/data/*.json"
        ]
      }
    }
  }
}
```

### Caching pacote downloads in CI[​](#caching-pacote-downloads-in-ci "Direct link to Caching pacote downloads in CI")

The bundler caches downloads under `process.env.NPM_CACHE_DIR` (default `<tmpDir>/cache/npm`). On CI, persist that path with `actions/cache`:

```
- uses: actions/cache@v4
  with:
    path: .walkeros-cache/npm
    key: walkeros-${{ hashFiles('**/flow.json') }}
- run: WALKEROS_TMP_DIR=.walkeros-cache npx walkeros bundle flow.json -o dist/
```

### Bundle output is large[​](#bundle-output-is-large "Direct link to Bundle output is large")

`dist/node_modules/` for a server flow with the GCP destination typically holds 30-50MB across 10k+ files (grpc, protobufjs, etc.). Recommendations:

* Use `.dockerignore` to exclude irrelevant build output from non-bundle contexts.
* Prefer `COPY --from=builder /build/dist/ /app/flow/` over file-by-file in your Dockerfile.
* Expect \~30s artifact upload time on slow CI.

## Next steps[​](#next-steps "Direct link to Next steps")

* **[Flow Configuration](https://www.walkeros.io/docs/getting-started/modes/bundled.md)** - Learn about flow config structure
* **[Docker Deployment](https://www.walkeros.io/docs/apps/docker.md)** - Deploy flows to production
* **[Runner](https://www.walkeros.io/docs/apps/runner.md)** - Self-hosted runner with config polling and hot-swap
* **[MCP Server](https://www.walkeros.io/docs/apps/mcp.md)** - Use CLI tools from AI assistants
* **[Sources](https://www.walkeros.io/docs/sources.md)** - Explore event sources
* **[Destinations](https://www.walkeros.io/docs/destinations.md)** - Configure analytics tools
* **[Mapping](https://www.walkeros.io/docs/mapping.md)** - Transform events for destinations

***

## See Also[​](#see-also "Direct link to See Also")

**Using Integrated mode instead?** The CLI uses JSON configuration (Bundled mode). If you prefer TypeScript and want the collector built into your application, see [Integrated Mode](https://www.walkeros.io/docs/getting-started/modes/integrated.md) and the [Collector documentation](https://www.walkeros.io/docs/collector.md).

Both approaches use the same underlying architecture. The difference is how you configure and deploy.
