# CLI Reference
> For guides, recipes, and getting started, see [lmn.talek.cloud](https://lmn.talek.cloud).
## Subcommands
- [`run`](#run) — Execute a load test
- [`configure-request`](#configure-request) — Store a reusable request template
- [`configure-response`](#configure-response) — Store a reusable response template
---
## `run`
Execute a load test against a target host.
```
lmn run [OPTIONS] -H <HOST>
```
### Flags
| `-H` | `--host` | required | Target host URL |
| `-R` | `--request-count` | `100` | Total number of requests to send |
| `-C` | `--concurrency` | `10` | Max in-flight requests at any time |
| `-M` | `--method` | `get` | HTTP method (`get`, `post`, `put`, `patch`, `delete`) |
| `-B` | `--body` | — | Inline JSON request body |
| `-T` | `--request-template` | — | Path to a request template file |
| `-A` | `--request-alias` | — | Alias of a stored request template |
| `-S` | `--response-template` | — | Path to a response template file |
| `-E` | `--response-alias` | — | Alias of a stored response template |
| `-L` | `--load-curve` | — | Path to a load curve JSON file (time-based VU scaling mode) |
| — | `--output` | `table` | Output format: `table` (default) or `json` |
| — | `--output-file` | — | Write JSON result to `<path>` (always JSON regardless of `--output`) |
| `-f` | `--config` | — | Path to a YAML config file. CLI flags take precedence over config values. |
| — | `--header` | — | Custom HTTP header in `'Name: Value'` format (repeatable) |
### Conflicts
- `-B`, `-T`, `-A` are mutually exclusive (only one request body source allowed)
- `-S` and `-E` are mutually exclusive (only one response template source allowed)
- `-L` conflicts with `-R` and `-C` (curve mode is time-based, not count-based)
### Custom Headers
Use `--header` (repeatable) to attach static HTTP headers to every request:
```bash
lmn run -H http://localhost:3000/api --header 'Authorization: Bearer mytoken' --header 'X-Request-ID: abc123'
```
Headers can also be set in the config file under `run.headers`:
```yaml
run:
host: https://api.example.com
headers:
Authorization: "Bearer ${API_TOKEN}"
X-Custom-Header: "static-value"
```
**Precedence:** CLI `--header` wins over config `headers:` on the same key (case-insensitive match). Duplicate entries for the same key are removed before the CLI value is added.
**Secret management:** Use `${ENV_VAR}` syntax in header values to avoid hardcoding secrets. The variable must use uppercase letters, digits, and underscores only. A `.env` file in the working directory is loaded automatically at startup (silently ignored if absent). If the referenced variable is not set, lmn exits with an error.
```bash
# .env file
API_TOKEN=my-secret-token
# config file
run:
headers:
Authorization: "Bearer ${API_TOKEN}"
```
A startup warning is printed to stderr when a header with a security-sensitive name (e.g. `Authorization`, `X-Api-Key`) contains a plain string value longer than 4 characters without `${` — this is a reminder to use env var substitution instead.
### Exit Codes
| `0` | Run completed successfully; all thresholds satisfied (or no thresholds configured) |
| `1` | Run error — invalid arguments, unreachable host, config parse failure, or I/O error |
| `2` | Run completed but one or more threshold rules were not satisfied |
Exit code `2` is only possible when `--config`/`-f` is supplied with a YAML file that contains a `thresholds` section.
### Config File Format
When `--config`/`-f` is supplied, lmn loads a YAML file before the run. CLI flags always take precedence over values in the config file.
**Supported config fields:**
Run parameters are nested under a `run:` section. Execution strategy is configured under `execution:`. The `thresholds:` section is top-level.
**`run:` section**
| `host` | string | `-H` / `--host` | Target host URL |
| `method` | string | `-M` / `--method` | HTTP method (`get`, `post`, `put`, `patch`, `delete`) |
| `output` | string | `--output` | Output format (`table` or `json`) |
| `output_file` | string | `--output-file` | Path to write JSON report |
| `headers` | map | `--header` | Static HTTP headers sent with every request (key: value pairs) |
**`execution:` section**
| `request_count` | number | `-R` / `--request-count` | Total requests to send (fixed mode) |
| `concurrency` | number | `-C` / `--concurrency` | Max in-flight requests (fixed mode) |
| `stages` | list | `-L` / `--load-curve` | Load curve stages (curve mode — cannot be combined with `request_count`/`concurrency`) |
When `execution.stages` is present, lmn runs in curve mode. Otherwise it runs in fixed mode using `execution.request_count` and `execution.concurrency`.
**Threshold rule fields:**
| `metric` | string | One of: `error_rate`, `throughput_rps`, `latency_min`, `latency_avg`, `latency_p50`, `latency_p75`, `latency_p90`, `latency_p95`, `latency_p99`, `latency_max` |
| `operator` | string | One of: `lt`, `lte`, `gt`, `gte`, `eq` |
| `value` | number | Threshold value to compare against |
**Fixed-mode example:**
```yaml
run:
host: https://httpbin.org
method: post
output: table
execution:
request_count: 500
concurrency: 50
thresholds:
- metric: error_rate
operator: lt
value: 0.01
- metric: latency_p99
operator: lt
value: 500.0
```
**Curve-mode example:**
```yaml
run:
host: https://httpbin.org/post
method: post
execution:
stages:
- duration: 30s
target_vus: 5
ramp: linear
- duration: 1m
target_vus: 20
ramp: linear
- duration: 30s
target_vus: 0
ramp: linear
thresholds:
- metric: error_rate
operator: lt
value: 0.005
- metric: latency_p95
operator: lt
value: 2000.0
```
### Output Behaviour Matrix
| `table` (default) | not set | ASCII table | progress | — |
| `json` | not set | JSON document | progress | — |
| `table` | set | ASCII table | progress | JSON document |
| `json` | set | JSON document | progress | JSON document (same content) |
- When `--output json` and no `--output-file`: JSON goes to stdout; ASCII table is suppressed.
- When `--output-file` is set: JSON is always written to the file regardless of `--output`. This allows `--output table --output-file run.json` for users who want both a readable terminal table and a machine-readable artifact.
- All run-time messages (shutdown notice, errors) always go to stderr.
### Examples
```bash
# Inline body
lmn run -H http://localhost:3000/api -M post -B '{"name":"test"}'
# From a template file with placeholders
lmn run -H http://localhost:3000/api -M post -T ./my-template.json
# From a stored request alias
lmn run -H http://localhost:3000/api -M post -A my-alias
# With a stored response template to track response fields
lmn run -H http://localhost:3000/api -A my-alias -E my-response
# Full example
lmn run -H http://localhost:3000/api -M post -R 1000 -C 50 -A my-alias -E my-response
# Load curve (time-based VU scaling)
lmn run -H http://localhost:3000/api -M post -L ./my-curve.json
# Emit JSON result to stdout instead of ASCII table
lmn run -H http://localhost:3000/api --output json
# Emit ASCII table to terminal AND write JSON artifact to a file
lmn run -H http://localhost:3000/api --output-file run.json
# Both JSON to stdout and to file
lmn run -H http://localhost:3000/api --output json --output-file run.json
# Custom headers (repeatable)
lmn run -H http://localhost:3000/api --header 'Authorization: Bearer mytoken' --header 'X-Trace-ID: 123'
# Using env var substitution for secrets (API_TOKEN must be set)
lmn run -H http://localhost:3000/api --header 'Authorization: Bearer ${API_TOKEN}'
```
---
## Load Curve JSON Format
When using `-L`/`--load-curve`, provide a JSON file with the following structure:
```json
{
"stages": [
{ "duration": "30s", "target_vus": 10 },
{ "duration": "1m", "target_vus": 50, "ramp": "linear" },
{ "duration": "30s", "target_vus": 0, "ramp": "step" }
]
}
```
### Stage Fields
| `duration` | string | yes | — | Stage duration: `"30s"`, `"2m"`, `"1m30s"` |
| `target_vus` | number | yes | — | Target virtual user count at end of stage |
| `ramp` | string | no | `"linear"` | Ramp type: `"linear"` or `"step"` |
### Ramp Types
- `linear` — Smoothly interpolates VU count from the previous stage's target to this stage's `target_vus` over the stage duration.
- `step` — Immediately jumps to `target_vus` at the start of the stage.
---
## `configure-request`
Store a reusable request body template under an alias.
Templates are saved to `.templates/requests/<alias>.json`.
```
lmn configure-request -A <ALIAS> [OPTIONS]
```
### Flags
| `-A` | `--alias` | Template alias (required) |
| `-B` | `--body` | Inline JSON body to store |
| `-T` | `--template-path` | Path to an existing JSON file to store |
### Conflicts
- `-B` and `-T` are mutually exclusive
### Examples
```bash
# Store an inline body
lmn configure-request -A my-alias -B '{"name":"test"}'
# Store from an existing file
lmn configure-request -A my-alias -T ./payload.json
```
---
## `configure-response`
Store a reusable response template under an alias.
Templates are saved to `.templates/responses/<alias>.json`.
```
lmn configure-response -A <ALIAS> [OPTIONS]
```
### Flags
| `-A` | `--alias` | Template alias (required) |
| `-B` | `--body` | Inline JSON body to store |
| `-T` | `--template-path` | Path to an existing JSON file to store |
### Conflicts
- `-B` and `-T` are mutually exclusive
### Examples
```bash
# Store an inline response shape
lmn configure-response -A my-response -B '{"status":"ok","id":0}'
# Store from an existing file
lmn configure-response -A my-response -T ./response-shape.json
```
---
## Alias Resolution
When using `--request-alias` or `--response-alias`, the name resolves to:
```
.templates/requests/<alias>.json # for request aliases
.templates/responses/<alias>.json # for response aliases
```
The `.json` extension is optional — both `my-alias` and `my-alias.json` are accepted.