reading time: 10 minutes

foundry/— the functional area that hosts the code of my workspace — contains 18 projects. Eight are open source under Apache 2.0. One is closed source — the SaaS Edster. For months, these nine projects coexisted. in the same folder, separated only by…​ nothing. Their public/private status It was information in my head, not in the file system.

And then I wanted to hook up a RAG. And then everything fell apart.

The Accident Waiting to Happen

In April 2026, I started implementing the pgvector RAG for slider-gradle. The principle: indexing all repositories of`foundry/, producing some embeddings, and inject them into the LLM context so that it has a perception" of the file`foundry/}.

The pipeline was 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. Effective. Anddangerous.

Because this`fileTree`does not distinguish between`plantuml-gradle/` (Apache 2.0, public) and`edster/`(closed source, private). He eats everything. And if one day I publish these embeddings — on a dashboard, in a response LLM, in a training dataset — Edster’s proprietary code is leaking.

The problem isn’t that an LLM reads closed-source code. The problem, It’s that this code ends up in a public vector embedding — irreversible, non-deletable, non-auditable

The True Question

It is not "how to prevent the LLM from leaking code?". It is "how make escape structurally impossible?

The answer is not a prompt. The answer is to cut the folder in half.

The Solution: OSS/ and CSS/

Before:

foundry/
├── plantuml-gradle/       ← public
├── bakery-gradle/         ← public
├── magic-stick/           ← public
├── edster/                ← PRIVÉ
├── slider-gradle/         ← public
└── ...                    ← mélange invisible

After:

foundry/
├── OSS/                   ← tout est Apache 2.0
│   ├── plantuml-gradle/
│   ├── bakery-gradle/
│   ├── magic-stick/
│   ├── slider-gradle/
│   └── ...
└── CSS/                   ← tout est closed source / private
    └── edster/

Le `fileTree`becomes :

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é

Public RAG never sees`CSS/`. The closed-source code feeds a dataset deprived of fine-tuning — an internal model that never leaves my home. The The separation is mechanical, not declarative.

Why "OSS" and "CSS" and Not "Public" and "Private

The choice of the acronyms OSS (Open Source Software) and CSS (Closed Source Software) it is resolved:

  • Public"/"Private" describes thevisibility(what GitHub sees)

  • OSS"/"CSS" describes thenaturecode (what the LLM must know)

GitHub visibility is metadata for the remote repository. The nature of the code is a property of the content. The LLM does not have access to the GitHub API — but it has access to the file system.CSS/`he said to him, "Attention, this code is not under license free" without needing to read a`LICENSE`or to parse a`package.json.

The folder name is the most robust metadata you can give to an LLM. It cannot be mis-parsed, ignored, or misinterpreted. The LLM sees`CSS/edster/`in the file path — it knows.

The Complete Tree — Four Zones, Not Three

The OSS/CSS granularization complements the three-zone ontology. The zone `foundry/`goes from 1 to 2 functional sub-zones:

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam wrapWidth 260

title Organization of foundry/ — Granularization OSS/CSS
package "foundry/" {

  package "Circle 0 — The Root" #FFCCCC {
    rectangle "PICTURE_ME_ROLLIN_*.adoc" as STIM
    rectangle "WORKSPACE_*.adoc" as VISION_DOC
    note right of STIM
      Brain dump libre
      Jamais versionné
      [LLM : interdit d'indexer]
    end note
  }

  package "Circle 1 — configuration/" #FFD9CC {
    rectangle "Secrets, tokens" as SECRETS
    rectangle "Spring Cloud Config" as SCC
    note right of SECRETS
      Privé-CVS, solo access
      [LLM : INTERDIT]
    end note
  }

  package "Circle 2 — office/" #FFFFCC {
    rectangle "Articles, framing" as DATA
    rectangle "Trainings, SPG/SPD" as FORM
    note right of DATA
      Privé-CVS
      [LLM : filtré Vision/Opinion]
    end note
  }

  package "Circle 3 — foundry/" #CCE5FF {
    package "OSS/ — Apache 2.0" #CCFFCC {
      rectangle "Gradle plugins" as PLUGINS
      rectangle "engine" as ENGINE
    }
    package "CSS/ — Closed Source" #CCDDFF {
      rectangle "edster/" as EDSTER
    }
  }
}

