# CLI reference

> Every retoque command, flag, file and exit code, and what install checks before it touches anything.

Source: https://retoque.xyz/docs/cli
Markdown: https://retoque.xyz/docs/md/cli

The `retoque` CLI verifies, installs and switches patches. It never modifies base weights, never executes anything from a package, and never loads pickled data. This page describes version 0.1.0.

## Install

Python 3.11 or newer.

```sh
pip install retoque                 # core: verify, install, enable, disable, rollback
pip install 'retoque[runtime]'      # adds torch, transformers, peft, safetensors
```

The core package depends only on `pynacl>=1.5.0`. The runtime extra requires `torch>=2.5`, `transformers>=4.46`, `peft>=0.13` and `safetensors>=0.4.5`. Every command except `run` works without it.

## Global options

| Option | Meaning |
| --- | --- |
| `--version` | Print `retoque 0.1.0` and exit. |
| `--api-url URL` | Registry base URL for this call. Overrides `RETOQUE_API_URL`, the config file and the default `http://localhost:3400`. |
| `-h`, `--help` | Help for the CLI or any subcommand. |

Global options go before the command: `retoque --api-url https://<registry host> models list`.

## Commands

### login

```sh
retoque login [--key rtq_...] [--no-verify]
```

Stores a Retoque registry key. Without `--key` it prompts without echoing. The key must start with `rtq_`. Unless you pass `--no-verify`, it calls `GET /api/v1/me` and prints the email and organization the key belongs to. The key and the registry URL are written to `~/.config/retoque/credentials.json` with mode `0600`, in a directory with mode `0700`.

### logout

```sh
retoque logout
```

Deletes the stored credentials file. It does not revoke the key on the server. Revoke it in your workspace under API keys.

### models list

```sh
retoque models list [--json]
```

Lists the base models the registry supports, one per line: slug, `repository@revision` and license. `--json` prints the raw items.

### patches search

```sh
retoque patches search [query] [--model ref] [--limit N] [--cursor C] [--json]
```

Searches published, public patches by title and problem text. Prints `slug@latestVersion`, title and base model for each result.

