La Granularisation OSS/CSS — Pourquoi J'ai Coupé foundry/ en Deux
Publié le 03 May 2026
foundry/ — la zone fonctionnelle qui héberge le code de mon workspace — contient 18 projets. Huit sont open source sous Apache 2.0. Un seul est closed source — le SaaS Edster. Pendant des mois, ces neuf projets ont cohabité dans le même dossier, séparés uniquement par… rien. Leur statut public/privé était une information dans ma tête, pas dans le système de fichiers.
Et puis j’ai voulu brancher un RAG. Et là, tout s’est effondré.
L’Accident qui Attendait de se Produire
En avril 2026, j’ai commencé à implémenter le RAG pgvector pour slider-gradle. Le principe : indexer tous les dépôts de foundry/, produire des embeddings, et les injecter dans le contexte du LLM pour qu’il ait une "perception" du dossier foundry/.
Le pipeline était simple :
val repos = fileTree(rootDir) {
include("**/*.adoc", "**/*.kts", "**/*.kt", "**/*.json", "**/*.yml")
}
val chunks = repos.map { chunk(it) }
val embeddings = chunks.map { embed(it) }
pgvector.insert(embeddings)
Simple. Efficace. Et dangereux.
Parce que ce fileTree ne fait pas la différence entre plantuml-gradle/ (Apache 2.0, public) et edster/ (closed source, private). Il avale tout. Et si un jour je publie ces embeddings — sur un dashboard, dans une réponse LLM, dans un dataset d’entraînement — le code propriétaire d’Edster fuit.
|
Le problème n’est pas qu’un LLM lise du code closed source. Le problème, c’est que ce code finisse dans un embedding vectoriel public — irréversible, non supprimable, non auditable. |
La Vraie Question
Ce n’est pas "comment empêcher le LLM de fuiter du code ?". C’est "comment rendre la fuite structurellement impossible ?"
La réponse n’est pas un prompt. La réponse, c’est de couper le dossier en deux.
La Solution : OSS/ et CSS/
Avant :
foundry/
├── plantuml-gradle/ ← public
├── bakery-gradle/ ← public
├── magic-stick/ ← public
├── edster/ ← PRIVÉ
├── slider-gradle/ ← public
└── ... ← mélange invisible
Après :
foundry/
├── OSS/ ← tout est Apache 2.0
│ ├── plantuml-gradle/
│ ├── bakery-gradle/
│ ├── magic-stick/
│ ├── slider-gradle/
│ └── ...
└── CSS/ ← tout est closed source / private
└── edster/
Le fileTree devient :
val ossRepos = fileTree(File(rootDir, "OSS")) {
include("**/*.adoc", "**/*.kts", "**/*.kt", "**/*.json", "**/*.yml")
}
val cssRepos = fileTree(File(rootDir, "CSS")) {
include("**/*.adoc", "**/*.kts", "**/*.kt", "**/*.json", "**/*.yml")
}
// Embeddings publics — OSS seulement
pgvectorPublic.insert(ossRepos.map { chunk(it) }.map { embed(it) })
// Dataset fine-tuning privé — CSS seulement
fineTuningDataset.insert(cssRepos.map { chunk(it) }) // jamais publié
|
Le RAG public ne voit jamais |
Pourquoi "OSS" et "CSS" et Pas "Public" et "Private"
Le choix des acronymes OSS (Open Source Software) et CSS (Closed Source Software) est délibéré :
-
"Public"/"Private" décrit la visibilité (ce que GitHub voit)
-
"OSS"/"CSS" décrit la nature du code (ce que le LLM doit savoir)
La visibilité GitHub est une métadonnée du repo distant. La nature du code est une propriété du contenu. Le LLM n’a pas accès à l’API GitHub — mais il a accès au système de fichiers. CSS/ lui dit "attention, ce code n’est pas sous licence libre" sans avoir besoin de lire un LICENSE ou de parser un package.json.
|
Le nom du dossier est la métadonnée la plus robuste que vous puissiez donner à un LLM. Elle ne peut pas être mal parsée, ignorée, ou mal interprétée. Le LLM voit |
L’Arbre Complet — Quatre Zones, Pas Trois
La granularisation OSS/CSS complète l’ontologie à trois zones. La zone foundry/ passe de 1 à 2 sous-zones fonctionnelles :
| Zone | RGPD | Indexation RAG public | Dataset fine-tuning | | Racine | Niveau 0 — Intime | ✗ | ✗ | | configuration/ | Niveau 1 — Restreint | ✗ | ✗ | | office/ | Niveau 2 — Collaboratif | ✓ Filtré | ✓ Filtré | | foundry/private/ | Niveau 3 — Ouvert conditionnel | ✗ | ✓ Privé | | foundry/public/ | Niveau 4 — Public natif | ✓ Libre | ✓ Public |
Le Cas Particulier : cheroliv.com
Pendant que j’y étais, j’ai corrigé une autre incohérence. Mon site cheroliv.com vivait dans foundry/ comme un projet logiciel à part entière — avec son propre build Gradle, sa propre CI, sa propre gouvernance .agents/.
Mais les articles de blog ne sont pas du code. Ce sont des données éditoriales de Cercle 2 — au même titre que les cadrages de office/pilotage/ ou les formations de office/formations/.
AVANT APRÈS
foundry/ office/
cheroliv.com/ sites/
site/jbake/content/blog/ cheroliv.com/
2026/0117_....adoc 2026/0117_....adoc
Ce qui change :
-
Les articles sont dans
office/→ confidentialité de Cercle 2 -
La tâche
publishSitedevient une capability deengine
via bakery-gradle — le plugin reçoit un FileTree, il ne sait pas que les articles viennent de office/
-
Une seule CI, une seule gouvernance — zéro duplication
-
Le classifier Vision/Opinion (Règle 2bis) s’applique automatiquement :
les articles dans office/ sont filtrés avant publication
bakery-gradle S’En Fiche
Et c’est ça qui est beau. Le plugin bakery-gradle n’a pas été modifié. Il reçoit un répertoire de contenu AsciiDoc, il génère du HTML, il push sur GitHub Pages. Que ce répertoire s’appelle site/jbake/content/ ou office/sites/cheroliv/ — le plugin s’en fout.
// engine/build.gradle.kts
task("publishBlog") {
doLast {
val articles = fileTree("../../office/sites/cheroliv/2026/")
bakery.generate(articles) // ← bakery ne sait pas d'où ça vient
}
}
Le contrat d’interface est une tâche Gradle. Pas une API REST. Pas un webhook. Une tâche — typée, testable, exécutable en local et dans la CI.
Impact sur la Gouvernance Agent
La granularisation OSS/CSS modifie la gouvernance agent sur trois points :
-
RAG public : le périmètre d’indexation passe de
foundry/**
à foundry/public/ + office/Vision/. Le code closed source est exclu de l’index par configuration de tâche, pas par prompt.
-
Knowledge Graph : graphify-gradle produit deux graphes :
-
office/graph.json(public) — relations entre artéfacts OSS + office/Vision *configuration/graph_private.json(gitignored, privé) — relations entre artéfacts CSS, jamais publié
-
-
Dataset fine-tuning :
codebase-gradlea désormais deux sources :-
OSS/→ datasets publics (modèles communautaires, benchmarks) *CSS/→ datasets privés (modèle interne, fine-tuning propriétaire)
-
Ce Qu’on Gagne (Et Ce Qu’on Perd)
Avant (dossier plat) |
Après (OSS/CSS + office/sites) |
|
|
RAG indexe tout sans discrimination |
RAG indexe |
Impossible de savoir si un projet est open source |
Le chemin |
cheroliv.com a sa propre CI, sa propre gouvernance |
La CI et la gouvernance sont mutualisées dans engine |
Les articles de blog sont dans un dépôt "code" |
Les articles de blog sont dans |
Risque de fuite de code closed source dans des embeddings publics |
Structurellement impossible — |
Le seul coût : un git mv global sur les 17 dépôts OSS + un refactor du build.gradle.kts de engine. C’est une migration à froid — pas de données en vol, pas d’utilisateurs impactés.
Conclusion : le Signal Physique Bat le Signal Logiciel
Ce que j’ai fait cet après-midi, c’est remplacer une convention invisible par une séparation visible dans la zone foundry/. Avant, vous deviez savoir que edster/ était closed source. Maintenant, vous le voyez — le dossier s’appelle CSS/.
C’est le même principe que les permissions Unix, les VLAN réseau, ou les compartiments coupe-feu dans une base de données. La sécurité ne doit pas être une information que vous devez retenir. Elle doit être une propriété physique du système.
La granularisation OSS/CSS est la traduction de ce principe dans le domaine de la gouvernance LLM. Le LLM n’a pas besoin de savoir qu’Edster est confidentiel. Il a besoin d’être incapable de l’indexer.
Et c’est exactement ce que CSS/ garantit.