Quickstart
Build and install a module locally. Every module is self-contained: it pulls its shared dependencies from git tags and npm packages, with no need for the sibling repositories.
1. Clone and build #
git clone https://github.com/kubuno/calendar && cd calendar
cargo build --release
(cd frontend && npm ci && npm run build)2. Package and install #
bash build_deb.sh --install # → dist/kubuno-<id>_*.deb puis apt install localThe installed layout is /usr/lib/kubuno/modules/<id>/…. A module must have been installed once to exist in that directory.
3. Fast dev loop #
To iterate without rebuilding a .deb:
bash _tools/deploy_local.sh <module> # backend + frontend
bash _tools/deploy_local.sh <module> --frontend # frontend seul (le plus rapide)Your first endpoint — in the language of your choice #
A module is nothing more than a local HTTP service that the core proxies. The core natively supervises the Rust, Python and Node runtimes, but any language that speaks HTTP can be a module: declare runtime = "binary" in the manifest and provide the executable. Here is the same /ping endpoint in five languages:
// Rust — Axum
use axum::{routing::get, Json, Router};
use serde_json::json;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/ping", get(|| async { Json(json!({ "ok": true })) }));
let l = tokio::net::TcpListener::bind("127.0.0.1:3190").await.unwrap();
axum::serve(l, app).await.unwrap();
}<?php
// PHP — lancé avec : php -S 127.0.0.1:3190 router.php
header('Content-Type: application/json');
if (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) === '/ping') {
echo json_encode(['ok' => true]);
exit;
}
http_response_code(404);# Python — Flask
from flask import Flask, jsonify
app = Flask(__name__)
@app.get("/ping")
def ping():
return jsonify(ok=True)
app.run(host="127.0.0.1", port=3190)// Go — net/http
package main
import (
"encoding/json"
"net/http"
)
func main() {
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
})
http.ListenAndServe("127.0.0.1:3190", nil)
}# Perl — Mojolicious::Lite
use Mojolicious::Lite -signatures;
get '/ping' => sub ($c) {
$c->render(json => { ok => \1 });
};
app->start('daemon', '-l', 'http://127.0.0.1:3190');The port (3190 here) must match the one declared under [server] in module.toml. The module only listens on 127.0.0.1: the core is what exposes it.
4. End-of-task checklist #
cargo build --release(+SQLX_OFFLINE=truewhen using the.sqlxcache).cd frontend && npm run buildthentsc -b.- Actually test (curl, browser) — don't just compile.
cargo cleanafter a heavy build.
If you touch a shared crate or an @kubuno/* lib, plan a new git tag / npm version, then bump the dependency in the modules.