Skip to content
## [1.2] — 2026-03-04

### New Features

#### Dynamic Node Provisioning from Suites (`node_compose`, `node_compose_gen`)

Suites can add Cellframe nodes on top of the base topology without manually
editing Docker Compose.

`node_compose` — explicit per-node definitions:

```yaml
node_compose:
  vpn-server-1:
    role: full
    source: genesis         # genesis | node-<id> | fresh
    packages: "iperf3 tcpdump"
    setup:
      - cli: "srv_vpn enable"
        node: vpn-server-1
```

`node_compose_gen` — parametric template-based generation:

```yaml
node_compose_gen:
  - prefix: vpn-client
    count: "{{vpn_client_count}}"
    role: full
    source: genesis
    packages: "iperf3"
```

Expands to `vpn-client-1`, `vpn-client-2`, ...; IDs are assigned automatically
after the last node in the base topology.

#### Arbitrary Docker Services (`docker_compose`)

Embed third-party containers (iperf, redis, grafana, etc.) directly
in the suite descriptor:

```yaml
docker_compose:
  iperf-server:
    image: networkstatic/iperf3
    command: ["-s"]
    networks:
      vpntest:
        ipv4_address: 172.22.0.100
```

#### Flexible Includes with Inline Variable Overrides

`includes` now accepts both file paths and inline data blocks, allowing
variable overrides between file inclusions:

```yaml
includes:
  - ../common/vpn_defaults.yml       # vpn_client_count: 3
  - variables:                        # override
      vpn_client_count: 5
  - ../common/vpn_nodes.yml          # uses 5
```

#### Self-Contained Cert-Generator

Multi-stage `Dockerfile.cert-generator` clones SDK repos at build time.
No host SDK mounts required at runtime — the binary is baked into the image.
Configurable via `SDK_BRANCH`, `CELLFRAME_SDK_REPO`, `DAP_SDK_REPO`.
`CMakeLists.txt` simplified: links against `cellframe_sdk` and `dap-sdk`
shared libs instead of manually listing 15+ static libraries.

#### Glob Patterns for Local Packages

`local_path` in config now supports shell globs (`*.deb`, `build-*/*.deb`).
Picks the latest match (sorted alphabetically) for deterministic results.

#### JSON CLI Output Parsing (SDK 6.0+)

`NodeStateManager` and `DockerComposeManager` parse JSON status format.
Falls back to text format (5.x) on JSON parse failure — compatible
with both SDK 5.x and 6.0+.

#### Auto-Detection of Genesis Scenario Version

The framework detects the Cellframe Node major version from the DEB package
and automatically selects `v5.x/` or `v6.x/` genesis scenarios.
Variables `cf_version_major`, `cf_version_minor` are available at runtime.

#### Dynamic Network Name

Removed hardcoded `stagenet`. `WaitForDatumStep.network` and
`NetworkWaitStep.network` default to `"{{network_name}}"` — the name
is taken from configuration.

#### New `docker_op` Operations

- `pause` / `unpause` — freeze/unfreeze a container to simulate connectivity loss
- `logs` — retrieve logs with `tail:` (line count) and `save:` (into a variable)
- `exec` — execute a command inside a container with `save:`

```yaml
- docker_op: pause
  services: "node-1"

- docker_op: logs
  services: "node-1"
  tail: 50
  save: node_logs

- docker_op: exec
  services: "node-1"
  command: "pgrep cellframe-node"
  save: pid_output
```

#### `snapshot` Extensions

- `verify` operation — checks if a snapshot exists without raising an exception
- `save:` field — stores the result (`True`/`False`) in a variable

```yaml
- snapshot: verify
  name: cdb_ready
  save: has_cdb_snapshot
```

#### `wait` Extensions

- `group:` / `key:` — wait for GlobalDB sync by group/key
- `optional: true` — timeout does not raise an exception (for retry loops)

#### `dpkg` Operand

Install DEB packages on nodes directly from a scenario:

```yaml
- dpkg: /path/to/package.deb
  nodes: ["node-1", "node-2"]
```

#### Custom Operands (`CustomOperandStep`)

Extensible operand system — register new operands via `operand_registry`
without modifying the framework core.

#### `StepGroup`: `group:` / `name:` Alias

Both keys are equivalent for naming a group:

```yaml
- group: "Verify certs"    # OK
- name: "Verify certs"     # also OK
```

#### Regex in `contains`

CLI checks support regular expressions in `contains`:

```yaml
- cli: "block list"
  contains: "have blocks: [1-9]"
```

### Bug Fixes

- **eval() Namespace Unification** — variables and builtins merged into a single
  dict for `eval()`. Expressions like `len(items) == 3` and `range(max_nodes)`
  now work correctly
- **CLI Parser Always Initialized** — defaults applied even without
  `global_defaults.cli`
- **CLI Defaults Applied Without Options Whitelist** — defaults added
  for commands not in the help cache
- `expect_output:` (silently ignored by Pydantic) replaced with `contains:`
  across all genesis/e2e scenarios
- `for: { var: x, in: [...] }` syntax fixed to correct
  `for: x` / `in: [...]`
- `expect:` removed from `check:` sections (CLICheck does not support this field)
- `ToolStep` — added variable substitution for `node` (`ctx.substitute`)
- `Validator` — fixed handling of `SectionConfig`/`CheckSectionConfig`,
  added guard for `SuiteDescriptor`
- CLI check executor — catches `asyncio.TimeoutError` instead of
  `subprocess.TimeoutExpired`
- RPC step — `ctx.substitute()` called only for string parameters
- Bash step — guard against `None` timeout (fallback to 300s)
- Boolean value normalization in configs

### Configuration Templates

Reorganized for multi-version support:

```
config/templates/
├── v5.x/cellframe-node.cfg.j2
├── v6.x/cellframe-node.cfg.j2
├── network.cfg.j2             (formerly stagenet.cfg.j2)
└── chains/
    ├── main.cfg.j2
    └── zerochain.cfg.j2
```