Mosaic 🟢 Bêta — pile HTTPS complète livrée

Mosaic

Le framework web d'Amalgame. Serveur HTTPS productif dans la veine de Next.js (filesystem routing), Caddy (HTTPS automatique via ACME natif), Go net/http (thread pool, pas d'event loop). Stack 100 % Amalgame au-dessus d'OpenSSL 3.x et nghttp2 — sessions, auth, middleware de sécurité, statique : tout est déjà livré.

Installer & lancer

Mosaic se lance de deux façons. Comme serveur : on installe le binaire pré-compilé, on écrit un mosaic.toml, mosaic serve — aucun code Amalgame, aucun compilateur (façon nginx/Caddy). Comme bibliothèque : on ajoute la stack à son propre projet et on écrit ses handlers. Guide complet sur docs.amalgame.me → Mosaic.

Comme serveur — zéro code Amalgame

# installer une fois
curl -sSL https://raw.githubusercontent.com/amalgame-lang/mosaic/main/install.sh | bash

# lancer toute la pile depuis un fichier de config
mosaic serve /etc/mosaic/mosaic.toml

Un binaire, N sites en HTTPS — hébergement statique, TLS + ACME, middleware par site, et des hosts en reverse proxy / load balancing. Un mosaic.toml mélange sites statiques et proxies :

[server]
port = 443
tls  = true

[tls]
acme  = true
email = "admin@example.com"

# un site statique par Host
[[site]]
hosts = ["example.com", "www.example.com"]
root  = "/srv/example/public"

# un reverse proxy / load balancer par Host
[[proxy]]
hosts  = ["api.example.com"]
routes = [
  { prefix = "/",      upstream  = "http://127.0.0.1:8080" },
  { prefix = "/heavy", upstreams = ["http://127.0.0.1:9001", "http://127.0.0.1:9002"], strategy = "round_robin" },
]

Comme bibliothèque — écris tes propres handlers

# ajoute la stack à ton projet, puis build avec l'outil mosaic
amc package add web net-http tls
mosaic build  # ou : écris server.am et `amc run`

Quatre façons de déployer, du serveur tout-en-config au binaire fait main — voir le guide d'installation.

Architecture en packages composables

La pile suit la convention Amalgame : des packages petits et focalisés. Tu importes juste TLS pour un protocole custom, juste HTTP pour un client/serveur bas niveau, ou amalgame-web + l'outil mosaic pour la pile complète.

v0.23.0 livré

amalgame-web

La bibliothèque runtime : WebApp, Router (:param + *splat), sessions (mémoire / cookie signé HMAC / Redis), middleware sécurité, auth (Basic / JWT / OAuth2), statique, 7 modes de Serve dont l'HTTPS in-process, et MosaicServer — un front HTTPS multi-sites multi-domaines (dispatch par Host + SNI + :80→:443) qui monte aussi des reverse proxies via AddHandler.

v0.13.6 livré

amalgame-net-http

Parser HTTP/1.1 pur AM + client HTTPS (décodage chunked). HTTP/2 via nghttp2 (h2c + ALPN h2). Serveur HTTPS-sur-HTTP/1.1 avec SNI (N domaines, 1 cert chacun) et keep-alive. Serveur async fibres (epoll). WebSocket RFC 6455. Limites parser anti-DDoS.

v0.3.3 livré

amalgame-tls

Binding OpenSSL 3.x (LibreSSL aussi). TLS 1.2/1.3, ALPN, SNI, client-auth. ACME natif RFC 8555 — state machine pure-AM, plus de sous-process certbot. Helpers de renouvellement embarqué.

v0.2.1 livré

amalgame-net-proxy

Reverse proxy + load balancer HTTP/1.1 — routage par plus long préfixe, X-Forwarded-For, strip hop-by-hop, round-robin pondéré / ip-hash / least-connections. Piloté par [[proxy]] dans mosaic serve, ou monté en code.

v0.7.0 livré

