# The CLI

> The colis command line, in French: envoyer a deliverable to your delivery page, follow it with statut, recevoir and annuler, and verifier that a bucket or a server is really ready. Settings in a committable file, keys out of it.

Canonical: https://colis-site.vercel.app/en/cli · Markdown: https://colis-site.vercel.app/en/cli.md · Français: https://colis-site.vercel.app/cli

colis envoyer sends a file and prints its code; colis statut says whether your client opened it, approved it or asked for changes. The commands are in French, and put, get, rm and doctor still work as aliases.

## One npm command, nothing to configure.
`npm i -g @mdemb/colis` installs the `colis` command. Node 20 or later. With nothing configured, `colis envoyer` sends to the public colis service — files up to 100 MB, kept 24 hours at most, 20 uploads an hour from one address — and `colis statut` follows the delivery. For your own storage, configure a bucket or point it at your own delivery page.

```sh
npm i -g @mdemb/colis

# nothing to configure: the public colis service
colis envoyer ./maquette.pdf
colis statut <CODE>
```

## The code on stdout, the answer on demand.
Against your delivery page, every parcel gets a receipt. statut prints the state on its first line, then each step with its time, and the client’s comment last.

```sh
$ colis envoyer ./maquette-v2.pdf
maquette-v2.pdf · 1.8 MB · expires in 1 day
K7QP2M4X

$ CODE=$(colis envoyer ./export.zip)        # the code on stdout, the rest on stderr
$ tar cz ./site | colis envoyer - --name site.tar.gz

$ colis statut K7QP2M4X
ouvert
envoyé  2026-09-22 10:00
ouvert  2026-09-22 11:00
answer  not yet

$ colis statut K7QP2M4X | head -1           # the state alone, for a script
ouvert

$ colis envoyer ./maquette-v3.pdf --remplace K7QP2M4X   # changes asked: same code, same link
maquette-v3.pdf · 1.9 MB · expires in 1 day
Version 2 envoyée sur K7QP2M4X
K7QP2M4X
```

`statut` asks a delivery page: the receipt is kept there, not in the bucket. With nothing configured, that is the public service. With a bucket and no `--remote`, statut says so, says whether the file is still there, and exits non-zero. `--json` gives the whole receipt, and a parcel with a password wants it too, with `--token`.

**recevoir and annuler**
```sh
$ colis recevoir k7qp-2m4x                 # lower case, a dash: still found
Wrote /home/you/maquette-v2.pdf · 1.8 MB

$ colis recevoir K7QP2M4X -o - | less       # or to stdout
$ colis annuler K7QP2M4X                    # burn it before it expires
Burned K7QP2M4X
```

## The command worth running first.
Pointed at a server, verifier makes a real round trip. Pointed at a bucket, it performs the operations colis needs and reports what happened, lifecycle rule included. The probe is deleted before it returns.

**a starter for your delivery page**
```sh
$ colis init --provider remote
Wrote /home/you/client-acme/colis.config.json

Point "remote" at your deployment of the transfer routes.
Put its token in .env as COLIS_TOKEN. This machine needs no S3 credentials at all.
Run `colis verifier` — it performs the operations colis needs and reports what happened.
```
**and the check that proves it answers**
```sh
$ colis verifier
Using /home/you/client-acme/colis.config.json
✓ Server: https://livraison.example.com/api/transfers answered
✓ Create, read, delete: round-tripped code 8WTXQC8R

Ready to store transfers.
```
**straight against a bucket**
```sh
$ colis -p r2 verifier
Using /home/you/client-acme/colis.config.json (profile "r2")
✓ Configuration: bucket "livraisons", region "auto"
✓ Credentials: resolved, key ends in 1a2b
✓ Bucket reachable: HeadBucket succeeded
✓ Write, read, delete: round-tripped a probe object
! Expiry cleanup: no enabled expiration rule
  → Add an S3 lifecycle rule that expires objects under this bucket after a day or
    two. Without it, expired transfers stay stored and billed.

1 check(s) failed.
```

verifier exits non-zero when a check fails, so it works as a deployment smoke test. `init --provider` writes a starter for `aws`, `r2`, `minio`, `scaleway`, `wasabi` or `remote`, with `${VAR}` references rather than secrets.

## Committable settings, uncommittable keys.
${VAR} is read from the environment, and envFile names a file to load first without overwriting what the shell already set. Profiles hold several setups in one file. A flag beats a variable, which beats the file.

**colis.config.json**
```json
{
  "envFile": ".env",
  "profiles": {
    "livraison": {
      "remote": "https://livraison.example.com/api/transfers",
      "token": "${COLIS_TOKEN}"
    },
    "r2": {
      "bucket": "livraisons",
      "region": "auto",
      "endpoint": "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com",
      "expiresIn": "7d",
      "credentials": { "accessKeyId": "${R2_ACCESS_KEY_ID}", "secretAccessKey": "${R2_SECRET_ACCESS_KEY}" }
    },
    "local": { "bucket": "livraisons", "endpoint": "http://localhost:9000" }
  }
}
```
```sh
$ colis -p livraison envoyer ./maquette-v2.pdf   # through your delivery page
$ colis -p livraison statut K7QP2M4X
$ colis -p local verifier                       # against a MinIO container

$ colis config                   # which value won, and where it came from
```

The file is looked for from the working directory upwards, then in `~/.config/colis/config.json`. `colis config` prints which value won and where it came from; it masks the access key id and never prints the secret or the token.

## Seven commands, a dozen options.
### Commands
- `envoyer <file>`: Store a file and print the code to carry. "-" reads stdin
- `recevoir <code>`: Fetch what a code points at, to the stored filename, -o, or stdout
- `statut <code>`: Delivery tracking: sent, opened, approved or changes. Needs --remote
- `annuler <code>`: Burn a code
- `verifier`: Check this setup can actually store transfers. Exits non-zero when a check fails
- `init`: Write a starter colis.config.json for aws, r2, minio, scaleway, wasabi or remote
- `config`: Print the resolved configuration, and where each value came from
- `put, get, rm, doctor`: The English names, kept as aliases

### Options
- `-c, --config`: Configuration file to read
- `-p, --profile`: Profile to use inside it
- `--env-file`: Read KEY=value pairs from this file first
- `--bucket, --prefix, --region, --endpoint`: Bucket settings, overriding the file and the environment
- `--expires-in`: 3600, 30m, 24h, 7d, or never
- `--remote, --token`: Talk to a colis server instead of S3 directly, with its password
- `--name`: Filename to store the transfer under
- `-o, --output`: Where recevoir writes. "-" is stdout
- `--json`: Machine-readable output, for scripts and for verifier in CI
- `--provider, --force`: Which starter init writes, and whether it may overwrite

Configuration otherwise comes from environment variables: `COLIS_REMOTE`, `COLIS_TOKEN`, `COLIS_BUCKET`, `COLIS_ENDPOINT` and the usual AWS credentials. The argument parser is `node:util`’s `parseArgs`, so there is nothing else to install.
