Ce qui sort d’un espace est déclaré, classé, journalisé
Un workflow est un graphe versionné de nœuds typés où circulent des lots d’éléments : une source, des transformations, un fichier produit en route, une destination. Une exécution dure des secondes ou des minutes. Ce n’est pas le moteur des parcours — qui suit un contact pendant des semaines — mais il s’appuie sur les mêmes briques. Et ce qui le distingue d’un orchestrateur générique tient en une phrase : un nœud Missivia sait quelle donnée il manipule, et ce qu’elle a le droit de devenir.
Un graphe, des lots, des versions
Des nœuds typés, des ports typés, et une compilation qui refuse avant d’exécuter.
- Une définition tient en trois listes : des déclencheurs, des nœuds à identifiant stable, des arêtes. Le graphe est acyclique ; les ports sont typés — éléments, fichier, rien — et le compilateur refuse une connexion qui ne concorde pas.
- Un brouillon s’enregistre sans compiler : un éditeur sauve un travail en cours. Une version publiée a compilé : les règles de gouvernance s’appliquent au moment où l’on décide que ce graphe tournera, pas la nuit où il tourne.
- Le catalogue des nœuds est publié avec le schéma JSON de chaque configuration, généré depuis les schémas mêmes que l’API valide : un éditeur ne peut pas dériver du moteur.
- Chaque nœud persiste son résultat avant que le suivant ne commence. Une exécution interrompue reprend au premier nœud non terminé ; un nœud réussi n’est jamais rejoué. Le partenaire reçoit un fichier, pas deux.
- Chaque nœud choisit sa conduite en cas d’erreur — échouer, sauter, dérouter — et son nombre de tentatives, espacées.
- Les branches d’un graphe s’exécutent en séquence déterministe dans la même exécution : l’ordre ne dépend ni de l’horloge ni de la charge, et la mémoire reste plate.
Ce qui distingue un workflow d’un parcours
Un parcours est une machine à états par contact, qui vit des semaines : attentes, branches, objectifs de conversion. Un workflow traite des lots et produit des fichiers, en quelques secondes ou minutes. Les deux modèles ne sont pas fusionnés — un même moteur ferait mal les deux.
Ils partagent en revanche leur socle : le moteur d’expressions, le stockage des fichiers, le chiffrement des identifiants, la garde contre les cibles réseau internes, le journal d’audit et les classes de clés. Deux ponts les relient : une étape de parcours qui lance un workflow, et un nœud de workflow qui inscrit des contacts dans un parcours.
Déclencheurs
Six manières de démarrer. Un workflow en pause, ou sans version publiée, ne se réveille jamais.
- Planification : une expression à cinq champs, évaluée dans le fuseau de l’espace — « deux heures du matin » veut dire deux heures chez le client, y compris la nuit du changement d’heure. Une occurrence produit exactement une exécution, quel que soit le nombre d’instances du planificateur.
- Manuel : un appel d’API avec clé d’idempotence. Un workflow qui ne déclare pas ce déclencheur refuse l’appel.
- Événement métier ou de plateforme — contact créé, inscription à une liste, consentement accordé, désinscription, fin d’inscription, campagne terminée, export terminé, changement de sévérité de réputation, échec d’un workflow — avec une condition facultative évaluée avant que l’élément n’entre dans le lot, et une fenêtre de regroupement de zéro à une heure qui rassemble les événements d’un même intervalle dans une seule exécution.
- URL d’entrée à jeton : un appel HTTP entrant dont le corps JSON devient le premier lot. Le jeton est montré une fois, stocké haché, régénérable, révocable ; une signature peut être exigée. Rien du corps n’est jamais journalisé, et un jeton inconnu, révoqué ou en pause répond la même chose : sonder n’apprend rien.
- Depuis un parcours : une étape lance le workflow pour le contact courant — parce que le workflow l’a autorisé en déclarant ce déclencheur.
- Depuis un autre workflow : un sous-workflow appelé avec le lot courant, au même consentement : l’appelé déclare qu’il accepte d’être appelé.
Le lot déclencheur se lit par un seul nœud
Événement, URL d’entrée, étape de parcours ou workflow appelant : les quatre déclencheurs qui apportent un lot le font lire par un seul nœud source, et chaque élément a la même forme — l’événement, le contact, la charge utile. La charge utile est classée personnelle sans exception : son contenu est écrit par l’appelant, pas par nous, et une omission se verrait chez le partenaire plutôt qu’à la compilation. Pour la livrer à une cible technique, on la pseudonymise, ou l’on déclare la destination comme personnelle.
La déduplication se fait par événement : deux livraisons du même événement donnent un seul élément, même à travers deux fenêtres. Les éléments vivent en base, jamais dans la file, et disparaissent avec leur exécution.
Sources
Chaque source déclare une projection explicite. Un workflow ne lit jamais « tout le contact ».
- Membres d’un segment — refusé si le segment dérive d’un critère de santé : l’appartenance elle-même révélerait la donnée.
- Abonnés d’une liste, et contacts filtrés par une expression. Filtrer n’est pas exporter : l’expression peut lire une colonne qui n’est pas projetée.
- Événements métier sur une fenêtre de temps, avec leurs références opaques ; tout champ de charge utile est personnel.
- Agrégats de statistiques, du même vocabulaire fermé que les tableaux de bord : un compte masqué par le seuil d’affichage sort vide, pas en clair.
- Journal des messages : identifiants opaques, statuts, compteurs, dates — ni adresse de destinataire, ni sujet rendu, ni texte d’erreur du fournisseur.
- Journal d’audit : des noms de champs, jamais des valeurs, et les empreintes de la chaîne pour la vérifier hors de Missivia.
- Les contacts sont lus par lots, sur un curseur : jamais une table entière en mémoire.
Les fenêtres de temps se comptent depuis le départ de l’exécution
« Depuis » et « jusqu’à » acceptent l’instant présent, un décalage relatif au début de l’exécution — sept jours, trente-six heures — ou une date. La référence est le début de l’exécution, pas l’heure où le nœud tourne : une reprise après incident relit exactement la même fenêtre, et un dépôt rejoué reste idempotent.
Transformations et fonctions
Pas de nœud de code. Des nœuds typés, et un vocabulaire fermé de fonctions.
- Projection (sélection, renommage, gabarits composés), filtre, tri, dédoublonnage, limite, découpage en lots.
- Agrégation par une à trois clés — compte, somme, moyenne, minimum, maximum, compte distinct — ; jonction de deux flux par clé ; aiguillage de chaque élément vers une sortie nommée selon la valeur d’une colonne ; duplication du même lot vers plusieurs branches ; union de branches.
- La classe d’une colonne composée est la plus élevée des colonnes qu’elle cite : un prénom et un identifiant donnent une colonne personnelle. Un compte est technique — compter n’est pas divulguer. Une jonction fait entrer les colonnes de droite avec leur propre classe : joindre une adresse à un lot technique le rend personnel.
- Les fonctions — dates, chaînes, tableaux, nombres, logique, hachage — sont résolues à la compilation dans un registre fermé : un nom inconnu, une mauvaise arité, un motif dangereux sont refusés à l’écriture, avec leur position dans le texte de l’auteur.
- Aucune fonction ne lit le réseau, un fichier ou l’horloge système : « maintenant » est l’instant de référence de l’exécution, et le premier du mois se calcule dans le fuseau de l’espace.
- Une colonne hachée devient technique : une empreinte d’adresse n’est pas une adresse. Elle peut sortir vers une cible technique — c’est précisément l’usage : donner à un partenaire une clé de rapprochement sans lui donner les personnes. Le secret d’un hachage à clé est désigné par un nom, dérivé par espace, et ne s’écrit jamais dans l’expression.
Ce que le vocabulaire ne contient pas — et qu’un graphe ne peut pas y ajouter
Ni évaluation de code, ni fonction définie par l’utilisateur, ni appel HTTP, ni accès à un fichier, ni aléa, ni horloge système, ni expression régulière construite depuis une valeur, ni quoi que ce soit qui déchiffre ou inverse. Le registre est fermé — quarante-deux fonctions en six familles —, l’absence est vérifiable, et elle est testée. Étendre le vocabulaire est une livraison du produit, pas un contournement dans un graphe.
Hacher n’est pas une façon de faire sortir une donnée de santé : elle est refusée en amont, à la projection de la source. Le même moteur sert les déclencheurs, les parcours et les templates ; le registre est publié comme une donnée de l’API, et la documentation en est générée — un exemple faux fait échouer un test.
Formats et chiffrement
Un fichier au format du partenaire, produit en flux, au nom déterministe.
- CSV (séparateur, guillemets, en-tête, UTF-8 ou Latin-1), JSON Lines, JSON, XML (racine et élément paramétrables, nom de colonne conservé), Excel — une feuille, écrite ligne à ligne : un million de lignes ne fait pas grossir la mémoire.
- Compression gzip, ou archive zip de plusieurs fichiers de la même exécution. L’archive porte la classe la plus élevée de ce qu’elle contient, et dit ce qu’elle contient — noms et tailles, jamais un contenu.
- Chiffrement PGP pour la clé publique du destinataire, ou age.
- Le nom du fichier dépend de l’exécution, pas de l’horloge au moment du dépôt : un dépôt rejoué donne le même nom, donc un seul fichier.
- Chiffrer ne change pas la classe d’une donnée : ce qui autorise la sortie est la destination déclarée, pas la protection du transport.
Destinations déclarées
Rien ne sort vers une cible qui n’a pas été déclarée, une fois, par un administrateur de l’espace.
- Quatre types : SFTP, HTTP, S3 compatible, e-mail avec pièce jointe. Les identifiants sont chiffrés par espace et jamais restitués ; une empreinte dit lesquels sont stockés.
- Chaque destination porte la classe de données qu’elle peut recevoir : technique ou personnelle. Il n’existe pas de classe « santé » : aucune destination ne peut recevoir un attribut de santé.
- SFTP : l’empreinte de la clé d’hôte est obligatoire et comparée avant l’authentification — aucun mot de passe n’est envoyé à un hôte qui ne correspond pas. Le fichier est écrit sous un nom temporaire puis renommé : un partenaire qui scrute le dossier ne ramasse jamais un fichier à moitié écrit.
- HTTP : clé d’idempotence par exécution et par nœud ; l’hôte repasse la garde contre les cibles internes au moment du dépôt, pas seulement à la déclaration. S3 : point d’accès épinglé sur la destination, clé d’objet déterministe, chiffrement côté serveur demandé à l’écriture.
- E-mail : le fichier part en pièce jointe, par le relais interne, aux adresses déclarées sur la destination. Le nœud n’a pas de champ destinataire : changer qui reçoit une extraction est un acte d’administration de l’espace, pas une retouche de graphe.
- Déclarer les destinations exige la classe de clé d’administration ; construire et lancer des workflows est ouvert à la classe opérateur. Une destination est vérifiée avant publication — connexion, clé d’hôte, dossier, sans rien écrire — et ne peut pas être supprimée tant qu’une version publiée la nomme.
Une seule adresse IP sortante, pour les trois protocoles
Les partenaires filtrent par adresse source. Tout dépôt SFTP, HTTP ou S3 — et chaque vérification de destination — sort par un mandataire placé dans le périmètre, sur un hôte à adresse fixe : le partenaire a une adresse à autoriser, la même pour les trois protocoles. Le chiffrement TLS et SSH est posé au-dessus du tunnel, contre le point d’accès ou la clé d’hôte épinglés : le mandataire ne lit rien de ce qui passe.
Il n’y a aucun repli automatique : si le mandataire ne répond pas, le dépôt échoue et le dit, plutôt que de sortir par une adresse que le partenaire n’autorise pas sans que personne le sache.
Actions et ponts avec les parcours
Des nœuds qui écrivent dans l’espace — une autre question que la sortie, qui reçoit une autre réponse.
- Créer ou mettre à jour un contact — avec une base légale déclarée pour créer une fiche —, poser des attributs, entrer ou sortir d’une liste, émettre un événement métier, inscrire dans un parcours publié.
- Chaque action passe par la fonction métier qui la porte déjà : liste de suppression, contact inactif, quotas, consentement exigé par un parcours s’appliquent comme à un appel d’API. Aucune action n’écrit un consentement — un consentement se recueille, il ne se calcule pas — ni un attribut de santé.
- L’idempotence est au grain de l’élément : une reprise ne rejoue pas un élément déjà réussi. Un refus métier sur un élément n’échoue pas le lot : l’élément sort marqué « sauté », et l’exécution continue.
- Depuis un parcours, une étape lance un workflow pour le contact courant, en continuant aussitôt ou en attendant l’issue — bornée : une inscription ne reste pas bloquée parce qu’un partenaire est en panne.
- Un sous-workflow appelé rend des colonnes présumées personnelles, revérifiées à l’exécution : un enfant ne peut pas rétrograder une donnée en la faisant transiter par lui.
- Les boucles sont bornées dans les deux sens : une exécution née d’un événement ne peut pas le réémettre, la profondeur de chaînage est limitée, et un cycle d’appels entre workflows est refusé à la compilation.
Gouvernance
Quatre règles, appliquées à la compilation et rejouées à l’exécution — par le serveur, pas par une relecture.
- Un attribut classé santé ne quitte jamais un espace par un workflow.
- Un segment dérivé d’un critère de santé ne peut pas servir de source.
- Rien ne va nulle part qui n’ait été déclaré comme destination de l’espace.
- Une colonne personnelle n’atteint qu’une destination autorisée à en recevoir ; une destination technique ne reçoit que des champs techniques, des identifiants opaques et des agrégats.
- Chaque dépôt écrit au journal d’audit une entrée d’export : les noms des champs, le compte d’éléments, la destination et l’empreinte du fichier — jamais une valeur. Un refus constaté à l’exécution — un segment devenu dérivé de la santé depuis la publication, une destination rétrogradée, une cible réseau bloquée — écrit une entrée de refus, et rien ne sort.
- Les échantillons d’une exécution sont masqués selon la classe de la clé qui les lit : une clé opérateur voit les colonnes techniques et les comptes, le reste est « masqué ». Le fichier réel ne se télécharge qu’avec une clé d’administration. L’opérateur de la plateforme observe les exécutions d’un espace sans échantillon, sans destination et sans nom de fichier.
- Des bornes par espace — exécutions par jour, destinations déclarées, éléments, taille, durée —, des métriques d’exploitation sans identifiant de nœud, et une alerte sur échec répété qui ne porte jamais le message d’erreur : il pourrait nommer l’hôte d’un partenaire.
Les bornes d’une exécution, par défaut
- 200 000 éléments par exécution, 200 Mio par fichier produit, 15 minutes par exécution, une exécution à la fois par espace.
- 500 exécutions démarrées par jour et 20 destinations déclarées par espace — deux bornes que l’opérateur règle, zéro valant « sans borne ». Un refus de quota dit quand le compteur se rouvre.
- Profondeur de chaînage limitée à trois, quelle que soit la voie — sous-workflow, étape de parcours, événement émis.
- Alerte au troisième échec consécutif ; le compteur vit en base, pour qu’un redéploiement n’oublie pas que le dépôt de minuit rate depuis trois nuits.
- Exécutions, échantillons et fichiers produits conservés trente jours par défaut — plancher un jour, plafond : la durée de conservation des contacts de l’espace.