Skip to content

Meine Graal

CYPT71 edited this page Aug 18, 2026 · 1 revision

v6 — Graal : Platform Factory

Vision

Platform Factory fournit une interface unifiée permettant de transformer une source applicative, une image OCI ou une machine virtuelle historique en charge de travail sécurisée, reproductible et déployable.

L’expérience principale repose sur trois commandes :

pf init <source>
pf build
pf publish <target>

Le nom officiel du produit est :

Platform Factory

Le nom du binaire installé est :

platform-factory

L’alias CLI recommandé est :

pf

Principes non négociables

  • Conserver la génération OCI indépendante de Docker, Podman et BuildKit.
  • Ne pas utiliser de moteur de build tiers dans le chemin principal.
  • Ne pas utiliser de moteur de virtualisation tiers dans le chemin MicroVM natif.
  • Ne pas déléguer silencieusement une capacité critique à une commande externe.
  • Valider toutes les entrées non fiables.
  • Produire des résultats reproductibles et adressés par contenu.
  • Relier chaque preuve au digest exact de l’artefact et au SHA du code testé.
  • Conserver une seule source de vérité pour le cycle de vie des workloads.
  • Expliquer les décisions automatiques avant toute mutation.
  • Refuser explicitement les fonctionnalités dont la sémantique n’est pas prise en charge.
  • Préserver les modes conteneur, MicroVM OCI et MicroVM sans OCI.
  • Ne jamais modifier une image disque source par défaut.

Architecture cible

Source
  │
  ├── dépôt local ou Git
  ├── projet applicatif
  ├── archive
  ├── image ou layout OCI
  └── disque de VM
          │
          ▼
       pf init
          │
          ▼
Analyse, inventaire et plan de transformation
          │
          ▼
       pf build
          │
          ▼
OCI / boot bundle / MicroVM
SBOM / provenance / signature
          │
          ▼
      pf publish
          │
   ┌──────┼─────────┬──────────────┐
   ▼      ▼         ▼              ▼
Registry Podman   Docker       Kubernetes
                   │              │
                   └──── MicroVM ─┘

v6.0 — Fondation de la CLI

Identité et distribution

  • Créer le binaire platform-factory. cmd/platform-factory/main.go (rebaptisé le 5 août 2026).
  • Fournir l’alias officiel pf. Alias direct sans délégateur, créé par cmd/platform-factory-installer et scripts/local/install.sh/ bootstrap.ps1 (5 août 2026).
  • Ajouter pf version. cmd/platform-factory/main.go:38.
  • Ajouter pf help. cmd/platform-factory/main.go:34.
  • Ajouter la complétion Bash. completionScripts["bash"], cmd/platform-factory/lifecycle.go:517-548, testé par TestCompletionAndCommandFormatting.
  • Ajouter la complétion Zsh. Même emplacement, completionScripts["zsh"].
  • Ajouter la complétion Fish. Même emplacement, completionScripts["fish"].
  • Ajouter la complétion PowerShell. Même emplacement, completionScripts["powershell"].
  • Fournir des paquets Linux.
  • Fournir un paquet macOS.
  • Fournir un paquet Windows.
  • Documenter la politique de compatibilité CLI. docs/api-compatibility.md existe mais documente la politique du protocole réseau (api_version, journal, MIME types), pas la surface CLI (flags, codes de sortie, sous-commandes) - pas la même chose, reste à écrire.
  • Stabiliser les codes de sortie.
  • Stabiliser le format de sortie machine.
  • Propager un identifiant de trace sur toute opération. PLATFORM_FACTORY_TRACE_ID existe et est propagé pour platform-factory build (cmd/platform-factory/main.go:613), mais pas vérifié sur les autres commandes - "toute opération" reste à prouver.

Commandes principales

  • Implémenter pf init. cmd/platform-factory/init.go (runInit) - voir le détail complet dans la section v6.1 ci-dessous.
  • Implémenter pf build. cmd/platform-factory/main.go:66,547 (runBuild, flagset "build").
  • Implémenter pf publish. cmd/platform-factory/lifecycle.go:46 (flagset "publish").
  • Ajouter --yes. cmd/platform-factory/launch_publish.go, lifecycle.go, et init.go (saute les trois invites interactives - disque de boot, écosystème, confirmation finale) - pas encore sur toutes les commandes mutantes.
  • Ajouter --dry-run. init.go, main.go (build), lifecycle.go (publish/rollback), project.go.
  • Ajouter --json. doctor.go, diff.go, pipeline.go, sbom.go, verify_release.go, lifecycle.go, main.go.
  • Ajouter --quiet.
  • Ajouter --verbose.
  • Ajouter --trace-id. Seule la variable d'environnement PLATFORM_FACTORY_TRACE_ID existe (main.go:613) - pas de flag CLI dédié.
  • Ajouter --config. launch_publish.go:45, main.go:552 (build), project.go:42.
  • Ajouter --profile. cmd/platform-factory/main.go:558 (flag build).
  • Garantir un comportement homogène entre les commandes.

Accessibilité CLI pour un débutant complet (5 août 2026)

Retour utilisateur explicite : l'outil doit rester utilisable par quelqu'un qui ne connaît pas le vocabulaire du projet. Deux changements concrets, hors des cases ci-dessus :

  • pf init (init_ux.go) : l'invite de langage n'est plus du texte libre ("go", "node"...) mais un menu numéroté (1-9, go→9=custom) avec un exemple concret de chemin d'artefact par langage (ex. Go → cmd/app/main.go) - un chiffre ne peut pas être mal orthographié vers une valeur non supportée. Testé par TestRunInitLanguageMenuRejectsOutOfRangeChoice, TestResolveEcosystemInteractivelyRejectsOutOfRangeChoice.
  • Messages d'erreur reformulés en langage clair avec la correction suggérée, sur le chemin pf initpf project build/freeze : artefact introuvable (cmd/platform-factory/project.go:buildProject, distingue explicitement "le build_command a tourné mais n'a rien produit là" de "le chemin configuré est simplement faux"), langage non reconnu par le freeze intégré, absence de requirements.txt/Maven- Gradle, et les deux erreurs Validate() les plus fréquentes (internal/project/config.go). Une régression a été trouvée et corrigée en cours de route : resolveFreezeSteps détectait le cas "pas d'adaptateur intégré" par un strings.Contains sur le texte d'erreur - reformuler ce texte aurait cassé silencieusement le repli vers les plugins de freeze. Remplacé par une vraie erreur sentinelle (errNoBuiltinFreezeAdapter, errors.Is), texte libre à partir de maintenant.

Plugins de langage en modules séparés (6 août 2026)

