# V3 packages (beta)

!!! danger "Beta — not for production use"
    V3 is an **opt-in preview** of the next conda repodata revision and is
    still under active development. The on-disk repodata format, the
    `index.json` layout, recipe fields (`build.flags`, `requirements.extras`,
    …) and the `MatchSpec` bracket syntax described on this page **can and
    will change in backwards-incompatible ways** before V3 is finalized.
    Concretely, this means:

    - Packages built with today's `--v3` may stop being parseable by a
      future rattler-build / rattler / pixi without a rebuild.
    - The `repodata_revision: 3` shape is **not stable**; the field name,
      value, and surrounding schema may all change.
    - Recipes using V3-only features may need edits when the syntax
      stabilizes.

    **Do not** publish V3 packages to channels that other users or CI
    systems depend on. Use V3 only for experimentation, prototyping, and
    feedback — and expect to rebuild from source when the format
    stabilizes. Track the discussion in the relevant CEPs and the
    [rattler-build issue tracker](https://github.com/prefix-dev/rattler-build/issues)
    before depending on it.

V3 is a new revision of the conda repodata format. It extends the existing
package record with two new fields and adds a richer `MatchSpec` bracket
syntax that lets you express conditional, optional and variant-tagged
dependencies. Rattler-Build can produce V3 packages and parse V3 recipes,
but only when the feature is explicitly enabled.

## Opting in

V3 is gated behind a single flag. There are two ways to turn it on:

- Pass `--v3` on the command line:

    ```bash
    rattler-build build --v3 --recipe recipe.yaml
    ```

- Or use the equivalent option from the Python bindings / library API
  (`CommonData::new(..., v3: true, ...)`, `RenderConfig::new().with_v3(true)`,
  `ParseConfig { v3: true }`).

When the flag is **off** (the default), Rattler-Build behaves exactly like
before:

- Recipes that use V3-only fields (`build.flags`, `requirements.extras`) fail
  to evaluate with an error pointing at `--v3`.
- `MatchSpec` strings that use V3-only bracket keys (`flags=[…]`,
  `extras=[…]`, `when="…"`) fail to parse with the same error.
- Built packages get the legacy `index.json` shape — there is no
  `repodata_revision` field and no `flags` array.

When the flag is **on**:

- The new recipe fields are accepted and round-tripped through Stage 0/Stage 1
  evaluation.
- V3 `MatchSpec` syntax is parsed everywhere a dependency string is allowed
  (`requirements.{build,host,run,run_constraints,extras}`, `run_exports`).
- Built packages are tagged with `repodata_revision: 3` in `index.json` and
  carry their declared variant flags in a top-level `flags` array. Optional
  dependency groups are written into the `extra_depends` mapping.

## What V3 adds

### `build.flags` — package variant flags

Flags are short lowercase tags that describe a *variant* of a package — the
canonical examples are `cuda`, `blas:openblas`, `mpi:mpich`. They are stored
on the package record and can be matched by downstream specs (see below).

```yaml
build:
  flags:
    - cuda
    - blas:openblas
```

A flag must match `^[a-z0-9_]+(:[a-z0-9_]+)?$` — i.e. lowercase ASCII,
digits and underscores, optionally with a single `:`-separated suffix.
Flags are conditional and templated like other recipe lists, so this is
valid:

```yaml
build:
  flags:
    - if: cuda_compiler_version != "None"
      then: cuda
    - blas:${{ blas_impl }}
```

Flags that depend on a Jinja variable participate in variant hashing, so
each flag combination produces its own build hash.

### `requirements.extras` — optional dependency groups

`extras` is a mapping from a group name to a list of dependencies. The group
is *not* installed by default — consumers opt in via the `extras=[…]`
`MatchSpec` bracket key. This mirrors the "optional dependencies" idea from
`pyproject.toml`.

```yaml
requirements:
  run:
    - python >=3.10
  extras:
    plot:
      - matplotlib >=3.8
    full:
      - matplotlib >=3.8
      - pandas >=2
```

A consumer that wants the `plot` group writes:

```yaml
requirements:
  run:
    - mypkg[extras=[plot]]
```

In the built package, optional groups land in
`index.json::extra_depends`.

### Richer `MatchSpec` syntax

V3 extends the bracket form of `MatchSpec` with three new keys. They can be
combined with the existing keys (`version=…`, `build=…`, `channel=…`, …).

#### `flags=[…]` — match by variant flag

Selects builds whose `flags` array contains the listed entries. A `*`
suffix is a glob over the second segment.

```yaml
requirements:
  run:
    - pytorch[flags=[cuda]]
    - numpy[flags=[blas:*]]          # any BLAS flavour
    - mamba[flags=[cuda, blas:openblas]]
```

#### `when="…"` — conditional dependencies

A spec with `when="…"` is only included when the embedded predicate is
satisfied by the rest of the resolved environment. This replaces the old
`; if` syntax (which is now deprecated).

```yaml
requirements:
  run:
    - python
    - scipy[when="python >=3.10"]
    - numpy >=2.0[when="python >=3.10 and __linux"]
```

The predicate is itself a small spec language and supports `and`, `or`,
`not`, parentheses, virtual packages (`__linux`, `__glibc`, …) and nested
`MatchSpec`s.

#### `extras=[…]` — pull in optional groups

Already shown above — uses the group names declared by the producing
package's `requirements.extras`.

```yaml
requirements:
  run:
    - mypkg[extras=[plot, full]]
```

## What ends up in `index.json`

A V3 build writes (in addition to the existing fields):

```json
{
  "repodata_revision": 3,
  "flags": ["cuda", "blas:openblas"],
  "extra_depends": {
    "plot": ["matplotlib >=3.8"]
  }
}
```

`flags` and `extra_depends` are omitted when empty. The `repodata_revision`
field is the signal that downstream tools should use the V3 parser; it is
only written when the build was run with `--v3`.

## Compatibility notes

- V3 packages are still valid `.conda` archives — they only differ in
  metadata. Older clients that ignore unknown fields will still install
  the base package, but will silently miss conditional dependencies,
  flag-based selection and optional groups.
- Consuming a V3 package correctly requires a resolver that understands
  V3 (recent `rattler` / `pixi` / `conda` versions). Check your toolchain
  before publishing.
- Mixing V3 syntax in a recipe without passing `--v3` is always an error —
  it never silently downgrades.
