-
Notifications
You must be signed in to change notification settings - Fork 0
Meine Graal
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
- 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.
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 ─┘
- 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éé parcmd/platform-factory-installeretscripts/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é parTestCompletionAndCommandFormatting. - 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.mdexiste 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_IDexiste et est propagé pourplatform-factory build(cmd/platform-factory/main.go:613), mais pas vérifié sur les autres commandes - "toute opération" reste à prouver.
- 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, etinit.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'environnementPLATFORM_FACTORY_TRACE_IDexiste (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(flagbuild). - Garantir un comportement homogène entre les commandes.
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é parTestRunInitLanguageMenuRejectsOutOfRangeChoice,TestResolveEcosystemInteractivelyRejectsOutOfRangeChoice. - Messages d'erreur reformulés en langage clair avec la correction
suggérée, sur le chemin
pf init→pf 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 derequirements.txt/Maven- Gradle, et les deux erreursValidate()les plus fréquentes (internal/project/config.go). Une régression a été trouvée et corrigée en cours de route :resolveFreezeStepsdétectait le cas "pas d'adaptateur intégré" par unstrings.Containssur 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.
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 parBuildlui-même - rien de ce qu'un plugin prétend n'est jamais cru sur parole. 8 tests, dont vérification indépendante viainternal/layout.Verifyet déterminisme. -
sdk/langplugin(sdk/langplugin/langplugin.go) : l'unique implémentation partagée deWriteDeterministicTar(trié, timestamps à zéro, symlinks refusés) que chaque moduleplugins/lang-*appelle pour sonbuild-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-dossiercmd/). Chaque module dépend du module principal pour une seule chose -sdk/langplugin- via le même schéma
require+replace ... => ../..queplugins/containerd/plugins/kubevirtutilisent déjà pour leurs propres dépendances sdk (sdk/microvm, etc.). Le sens de dépendance ne va jamais deplugin/internalversplugins: un plugin de langage importesdk/langplugin(une surface d'API publique et versionnée, pas un paquet interne) ; rien dans le module principal n'importe jamais depuisplugins/*, et aucun plugin n'importesdk/pluginniinternal/plugin.freezereproduit 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,freezeredirige 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-layerappellesdk/langplugin.WriteDeterministicTarsur ce que freeze a installé.
- via le même schéma
-
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é parlanguage_plugin.go) ne trouve que ce qui a été explicitement chargé viapf plugin load, dans~/.platform-factory/plugins(surchargeable viaPLATFORM_FACTORY_LANG_PLUGIN_DIR, commePLATFORM_FACTORY_PLUGIN_DIRle 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 binaireplatform-factoryen cours d'exécution, puis sur PATH) ;pf plugin load --from PATH nompour 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 casgo build -o <temp> .tourne d'abord automatiquement (prepareSourcedansplugin.go, côté CLI, pas dans le SDK) :pf plugin load --from ./mon-plugin-src nomfonctionne 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 danssdk/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 dansplugin_test.go(dont build réel d'un module Go minimal) + 9 dansregistry_test.go.
- PATH peut être un binaire déjà construit, ou un répertoire contenant
son module Go (
-
cmd/platform-factory/language_plugin.go: nouveau champlanguage_plugin: truedansplatform-factory.yaml- opt-in explicite, aucune détection automatique d'un binaire simplement présent. Le dispatch prend maintenant unpluginResolverinjectable (production :sdk/langplugin.Resolve; tests : un faux résolveur), plus l'executeexistant.buildProjectappelleplatform-factory-lang-<langage> build-layersur le chemin résolu et ajoute le résultat àExtraLayers. 7 tests avec résolveur + exécuteur simulés. -
go/customrestent sans moduleplugins/lang-*:gofige viago mod tidy/go mod vendor(rien à rediriger, le vendoring est déjà dans l'arbre) etcustomn'a par définition aucune étape freeze intégrée à reproduire. - L'installeur (
cmd/platform-factory-installer) ne construit/ne fournit pas encore les binairesplugins/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.
- Utiliser les bibliothèques internes existantes sans recopier leur
logique dans la CLI.
cmd/platform-factoryimporte directementinternal/oci,internal/executor,internal/layout,internal/networking,internal/plugin,internal/registry,internal/sbom,internal/provenance,internal/signing,internal/attestationplutôt que de les réimplémenter. - Relier
pf buildau pipeline et au constructeur OCI existants.cmd/platform-factory/main.goimporteinternal/oci;pipeline.gorelie au moteur de pipeline existant. - Relier
pf publishau Registry natif existant.cmd/platform-factory/lifecycle.go:21importeinternal/registry. - Relier les preuves au moteur natif de SBOM, provenance et signature.
cmd/platform-factory/sbom.go/evidence.go/lifecycle.goimportentinternal/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:166etplugins/containerd/cmd/platform-factory-shim/plugin.goimportent tous deuxinternal/hypervisor/internal/hypervisor/kvm- même superviseur, pas une copie. - Ne pas créer une seconde base d’état propre à Platform Factory.
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- Détecter la racine du projet.
pf initopè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.gitest absent). - Refuser d’écraser un dépôt Git existant. Skip inconditionnel si
.gitexiste déjà; testé parTestRunInitSkipsAnAlreadyExistingGitRepo. - Générer un
.gitignore.init.go:177-180, préserve un fichier existant - testé parTestRunInitPreservesAnExistingGitignore. - 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 unpolicies/default.jsonréel chargeable parinternal/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.
fileExistssaute tout répertoire/fichier déjà présent avant de l'ajouter au plan. - Produire un plan avant toute écriture.
buildInitPlanne fait que stat/lire;commitInitPlan(séparée) exécute;--dry-runimprime le plan sans écrire - testé parTestRunInitDryRunWritesNothing. 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.yamlgénéré est revalidé parproject.Loadavant 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é parTestRunInitRollsBackEverythingOnMidPlanFailure.
- Découvrir les fichiers
.config_image.yaml.internal/project/config.go:31(ConfigNames), utilisé parfindLegacyConfigInDir. - 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.jsondans la même liste. - Importer leur contenu vers
platform-factory.yaml.init.go:142-151(actionMigrateConfig) - testé parTestRunInitMigratesLegacyConfigAndRemovesOldFile. - 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.
- 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é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: customavecbuild_command/freeze_commandexplicites (internal/project/files.go:21, message deinit.go:266-270). - Détecter les projets multi-écosystèmes.
detect.gosignale 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é parTestDetectDirectoryAndRequireAmbiguity. 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 placeholderREPLACE_MEnon fonctionnel quand aucun des deux n'est possible (TestRunInitAmbiguousEcosystemNonInteractiveFailsClosedWithoutWritingJunk) : unplatform-factory.yamlqui 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 (LegacyDisksexempteValidate(),internal/project/config.go). - Produire un inventaire de gel SHA-256.
platform-factory project freezeexiste déjà (.platform-factory/freeze.lock.json) maispf initne 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.
- 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.Detectn'appelle jamais mount/loop-attach; échoue fermé (ErrUnsupportedFormat) sur tout en-tête non reconnu plutôt que de deviner - testé parTestDetectRejectsUnrecognizedContent. - Valider les en-têtes et tailles avant lecture approfondie.
minDiskSize(512 o) rejeté avant toute lecture de contenu - testé parTestDetectRejectsTooSmallFile/TestDetectRejectsEmptyFile. - Borner le temps, la mémoire et le nombre d’objets analysés.
headerWindow(64 KiB) etfooterWindow(512 o) bornent chaque lecture quelle que soit la taille réelle du fichier - testé parTestDetectBoundsHeaderReadOnSparseFile(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.
- 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.
- 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.shattache chaque disque (le disque de boot en premier), etpf initscanne et déclare tous les disques trouvés danslegacy_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.
- 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.
- 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.
- 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,/syset/dev. - Identifier le processus principal probable.
- Produire un niveau de confiance pour chaque détection.
- 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.
Platform Factory doit choisir ou proposer l’un des modes suivants :
container
microvm-oci
microvm-direct
vm-encapsulation
unsupported
- 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.
- 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.
- É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.
pf buildOptions principales :
pf build --target container
pf build --target microvm
pf build --target auto
pf build --verify-only
pf build --reproducible
pf build --no-cache- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- Générer la SBOM.
internal/sbom/sbom.go, exposé parcmd/platform-factory/sbom.go. - Générer la provenance.
internal/provenance/provenance.go, exposé parevidence.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 encache: 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.
- 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.
pf publish <target>Exemples :
pf publish registry
pf publish podman
pf publish docker
pf publish kubernetes
pf publish productionOptions 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éparer conceptuellement la publication d’artefact et le déploiement.
- Permettre
--push-only. - Permettre
--deploy-only. - Refuser
--deploy-onlylorsque 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.
- Utiliser le client Registry OCI natif.
internal/registry/client.go, utilisé parcmd/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êteRange). - Supporter le montage cross-repository.
client.go:357-359(MountFrom, paramètremount/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.
- 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.
- 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,stopetrm.
- 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:31documente 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
.startpar un socket Unix READY/ACK.internal/ociruntime/supervisor_linux.go:159,168,273(handshakeREADY\nsur 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.
- 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.ymlettests/kind/test-kind-runtimeclass.sh. - Partager le superviseur MicroVM existant.
plugins/containerd/cmd/platform-factory-shim/plugin.goimporteinternal/hypervisor, le même package quecmd/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é partask_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.
- 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.
- 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.
- Fournir une
RuntimeClassplatform-factory.examples/containerd-kubernetes/runtimeclass.yaml(handler: platform-factory), générée aussi parplatform-factory-containerd runtimeclasset 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 doctoragrè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é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
Secretsans 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.
- 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.
- 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.
- 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é sansCAP_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_ADMINdansCapEffne 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 parTestMicroVMRunNativeKVMRealBoot(5 août 2026). - Supporter UDP.
- Supporter plusieurs ports. Le relais gère un port par appel
Relayetplatform-factory microvm runen 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--publishest 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.
- Implémenter le disque virtio minimal. virtio-blk (IN/OUT/FLUSH/
GET_ID),
internal/hypervisor/kvm/linux_virtio_blk.go, prouvé contre un pilotevirtio_blkde 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.
- 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/Shutdownexiste. - Transporter stdout.
Exec(..., io.Writer, io.Writer). - Transporter stderr. Idem.
- Transporter le code de sortie.
Execretourne(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 parTestCodecRejectsTamperingReplayAndOversizedFrames. - Refuser les versions incompatibles.
protocol.go:130-131, testé parTestCodecRejectsWeakKeysAndUnknownEnvelopeFields. - 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.
- 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.
- 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.
- 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
kubectlest vérifiée (doctor.go:48), pas la connectivité au cluster. - Vérifier
/dev/kvm. Viahypervisor.ProbeNativesur Linux (doctor.go:53-61→internal/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-runDoctorne fait que rapporter, jamais d'action corrective automatique.
- Relier
pf buildau 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.
- 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.
- 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.
- 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.
- 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.
Le scénario suivant doit fonctionner sans édition manuelle :
pf init ./application
pf build
pf publish kubernetesLe 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.
Le scénario suivant doit fonctionner sans modification du disque source :
pf init ./legacy-server.qcow2
pf build
pf publish kubernetesLe 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.
Le même artefact doit pouvoir être utilisé selon la cible :
pf publish podman
pf publish docker
pf publish kubernetesPour 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,waitetrmdoivent fonctionner ; - signaux et codes de sortie doivent être exacts ;
- aucune ressource ne doit rester après suppression.
- 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.
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-factoryoupf; - 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.
- Finaliser Podman E2E.
- Remplacer PID et marqueur
.start. Le marqueur.startest 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/kvmré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/startutilisekvm.RunLinuxWithOptionsdirectement dès que l'hôte est linux/amd64 avec KVM natif et que chaque--publishest 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.
- Créer
platform-factory. - Créer l’alias
pf. - Implémenter
pf initpour 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 buildau moteur existant. - Relier
pf publishau 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}).
- 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.
- 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.
- Stabiliser Podman.
- Stabiliser Docker.
- Stabiliser containerd.
- Stabiliser Kubernetes.
- Ajouter rollout et rollback.
- Ajouter les suites de conformance multi-moteur.
- 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.
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é à.gitignoreavec*.log; pointeur du sous-module.wiki-worktreemis à jour ;git statusretourne "nothing to commit, working tree clean". Tagstabilization-baseline-2026-08posé 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, puisgo mod downloadpour le module principal et les 10 modules séparés dugo.work, lus depuis son propre blocuseplutôt que dupliqués à la main) ;containers/dev/Dockerfile(image Go officielle, pinnée par son digest de manifest-list multi- architecture - vérifié viaskopeo inspect --raw, pas un digest mono-plateforme, pour rester correct sur les runners CI amd64 et arm64) ;Makefileavecmake 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 verifypassent 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é carci-quality.ymlne 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é danscontainers/dev/Dockerfileet les deux scripts de bootstrap (avertissement explicite sur hôte linux/arm64).
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/pipelinene réexportent plus directementinternal/oci/internal/pipeline(API publique viaapi/oci/,api/pipeline/) ;internal/policy → internal/plugincorrigé viaapi/plugin/manifest.go;internal/executor → internal/cacheet→ internal/networkingcorrigés via des interfacescore.CacheStore/core.NetworkRelay;internal/assemble → internal/cachecorrigé de la même façon.internal/archtest/(nouveau) fait maintenant partie demake verifyet détecte automatiquement les imports interdits - ce qui manquait explicitement dans mon évaluation précédente est fait. Reste ouvert et documenté honnêtement dansSanetizer-todo.md:internal/assemble → internal/oci(pas encore traité) et les 21 « inversions » signalées ont été triées (5 faux positifs surinternal/errors, 3 surdirectboot→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:Registryindexé 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 fournitdeployment.apply? » au lieu de coder en dur une identité de plugin. - Propagation
trace_idbout en bout (item 18) - complète.cmd/platform-factory/main.gocrée un contexte tracé à l'entrée de chaque commande ;runBuildWithContext/runPublishWithContext/runDeployWithContextle propagent jusqu'aux appels registry/plugin ; le protocolesdk/pluginporteTraceIDdansRequest/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.CallWithIdempotencydéduplique les appels RPC mutants parOperationID, 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.gopin déjà le digest, vérifie une signature Ed25519, etVerifyAndStartrecoupe 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
languagene peuvent plus déclarer de permission réseau (Validate()refuse tout manifestelanguageavecpermissions.networknon vide) - la seule règle du modèlePluginPermissions(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.
Le Graal est atteint lorsque cette séquence fonctionne réellement :
pf init legacy-server.qcow2
pf build
pf publish kuberneteset que Platform Factory :
- analyse la source sans la modifier ;
- identifie le workload et ses dépendances ;
- choisit entre conteneur, MicroVM OCI ou encapsulation ;
- produit un artefact déterministe ;
- génère SBOM, provenance, attestations et signatures ;
- applique les politiques ;
- publie l’artefact par digest ;
- déploie le véritable workload ;
- expose son cycle de vie complet ;
- 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.
© 2026 CYPT71
platform-factory
Core
- Architecture and OCI Layout
- Next-generation Architecture
- Architecture Decision Records
- Security Model
- Threat Model and Residual Risks
- Independent Security Review Process
- CLI Reference
- Project Configuration and Dependency Freezing
- mTLS Configuration
- Meine Graal
CI/CD
Running an image
- Production Adoption Guide
- Dockerfile Consumer
- Local Dev (Podman/macOS)
- MicroVM Support
- MicroVM Administration
- Large-image streaming
Operating