Cycle de vie & proxy
Au démarrage, un module s'enregistre auprès du core ; ensuite le core proxifie ses routes et surveille sa santé par heartbeat.
Enregistrement #
Le module appelle les routes /internal/* du core (sur 127.0.0.1:8080), protégées par l'en-tête X-Internal-Secret. Il transmet son identité, son base_url, ses routes, ses entrées de sidebar, ses événements souscrits et sa version. Le core conserve l'instance en mémoire et en base (table core.module_instances).
En pratique, l'enregistrement au démarrage se réduit à un POST — dans le langage de votre choix :
// Rust — reqwest
let core = std::env::var("KC__CORE__URL")?;
let secret = std::env::var("KC__CORE__INTERNAL_SECRET")?;
reqwest::Client::new()
.post(format!("{core}/internal/modules/register"))
.header("X-Internal-Secret", secret)
.json(&serde_json::json!({
"module_id": "demo",
"base_url": "http://127.0.0.1:3120",
"version": "0.1.0"
}))
.send().await?;<?php
// PHP — cURL
$core = getenv('KC__CORE__URL');
$secret = getenv('KC__CORE__INTERNAL_SECRET');
$ch = curl_init("$core/internal/modules/register");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"X-Internal-Secret: $secret",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"module_id" => "demo",
"base_url" => "http://127.0.0.1:3120",
"version" => "0.1.0",
]),
]);
curl_exec($ch);# Python — requests
import os, requests
core = os.environ["KC__CORE__URL"]
secret = os.environ["KC__CORE__INTERNAL_SECRET"]
requests.post(
f"{core}/internal/modules/register",
headers={"X-Internal-Secret": secret},
json={
"module_id": "demo",
"base_url": "http://127.0.0.1:3120",
"version": "0.1.0",
},
)// Go — net/http
core := os.Getenv("KC__CORE__URL")
secret := os.Getenv("KC__CORE__INTERNAL_SECRET")
body, _ := json.Marshal(map[string]string{
"module_id": "demo",
"base_url": "http://127.0.0.1:3120",
"version": "0.1.0",
})
req, _ := http.NewRequest("POST", core+"/internal/modules/register", bytes.NewReader(body))
req.Header.Set("X-Internal-Secret", secret)
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)# Perl — LWP::UserAgent
use LWP::UserAgent;
use JSON::PP;
my $core = $ENV{'KC__CORE__URL'};
my $secret = $ENV{'KC__CORE__INTERNAL_SECRET'};
my $ua = LWP::UserAgent->new;
$ua->post("$core/internal/modules/register",
'X-Internal-Secret' => $secret,
'Content-Type' => 'application/json',
Content => encode_json({
module_id => "demo",
base_url => "http://127.0.0.1:3120",
version => "0.1.0",
}),
);Le secret et l'URL du core sont fournis au processus par l'environnement (KC__CORE__INTERNAL_SECRET, KC__CORE__URL). Voir Configuration.
Cycle de vie #
starting ──► healthy ──(heartbeat manqué)──► degraded ──► stopped- heartbeat — le module ping périodiquement le core pour rester
healthy. - unregister — à l'arrêt propre, le module se retire du registre.
Le reverse-proxy #
Le core intercepte /api/v1/<module_id>/* et le réécrit vers http://127.0.0.1:<port>/*, en injectant l'identité authentifiée :
Requête navigateur : GET /api/v1/calendar/events
▼ (core : auth + réécriture)
Vers le module : GET http://127.0.0.1:3102/events
+ X-Internal-Secret, X-Kubuno-User-Id, X-Kubuno-User-Role, X-Kubuno-User-EmailLe module n'est jamais exposé directement : il n'écoute que sur 127.0.0.1, et tout passe par le core qui authentifie et réécrit.