Mosaic 🏗️ En chantier — fondation livrée

Mosaic

Le framework web d'Amalgame. Serveur HTTPS productif dans la veine de Next.js (filesystem routing), Phoenix (channels WebSocket), Caddy (HTTPS automatique via ACME). Stack 100 % Amalgame au-dessus d'OpenSSL 3.x et nghttp2 — pas une lib jouet IoT.

Architecture en trois packages

Trois packages composables, suivant la convention Amalgame (petits, focalisés). Tu peux importer juste TLS pour un protocole custom, juste HTTP pour un client/serveur bas niveau, ou Mosaic pour la pile complète.

v0.2.0 livré

amalgame-tls

Binding OpenSSL 3.x (LibreSSL aussi supporté). TLS 1.2/1.3, ALPN, SNI. ACME via certbot wrapper en attendant l'impl native pure-AM.

v0.4.1 livré

amalgame-net-http

Parser HTTP/1.1 pur AM. HTTP/2 via nghttp2 (h2c + ALPN h2). WebSocket RFC 6455. Limites du parser (header timeout, body size, slowloris) anti-DDoS.

v0.2.1 livré

amalgame-web

Router (:param + *splat), WebContext, MemorySessionStore. Pure runtime library — le build tool mosaic vit dans son propre repo.

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

namespace App
import Amalgame.Web

public class Program {
    public static void Main(string[] args) {
        WebApp.New()
            .Routes("./app")                              // filesystem routing à la Next.js
            .Static("/public", "./public")
            .WithSessions(SessionStore.JsonFile("./data/sessions"))
            .WithRateLimit(rps: 100, burst: 200)
            .WithCompression()                                // gzip + brotli
            .ListenAcme("mon-site.com", "admin@mon-site.com")  // HTTPS auto
    }
}

Filesystem routing — convention Next.js

Chaque fichier .am sous app/ devient un endpoint. Les noms de fichiers en [id] capturent un paramètre, [...slug] capture un catch-all. Le routeur est généré automatiquement à chaque build.

mon-app/
├── amalgame.toml
├── mosaic.toml
├── app/
│   ├── index.am             → GET /
│   ├── about.am             → GET /about
│   ├── _middleware.am       → middleware sur app/
│   ├── _layout.am           → layout HTML
│   ├── 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/
└── data/

Chaque fichier exporte des fonctions Get / Post / Put / Patch / Delete. Le HTTP verb est déduit du nom :

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

public Function Get(req: HttpRequest, ctx: WebContext): HttpResponse {
    let id = 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)
}

Trois modes d'exécution, une commande chacun

mosaic dev

DEV — hot reload

Filewatcher sur app/ et lib/. Chaque modif déclenche un recompile incrémental + livereload navigateur via WebSocket. TLS dev optionnel, ACME désactivé pour éviter les rate limits Let's Encrypt.

mosaic build --modular

PROD modulaire — .so hot-swap

Binaire mosaic + app.so rechargeable. Shipping d'un patch = remplacer app.so + SIGHUP. Filewatcher désactivé en prod pour la sécurité.

mosaic build --mono

PROD mono — single binary

Binaire statiquement lié, nommé d'après ton projet. Public static embarqué. Idéal FROM scratch Docker, ou pour un déploiement zero-config sur VPS.

Sécurité L7 built-in

