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.
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.
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.
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
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.
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é.
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-list —
X-Forwarded-Fortrusté 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 viamosaic.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.
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.
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.
Static file serving
Middleware Mosaic, sendfile(2) zero-copy sur Linux, range parsing, ETag, MIME DB, Cache-Control. ~300 LoC.
TCP/UDP raw proxy
amalgame-net-stream — équivalent stream {} nginx ou HAProxy TCP mode. Front de Postgres, Redis Sentinel, MQTT, serveurs UDP. ~250 LoC.
SFTP via libssh2
amalgame-net-ssh — binding libssh2. SFTP est ce qui reste recommandé pour le transfert de fichiers automation. ~500 LoC de glue.
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.