Retour utilisateur explicite : le support des langages doit vivre dans plugins/ comme module Go séparé (même schéma que plugins/containerd/ plugins/kubevirt - propre go.mod, propre binaire execé par nom), distinct du protocole sdk/plugin (RPC sur stdin/stdout) déjà existant pour detect/freeze/plan. Et le plugin doit pouvoir livrer sa propre couche OCI pré-construite, pas seulement une liste de fichiers.

  • internal/oci.Options.ExtraLayers (internal/oci/extralayers.go, 6 août 2026) : chemins vers des tar non compressés qui deviennent des couches de manifeste additionnelles. Chaque entrée est validée indépendamment (chemins relatifs uniquement, aucun .., uniquement fichiers réguliers/répertoires, aucun bit setuid/setgid/sticky, borné à 4 GiB/200 000 entrées) puis re-hachée par Build lui-même - rien de ce qu'un plugin prétend n'est jamais cru sur parole. 8 tests, dont vérification indépendante via internal/layout.Verify et déterminisme.
  • sdk/langplugin (sdk/langplugin/langplugin.go) : l'unique implémentation partagée de WriteDeterministicTar (trié, timestamps à zéro, symlinks refusés) que chaque module plugins/lang-* appelle pour son build-layer - auparavant copiée-collée par langage, elle vit maintenant à un seul endroit pour que la règle d'empaquetage ne puisse pas diverger entre langages. 3 tests, dont déterminisme.
  • plugins/lang-<langage> pour les 7 langages intégrés - python, node, ruby, php, java, dotnet, rust (module séparé par langage, main.go à la racine du module, aucun sous-dossier cmd/). Chaque module dépend du module principal pour une seule chose - sdk/langplugin
    • via le même schéma require+replace ... => ../.. que plugins/containerd/plugins/kubevirt utilisent déjà pour leurs propres dépendances sdk (sdk/microvm, etc.). Le sens de dépendance ne va jamais de plugin/internal vers plugins : un plugin de langage importe sdk/langplugin (une surface d'API publique et versionnée, pas un paquet interne) ; rien dans le module principal n'importe jamais depuis plugins/*, et aucun plugin n'importe sdk/plugin ni internal/plugin. freeze reproduit l'étape intégrée existante à l'identique pour python/node/ruby/php (même commandes, emplacement d'installation déjà local au projet par défaut) ; pour java/dotnet/rust, freeze redirige délibérément le cache global (Maven/Gradle, NuGet, Cargo) vers un répertoire local au projet, seule façon de l'empaqueter en couche. build-layer appelle sdk/langplugin.WriteDeterministicTar sur ce que freeze a installé.
  • pf plugin load/unload/list (6 août 2026, retour utilisateur explicite : "le plugin doit être l'outil le plus puissant et accessible pour l'utilisateur ... sans connaître les coulisses ... le produit final doit être accessible à un débutant sans connaissance de Docker/OCI/VM/microVM"). Le cœur ne résout plus jamais un plugin de langage via le PATH brut : sdk/langplugin.Resolve (appelé par language_plugin.go) ne trouve que ce qui a été explicitement chargé via pf plugin load, dans ~/.platform-factory/plugins (surchargeable via PLATFORM_FACTORY_LANG_PLUGIN_DIR, comme PLATFORM_FACTORY_PLUGIN_DIR le fait déjà pour l'autre mécanisme de plugin). Trois verbes suffisent : pf plugin load python (résout automatiquement l'un des 7 langages intégrés, en cherchant d'abord à côté du binaire platform-factory en cours d'exécution, puis sur PATH) ; pf plugin load --from PATH nom pour charger un binaire de plugin arbitraire sous un nom de son choix
    • PATH peut être un binaire déjà construit, ou un répertoire contenant son module Go (go.mod), auquel cas go build -o <temp> . tourne d'abord automatiquement (prepareSource dans plugin.go, côté CLI, pas dans le SDK) : pf plugin load --from ./mon-plugin-src nom fonctionne sans étape de build manuelle séparée ; pf plugin unload nom ; pf plugin list. "Chargé" a un sens unique et vérifiable : présent dans ce répertoire, rien d'autre - pas de registre en mémoire à synchroniser. Toute la logique de gestion (hors construction depuis les sources) vit dans sdk/langplugin/registry.go (Dir/Resolve/Load/Unload/List), le même paquet public qu'un auteur de plugin tiers utiliserait - la CLI n'a aucun accès que le SDK n'ait pas. 11 tests dans plugin_test.go (dont build réel d'un module Go minimal) + 9 dans registry_test.go.
  • cmd/platform-factory/language_plugin.go : nouveau champ language_plugin: true dans platform-factory.yaml - opt-in explicite, aucune détection automatique d'un binaire simplement présent. Le dispatch prend maintenant un pluginResolver injectable (production : sdk/langplugin.Resolve ; tests : un faux résolveur), plus l'execute existant. buildProject appelle platform-factory-lang-<langage> build-layer sur le chemin résolu et ajoute le résultat à ExtraLayers. 7 tests avec résolveur + exécuteur simulés.
  • go/custom restent sans module plugins/lang-* : go fige via go mod tidy/go mod vendor (rien à rediriger, le vendoring est déjà dans l'arbre) et custom n'a par définition aucune étape freeze intégrée à reproduire.
  • L'installeur (cmd/platform-factory-installer) ne construit/ne fournit pas encore les binaires plugins/lang-* - pf plugin load <langage> ne trouve donc rien à côté du binaire installé tant qu'on n'a pas construit soi-même depuis les sources. Reste à faire pour que "accessible à un débutant" soit vrai dès une installation fraîche, pas seulement depuis un checkout source.

Vérification : go build ./..., go vet ./... et go test ./... passent intégralement depuis la racine (tout le go.work, y compris les 7 modules plugins/lang-*) ; chaque module plugins/lang-* est en plus vérifié isolément (GOWORK=off go build/vet/test ./..., gofmt -l .). Le blocage externe précédent (internal/guesttransport/protocol.go disparu de l'arbre de travail) est résolu - cmd/platform-factory compile et ses tests passent, y compris language_plugin_test.go.

Réutilisation du moteur existant

  • Utiliser les bibliothèques internes existantes sans recopier leur logique dans la CLI. cmd/platform-factory importe directement internal/oci, internal/executor, internal/layout, internal/networking, internal/plugin, internal/registry, internal/sbom, internal/provenance, internal/signing, internal/attestation plutôt que de les réimplémenter.
  • Relier pf build au pipeline et au constructeur OCI existants. cmd/platform-factory/main.go importe internal/oci; pipeline.go relie au moteur de pipeline existant.
  • Relier pf publish au Registry natif existant. cmd/platform-factory/lifecycle.go:21 importe internal/registry.
  • Relier les preuves au moteur natif de SBOM, provenance et signature. cmd/platform-factory/sbom.go/evidence.go/lifecycle.go importent internal/sbom, internal/provenance, internal/signing, internal/attestation.
  • Relier les workloads MicroVM au même superviseur que les façades OCI. cmd/platform-factory/microvm_native_linux_amd64.go:166 et plugins/containerd/cmd/platform-factory-shim/plugin.go importent tous deux internal/hypervisor/internal/hypervisor/kvm - même superviseur, pas une copie.
  • Ne pas créer une seconde base d’état propre à Platform Factory.

v6.1 — pf init

Contrat utilisateur

pf init <source>

La commande initialise un espace Platform Factory à partir d’une source existante ou d’un nouveau projet.

Exemples :

pf init .
pf init ./application
pf init ./legacy.qcow2
pf init ./server.vmdk
pf init oci://registry.example.com/team/app:1.0
pf init git://example.com/team/application.git

Initialisation du projet

  • Détecter la racine du projet. pf init opère sur le répertoire donné (ou .), sans remonter chercher une racine - pas de "détection".
  • Initialiser un dépôt Git lorsqu’aucun dépôt n’existe. cmd/platform-factory/init.go:184-186 (actionGitInit, seulement si .git est absent).
  • Refuser d’écraser un dépôt Git existant. Skip inconditionnel si .git existe déjà; testé par TestRunInitSkipsAnAlreadyExistingGitRepo.
  • Générer un .gitignore. init.go:177-180, préserve un fichier existant - testé par TestRunInitPreservesAnExistingGitignore.
  • Générer platform-factory.yaml. init.go:132-162.
  • Générer platform-factory.lock. init.go:182,278-300.
  • Générer le répertoire .pf/. init.go:164-175 (boucle sur .pf, policies, deploy, dist, reports).
  • Générer le répertoire policies/. Même boucle, avec un policies/default.json réel chargeable par internal/policy.
  • Générer le répertoire deploy/. Même boucle.
  • Générer le répertoire dist/. Même boucle.
  • Générer le répertoire reports/. Même boucle.
  • Conserver les fichiers utilisateur existants. fileExists saute tout répertoire/fichier déjà présent avant de l'ajouter au plan.
  • Produire un plan avant toute écriture. buildInitPlan ne fait que stat/lire; commitInitPlan (séparée) exécute; --dry-run imprime le plan sans écrire - testé par TestRunInitDryRunWritesNothing. Depuis le 5 août 2026, le plan complet est aussi affiché puis confirmé interactivement (confirmPlan, "proceed? [y/N]") avant toute écriture réelle, sauf --yes/--dry-run/stdin non interactif - une invite déclinée n'écrit rien (TestRunInitDeclinedConfirmationWritesNothing).
  • Rendre toutes les mutations atomiques. writeValidatedIfConfig écrit via fichier temporaire + os.Rename (init.go:351-382); platform-factory.yaml généré est revalidé par project.Load avant d'être installé.
  • Permettre l’annulation sans laisser de projet partiellement initialisé. rollbackInitPlan (init.go:389-393) supprime tout ce qui a été créé, en ordre inverse - testé par TestRunInitRollsBackEverythingOnMidPlanFailure.

Compatibilité avec les configurations existantes

  • Découvrir les fichiers .config_image.yaml. internal/project/config.go:31 (ConfigNames), utilisé par findLegacyConfigInDir.
  • Découvrir les fichiers .config_image.yml. Même liste.
  • Découvrir les fichiers .config_img.yaml. Même liste.
  • Découvrir les fichiers .config_img.yml. Même liste.
  • Découvrir leurs variantes JSON. .config_image.json/.config_img.json dans la même liste.
  • Importer leur contenu vers platform-factory.yaml. init.go:142-151 (actionMigrateConfig) - testé par TestRunInitMigratesLegacyConfigAndRemovesOldFile.
  • Conserver une trace de migration. internal/project/migrate.go: MigrationChange{Field, From, To, Reason} pour chaque réécriture.
  • Refuser les champs inconnus. internal/project/config.go:120 (decoder.KnownFields(true)).
  • Expliquer toute valeur modifiée ou normalisée. MigrationChange.Reason (voir ci-dessus).
  • Maintenir la compatibilité ascendante des schémas. Framework de migration versionné (configMigrations, v0→v1); une version future inconnue est explicitement refusée plutôt que silencieusement acceptée.

Sources applicatives

  • Supporter un répertoire local. Seule source supportée aujourd'hui par pf init (init.go:45-50).
  • Supporter un dépôt Git local. Refusé explicitement (init.go:47-49, message pointant vers Meine-Graal v6.1/v6.2).
  • Supporter une archive tar. Idem.
  • Supporter une archive tar compressée. Idem.
  • Supporter un layout OCI. Idem.
  • Supporter une archive OCI. Idem.
  • Supporter une archive Docker Save. Idem.
  • Supporter une référence Registry OCI. Idem.
  • Supporter plusieurs sources dans un même projet.
  • Vérifier les digests fournis.
  • Épingler les sources distantes.
  • Refuser les redirections ou références non conformes à la politique.

Détection des écosystèmes

  • Détecter Go. internal/detect/detect.go:70 (go.mod).
  • Détecter Node.js. detect.go:64 (package-lock.json, etc.).
  • Détecter Python. detect.go:65 (pyproject.toml, etc.).
  • Détecter Java. detect.go:66 (pom.xml, build.gradle, etc.).
  • Détecter .NET. detect.go:67,54 (global.json, .dll).
  • Détecter Rust. detect.go:71 (Cargo.toml/Cargo.lock).
  • Détecter Ruby. detect.go:72 (Gemfile/Gemfile.lock).
  • Détecter PHP. detect.go:73 (composer.json/composer.lock).
  • Supporter les commandes personnalisées. language: custom avec build_command/freeze_command explicites (internal/project/files.go:21, message de init.go:266-270).
  • Détecter les projets multi-écosystèmes. detect.go signale l'ambiguïté (candidats multiples) mais ne combine pas plusieurs écosystèmes dans un seul projet.
  • Résoudre ou signaler les ambiguïtés. detect.go:26-27,94 (Result.Ambiguous/Candidates) - testé par TestDetectDirectoryAndRequireAmbiguity. Côté pf init (resolveEcosystemInteractively, init_ux.go, 5 août 2026) : résolu par --language/--artifact, par une invite interactive demandant langage et chemin de l'artefact quand un terminal est disponible (TestRunInitAmbiguousEcosystemPromptsForLanguageAndArtifact), ou - depuis la correction du même jour - refusé fermé plutôt que d'écrire un placeholder REPLACE_ME non fonctionnel quand aucun des deux n'est possible (TestRunInitAmbiguousEcosystemNonInteractiveFailsClosedWithoutWritingJunk) : un platform-factory.yaml qui se charge mais ne construit jamais était pire qu'un refus explicite. Exception : un projet disque legacy pur n'a besoin d'aucun langage du tout (LegacyDisks exempte Validate(), internal/project/config.go).
  • Produire un inventaire de gel SHA-256. platform-factory project freeze existe déjà (.platform-factory/freeze.lock.json) mais pf init ne le déclenche pas lui-même.
  • Identifier toolchains, dépendances, application et métadonnées. Seul le "kind" de l'écosystème est détecté, pas un inventaire complet.
  • Préparer un DAG initial pour pf build.

v6.2 — Import de VM legacy

Formats de disques

  • Détecter RAW. internal/vmdisk (5 août 2026) - signature MBR (0x55AA) ou en-tête GPT ("EFI PART"), jamais présumé par défaut.
  • Détecter QCOW2. Magie QFI\xfb + version 2/3 (vmdisk.go:detectQCOW2).
  • Détecter VMDK. Variantes binaire sparse ("KDMV") et descripteur texte ("# Disk DescriptorFile").
  • Détecter VHD. Cookie "conectix" en début de fichier (dynamique) ou dans le footer 512 octets en fin de fichier (fixe).
  • Détecter VHDX. Identifiant "vhdxfile" à l'offset 0.
  • Détecter ISO. Descripteur de volume "CD001" au secteur 16.
  • Détecter les formats non supportés sans tenter de les monter. vmdisk.Detect n'appelle jamais mount/loop-attach; échoue fermé (ErrUnsupportedFormat) sur tout en-tête non reconnu plutôt que de deviner - testé par TestDetectRejectsUnrecognizedContent.
  • Valider les en-têtes et tailles avant lecture approfondie. minDiskSize (512 o) rejeté avant toute lecture de contenu - testé par TestDetectRejectsTooSmallFile/TestDetectRejectsEmptyFile.
  • Borner le temps, la mémoire et le nombre d’objets analysés. headerWindow (64 KiB) et footerWindow (512 o) bornent chaque lecture quelle que soit la taille réelle du fichier - testé par TestDetectBoundsHeaderReadOnSparseFile (fichier creux de 1 TiB détecté sans lire au-delà de la fenêtre bornée).
  • Tester les images tronquées, cycliques et malformées. Tronquées et malformées : testé (TestDetectRejectsTruncatedQCOW2Magic, TestDetectRejectsUnrecognizedContent, TestDetectQCOW2RejectsVersion1). Cycliques : non applicable à ce stade - la détection n'analyse pas les tables de partitions, seule la lecture de ces dernières (hors scope actuel, voir "Partitions et volumes" ci-dessous) pourrait boucler.

Voir docs/legacy-vm-disk-boot.md pour le détail complet, y compris le nouveau mode de démarrage BIOS/OVMF (scripts/microvm/run-legacy-disk.sh, platform-factory microvm run-legacy-disk --disk <path>) qui consomme cette détection - un changement de modèle de confiance documenté séparément, puisqu'il exécute le bootloader/noyau du disque, non celui du projet.

Lecture sécurisée

  • Ouvrir les disques en lecture seule.
  • Refuser l’écriture implicite.
  • Ne pas utiliser un montage hôte non confiné.
  • Isoler le parseur de disque.
  • Borner les offsets et tailles.
  • Détecter les chevauchements de partitions.
  • Détecter les tables de partitions corrompues.
  • Ne jamais suivre un chemin hors de l’image analysée.
  • Journaliser les décisions sans exposer de secrets.

Partitions et volumes

  • Supporter MBR.
  • Supporter GPT.
  • Détecter les partitions étendues.
  • Détecter LVM.
  • Détecter les volumes chiffrés.
  • Signaler les volumes nécessitant une clé.
  • Supporter plusieurs disques associés à une VM. vmdisk.SelectBootDisk (5 août 2026) accepte plusieurs chemins, scripts/microvm/run-legacy-disk.sh attache chaque disque (le disque de boot en premier), et pf init scanne et déclare tous les disques trouvés dans legacy_disks. Au niveau disque entier seulement - pas au niveau volume/partition à l'intérieur d'un même disque.
  • Identifier le disque racine probable. ScanBootPartition + SelectBootDisk (indicateur actif MBR, ESP GPT, ou attribut GPT Legacy BIOS Bootable) - refuse explicitement de deviner si zéro ou plusieurs disques correspondent, plutôt que de choisir au hasard.
  • Identifier les volumes de données. Les disques secondaires sont déclarés (LegacyDiskConfig.Data) mais rien n'inspecte encore les volumes/partitions à l'intérieur d'un disque donné.
  • Détecter les volumes swap.
  • Produire une cartographie stable des volumes.

Systèmes de fichiers

  • Lire ext2.
  • Lire ext3.
  • Lire ext4.
  • Lire XFS.
  • Lire Btrfs.
  • Lire FAT.
  • Lire NTFS en lecture seule.
  • Détecter les filesystems non supportés.
  • Détecter les métadonnées incohérentes.
  • Limiter le nombre de fichiers inventoriés.
  • Limiter la taille totale logique analysée.
  • Préserver les informations de permissions et propriétaires.
  • Préserver ou signaler les ACL et attributs étendus.

Détection du système

  • Identifier la distribution Linux.
  • Identifier la version.
  • Identifier l’architecture CPU.
  • Identifier le noyau attendu.
  • Identifier systemd.
  • Identifier SysV init.
  • Identifier OpenRC.
  • Identifier BusyBox init.
  • Identifier les unités de service.
  • Identifier les scripts de démarrage.
  • Identifier les tâches cron.
  • Identifier les timers systemd.
  • Identifier les utilisateurs et groupes.
  • Identifier les services réseau.
  • Identifier les ports probables.
  • Identifier les mounts et volumes persistants.

Inventaire applicatif

  • Détecter les binaires exécutables.
  • Détecter les dépendances ELF.
  • Détecter les interpréteurs.
  • Détecter les runtimes applicatifs.
  • Détecter les bibliothèques partagées.
  • Détecter les fichiers de configuration.
  • Détecter les certificats.
  • Détecter les secrets probables sans révéler leur contenu.
  • Détecter les sockets Unix.
  • Détecter les périphériques requis.
  • Détecter les modules noyau requis.
  • Détecter les dépendances à /proc, /sys et /dev.
  • Identifier le processus principal probable.
  • Produire un niveau de confiance pour chaque détection.

Rapport d’analyse

  • Générer reports/discovery.json.
  • Générer un rapport humain lisible.
  • Lister les applications détectées.
  • Lister les services exclus.
  • Lister les données persistantes.
  • Lister les dépendances système.
  • Lister les risques de migration.
  • Lister les éléments nécessitant une intervention humaine.
  • Expliquer les limites de l’analyse.
  • Relier le rapport au digest exact du disque source.

v6.3 — Plan de transformation

Modes d’exécution

Platform Factory doit choisir ou proposer l’un des modes suivants :

container
microvm-oci
microvm-direct
vm-encapsulation
unsupported

Classification

  • Déterminer si l’application peut fonctionner en conteneur standard.
  • Déterminer si elle nécessite un noyau dédié.
  • Déterminer si elle nécessite un périphérique particulier.
  • Déterminer si elle dépend de services système non extractibles.
  • Déterminer si les données peuvent être séparées de l’application.
  • Déterminer si une extraction OCI est sûre.
  • Déterminer si une encapsulation MicroVM est nécessaire.
  • Refuser toute décision automatique dont la confiance est insuffisante.
  • Permettre une stratégie explicitement choisie par l’utilisateur.
  • Ne jamais masquer les incompatibilités.

Rapport de compatibilité

  • Générer reports/compatibility.json.
  • Fournir le mode recommandé.
  • Fournir les modes alternatifs.
  • Fournir un score de confiance.
  • Expliquer chaque décision.
  • Documenter les fonctionnalités perdues.
  • Documenter les différences réseau.
  • Documenter les différences de stockage.
  • Documenter les différences de sécurité.
  • Documenter les écarts de performances attendus.
  • Indiquer les conditions empêchant le déploiement.

Manifeste produit

  • Écrire le plan retenu dans platform-factory.yaml.
  • Épingler les sources dans platform-factory.lock.
  • Épingler les toolchains.
  • Épingler les bases.
  • Épingler les plugins.
  • Épingler le kernel et l’initramfs en mode MicroVM.
  • Calculer une empreinte canonique du plan.
  • Refuser un build lorsque le plan et le lock sont incohérents.

v6.4 — pf build

Contrat utilisateur

pf build

Options principales :

pf build --target container
pf build --target microvm
pf build --target auto
pf build --verify-only
pf build --reproducible
pf build --no-cache

Validation préalable

  • Charger strictement platform-factory.yaml.
  • Charger strictement platform-factory.lock.
  • Vérifier la version des schémas.
  • Vérifier les digests de toutes les sources.
  • Vérifier les signatures exigées.
  • Vérifier les capacités de la plateforme.
  • Vérifier les politiques.
  • Vérifier les budgets de ressources.
  • Vérifier les chemins et références d’artefacts.
  • Vérifier le DAG.
  • Détecter les cycles.
  • Produire le plan résolu avant exécution.

Construction OCI standard

  • Construire un layout OCI déterministe.
  • Construire en streaming.
  • Borner la mémoire.
  • Normaliser les timestamps.
  • Normaliser UID et GID.
  • Normaliser les permissions.
  • Normaliser l’ordre des fichiers.
  • Vérifier les tailles et digests.
  • Vérifier les diff_id.
  • Vérifier les plateformes.
  • Produire amd64.
  • Produire arm64.
  • Produire un index multi-architecture.
  • Supporter plusieurs images et tags.
  • Produire plusieurs couches sémantiques.
  • Séparer toolchain, dépendances, application et métadonnées.
  • Garantir la stabilité octet par octet.
  • Expliquer toute divergence.

Construction depuis un disque legacy

  • Extraire les fichiers applicatifs sélectionnés.
  • Préserver les permissions nécessaires.
  • Préserver les propriétaires nécessaires.
  • Convertir le service principal en configuration OCI.
  • Convertir les variables d’environnement.
  • Convertir le répertoire de travail.
  • Convertir l’utilisateur et les groupes.
  • Convertir les volumes persistants.
  • Convertir les ports détectés.
  • Exclure les fichiers système inutiles.
  • Exclure les secrets par défaut.
  • Produire un rapport des éléments exclus.
  • Vérifier les dépendances dynamiques.
  • Refuser une extraction incomplète non déclarée.
  • Permettre un mode encapsulé lorsque l’extraction n’est pas possible.

Construction MicroVM

  • Produire un rootfs vérifié.
  • Produire un initramfs déterministe.
  • Produire un boot bundle adressé par contenu.
  • Épingler kernel, initramfs et ligne de commande.
  • Configurer l’entrypoint invité.
  • Configurer environnement, cwd, UID, GID et groupes.
  • Produire les métadonnées de volumes.
  • Produire les métadonnées réseau.
  • Vérifier le boot bundle.
  • Tester le démarrage avant publication lorsque la plateforme le permet.
  • Refuser les options OCI non traduites dans le guest.
  • Conserver le chemin MicroVM direct sans OCI.

Pipeline et caches

  • Utiliser le DAG multi-stage existant.
  • Exécuter les branches indépendantes en parallèle.
  • Propager les annulations.
  • Bloquer les descendants d’un stage en échec.
  • Laisser poursuivre les branches indépendantes.
  • Vérifier les digests lors des transferts d’artefacts.
  • Utiliser le CAS natif.
  • Dédupliquer blobs et chunks.
  • Reprendre les transferts interrompus.
  • Gérer leases et garbage collection.
  • Exclure la valeur des secrets des clés de cache.
  • Désactiver la persistance pour les stages sensibles.
  • Nettoyer les écritures partielles après crash ou ENOSPC.

Sécurité du build

  • Isoler chaque stage.
  • Appliquer les politiques réseau.
  • Appliquer CPU, mémoire et PIDs.
  • Appliquer les limites filesystem.
  • Monter les secrets en mémoire.
  • Ne jamais inclure les secrets dans les logs.
  • Ne jamais inclure les secrets dans les caches.
  • Ne jamais inclure les secrets dans les attestations.
  • Refuser les plugins non vérifiés.
  • Confiner fichiers et ressources des plugins.
  • Propager les budgets au pipeline complet.

Preuves natives

  • Générer la SBOM. internal/sbom/sbom.go, exposé par cmd/platform-factory/sbom.go.
  • Générer la provenance. internal/provenance/provenance.go, exposé par evidence.go/launch_publish.go.
  • Produire les attestations DSSE. internal/attestation/envelope.go.
  • Signer avec Ed25519. internal/signing/signing.go.
  • Signer avec ECDSA. internal/signing/filestore_ecdsa.go.
  • Vérifier les chaînes X.509. internal/signing/x509.go.
  • Exécuter deux rebuilds propres. Confirmé en exécution réelle (examples/reproducible-build/run.sh, 5 août 2026) : deux runs indépendants contre un CAS explicite, second run entièrement en cache: hit.
  • Comparer tous les descripteurs. Même exécution: les deux journaux (run-1, run-2) comparent chaque stage.
  • Produire un rapport de non-reproductibilité. Pas d'évidence d'un rapport dédié en cas de divergence détectée.
  • Relier les preuves au digest exact. provenancePath/evidenceDir (launch_publish.go:104) associés au digest de build.
  • Refuser un artefact non reproductible selon la politique.
  • Refuser une preuve incohérente avec le journal réel.

Sorties

  • Produire dist/oci-layout/.
  • Produire dist/boot-bundle/ lorsque nécessaire.
  • Produire dist/sbom.json.
  • Produire dist/provenance.json.
  • Produire dist/attestations/.
  • Produire dist/signatures/.
  • Produire reports/build.json.
  • Produire reports/policy.json.
  • Produire reports/reproducibility.json.
  • Produire un résumé humain lisible.

v6.5 — pf publish

Contrat utilisateur

pf publish <target>

Exemples :

pf publish registry
pf publish podman
pf publish docker
pf publish kubernetes
pf publish production

Options principales :

pf publish --target registry
pf publish --target podman
pf publish --target docker
pf publish --target kubernetes
pf publish --push-only
pf publish --deploy-only
pf publish --dry-run
pf publish --wait

Séparation des opérations

  • Séparer conceptuellement la publication d’artefact et le déploiement.
  • Permettre --push-only.
  • Permettre --deploy-only.
  • Refuser --deploy-only lorsque le digest distant attendu est absent.
  • Déployer uniquement par digest immutable.
  • Ne pas déplacer un tag après un refus de politique.
  • Produire un plan complet avant toute mutation.

v6.6 — Publication Registry

  • Utiliser le client Registry OCI natif. internal/registry/client.go, utilisé par cmd/platform-factory/lifecycle.go:21.
  • Supporter Basic. client.go:653,686 (request.SetBasicAuth).
  • Supporter Bearer. client.go:651,660 (challenge Bearer + token).
  • Reprendre les uploads. client.go:349,497 (resumeUpload).
  • Réconcilier l’offset avec le Registry. client.go:546-557 (parsing de l'en-tête Range).
  • Supporter le montage cross-repository. client.go:357-359 (MountFrom, paramètre mount/from).
  • Vérifier chaque blob distant.
  • Publier par digest avant de déplacer les tags.
  • Publier la SBOM comme artefact lié.
  • Publier la provenance comme artefact lié.
  • Publier les signatures comme artefacts liés.
  • Publier les attestations comme artefacts liés.
  • Vérifier le digest installé.
  • Nettoyer les sessions persistées.
  • Reprendre correctement après crash.
  • Refuser toute mise à jour de tag si une preuve échoue.

Compatibilité Registry

  • Tester OCI Distribution standard.
  • Tester GHCR.
  • Tester Harbor.
  • Tester Artifactory.
  • Tester ECR.
  • Tester les Registries limitant le montage cross-repository.
  • Tester les réponses Range incohérentes.
  • Tester les interruptions réseau.
  • Tester les blobs déjà présents.
  • Documenter les extensions propres aux fournisseurs.

v6.7 — Déploiement Podman

Conteneur standard

  • Importer silencieusement le layout si nécessaire.
  • Déployer avec le runtime standard par défaut.
  • Supporter plusieurs ports.
  • Supporter volumes et environnement.
  • Relayer stdin, stdout et stderr.
  • Relayer les signaux.
  • Propager le code de sortie.
  • Permettre ps, logs, inspect, stop et rm.

MicroVM platform-factory

  • Installer platform-factory-runtime. cmd/platform-factory-runtime, scripts/microvm/install-podman-runtime.sh (confirmé en exécution réelle, examples/podman-microvm/README.md).
  • Sélectionner le runtime par workload.
  • Faire représenter la vraie MicroVM dans podman ps.
  • Utiliser le superviseur VMM unique.
  • Ne créer aucun conteneur proxy.
  • Finaliser les flags Podman et conmon.
  • Implémenter create. cmd/platform-factory-runtime/main.go:138.
  • Implémenter start. main.go:164.
  • Implémenter state. main.go:151.
  • Implémenter kill. main.go:112,167.
  • Implémenter delete. main.go:161.
  • Implémenter wait. main.go:177.
  • Implémenter les logs persistants.
  • Relayer exactement stdin, stdout et stderr.
  • Relayer les signaux jusqu’au guest.
  • Propager exactement le code de sortie invité.
  • Remplacer le PID réutilisable par pidfd. main.go:31 documente la compatibilité pidfd-poll de conmon/Podman, mais le remplacement effectif du PID réutilisable n'est pas confirmé plus loin - à vérifier avant de cocher.
  • Remplacer le marqueur .start par un socket Unix READY/ACK. internal/ociruntime/supervisor_linux.go:159,168,273 (handshake READY\n sur socket Unix).
  • Nettoyer les ressources après crash.
  • Vérifier l’absence de PID orphelin.
  • Vérifier l’absence de descripteur KVM orphelin.
  • Vérifier l’absence d’état runtime orphelin.

v6.8 — Déploiement Docker et containerd

Shim containerd

  • Implémenter un shim containerd v2. plugins/containerd/cmd/platform-factory-shim/task_service.go, sandbox_service.go, main.go - exercé en conditions réelles contre un cluster kind par .github/workflows/ci-kind-multinode.yml et tests/kind/test-kind-runtimeclass.sh.
  • Partager le superviseur MicroVM existant. plugins/containerd/cmd/platform-factory-shim/plugin.go importe internal/hypervisor, le même package que cmd/platform-factory.
  • Partager l’identité du workload.
  • Partager le state store.
  • Ne pas créer une seconde source de vérité.
  • Implémenter create/start/state/kill/delete. task_service.go (TaskService), testé par task_service_test.go.
  • Implémenter exec lorsque pris en charge.
  • Relayer stdin, stdout et stderr.
  • Relayer les signaux.
  • Propager le code de sortie.
  • Gérer les événements containerd.
  • Gérer la reconnexion après redémarrage du daemon.
  • Nettoyer après crash du shim.
  • Nettoyer après crash du guest.
  • Nettoyer après crash du VMM.

Docker

  • Installer la configuration du runtime sans remplacer le runtime par défaut.
  • Permettre une sélection explicite du runtime.
  • Afficher la vraie charge MicroVM dans Docker.
  • Faire fonctionner docker ps.
  • Faire fonctionner docker logs.
  • Faire fonctionner docker inspect.
  • Faire fonctionner docker stop.
  • Faire fonctionner docker rm.
  • Faire fonctionner docker wait.
  • Documenter les limites de sélection runtime selon la version de Docker.
  • Tester le cycle complet sur un daemon réel.

Nerdctl

  • Supporter nerdctl via containerd.
  • Faire fonctionner nerdctl ps.
  • Faire fonctionner nerdctl logs.
  • Faire fonctionner nerdctl stop.
  • Faire fonctionner nerdctl rm.
  • Tester le cycle de vie complet.

v6.9 — Déploiement Kubernetes

Runtime

  • Fournir une RuntimeClass platform-factory. examples/containerd-kubernetes/runtimeclass.yaml (handler: platform-factory), générée aussi par platform-factory-containerd runtimeclass et testée contre un vrai cluster kind (ci-kind-multinode.yml, tests/kind/test-kind-runtimeclass.sh).
  • Installer le shim containerd sur les nœuds compatibles.
  • Détecter les capacités KVM, Hyper-V ou Virtualization.framework. internal/hypervisor/probe_linux.go (KVM), probe_darwin.go (HVF = Virtualization.framework), probe_windows.go (WHPX, l'API d'accès à Hyper-V) - platform-factory doctor agrège la sonde native (cmd/platform-factory/doctor.go:53-61).
  • Ajouter des labels de nœuds.
  • Ajouter les tolérances nécessaires.
  • Fournir un installateur idempotent.
  • Fournir une procédure de rollback.

Génération des manifests

  • Générer un Deployment.
  • Générer un StatefulSet.
  • Générer un DaemonSet.
  • Générer un Job.
  • Générer un CronJob.
  • Générer un Service.
  • Générer un Ingress.
  • Générer les PersistentVolumeClaim.
  • Générer les ConfigMap.
  • Générer les références aux Secret sans en copier les valeurs.
  • Générer la RuntimeClass. Voir ci-dessus, "Runtime".
  • Déployer uniquement par digest.
  • Ajouter les probes compatibles avec le guest.
  • Ajouter les ressources demandées.
  • Ajouter les politiques de sécurité.
  • Produire des manifests déterministes.

Cycle de vie

  • Faire représenter le vrai workload MicroVM dans Kubernetes.
  • Relayer les logs.
  • Relayer les signaux de terminaison.
  • Propager le code de sortie.
  • Supporter les redémarrages.
  • Supporter la suppression forcée.
  • Supporter le drainage de nœud.
  • Supporter les timeouts de terminaison.
  • Ne laisser aucune MicroVM après suppression du Pod.
  • Ne laisser aucun port ou volume orphelin.

Déploiements progressifs

  • Supporter rollout.
  • Supporter rollback.
  • Supporter blue/green.
  • Supporter canary.
  • Vérifier les preuves avant admission.
  • Bloquer le déploiement après refus de politique.
  • Relier le statut du déploiement au digest exact.

v6.10 — Réseau et stockage MicroVM

Réseau

  • Implémenter un périphérique réseau natif minimal. virtio-net sur transport virtio-mmio, internal/hypervisor/kvm (5 août 2026).
  • Supporter TAP lorsque disponible. OpenTAP/ProbeTAPSupport (internal/hypervisor/kvm/linux_tap_linux.go) ; échoue fermé sans CAP_NET_ADMIN/network namespace propre plutôt que de faire semblant.
  • Supporter un mode rootless. Limite réelle documentée (5 août 2026, trouvée via podman unshare) : CAP_NET_ADMIN dans CapEff ne suffit pas sans network namespace propre. Une couche NAT en espace utilisateur (slirp4netns/pasta-équivalent) resterait à écrire.
  • Supporter TCP. Relais hôte→invité TCP (internal/microvm/forward), prouvé de bout en bout par TestMicroVMRunNativeKVMRealBoot (5 août 2026).
  • Supporter UDP.
  • Supporter plusieurs ports. Le relais gère un port par appel Relay et platform-factory microvm run en lance un par --publish, mais cette combinaison n'a pas été prouvée pour de vrai avec plusieurs ports simultanés.
  • Supporter les redirections entrantes. C'est exactement ce que fait le relais TCP ci-dessus pour --publish - pas un NAT complet, mais le contrat réel de --publish est rempli.
  • Supporter les connexions sortantes selon la politique.
  • Fournir un DNS détenu par le projet.
  • Isoler le réseau hôte par défaut.
  • Nettoyer les interfaces après crash.
  • Nettoyer les règles de redirection après crash.

Stockage

  • Implémenter le disque virtio minimal. virtio-blk (IN/OUT/FLUSH/ GET_ID), internal/hypervisor/kvm/linux_virtio_blk.go, prouvé contre un pilote virtio_blk de noyau réel non modifié (session précédente).
  • Supporter un rootfs en lecture seule.
  • Supporter les volumes en lecture-écriture.
  • Supporter les volumes temporaires.
  • Vérifier les digests des volumes immuables.
  • Empêcher les traversées de chemins.
  • Gérer les limites de taille.
  • Gérer le flush et l’arrêt propre.
  • Nettoyer les snapshots temporaires.
  • Tester les corruptions et interruptions d’entrée/sortie.

v6.11 — Agent invité

  • Implémenter un agent invité minimal détenu par le projet. internal/guest/guest_agent.go, internal/guesttransport/, cmd/microvm-init/guest_agent.go.
  • Définir un protocole versionné. internal/guesttransport/protocol.go:21 (Version = 1), rejeté explicitement si non supporté (protocol.go:130-131).
  • Authentifier le canal hôte-invité. Enveloppes MAC-authentifiées (protocol.go:88,137-139), clé de session par démarrage (guest.GuestAgentConnector).
  • Transporter les commandes. GuestAgent.Exec (sdk/microvm/vmm.go:76).
  • Transporter les signaux. GuestAgent.Signal (vmm.go:77).
  • Transporter l’état. Pas de message d'état séparé identifié - seul l'agrégat Exec/Signal/Shutdown existe.
  • Transporter stdout. Exec(..., io.Writer, io.Writer).
  • Transporter stderr. Idem.
  • Transporter le code de sortie. Exec retourne (int, error).
  • Supporter l’arrêt propre. GuestAgent.Shutdown (vmm.go:78).
  • Supporter les heartbeats.
  • Détecter un guest bloqué.
  • Borner les messages. internal/guesttransport/agent.go:14 (MaxIOBytes); trames surdimensionnées testées par TestCodecRejectsTamperingReplayAndOversizedFrames.
  • Refuser les versions incompatibles. protocol.go:130-131, testé par TestCodecRejectsWeakKeysAndUnknownEnvelopeFields.
  • Tester les messages malformés. TestCodecRejectsTamperingReplayAndOversizedFrames, TestServeOneReturnsValidationAndHandlerErrors.
  • Tester les replays. Même test - séquences rejouées/désordonnées rejetées (protocol.go:134).
  • Tester la perte du canal. TestClientHonorsAlreadyCancelledContext, TestAgentDeadlineAndCloseLifecycle.

v6.12 — Observabilité

Commandes

  • Ajouter pf status.
  • Ajouter pf logs.
  • Ajouter pf inspect. cmd/platform-factory/main.go:141.
  • Ajouter pf events.
  • Ajouter pf doctor. main.go:110, doctor.go.
  • Ajouter pf explain.
  • Ajouter pf rollback. main.go:104, lifecycle.go:396.

Données observables

  • Journaux structurés.
  • Identifiant de trace de bout en bout.
  • Métriques de build.
  • Métriques de publication.
  • Métriques de déploiement.
  • Métriques VMM.
  • Événements de cycle de vie.
  • Codes de sortie.
  • Raisons de décision de politique.
  • Raisons de sélection container/MicroVM.
  • Diagnostics actionnables.
  • Redaction automatique des secrets.

pf doctor

  • Vérifier Git. cmd/platform-factory/doctor.go:44 (tool-git).
  • Vérifier l’accès Registry.
  • Vérifier Docker. doctor.go:45.
  • Vérifier Podman. doctor.go:46.
  • Vérifier containerd. doctor.go:47.
  • Vérifier Kubernetes. Seule la présence du binaire kubectl est vérifiée (doctor.go:48), pas la connectivité au cluster.
  • Vérifier /dev/kvm. Via hypervisor.ProbeNative sur Linux (doctor.go:53-61internal/hypervisor/kvm).
  • Vérifier les extensions KVM. Même sonde (kvm.ProbeNative).
  • Vérifier Hyper-V. Via WHPX sur Windows (probe_windows.go), l'API d'accès à Hyper-V.
  • Vérifier Virtualization.framework. Via HVF sur macOS (probe_darwin.go).
  • Vérifier les permissions.
  • Vérifier les cgroups. doctor.go:67 (sandbox-cgroups, sandbox.ProbeSandbox).
  • Vérifier les namespaces. doctor.go:66 (sandbox-namespaces).
  • Vérifier les politiques.
  • Produire des corrections proposées sans les appliquer silencieusement. doctorCheck.Suggestion - runDoctor ne fait que rapporter, jamais d'action corrective automatique.

v6.13 — Déploiement distribué

  • Relier pf build au control plane distribué.
  • Relier les vrais stages pipeline aux leases.
  • Persister l’état du scheduler.
  • Séparer scheduler et control plane.
  • Authentifier chaque rôle.
  • Ajouter le fencing par incarnation.
  • Ajouter l’annulation distante.
  • Reprendre un build après perte d’un worker.
  • Planifier selon plateforme.
  • Planifier selon capacités.
  • Planifier selon charge.
  • Planifier selon localité du cache.
  • Exécuter amd64 et arm64 en parallèle.
  • Ajouter quotas.
  • Ajouter priorités.
  • Ajouter fairness.
  • Ajouter limites par tenant.
  • Répliquer le CAS.
  • Vérifier chaque blob répliqué.
  • Signer les résultats avec l’identité du worker.
  • Rendre le journal append-only.
  • Tester partitions, replay, duplication et corruption.

v6.14 — Sécurité et stabilisation

VMM

  • Appliquer seccomp au VMM.
  • Appliquer les namespaces.
  • Appliquer les cgroups.
  • Abandonner les privilèges.
  • Borner vCPU et mémoire.
  • Borner les périphériques.
  • Protéger le state store.
  • Tester le crash du VMM.
  • Tester le crash du guest.
  • Tester le timeout de boot.
  • Tester la récupération d’état.
  • Tester les noyaux et initramfs hostiles.

CLI et API

  • Stabiliser le schéma platform-factory.yaml.
  • Stabiliser le schéma platform-factory.lock.
  • Publier une politique de compatibilité.
  • Publier une politique de dépréciation.
  • Fournir les migrations automatisées.
  • Fournir une suite de conformance publique.
  • Fournir une suite de conformance des backends.
  • Fournir une suite de conformance des cibles de publication.
  • Garantir la compatibilité des formats de sortie machine.

Audit externe

  • Obtenir une revue sécurité indépendante.
  • Auditer les parseurs de disque.
  • Auditer le Registry.
  • Auditer les plugins.
  • Auditer le VMM.
  • Auditer l’agent invité.
  • Exécuter des tests de pénétration.
  • Publier les risques résiduels.
  • Publier les procédures de réponse aux incidents.

Exploitation

  • Publier des SLO.
  • Publier les runbooks.
  • Publier les procédures de rollback.
  • Publier les procédures de récupération.
  • Tester l’installation par un opérateur indépendant.
  • Tester le rollback par un opérateur indépendant.
  • Tester une montée de version sans perte d’état.
  • Tester une montée de version sans perte de cache.
  • Tester la compatibilité avec des workloads existants.

Critères de sortie du Graal

Parcours projet moderne

Le scénario suivant doit fonctionner sans édition manuelle :

pf init ./application
pf build
pf publish kubernetes

Le système doit :

  • détecter l’écosystème ;
  • produire un plan explicable ;
  • construire un OCI reproductible ;
  • produire SBOM, provenance et signatures ;
  • publier par digest ;
  • générer les manifests ;
  • déployer la charge ;
  • exposer logs, état et code de sortie ;
  • permettre rollout et rollback.

Parcours legacy

Le scénario suivant doit fonctionner sans modification du disque source :

pf init ./legacy-server.qcow2
pf build
pf publish kubernetes

Le système doit :

  • identifier le format de disque ;
  • analyser partitions et filesystems ;
  • identifier le système d’exploitation ;
  • inventorier services et dépendances ;
  • séparer application et données ;
  • choisir container, MicroVM ou encapsulation ;
  • justifier cette décision ;
  • produire un artefact reproductible ;
  • publier les preuves ;
  • déployer avec le runtime approprié ;
  • administrer honnêtement le véritable workload.

Parcours multi-runtime

Le même artefact doit pouvoir être utilisé selon la cible :

pf publish podman
pf publish docker
pf publish kubernetes

Pour une charge MicroVM :

  • chaque moteur doit afficher la vraie MicroVM ;
  • chaque moteur doit utiliser la même identité ;
  • chaque moteur doit utiliser le même superviseur ;
  • aucun moteur ne doit utiliser de conteneur proxy ;
  • ps, logs, inspect, stop, wait et rm doivent fonctionner ;
  • signaux et codes de sortie doivent être exacts ;
  • aucune ressource ne doit rester après suppression.

Résilience

  • Un crash du builder ne laisse aucun blob partiel installé.
  • Un crash du Registry ne déplace aucun tag prématurément.
  • Un crash du runtime ne laisse aucune MicroVM orpheline.
  • Un crash du guest est visible par la plateforme.
  • Un crash du worker permet la reprise du build.
  • Une partition réseau ne provoque ni double publication ni double completion.
  • Une image altérée est refusée.
  • Une preuve ne correspondant pas au digest est refusée.
  • Une source legacy malformée ne compromet pas l’hôte.

Définition de « terminé »

Une case Graal ne peut être cochée que si :

  • l’implémentation existe dans le dépôt ;
  • le chemin est accessible via platform-factory ou pf ;
  • les entrées non fiables sont validées ;
  • des tests positifs existent ;
  • des tests négatifs existent ;
  • les limites sont documentées ;
  • les risques résiduels sont documentés ;
  • la CI exécute le chemin réel ;
  • aucune commande externe interdite ne fournit la capacité ;
  • les preuves sont liées au SHA testé ;
  • le comportement est couvert par la conformance publique ;
  • le rollback ou nettoyage est testé lorsqu’une mutation est impliquée.

Ordre d’exécution recommandé

Priorité 0 — Fermer v4

  • Finaliser Podman E2E.
  • Remplacer PID et marqueur .start. Le marqueur .start est remplacé par un handshake Unix READY/ACK (internal/ociruntime/supervisor_linux.go:159,168,273), mais le remplacement du PID réutilisable par pidfd n'est pas confirmé au-delà d'un commentaire de compatibilité (cmd/platform-factory-runtime/main.go:31)
    • laissé non coché tant que le PID lui-même n'est pas vérifié.
  • Implémenter l’agent invité. Protocole versionné et authentifié (internal/guesttransport/), agent minimal (internal/guest/guest_agent.go, cmd/microvm-init/guest_agent.go) - voir le détail complet en v6.11 ci-dessus.
  • Implémenter réseau et stockage virtio minimaux. Voir « Linux — KVM natif » dans Next-Generation-Implementation-Roadmap.md (5 août 2026) : virtqueue scindée, transport virtio-mmio, virtio-blk et virtio-net/TAP, tous prouvés contre /dev/kvm réel.
  • Relayer exactement entrées, sorties, signaux et codes de sortie.
  • Remplacer le chemin QEMU de production sur Linux. Voir la même section (« Chemin de production fermé le 5 août 2026 ») : platform-factory microvm run/start utilise kvm.RunLinuxWithOptions directement dès que l'hôte est linux/amd64 avec KVM natif et que chaque --publish est en TCP ; QEMU reste le repli réel pour arm64, hôte sans KVM et UDP, jamais supprimé.
  • Ajouter le shim containerd v2. Voir v6.8 ci-dessus - testé contre un vrai cluster kind en CI.
  • Ajouter la RuntimeClass Kubernetes. Voir v6.9 ci-dessus.

Priorité 1 — Façade Platform Factory

  • Créer platform-factory.
  • Créer l’alias pf.
  • Implémenter pf init pour les projets modernes. Source répertoire local uniquement (voir v6.1 "Sources applicatives" ci-dessus - le VM legacy et les autres sources restent v6.2/hors scope).
  • Relier pf build au moteur existant.
  • Relier pf publish au Registry et aux runtimes existants.
  • Stabiliser platform-factory.yaml. Schéma versionné (internal/project/config.go, CurrentConfigVersion), champs inconnus refusés (KnownFields(true)), framework de migration versionné (migrate.go).
  • Stabiliser platform-factory.lock. cmd/platform-factory/init.go:278-300 (lockFile{Version, Source, GitCommit}).

Priorité 2 — Import legacy en inventaire

  • Détecter les formats de disques.
  • Lire partitions et filesystems en lecture seule.
  • Inventorier OS, services, utilisateurs, ports et données.
  • Produire un rapport de compatibilité.
  • Ne réaliser aucune conversion automatique à ce stade.

Priorité 3 — Conversion legacy vers OCI

  • Extraire une application simple.
  • Convertir son service principal.
  • Construire un OCI reproductible.
  • Vérifier son exécution en conteneur.
  • Basculer vers MicroVM lorsque nécessaire.
  • Ajouter un corpus public de VM de test.

Priorité 4 — Déploiement multi-cible

  • Stabiliser Podman.
  • Stabiliser Docker.
  • Stabiliser containerd.
  • Stabiliser Kubernetes.
  • Ajouter rollout et rollback.
  • Ajouter les suites de conformance multi-moteur.

Priorité 5 — Production distribuée

  • Persister le control plane.
  • Relier les vrais builds aux workers.
  • Répliquer le CAS.
  • Ajouter HA, quotas et audit.
  • Valider les mises à niveau et récupérations.

Stabilisation, phase 1 : arbre propre et toolchain verrouillée (6 août 2026)

Suite à Sanetizer-todo.md (roadmap de stabilisation ajoutée ce jour-même), retour utilisateur explicite : geler temporairement l'ajout de capacités et exécuter en premier la phase 1 - "aucune stabilisation sérieuse n'est possible tant que l'on ne sait pas exactement quel code est testé."

  • Arbre Git propre (commit 985f08f) : .DS_Store (suivi depuis le commit initial, modifié à chaque ouverture Finder) désindexé et ajouté à .gitignore avec *.log ; pointeur du sous-module .wiki-worktree mis à jour ; git status retourne "nothing to commit, working tree clean". Tag stabilization-baseline-2026-08 posé sur ce SHA - tout rapport de test ultérieur doit s'y référer.
  • Toolchain verrouillée (commit 49ce614) : tools/go-version (source unique de vérité, 1.25.12) ; scripts/bootstrap-dev.sh/.ps1 (vérifient la version installée, puis go mod download pour le module principal et les 10 modules séparés du go.work, lus depuis son propre bloc use plutôt que dupliqués à la main) ; containers/dev/Dockerfile (image Go officielle, pinnée par son digest de manifest-list multi- architecture - vérifié via skopeo inspect --raw, pas un digest mono-plateforme, pour rester correct sur les runners CI amd64 et arm64) ; Makefile avec make bootstrap/make verify/make dev-image/make help. Tout testé pour de vrai sur cette machine (pas seulement lu) : scripts exécutés, image construite et lancée avec le dépôt monté, make bootstrap && make verify passent intégralement.
  • Découverte réelle en testant : internal/hypervisor/sandbox (table de syscalls seccomp, syscalls_linux.go) est câblée en dur pour x86_64 et ne compile pas sur linux/arm64 - jamais détecté car ci-quality.yml ne construit/teste jamais réellement le dépôt sur sa cible arm64 (seul un binaire unique y est cross-compilé). Confirmé spécifique à linux/arm64 (linux/amd64 et darwin/quelconque compilent sans problème). Volontairement non corrigé dans cette passe : une liste blanche seccomp est une frontière de sécurité, pas un endroit où deviner des numéros de syscall en marge d'un travail de verrouillage de toolchain. Documenté dans containers/dev/Dockerfile et les deux scripts de bootstrap (avertissement explicite sur hôte linux/arm64).

Stabilisation, suite : phases 2-6 (6-7 août 2026)

Suite directe de la section précédente. Travail réparti entre plusieurs sessions/collaborateurs sur le même arbre - voir Sanetize-Stabilisation/Sanetizer-progress.md pour le détail chronologique complet et l'historique de commits ; ce qui suit est le résumé côté vision produit.

  • Dépendances croisées (item 9-10) : 6/6 violations réelles corrigées. sdk/oci/sdk/pipeline ne réexportent plus directement internal/oci/internal/pipeline (API publique via api/oci/, api/pipeline/) ; internal/policy → internal/plugin corrigé via api/plugin/manifest.go ; internal/executor → internal/cache et → internal/networking corrigés via des interfaces core.CacheStore/ core.NetworkRelay ; internal/assemble → internal/cache corrigé de la même façon. internal/archtest/ (nouveau) fait maintenant partie de make verify et détecte automatiquement les imports interdits - ce qui manquait explicitement dans mon évaluation précédente est fait. Reste ouvert et documenté honnêtement dans Sanetizer-todo.md : internal/assemble → internal/oci (pas encore traité) et les 21 « inversions » signalées ont été triées (5 faux positifs sur internal/errors, 3 sur directboot→hypervisor, 1 déjà connu, 12 réelles dont 6 corrigées ici).
  • Négociation de capacités (item 12) - complète. internal/plugin/registry.go : Registry indexé par capacité et par famille, HasCapability/GetPluginsWithCapability/ GetPluginsByFamily/RequireCapability. Manifestes réels mis à jour avec capacités en dot-notation (plugins/containerd/plugin.json, plugins/kubevirt/plugin.json, plugins/lang-go/plugin.json). Le cœur peut désormais demander « qui fournit deployment.apply ? » au lieu de coder en dur une identité de plugin.
  • Propagation trace_id bout en bout (item 18) - complète. cmd/platform-factory/main.go crée un contexte tracé à l'entrée de chaque commande ; runBuildWithContext/runPublishWithContext/ runDeployWithContext le propagent jusqu'aux appels registry/plugin ; le protocole sdk/plugin porte TraceID dans Request/Response. Format robuste 10+ ans (v1-{origin}-{command}-{24 hex} : PID + StartTime + compteur atomique + hash hostname + UID).
  • Idempotence par OperationID (item 13) - complète. internal/plugin/operation_journal.go : journal durable (un fichier par opération, écriture atomique temp+rename, survit à un crash processus entre « démarré » et « confirmé » - le scénario que l'item exige explicitement de tester). Client.CallWithIdempotency déduplique les appels RPC mutants par OperationID, compare-and-set protégé par mutex. 13 tests sur le journal, tous verts.
  • Signature et provenance des plugins (item 15) - digest/ signature/identité déjà solides, révocation ajoutée. api/plugin/manifest.go pin déjà le digest, vérifie une signature Ed25519, et VerifyAndStart recoupe le handshake réel du plugin contre le manifeste déclaré. Ajouté : TrustPolicy.RevokedKeyIDs/ RevokedDigests + IsRevoked() - un plugin correctement signé et dont le digest correspond est quand même refusé s'il est révoqué. Testé de bout en bout avec un vrai sous-processus plugin. Non fait : provenance de build (qui a construit le binaire, depuis quelle source) - aucun champ ni vérification pour ça encore.
  • Sandbox par niveau de risque (item 14) - une règle concrète. Les plugins de famille language ne peuvent plus déclarer de permission réseau (Validate() refuse tout manifeste language avec permissions.network non vide) - la seule règle du modèle PluginPermissions (listes de chaînes) assez concrète pour être vérifiée sans mentir. Les règles containerd/KubeVirt (socket explicite, RBAC minimal, namespace borné) ne se réduisent pas à une vérification booléenne avec ce modèle et ne sont pas appliquées - documenté comme tel plutôt que de prétendre le contraire.

Ce qui reste ouvert, sans enjolivure : items 11 (négociation de version de protocole à l'exécution - le schéma existe, l'application au dispatch réel non), 16-17 (taxonomie d'erreurs/codes de sortie stables - internal/errors/taxonomy.go construit et testé, pas câblé dans les handlers CLI), 19-31 (pyramide de tests, chaos, fuzzing, audit de sécurité, SLO, runbooks, matrice de release), l'extraction complète de cmd/platform-factory en couche applicative (doctor/sbom/verify faits ; build/publish/deploy/runtime non, délibérément - trop gros et trop sensible pour être bâclés en fin de budget de session), la dette d'interfaces domaine/implémentation pour les 12 inversions restantes, et l'écart de compilation linux/arm64 du sandbox VMM. Rien de tout cela n'est caché : chaque item ouvert porte sa propre note d'évidence dans Sanetize-Stabilisation/Sanetizer-todo.md et Sanetizer-progress.md.


Démonstration finale

Le Graal est atteint lorsque cette séquence fonctionne réellement :

pf init legacy-server.qcow2
pf build
pf publish kubernetes

et que Platform Factory :

  1. analyse la source sans la modifier ;
  2. identifie le workload et ses dépendances ;
  3. choisit entre conteneur, MicroVM OCI ou encapsulation ;
  4. produit un artefact déterministe ;
  5. génère SBOM, provenance, attestations et signatures ;
  6. applique les politiques ;
  7. publie l’artefact par digest ;
  8. déploie le véritable workload ;
  9. expose son cycle de vie complet ;
  10. garantit qu’aucune ressource n’est laissée orpheline.

À ce stade, Platform Factory n’est plus seulement le nouveau nom des binaires, variables d’environnement et identifiants v1 à v5 (rebaptisés le 5 août 2026). Elle devient le produit qui orchestre proprement les capacités construites de v1 à v5.

Clone this wiki locally