mosaic (l'outil)

L'outil de build/CLI — pas un package. Scanne app/ et génère _routes.am (convention Next.js), pilote amc + gcc, sert public/ automatiquement. dev, build, new, hot-reload — et serve, le serveur tout-en-config (binaire pré-compilé, zéro code Amalgame).

Hello world HTTPS — ACME natif, le binaire gère le reste

Un appel à AcmeNative.EnsureCert provisionne le certificat (challenge http-01, RFC 8555, sans certbot), puis ServeHttpsMt termine le TLS in-process — pas de reverse proxy requis.

namespace App
import Amalgame.Web
import Amalgame.Tls

public class Program {
    public static void Main(string[] args) {
        // ACME RFC 8555 natif — pas de sous-process, clé compte ES256
        AcmeNative.EnsureCert("mon-site.com", "admin@mon-site.com",
                            "./certs", AcmeNative.LeStaging())

        let app: WebApp = WebApp.New()
            .WithSecurityHeaders(SecurityHeaders.StrictHtml())  // HSTS, CSP, XFO…
            .WithCsrf(Csrf.Default())                          // double-submit cookie
            .WithRateLimit(RateLimit.PerIp(100, 60))            // 100 req / 60 s / IP
            .WithStatic(Static.New("/assets", "./public").WithCacheMaxAge(3600))
        Routes.Bind(app)                                  // routes générées depuis app/

        app.ServeHttpsMt(443, "./certs/mon-site.com/fullchain.pem",
                              "./certs/mon-site.com/privkey.pem")
    }
}

Filesystem routing — convention Next.js

Chaque fichier .am sous app/ devient une route. Les noms en [id] capturent un paramètre (:id), [...slug] capture un catch-all (*slug). mosaic build scanne l'arbo et génère _routes.am ; Routes.Bind(app) branche le tout. Les fichiers de public/ sont servis automatiquement.

mon-app/
├── amalgame.toml
├── mosaic.toml              # [security.*], [tls], [logging]…
├── server.am                # point d'entrée : WebApp + Routes.Bind
├── app/
│   ├── index.am             → GET /
│   ├── about.am             → GET /about
│   ├── users/
│   │   ├── index.am         → GET /users
│   │   ├── [id].am          → GET /users/:id
│   │   └── [id]/posts.am    → GET /users/:id/posts
│   └── api/
│       ├── login.am         → POST /api/login
│       └── [...path].am     → catch-all route
├── lib/
├── public/                  # servi à / (ETag, 304, binary-safe)
└── data/

Chaque fichier exporte une classe Page avec des méthodes nommées d'après le verbe HTTP (GET / POST / PUT / PATCH / DELETE), chacune recevant un WebContext :

// app/users/[id].am
namespace App.Users.Detail
import Amalgame.Web
import Amalgame.Net.Http

public class Page {
    public static HttpResponse GET(ctx: WebContext) {
        let id: string = ctx.Param("id")
        let user = Db.FindUser(id)
        if (user == null) {
            return HttpResponse.New().Status(404).Text("User not found")
        }
        return HttpResponse.New().Json(user)
    }
}

L'outil mosaic — du scaffold à la prod

Quatre commandes couvrent tout le cycle. L'outil s'installe une fois (curl … install.sh | bash) et se lance depuis n'importe quel projet.

mosaic new

Scaffold (v0.4+)

mosaic new mon-app dépose un squelette de projet qui sert, et lance amc package add automatiquement — mosaic dev produit un binaire qui répond dès la commande suivante.

mosaic dev

DEV — watch + livereload (v0.2/0.3+)

Surveille app/ et server.am, recompile + redémarre le serveur à chaque sauvegarde. Daemon WebSocket sur :35729 → le navigateur se rafraîchit après chaque build.

mosaic dev --supervise

Hot-reload superviseur (v0.5+)

Le worker fraîchement buildé est swappé derrière un shim TCP : les connexions WebSocket en cours survivent aux éditions de code. Pas de coupure pour les clients connectés.

mosaic build

PROD — build one-shot

Pipeline complet : régénère _routes.am, lance amc puis gcc, produit le binaire. Lit amalgame.lock pour résoudre les archives de packages. Tu ships un binaire natif.

mosaic serve

PROD — serveur tout-en-config (v0.7+)

mosaic serve mosaic.toml lance toute la pile depuis un fichier — N sites statiques, TLS + ACME, middleware par site, et des hosts en reverse proxy / load balancing. Un binaire pré-compilé, zéro code Amalgame (façon nginx/Caddy).

Sécurité L7 + auth built-in — déjà livré

La défense L3/L4 reste à l'opérateur (kernel sysctl, firewall, CDN). Tout l'applicatif est dans le framework, opt-in via .With*() et orchestré par WebApp dans le bon ordre du pipeline :

  • WithSecurityHeaders (v0.4) — StrictHtml() / StrictApi() : HSTS, CSP, X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy, COOP/COEP
  • WithCsrf (v0.7) — double-submit cookie, token 256 bits, Secure + SameSite=Lax
  • WithCors (v0.5) — deny par défaut, AllowAll / Strict presets, gestion preflight OPTIONS
  • WithRateLimit (v0.6) — fenêtre fixe par IP, 429 + Retry-After au dépassement
  • WithStatic (v0.13 · caching v0.27) — service de fichiers, ETag fort + Last-Modified, 304 (If-None-Match / If-Modified-Since), Range 206 / 416, sélection .gz pré-compressé, garde anti-traversée, ~35 types MIME
  • WithCompress (v0.26) — compression gzip à la volée, négociée par Accept-Encoding (text/*, JSON, JS, SVG, XML), Vary posé ; via amalgame-compress
  • Uploads multipart (v0.25) — ctx.Multipart()UploadedFile (binaire-safe, SaveTo / Bytes / Text) + champs texte, plafonné par max_body_bytes
  • Moteur de templates (v0.24) — Template Mustache-like, auto-échappement HTML par défaut (ferme le trou XSS de .Html()), ctx.Render(path, data) ; {{{x}}} pour le brut
  • BasicAuth (v0.15, RFC 7617) · JwtAuth (v0.16, HS256) · OAuth2Client (v0.17, presets GitHub/Google, flow authorization code)
  • SessionsMemorySessionStore, SignedCookieSessionStore (HMAC-SHA-256, stateless), RedisSessionStore
  • WithLogging (v0.8.2) — access logs structurés via amalgame-logging, pilotés par [logging]
  • Timeouts anti-slowloris via HttpServerConfig — header/body/idle timeouts, max_body_bytes, max_header_bytes

HTTPS automatique via ACME natif RFC 8555

Un appel à AcmeNative.EnsureCert(domain, email, dir, server) et Mosaic provisionne le certificat via le challenge http-01, sur une state machine ACME pure-Amalgame (clé de compte ES256) — fini le sous-process certbot. Les certs sont écrits dans <dir>/<domain>/{fullchain.pem, privkey.pem}. Pour le renouvellement embarqué : AcmeNative.NeedsRenewal(certPath, days) + CertDaysRemaining(certPath) dans un thread. Staging Let's Encrypt via AcmeNative.LeStaging(). (Le wrapper certbot historique Acme.EnsureCertMulti reste dispo pour le multi-SAN en attendant le multi-SAN natif.)

WebSocket RFC 6455 — serveur intégré

Le serveur WebSocket vit dans amalgame-net-http : Ws.Serve en clair, Wss.Serve sur TLS. Le handler reçoit une connexion et boucle sur WsConn. Les variantes *With appliquent les mêmes timeouts socket que le serveur HTTP.

import Amalgame.Net.Http

let handler = conn => {
    while (!WsConn.IsClosed(conn)) {
        let msg: string = WsConn.ReceiveText(conn)
        if (String_Length(msg) == 0) { return 0 }   // déconnexion
        WsConn.SendText(conn, "echo @ " + msg)
    }
    return 0
}

Ws.Serve(8080, handler)                          // ws://  — Wss.Serve(port, cert, key, handler) pour wss://

Négociation de sous-protocole (v0.17) — Ws.ServeWithProtocols(port, "v2.json,v1", handler) renvoie au client le premier Sec-WebSocket-Protocol supporté ; WsConn.Subprotocol(conn) le relit. Le pub/sub multi-clients façon Phoenix (channels, topics, broadcast) n'est pas encore une primitive du framework : à construire au-dessus de WsConn, ou en attendre une version dédiée (voir roadmap).

Server-Sent Events — push unidirectionnel

Pour du push live (progression, notifications, tickers) sans le poids d'un WebSocket : app.Sse("/events", sc => { ... }) (v0.28). Le handler tient la connexion et pousse des frames via SseConnSend(data) (false quand le client part), SendEvent(event, data, id), Comment (heartbeat), Retry. Côté navigateur : new EventSource("/events").

Concurrence : thread pool ou fibres, au choix

Modèle Go net/http — blocking I/O, multi-core natif, pas d'async/await qui contamine le code. WebApp expose 7 modes de Serve ; tu choisis selon la charge.

  • Serve / ServeWith — boucle série HTTP/1.1, simple, pour dev ou charge faible
  • ServeMt / ServeMtWith — un thread par connexion (~8 MB de stack lazy), défaut prod pour handlers CPU-bound
  • ServeAsync (v0.12) — 1 thread, N fibres (~64 KB/conn), epoll Linux — idéal handlers I/O-bound, milliers de connexions
  • ServeHttps / ServeHttpsMt (v0.14) — terminaison TLS in-process, pas de reverse proxy requis
  • HTTP/1.1 keep-alive — actif dès que idle_timeout_sec > 0
  • Graceful shutdown — handler SIGTERM/SIGINT, drain des requêtes en vol, exit propre

Roadmap — où on en est

La pile a démarré de zéro mi-mai 2026 et a livré, en quelques semaines, une pile web utilisable de bout en bout. Voici l'état réel.

Livré

Le cœur

Filesystem routing, HTTPS in-process, ACME natif RFC 8555, pile sécurité complète (CSRF/CORS/headers/rate-limit), auth (Basic/JWT/OAuth2), sessions (mémoire/cookie signé/Redis), statique, async fibres, hot-reload superviseur, client HTTPS.

Livré

Rendu, uploads & temps réel

Moteur de templates auto-échappé (anti-XSS), uploads multipart binaire-safe, compression gzip négociée, statique Last-Modified + Range 206 + sélection .gz, Server-Sent Events (WebApp.Sse), négociation de sous-protocole WebSocket. (Juin 2026.)

Proche

Affinage HTTP & TLS

sendfile(2) zero-copy pour le statique. ACME natif multi-SAN, SNI multi-certificats, OCSP stapling, challenge dns-01 + wildcards. PKCE + OIDC/id_token + JWT RS256. I/O async pour H2/HTTPS/WS (dépend des fibres côté TLS).

Plus tard

WebSocket & temps réel

Frames binaires + continuation, wss:// côté client, per-message-deflate. Primitive pub/sub channels façon Phoenix (topics + broadcast), multi-nœud via Redis Pub/Sub.

Plus tard

Ops & breadth

/metrics Prometheus, tracing OpenTelemetry, /healthz + /readyz, systemd Type=notify. Validation typée, itération de liste dans les templates. amalgame-email SMTP, amalgame-queue, amalgame-cron, amalgame-openapi.

Beyond HTTP — ambition « front door » nginx

Au-delà de l'écoute HTTPS, Mosaic vise le rôle de front door nginx/apache : reverse proxy, load balancing, proxy TCP/UDP brut. Inventaire dans la proposal beyond-http.md. Le reverse proxy et le load balancing sont livrés ; les briques raw-stream et relais restent de la roadmap.

livré

Reverse proxy HTTP/HTTPS

amalgame-net-proxy — proxy en front de N upstreams : routage par plus long préfixe, X-Forwarded-For, strip hop-by-hop. Se configure en un bloc [[proxy]] de mosaic serve. HTTP/2 + WebSocket forwarding transparent encore en roadmap.

livré

Load balancing

Round-robin pondéré, least-connections, IP-hash — un pool par route via upstreams = [...] + strategy. Active health checks, outlier detection et sticky sessions encore en roadmap.

prio MED

TCP/UDP raw proxy

amalgame-net-stream — équivalent stream {} nginx ou HAProxy TCP mode. Front de Postgres, Redis Sentinel, MQTT, serveurs UDP.

prio LOW

SFTP / SMTP relay

amalgame-net-ssh (binding libssh2, SFTP automation) et amalgame-net-smtp (receive + forward, relay basique v0.1).

Suivre l'avancement

Mosaic est public et utilisable. La spec est la source de vérité — les phases avancent par PRs trackées sur les repos.