La défense L3/L4 reste à l'opérateur (kernel sysctl, firewall, CDN). Mais tout ce qui est applicatif est dans le framework, opt-in via .With*() :

  • WithSecurityHeaders — HSTS, X-Frame-Options=DENY, X-Content-Type-Options=nosniff, Referrer-Policy strict, CSP de base
  • WithCsrf — double-submit cookie + token per-form
  • WithCors — deny par défaut, allow list explicite
  • WithRateLimit — token bucket par IP, configurable rps + burst
  • Slowloris protection — header timeout 10 s, body timeout 30 s, idle keep-alive 60 s
  • Backpressure — quand la queue worker est pleine, 503 immédiat (jamais d'OOM)
  • Trusted-proxy allow-listX-Forwarded-For trusté seulement depuis des IPs whitelistées
  • Cookies safe par défaut : Secure + HttpOnly + SameSite=Lax

HTTPS automatique via ACME / Let's Encrypt

Un appel à ListenAcme(domain, email) et Mosaic gère tout : binding :80 + :443, négociation ACME v2, challenge tls-alpn-01 in-process, cache des certs, renouvellement 30 jours avant expiry. Multi-domaine via SAN. Staging Let's Encrypt opt-in via ACME_STAGING=1.

WebSocket avec channels pub/sub façon Phoenix

Les endpoints WebSocket vivent sur le même routeur que les routes HTTP. Le système de channels est lightweight et single-process en v0.x (multi-node via Redis Pub/Sub prévu en v1.x).

let chat: Channel = app.Channel("/chat")             // un topic

app.WebSocket("/chat", fn(ws, ctx) {
    chat.Subscribe(ws)                                // ws reçoit les broadcasts
    while (ws.IsConnected()) {
        let msg = ws.RecvText()
        chat.Broadcast(msg)                           // envoyé à tous les subscribers
    }
})

Concurrence : thread pool, pas event loop

Modèle Go net/http simplifié — un thread par requête active, worker pool borné, blocking I/O. Pas d'async/await (pas de « function colors » qui contaminent tout le code). Multi-core natif. La queue MPSC garantit la backpressure : si elle se remplit, 503 immédiat.

  • N workers par défaut 2 × NumCpus, configurable via mosaic.toml
  • Queue bornée à N × 4 — pleine = 503 immédiat (pas d'OOM)
  • Posé sur amalgame-threading (pthread + Win32, GC-safe)
  • Graceful shutdown — acceptor s'arrête, workers drainent, exit propre
  • ~100 KB par requête — 256 workers actifs ≈ 25 MB pour la concurrence

Roadmap — 4 phases

Le stack a été monté en deux jours intensifs (2026-05-18 et 2026-05-19) : nothing → HTTPS + HTTP/2 + WebSocket + filesystem routing + livereload, end-to-end. Voici ce qui reste, en quatre phases :

Phase 1 — Production hardening (~2-3 semaines)

Bloquant pour toute prod sérieuse. Security pack (CSRF/CORS/headers/rate-limit), fix d'injection dans l'ACME wrapper, anti-injection CRLF/SSRF/open-redirect, slowloris timeouts, worker pool concurrent, HTTP/1.1 keep-alive, IPv6 dual-stack, graceful shutdown SIGTERM.

Phase 2 — Real-world apps (~3-4 semaines)

amalgame-crypto (Argon2id, RSA, Ed25519, CSPRNG) → puis auth basic + bearer + JWT + WebAuthn + TOTP. Bindings DB SQLite + Postgres + framework de migration. amalgame-template avec auto-escape (ferme le hole XSS par défaut). Validation typée. Multipart upload. JsonFileSessionStore et RedisSessionStore.

Phase 3 — UX + ops (~1-2 semaines)

Access logs structurés JSON/CLF. /metrics Prometheus. OpenTelemetry tracing. /healthz + /readyz. systemd Type=notify. mosaic new <template> scaffold. dlopen hot-swap pour reload sans casser les WS. mosaic test integration harness.

Phase 4 — Polish + breadth (~2-3 semaines)

ACME pur AM (remplace le wrapper certbot). DNS-01 challenges + wildcards via Cloudflare/Route53/OVH. mTLS client cert. OCSP stapling. Static file sendfile(2) + ETag + range. SSE. WebSocket fragmentation + subprotocols. amalgame-email SMTP. amalgame-queue background jobs. amalgame-cron. amalgame-openapi swagger gen.

Beyond HTTP — capacités nginx-equivalent

Au-delà de l'écoute HTTPS, Mosaic ambitionne le rôle de « front door » nginx/apache : reverse proxy, load balancing, proxy TCP/UDP brut. Inventaire planifié dans la proposal beyond-http.md.

prio HIGH

Reverse proxy HTTP/HTTPS

amalgame-net-proxy — proxy en front de N upstreams. HTTP/1.1 + HTTP/2 + WebSocket transparent forwarding. ~400 LoC pour la base.

prio HIGH

Load balancing

Round-robin, least-connections, IP-hash. Active health checks, outlier detection, sticky sessions via cookie hash. Partie de amalgame-net-proxy v0.2.

prio HIGH

Static file serving

Middleware Mosaic, sendfile(2) zero-copy sur Linux, range parsing, ETag, MIME DB, Cache-Control. ~300 LoC.

prio MED

TCP/UDP raw proxy

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

prio LOW

SFTP via libssh2

amalgame-net-ssh — binding libssh2. SFTP est ce qui reste recommandé pour le transfert de fichiers automation. ~500 LoC de glue.

prio LOW

SMTP relay

amalgame-net-smtp — receive + forward. Le surface prod (DKIM, SPF, bounce, anti-spam) est massif ; v0.1 vise un relay basique sans antispam.

Suivre l'avancement

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