Aller au contenu
colis

Sous l’étiquette

Un fichier dans votre bucket, une réponse de votre client.

colis est volontairement petit. Cette page en fait le tour : ce qu’est un colis, ce que note l’accusé de réception, comment voyagent les gros fichiers, et le protocole que partagent la page, la CLI et les automatisations.

Vous. Votre bucket. Votre client. Rien d’autre.

● une livraison entière · 14 s · en boucle

  1. 01 · envoi

    Vous envoyez

    Le fichier arrive dans votre bucket en un seul objet, sous un code tout neuf de huit caractères, avec son expiration inscrite sur l’objet et un accusé de réception à côté.

  2. 02 · le code

    Le code change de mains

    Un lien dans un e-mail, huit caractères dictés au téléphone, un QR code sur un écran. Aucun compte, d’un côté comme de l’autre.

  3. 03 · validation

    Votre client répond

    Il tape le code, fautes réparées, prévisualise le fichier et le valide. L’accusé le note, et le webhook part.

Vous envoyez un fichier dans votre bucket et obtenez un code ; le code passe à votre client ; il ouvre le fichier, le prévisualise et le valide, et un webhook part. Recommence toutes les quatorze secondes ; avec les animations réduites, la livraison terminée s’affiche directement.

01L’accusé de réception

Envoyé, ouvert, validé ou à corriger.

Chaque colis a son accusé de réception, rangé à côté de lui dans votre bucket : quatre états, chacun avec son heure, et une réponse qui ne change plus.

  1. envoyé

    Écrit à la création du colis, avec l’empreinte de votre jeton d’expéditeur. Ce jeton permet de distinguer vos propres visites de celles de votre client.

  2. ouvert

    La première fois que quelqu’un d’autre que vous ouvre la page de retrait, télécharge le fichier ou répond. La première fois seulement.

  3. validé

    Le client a cliqué sur valider. Définitif : une seconde réponse est refusée.

  4. à corriger

    Le client demande des corrections et dit lesquelles, en 1 000 caractères au plus. Définitif aussi : la version suivante est un nouveau colis.

l’accusé, depuis un terminal
$ colis statut K7QP2M4X
à corriger
envoyé      2026-09-22 10:00
ouvert      2026-09-22 11:30
à corriger  2026-09-22 12:45
comment     Le logo en SVG, et le fond plus clair.

$ colis statut K7QP2M4X --json    # the whole receipt, for a script

L’accusé survit au fichier : il répond encore après qu’un téléchargement unique a supprimé le code, aussi longtemps que la plus longue durée de vie du déploiement. Votre page d’envoi le suit toute seule, colis statut l’affiche, et chaque changement est un webhook.

Webhooks et n8n

02Un colis

Un objet, un code, une durée de vie.

Le fichier est stocké tel quel, avec son nom, son type et son expiration dans les métadonnées de l’objet. Tout ce que la page sait d’autre tient dans de petits enregistrements à côté, sous `meta/`, hors de portée des routes de transfert.

un colis, tel qu’il arrive dans le bucket
drop/K7QP2M4X                 # the file
  Content-Type:         application/pdf
  Content-Disposition:  attachment; filename="maquette-v2.pdf"
  x-amz-meta-colis-kind:        file
  x-amz-meta-colis-expires-at:  2026-09-23T10:00:00.000Z
  Body:                 the bytes, untouched

drop/meta/K7QP2M4X            # note, device, one-time flag, password hash
drop/meta/K7QP2M4X.delivery   # sent, and the sender's token hash
drop/meta/K7QP2M4X.opened     # the first time the client opened it
drop/meta/K7QP2M4X.verdict    # approved, or changes and the comment
expiration à la lecture

Un colis expiré n’est jamais servi, même si l’objet est encore dans le bucket. Un code expiré répond le même NOT_FOUND qu’un code qui n’a jamais existé.

règle de cycle de vie

Supprimer l’objet, c’est le travail de votre bucket, via une règle de cycle de vie sur le préfixe. colis verifier contrôle qu’elle existe.

mot de passe

Le mot de passe d’un colis est haché avec scrypt, avec son propre sel. Seule l’empreinte est stockée : personne ne peut le relire ni le réinitialiser.

03Gros fichiers

Du navigateur à votre bucket, sans détour.

Au-delà de `DROP_MAX_SIZE_MB`, rien ne passe par la fonction. Le serveur signe des URL et assemble ; le navigateur envoie les morceaux lui-même.

POST   /api/transfers/uploads                 # a fresh code, a multipart upload
       → { code, uploadId, partSize, partCount }   # parts of 8 MiB
