--- id: "f5296d8b-6bef-45c4-ac8a-cf6e0f90ae9c" number: 133 title: "Plugins: contrat de service des assets idea-plugin:// (multi-fichiers ESM) & confinement" status: "closed" priority: "high" sprint: null links: [] agentRefs: [{"agentId":"b4730d7f-c54d-4736-8a04-c6203aa2fd49","role":"assigned"}] attachments: [] createdBy: {"kind":"agent","agent_id":"b4730d7f-c54d-4736-8a04-c6203aa2fd49"} updatedBy: {"kind":"user"} createdAt: 1785702640094 updatedAt: 1785881187645 version: 5 --- Bug diagnostiqué : `crates/app-tauri/src/plugins.rs:504-536` (`asset_allowed`) n'autorise que `main`/`icon` déclarés au manifeste, ou un chemin préfixé `assets/`. Tout import ESM relatif secondaire (`./constants.js`, `./core/x.js`) depuis le `main` est donc rejeté 403 → "Importing a module script failed." côté navigateur. Le SDK (sdk/IdeaSDK/README.md) documente `main: dist/index.js` comme point d'entrée sans jamais imposer un bundle mono-fichier, ce qui sous-entend un support multi-fichiers jamais réellement vérifié (l'exemple hello-plugin est mono-fichier). Objectif de ce ticket : trancher le contrat d'architecture, PAS l'implémenter. À décider et documenter : 1. Élargir la politique de service à : tout chemin relatif confiné du package installé, dès lors que `entry.content_hash == hash` (intégrité du package entier déjà vérifiée) ET `entry.lifecycle_state.is_runtime_active()` ET confinement canonicalize (`target.starts_with(root)`, déjà en place lignes 467-483). Ces trois garanties suffisent déjà sans dépendre d'une déclaration par-fichier dans le manifeste. 2. Figer la limite explicite : imports ESM relatifs uniquement, pas de résolution `node_modules`/bare specifiers (hors scope, pas de résolveur de modules à construire) — un plugin qui a des dépendances tierces doit les vendorer en relatif ou les bundler lui-même, à son choix, jamais une obligation d'IdeA. 3. Formaliser le contrat de confinement + désinstallation propre : aucune écriture ne doit jamais sortir de `app_data/plugins/installed/` (pas de pollution project root ni `.ideai/`), et la désinstallation doit être 100% (dossier + entrée registry, zéro résidu), à la manière VSCode. Livrable : note d'architecture (+ mise à jour de la doc plugin existante si présente) qui fait foi pour les tickets d'implémentation liés (DevBackend, SDK/doc, QA). Critères d'acceptation : - Le contrat écrit référence explicitement le code actuel (plugins.rs:504-536) et explique pourquoi hash+lifecycle+confinement remplacent l'allowlist par fichier sans régression de sécurité. - La limite bare-specifiers/node_modules est tranchée noir sur blanc (in ou out, et pourquoi). - Le contrat de confinement/désinstallation est écrit explicitement (racine autorisée, ce qui est interdit, ce que "propre" veut dire).