> [!warning] **Known limitations in 0.1.0.** The registry filters `baseModel` by model slug, such as `qwen2.5-1.5b-instruct`, but the CLI only accepts and sends an `org/name@<commit>` reference, so `--model` currently returns no results. The registry also accepts page sizes of 10, 25 or 50 only, so the default `--limit 20` returns 10 results, and it pages by number, not cursor, so `--cursor` has no effect. Use the query text, or the [REST API](https://retoque.xyz/docs/api#get-api-v1-patches) directly.

### patches inspect

```sh
retoque patches inspect <slug>@<version> [--json]
```

Shows registry metadata for one version: title and status, version state and evidence state, base model, adapter type, rank, alpha and sha256, target modules, manifest and package sha256, and the held-out baseline, patched and introduced-regression counts. It reminds you that registry metadata alone is not proof. Nothing is verified by this command.

### verify

```sh
retoque verify <slug>@<version>
retoque verify ./<slug>-<version>.tar
```

Checks a version without installing it:

- A path to a `.tar` file is extracted into a private staging directory and fully verified offline.
- `slug@version` that is installed is re-verified offline from `~/.retoque/patches`.
- `slug@version` that isn't installed fetches the version's manifest and signature from the registry, recomputes the canonical manifest sha256, checks the ed25519 signature against your trusted keys, and checks that the signed manifest is for the requested slug and version. The package bytes are checked against the listed `packageSha256` when you install.

On success it prints the publisher key, manifest sha256, adapter sha256 and base model, and the line "verified: trusted publisher signature and exact bytes. Not verified: output quality."

### install

```sh
retoque install <slug>@<version> --model 'org/name@<full commit>' [--package-file ./pkg.tar] [--experimental-override]
```

| Flag | Meaning |
| --- | --- |
| `--model` | Required. The exact base model as `org/name@<full 40-character commit>`. |
| `--package-file` | Install from a local package `.tar` instead of downloading. No registry key is needed unless telemetry is on. |
| `--experimental-override` | Allow an `UNKNOWN_UNTESTED` or `INCOMPATIBLE` preflight, and a tokenizer or chat template mismatch, for non-production experiments. Recorded in `retoque.lock`. |

Installing makes the new version active and enabled. If the same version is already installed with an identical manifest, it is reactivated. The steps are listed under [What install does](#what-install-does).

### enable and disable

```sh
retoque disable <slug>
retoque enable <slug>
```

`disable` keeps the files and records the patch as disabled in `~/.retoque/state.json`, so `run` uses the pure base model. `enable` turns the active version back on. Neither command touches weights or the lockfile.

### rollback

```sh
retoque rollback <slug> --to <version>
```

Switches the active version to another version that is already installed, re-verifies its signature and file hashes, enables it, and re-pins `retoque.lock`. The version you switched away from is added to the history. If the target isn't installed, it lists the installed versions and changes nothing.

### status

```sh
retoque status [--json]
```

Lists installed patches with the active version, enabled or disabled, all installed versions, the active adapter sha256, the base model, any experimental override, the switch history, and whether a `retoque.lock` exists in the current directory.

### doctor

```sh
retoque doctor
```

Runs local checks and prints `[  ok]`, `[warn]` or `[fail]` for each:

| Check | Fails or warns when |
| --- | --- |
| `python` | Python is older than 3.11 (fail). |
| `config` | Reports the config path and how many trusted keys are loaded. |
| `credentials` | The credentials file isn't mode `0600` (fail), or you're not logged in (warn). |
| `home` | `RETOQUE_HOME` isn't writable (fail). |
| `patch <slug>` | The active version fails verification (fail). |
| `base <repository>` | The base snapshot isn't in the local Hugging Face cache (warn). |
| `runtime extra` | torch, transformers or peft can't be imported (warn). |
| `registry` | The registry can't be reached (warn). |

It exits with code 1 if any check fails.

### run

```sh
retoque run --patch <slug>[@<version>] --input task.json [--no-patch]
```

Generates once with the pinned base model and, if the patch is enabled, the verified adapter. Needs the runtime extra.

- The input is a JSON object with `user` and an optional `system`, or a chat `messages` list, plus an optional `max_new_tokens` (default 128).
- The adapter directory is re-verified right before loading.
- The base is loaded at the manifest's revision with `trust_remote_code=False`, `use_safetensors=True` and float32, on Apple MPS when available and otherwise CPU. If the snapshot is cached, its tokenizer and chat template digests are checked against the manifest.
- Decoding is greedy. The output goes to stdout, and a line naming the base model and the active adapter sha256, or "none, pure base" with the reason, goes to stderr.
- `--no-patch` runs the pure base without changing state. Naming a version with `slug@version` runs that installed version with its adapter even if the patch is disabled.

Qwen2.5-1.5B in float32 needs about 7 GB of free memory. The CLI doesn't make inference faster. If your machine can't hold the model, run it on a GPU host you control.

## What install does

Steps run in this order. Any failure aborts before the install directory, `state.json` or the lockfile is touched.

1. **Preflight.** Compare `--model` with the manifest: `SUPPORTED_EXACT`, `UNKNOWN_UNTESTED` or `INCOMPATIBLE`. Only an exact match continues without `--experimental-override`.
2. **Ticket.** Request a single-use install ticket from `POST /api/v1/patches/<slug>/versions/<version>/install-tickets`. This needs an active license for the version.
3. **Download.** Stream the package into a private staging directory under `~/.retoque/tmp`, without sending your registry key, and check its sha256 against the ticket's `packageSha256`. The registry's own version record must agree with the ticket.
4. **Extract.** Only the eight allowlisted regular files are written: `adapter_config.json`, `adapter_model.safetensors`, `README.md`, `LICENSE.txt`, `eval-report.json`, `retoque.manifest.json`, `signature.json`, `checksums.txt`. Symlinks, hard links, devices, absolute paths, `..`, duplicates, unknown names, members over 2 GiB and totals over 4 GiB are rejected. `tarfile.extract` is never used.
5. **Signature.** Verify the ed25519 signature over sha256(`retoque.manifest.json`) with a trusted publisher key, and check that the manifest is for the requested slug and version.
6. **Files.** Check every `manifest.files` entry by sha256 and size, then every line of `checksums.txt`.
7. **Adapter config.** Check that `adapter_config.json` agrees with the manifest on base model, rank and target modules.
8. **Base snapshot.** If the base is in the local Hugging Face cache, check `tokenizer.json` and the chat template against the manifest digests.
9. **Place.** Make the files read-only, rename the directory into `~/.retoque/patches/<slug>/<version>/`, update `~/.retoque/state.json`, and write `./retoque.lock`.

## Files

| Path | Contents |
| --- | --- |
| `~/.config/retoque/config.json` | Optional settings. See [Configuration](#configuration). |
| `~/.config/retoque/credentials.json` | Registry key and URL, mode `0600`. |
| `~/.retoque/patches/<slug>/<version>/` | Installed, read-only package files. |
| `~/.retoque/state.json` | Active version, enabled flag, history and install records per patch. |
| `~/.retoque/tmp/` | Staging for downloads and verification. Removed after each command. |
| `./retoque.lock` | Exact digests pinned per patch for the current project. |

### retoque.lock

```json
{
  "lockfileVersion": 1,
  "patches": {
    "ledger-tool-call": {
      "slug": "ledger-tool-call",
      "version": "1.0.0",
      "packageSha256": "<64 hex>",
      "manifestSha256": "<64 hex>",
      "adapterSha256": "<64 hex>",
      "publisherKey": "<64 hex>",
      "baseModel": {
        "repository": "Qwen/Qwen2.5-1.5B-Instruct",
        "revision": "989aa7980e4cf806f80c7fef2b1adb7bc71aa306",
        "tokenizerSha256": "<64 hex>"
      },
      "requestedModel": "Qwen/Qwen2.5-1.5B-Instruct@989aa7980e4cf806f80c7fef2b1adb7bc71aa306",
      "preflight": "SUPPORTED_EXACT",
      "experimentalOverride": false,
      "source": "registry",
      "installedAt": "<ISO 8601>"
    }
  }
}
```

## Configuration

```json
{
  "apiBaseUrl": "https://<registry host>",
  "trustedKeys": ["<64 hex chars ed25519 public key>"],
  "telemetry": false
}
```

All three keys are optional. The registry URL is chosen in this order: `--api-url`, `RETOQUE_API_URL`, `apiBaseUrl`, then `http://localhost:3400`. Only `https` URLs, or `http` on `localhost`, `127.0.0.1` and `::1`, are accepted.

| Variable | Default | Purpose |
| --- | --- | --- |
| `RETOQUE_API_URL` | config value | Registry base URL. |
| `RETOQUE_HOME` | `~/.retoque` | Installed patches, staging and `state.json`. |
| `RETOQUE_CONFIG_DIR` | `~/.config/retoque` | `config.json` and `credentials.json`. |
| `HF_HUB_CACHE`, `HF_HOME` | `~/.cache/huggingface/hub` | Where to find the base model snapshot. |

The first-party Retoque publisher key, `c7284ac2fd0b659098d1d7498446b14bba572d2d6e10d96c2e4e20acd41928f1`, is pinned in the client. More keys can only be added through `trustedKeys` in your own config file, never by the registry.

Install, enable, disable and rollback report an event to the registry only when `telemetry` is `true` and you are logged in. The event carries the slug, version, adapter sha256, base model reference, runtime name, state and client version.

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Success. |
| `1` | General error: network failure, bad input, invalid slug or version, patch not installed, expired download link, or a failed `doctor` check. Also used when interrupted. |
| `2` | Refused: a signature, digest, package layout or compatibility check failed. Nothing was installed or changed. The argument parser also exits with 2 on a usage error, such as a missing `--model`. |
| `3` | Not logged in, key rejected, or no license for the version. |

Error messages go to stderr prefixed with `error:`. See [Errors and troubleshooting](https://retoque.xyz/docs/errors) for fixes.