POST   /api/transfers/uploads/:code/parts     # presigned PUT URLs, 50 at most
PUT    <the bucket>                           # the browser, 4 parts at a time
POST   /api/transfers/uploads/:code/complete  # assembled, checked, then 201
DELETE /api/transfers/uploads/:code           # abandoned

Les morceaux font 8 Mio, davantage quand un fichier en demanderait plus de 10 000. Le navigateur en envoie quatre à la fois et retente trois fois un morceau qui échoue.

Il se souvient aussi de l’envoi. Redéposez le même fichier après une coupure ou un onglet fermé : il demande au bucket quels morceaux sont déjà là et envoie le reste.

Le bucket a besoin d’une règle CORS qui autorise PUT depuis votre page et expose ETag. Sans elle, les petits fichiers fonctionnent toujours, et un gros échoue avec un message qui dit pourquoi.

Livrer des fichiers lourds

04Le code

Quarante bits qui survivent à un appel.

Le code, c’est ce que manipule votre client. Il s’affiche sur votre écran et il le tape sur le sien : tout en découle.

Base32 de Crockford

Ni I, ni L, ni O, ni U. Les trois premiers se confondent à la lecture ; retirer le quatrième évite qu’un code aléatoire forme un mot malheureux.

Réparé au retour

Séparateurs retirés, casse unifiée, O lu comme zéro. La réparation se fait dans le navigateur, avant toute requête, et ce que le client a tapé reste tel quel.

Réservé par écriture conditionnelle

Le serveur choisit le code et écrit avec ifAbsent : une collision échoue franchement et réessaie avec un nouveau code au lieu d’écraser le colis de quelqu’un d’autre.

05Le protocole

Quatre routes, un format d’erreur. Écrits noir sur blanc.

Un navigateur ne peut pas détenir vos identifiants S3 : un serveur se place entre les deux. La page de livraison est ce serveur, et sa forme est un protocole, pas un détail d’implémentation.

relatives à /api/transfers
POST   /                # create a transfer, get the code back
GET    /:code           # metadata
GET    /:code/raw       # the bytes, or a 302 to a presigned URL
DELETE /:code           # burn it

# what the delivery page adds
GET    /:code/status    # sent, opened, approved or changes
POST   /:code/opened    # the client opened it
POST   /:code/verdict   # { "decision": "approved" }, or "changes" and a comment

# every error, same shape
{ "error": { "code": "NOT_FOUND", "message": "Unknown or expired code." } }
le client, dans un navigateur
import { createTransferClient } from '@colis/protocol'

const transfers = createTransferClient({ baseUrl: '/api/transfers' })

const { code } = await transfers.createFile({ body: file, filename: file.name })
const meta = await transfers.read(typed)        // null when unknown or expired
const bytes = await transfers.readBytes(code)   // the file back
await transfers.remove(code)

La CLI pointée vers --remote parle les mêmes quatre routes : c’est pourquoi colis envoyer fonctionne avec votre page de livraison et un jeton, sans clés S3.

La page de livraison ajoute trois routes à elle pour l’accusé de réception. Un client qui n’en connaît aucune fonctionne quand même : ce qu’il crée est un colis comme les autres, avec les réglages par défaut du déploiement.

Le protocole de transfert, route par route

06Les paquets

Une seule contrainte décide du découpage.

Un navigateur ne doit jamais se retrouver avec un client de stockage dans ses dépendances. Le paquet protocole est ce que les deux moitiés partagent.

@mdemb/colis

core, protocol
Le binaire

envoyer, recevoir, statut, annuler, verifier, init et config. Une seule implémentation, le client du protocole, branché sur fetch ou directement sur le handler.

@colis/core

aws-sdk, protocol
La primitive

Fichiers, enregistrements, écritures conditionnelles et handler de transfert. Le seul paquet qui détient des identifiants.

@colis/protocol

nanoid
Le contrat

Le format d’échange, un client fondé sur fetch, les codes, et la signature des webhooks : signWebhook et verifyWebhook.

@colis/react

protocol, react (peer)
Les hooks

Envoyer, recevoir, et un champ de code. Aucun chemin de votre bundle ne mène au SDK AWS.

n8n-nodes-colis

protocol, n8n-workflow (peer)
Le nœud n8n

Un déclencheur qui vérifie la signature, et un nœud d’action qui envoie, lit et supprime des colis.

Runtime serveur
Node 20 ou plus, ce qu’exige le SDK AWS v3
Page de livraison
Next.js, déployée sur Vercel depuis le monorepo
Paquets navigateur
fetch, et rien d’autre
Sur npm
Pas encore : construits depuis le dépôt

Vous envoyez. Ils valident. Vous le savez.

Votre stockage, une page de livraison à votre nom, un webhook à chaque étape. Rien d’hébergé chez un tiers.