@enduml

| Zone | GDPR | Public RAG indexing | Dataset fine-tuning | | Root | Level 0 — Intimate | ✗ | ✗ | |configuration/| Level 1 — Restricted | ✗ | ✗ | (empty)office/| Level 2 — Collaborative | ✓ Filtered | ✓ Filtered | | foundry/private/| Level 3 — Conditional open | ✗ | ✓ Private | |foundry/public/| Level 4 — Native audience | ✓ Free | ✓ Public |

The Special Case: cheroliv.com

While I was at it, I fixed another inconsistency. My site cheroliv.com`lived in`foundry/`as a software project fully-fledged — with its own Gradle build, its own CI, its own governance.agents/`.

But blog posts are not code. They are data editorials** of Cercle 2 — on an equal footing with the framings of`office/pilotage/` or the training of`office/formations/`.

AVANT                            APRÈS
foundry/                office/
  cheroliv.com/                    sites/
    site/jbake/content/blog/         cheroliv.com/
      2026/0117_....adoc               2026/0117_....adoc

What changes:

  • The articles are in`office/`→ confidentiality of Circle 2

  • The task`publishSite`becomes a capability of`engine`

via`bakery-gradle`— the plugin receives a`FileTree`, he does not know that the articles come from`office/`

  • One CI, one governance — zero duplication

  • The Vision/Opinion classifier (Rule 2bis) applies automatically:

the articles in`office/`are filtered before publishing

bakery-gradle doesn’t care

And that’s what’s beautiful. The plugin`bakery-gradle`has not been modified. It receives an AsciiDoc content directory, it generates HTML, it pushes on GitHub Pages. That this directory is called`site/jbake/content/` ou office/sites/cheroliv/— the plugin doesn’t care.

// 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
    }
}

The interface contract is a Gradle task. Not a REST API. Not a webhook. A task — typed, testable, executable locally and in CI.

Impact on Governance Agent

The OSS/CSS granularization modifies the agent governance on three points:

  1. RAG public : the indexing scope changes from`foundry/**`

à foundry/public/ + office/Vision/. The code closed source is excluded from the index by task configuration, not by prompt.

  1. Knowledge Graph : graphify-gradle produces two graphs :

*office/graph.json(public) — relations between OSS artifacts + office/Vision *configuration/graph_private.json(gitignored, private) — relations between CSS artifacts, never published

  1. Dataset fine-tuning:`codebase-gradle`now has two sources :

*OSS/→ public datasets (community models, benchmarks) *CSS/→ private datasets (internal model, proprietary fine‑tuning)

What We Gain (And What We Lose)

Front (flat file)

After (OSS/CSS + office/sites)

ls foundry/→ public/private mix

ls foundry/public/→ everything is publishable

RAG indexes everything without discrimination

RAG indexes`OSS/`+`office/Vision`exclusively

Impossible to know if a project is open source

The path`OSS/` ou `CSS/`the said

cheroliv.com has its own CI, its own governance

CI and governance are pooled in engine

Blog posts are in a 'code' repository

Blog posts are in`office/sites/`— data, not code

Risk of leaking closed-source code in public embeddings

Structurally impossible —`CSS/`out of scope RAG

The only cost: a`git mv`overall across the 17 OSS repositories + a refactor of the build.gradle.kts de engine. This is a cold migration — no data in flight, no users impacted.

Conclusion: The Physical Signal Beats the Software Signal

What I did this afternoon was replace an invisible convention by a visible separation in the area`foundry/. Before, you had to to know that`edster/`was closed source. Now you see it — the folder is called`CSS/.

It’s the same principle as Unix permissions, network VLANs, or the Fire compartments in a database. Safety must not be information that you must remember. It must be a property physicsof the system.

OSS/CSS granularization is the translation of this principle into the field of LLM governance. The LLM does not need to know that Edster is confidential. It needs to be unable to index it.

And that’s exactly what`CSS/`guarantees.

Related articles