← Projets

Ferry

2026

Open source · Apache-2.0

Un outil pair-à-pair open source pour les équipes de développeurs, sous licence Apache-2.0. Chaque machine fait tourner le même démon — client et serveur à la fois — et une CLI, une extension VS Code et une app de bureau Tauri lui parlent toutes via un socket local. Sept crates Rust dans un seul workspace Cargo, avec la direction des dépendances imposée par le graphe de crates : la casser est une erreur de build, pas quelque chose qu'un relecteur doit attraper. Deux démons font tourner tout le pipeline de bout en bout : découverte, pairing, rosters signés, transfert chiffré, livraison.

Rôle — Auteur unique — architecture, spec de pré-implémentation, implémentation Rust complète

Crates Rust
7
Tests
350+ Rust
Surfaces
CLI · VS Code · Desktop
Bindings générés
37 types
Primitives crypto
4 auditées
Docker pulls
546
GitHub release downloads
9
RustNoise XKSPAKE2ageed25519SQLitemDNSTauriReactts-rs

La contrainte qui l'a façonné

Il n'y a pas de tiers de confiance. Chaque règle doit être imposée par la machine qui s'en soucie, contre une preuve cryptographique plutôt que la parole d'un serveur. L'essentiel du travail d'architecture suppose l'inverse : un serveur qui impose une règle une fois, pour tout le monde.

L'architecture

daemon4,117 lignescli785 lignescore6,120 lignesnet609 lignesstore1,517 lignescrypto1,046 lignesproto451 lignesproto = types + codes d'erreur seulement, zéro logique. Bindings TS générés, jamais retranscrits.
Sept crates dans la colonne, une direction ; une huitième, remote, se tient derrière les fonctionnalités GitHub désactivées par défaut. Le graphe de crates Rust impose le découpage en couches — une violation est une erreur de build, pas une habitude de revue de code. net ne peut pas importer crypto, donc la vérification du roster entre comme un prédicat injecté sur des octets de clé bruts : le réseau l'impose mécaniquement, la couche domaine fournit la décision.

À quoi ça ressemble

3 écrans · cliquer pour agrandir
Envoyer un fichier, un secret ou un message à un pair. Le récapitulatif indique le TTL, si l'élément est détruit à la première ouverture, et qu'il est scellé avec age sur Noise XK. La CLI, l'extension VS Code et l'application de bureau affichent toutes cette même interface.

La contrainte qui façonne tout

Il n'y a pas de tiers de confiance. Quelques conséquences :

Un pair non vérifié ne peut rien faire. Refus par défaut, imposé au handshake Noise avant qu'aucune trame ne soit lue comme une commande — jamais dans l'interface. La découverte n'est pas la confiance : un nom d'hôte sur un réseau est une affirmation non authentifiée. Le pairing demande un code court hors bande et une phrase de vérification qu'un humain confirme, et il ne peut pas être contourné.

Envoyer est une écriture locale, pas un appel réseau. La ligne d'outbox est écrite dans la même transaction que le changement d'état, et un dispatcher la vide quand le pair est joignable. C'est un pattern familier utilisé autrement : d'habitude l'outbox existe pour qu'un effet de bord échoué ne puisse pas annuler une écriture, mais ici le pair hors ligne est le cas normal, donc c'est l'abstraction centrale plutôt qu'un filet de sécurité.

L'expiration tourne sur l'horloge monotone du receveur, à partir de la livraison. Deux portables sur un partage de connexion ne sont pas d'accord sur l'heure, et l'horloge de l'expéditeur est contrôlée par l'attaquant. Un secret de dix minutes resté vingt minutes en file obtient quand même ses dix minutes lisibles une fois arrivé.

La crypto est assemblée à partir de primitives auditées, jamais conçue. age, Noise XK, SPAKE2, ed25519. Aucune construction nouvelle et aucune dérivation de clé faite main.

Le compilateur possède le découpage en couches

Direction des dépendances : daemon/cli → core → net/store/crypto → proto. Jamais l'inverse. core ne peut pas importer le démon, un type IPC ou quoi que ce soit lié à l'UI, parce qu'il doit rester pilotable depuis le démon, la CLI ou un harnais de test avec un faux réseau, sans rien changer.

La crate réseau ne peut pas importer la crate crypto, donc la vérification du roster arrive comme un prédicat injecté sur des octets de clé bruts. Le réseau l'impose mécaniquement et n'entre jamais en mode transport sans un passage ; la couche domaine fournit la décision réelle adossée au roster. Aucune des deux couches ne peut se retrouver à faire confiance à l'autre par accident.

proto ne contient que des types, des enums et des codes d'erreur — aucune logique. Les 37 types de bindings TypeScript en sont générés avec ts-rs plutôt que retranscrits à la main, et la CI fait échouer le build sur tout diff.

Trois surfaces, un seul démon local

Le démon est la seule chose qui parle le protocole pair-à-pair. Tout ce qu'un humain touche — la CLI, une extension VS Code, et une coque de bureau Tauri par-dessus une interface React de 22 composants — lui parle sur un socket local à trames de longueur, et jamais directement à une autre machine.

C'est pour ça que le pairing vit dans la CLI plutôt que dans le démon. Il faut un humain pour lire une phrase de vérification à voix haute, et le démon est sans tête, sans terminal garanti. Une étape qui exige une personne ne peut pas vivre dans un processus qui ne peut pas en atteindre une.

La barre de test, c'est un harnais docker compose qui fait tourner deux démons sur un réseau et pilote un vrai envoi, une déconnexion et une reconnexion entre eux. Un test à deux nœuds en mémoire me renseigne sur la machine à états ; celui-ci me renseigne sur un partage de connexion avec un portable en veille dessus.

Comment ça a été construit

  • 24 août 2026

    Ferry — le cœur atterrit

    Écrit contre une spécification terminée avant tout code. Le workspace, les types de protocole, la crypto, le réseau, le stockage, le moteur de transfert et l'outbox, et le squelette du démon.

  • 27 août 2026

    Ferry parle à d'autres machines

    Un vrai écouteur qui accepte les connexions Noise XK autorisées contre le roster, la découverte mDNS, et le pairing SPAKE2 avec confirmation humaine.

Les problèmes qui sont sortis de ce projet9

Notes liées