Skip to content

ExtensionHub

ExtensionHub est le point d'entrée unique pour étendre GISPulse — qu'il s'agisse de plugins Python (capabilities, routers, sinks…) ou de contenus déclaratifs (templates, basemaps, projections, zonages). Il a été livré en 1.8.0 (chantier C des Foundations) et exposé tel quel dans 2.0.0 ; PluginHub reste disponible comme alias deprecated jusqu'en 2.1.0.

Pourquoi le rename ?

Le rename PluginHubExtensionHub n'est pas cosmétique. Il acte une décision d'architecture : l'extensibilité de GISPulse ne se réduit plus aux plugins de code. La v1.8.0 introduit explicitement un second régime — les data packs — qui n'embarquent aucun code Python et se chargent à partir d'un manifeste déclaratif. Garder le nom PluginHub aurait laissé entendre que tout passe par un entry-point Python à importer ; ExtensionHub reflète l'inventaire unifié des deux régimes.

diff
- from core.plugin_hub import PluginHub
+ from gispulse.core.plugin_hub import ExtensionHub

L'alias PluginHub = ExtensionHub est défini dans le même module ; aucun changement de code n'est nécessaire avant la 2.1.0 (voir la migration 2.0).

Les deux régimes en un coup d'œil

                        ┌─────────────────────────────────┐
                        │           ExtensionHub          │
                        │  (singleton, lazy, thread-safe) │
                        └────────────┬────────────────────┘

              ┌──────────────────────┴───────────────────────┐
              │                                              │
        ╔════════════╗                              ╔══════════════╗
        ║  Régime    ║                              ║   Régime     ║
        ║   CODE     ║                              ║  DATA PACK   ║
        ╚════════════╝                              ╚══════════════╝
              │                                              │
   ┌──────────┴──────────┐                       ┌───────────┴────────────┐
   │ entry_points        │                       │ bundle OSS             │
   │ ─ gispulse.routers  │                       │ ─ templates/manifest.yml│
   │ ─ gispulse.capabili │                       │                        │
   │ ─ gispulse.sources  │                       │ entry_point            │
   │ ─ gispulse.sinks    │                       │ ─ gispulse.data_packs  │
   │ ─ gispulse.protocol │                       │                        │
   │ ─ gispulse.mcp_*    │                       │ folder                 │
   │ ─ gispulse.middlewa │                       │ ─ $GISPULSE_DATA_PACKS_DIR │
   │ ─ gispulse.licence  │                       │                        │
   │ ─ ...               │                       └────────────────────────┘
   └─────────────────────┘
   import + instanciation                       lecture YAML/JSON
   trust = first_party / verified                trust = idem
           / community                           signature Ed25519 facultative
RégimeSourceCe qui est chargé
Codeentry-points Python via importlib.metadataclasses (capabilities, routers, middleware…)
Data packmanifestes YAML/JSON (bundle / PyPI / folder)DataPackManifest purement déclaratif

Les deux régimes produisent des PluginRecord unifiés, classés par PluginKind (SOURCE / CAPABILITY / SINK / PROTOCOL / EXTENSION / DATA_PACK). Le tier/trust est résolu pour les deux de la même façon — un seul gate par activation, pas de chemin parallèle.

Régime « code plugin »

Treize entry-point groups sont scannés. Les neuf premiers extensionnent l'app FastAPI hôte (router, middleware, providers, lifecycle, MCP), les quatre derniers étendent le graphe ETL :

Entry-point groupRôle
gispulse.routersSous-routers FastAPI (admin, billing…)
gispulse.middlewareMiddlewares ASGI
gispulse.auth_providerProviders d'authentification (AuthProvider)
gispulse.billing_providerProviders de facturation (BillingProvider)
gispulse.licence_providerProviders de licence (LicenceProvider)
gispulse.connectorsConnecteurs de session (DuckDB / PostGIS / …)
gispulse.lifecycleHooks de cycle de vie de l'app
gispulse.mcp_toolsOutils MCP supplémentaires (cf. page MCP)
gispulse.mcp_resourcesRessources MCP supplémentaires
gispulse.catalog_providersCatalog providers (legacy ETL, promus en DATA_PACK v1.8)
gispulse.data_sourcesSources ETL (DeclarativeSource)
gispulse.data_sinksSinks ETL
gispulse.protocolsAdaptateurs de transport (fetch/write)

Exposer un capability

toml
# pyproject.toml du paquet plugin
[project.entry-points."gispulse.capabilities"]
my_capability = "my_pkg.my_module:capability_factory"

Le moteur boucle sur le contrat (Capability, DeclarativeSource, AuthProvider…) défini par gispulse.core.plugin_contracts. Le PROTOCOL_VERSION (actuellement "1.1") doit être déclaré côté plugin via requires_protocol = ">=1.0,<2.0" — un mismatch déclenche un warn mais ne bloque pas l'activation (issue #182).

Trust et tier — gating commercial

Trois niveaux de confiance dérivés du registre marketplace (marketplace/registry.json) :

TrustOrigine typique
first_partyDistributions gispulse / gispulse-enterprise
verifiedPaquet PyPI tiers listé dans marketplace/registry.json avec un tier connu
communityPaquet PyPI tiers inconnu du registre

La variable GISPULSE_PLUGINS_ALLOW_UNVERIFIED (défaut true) contrôle si les plugins community peuvent s'activer ; sur un déploiement sensible, la fixer à false rejette tout plugin hors marketplace.

Le tier (community / pro / team / enterprise) est récupéré depuis le registre, croisé avec la licence active (résolue par LicenceProvider.current()), et un plugin demandant un tier supérieur à celui de l'org bascule en PluginState.LOCKED.

Régime « data pack »

Détaillé sur la page Data packs. Récapitulatif :

  • déclaratif (DataPackManifest), zéro import,
  • trois canaux : bundle OSS, entry-point gispulse.data_packs, dossier GISPULSE_DATA_PACKS_DIR,
  • signature Ed25519 optionnelle pour les manifestes EXTERNAL (story G1a #271),
  • contenus typés : template-pack / source-catalog / basemap-pack / projection-pack / regulatory-zoning.

Cycle de vie d'un record

discovered  →  resolve  →  gate  →  activate
                              ↘  LOCKED  (tier non satisfait)
                              ↘  FAILED  (exception au load)
                              ↘  ACTIVE  (chargé et câblé)

Inspectable via gispulse plugins list (CLI) ou l'outil MCP list_plugins() (page MCP).

Migration depuis 1.7.x

Avant 1.8.0Après 1.8.0 / 2.0.0
from core.plugin_hub import PluginHubfrom gispulse.core.plugin_hub import ExtensionHub
PluginHub.get()ExtensionHub.get() (alias PluginHub.get() toujours OK)
Templates ajoutés via copie dans templates/Manifeste template-pack (PyPI ou folder)
Catalog providers via gispulse.catalog_providerstoujours scanné, promus en DataPack côté inventaire

Voir aussi la migration 2.0.

Références code

Published under AGPL-3.0 license.