Skip to content
colis

@mdemb/colis

Send from a terminal. Follow the answer.

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.

$ 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

Install

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.

npm i -g @mdemb/colis

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

envoyer and statut

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.

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
$ 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

init and verifier

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
$ 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
$ 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
$ 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.

Configuration

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
{
  "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" }
  }
}
$ 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.

Reference

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.

Send it. They sign off. You know.

Your storage, a delivery page under your name, a webhook at every step. Nothing hosted by someone else.