Mosaic
Amalgame's web framework. A productive HTTPS server in the spirit of Next.js (filesystem routing), Caddy (automatic HTTPS via native ACME), Go net/http (thread pool, no event loop). 100% Amalgame stack on top of OpenSSL 3.x and nghttp2 — sessions, auth, security middleware, static files: all already shipped.
Install & run
Mosaic runs two ways. As a server: install the prebuilt binary, write a mosaic.toml, mosaic serve — no Amalgame code, no compiler (nginx/Caddy style). As a library: add the stack to your own project and write your own handlers. Full guide on docs.amalgame.me → Mosaic.
As a server — zero Amalgame code
# install once curl -sSL https://raw.githubusercontent.com/amalgame-lang/mosaic/main/install.sh | bash # run the whole stack from a config file mosaic serve /etc/mosaic/mosaic.toml
One binary, N sites over HTTPS — static hosting, TLS + ACME, per-site middleware, and reverse-proxy / load-balanced hosts. A mosaic.toml mixes static sites and proxies:
[server] port = 443 tls = true [tls] acme = true email = "admin@example.com" # a static site by Host [[site]] hosts = ["example.com", "www.example.com"] root = "/srv/example/public" # a reverse proxy / load balancer by 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" }, ]
As a library — write your own handlers
# add the stack to your project, then build with the mosaic tool amc package add web net-http tls mosaic build # or: write server.am and `amc run`
Four ways to deploy, from a config-only server to a hand-rolled binary — see the installation guide.
Composable-package architecture
The stack follows the Amalgame convention: small, focused packages. Import just TLS for a custom protocol, just HTTP for a low-level client/server, or amalgame-web + the mosaic tool for the full stack.
amalgame-web
The runtime library: WebApp, Router (:param + *splat), sessions (memory / signed cookie HMAC / Redis), security middleware, auth (Basic / JWT / OAuth2), static files, 7 Serve modes including in-process HTTPS, and MosaicServer — a multi-site, multi-domain HTTPS front (Host dispatch + SNI + :80→:443) that also mounts reverse proxies via AddHandler.
amalgame-net-http
Pure-AM HTTP/1.1 parser + HTTPS client (chunked decode). HTTP/2 via nghttp2 (h2c + ALPN h2). HTTPS-over-HTTP/1.1 server with SNI (N domains, 1 cert each) and keep-alive. Fiber-driven async server (epoll). RFC 6455 WebSocket. Parser limits for L7 anti-DDoS.
amalgame-tls
OpenSSL 3.x binding (LibreSSL too). TLS 1.2/1.3, ALPN, SNI, client-auth. Native RFC 8555 ACME — pure-AM state machine, no certbot subprocess. Embedded-renewal helpers.
amalgame-net-proxy
HTTP/1.1 reverse proxy + load balancer — longest-prefix routing, X-Forwarded-For, hop-by-hop stripping, weighted round-robin / ip-hash / least-connections. Driven by [[proxy]] in mosaic serve, or mounted in code.
mosaic (the tool)
The build tool / CLI — not a package. Scans app/ and generates _routes.am (Next.js convention), drives amc + gcc, serves public/ automatically. dev, build, new, hot-reload — and serve, the config-driven server (prebuilt binary, zero Amalgame code).
HTTPS Hello world — native ACME, the binary handles the rest
One AcmeNative.EnsureCert call provisions the certificate (http-01 challenge, RFC 8555, no certbot), then ServeHttpsMt terminates TLS in-process — no reverse proxy required.
namespace App import Amalgame.Web import Amalgame.Tls public class Program { public static void Main(string[] args) { // Native RFC 8555 ACME — no subprocess, ES256 account key AcmeNative.EnsureCert("my-site.com", "admin@my-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 generated from app/ app.ServeHttpsMt(443, "./certs/my-site.com/fullchain.pem", "./certs/my-site.com/privkey.pem") } }
Filesystem routing — Next.js convention
Every .am file under app/ becomes a route. Names with [id] capture a parameter (:id), [...slug] captures a catch-all (*slug). mosaic build scans the tree and generates _routes.am; Routes.Bind(app) wires it all up. Files in public/ are served automatically.
my-app/ ├── amalgame.toml ├── mosaic.toml # [security.*], [tls], [logging]… ├── server.am # entry point: 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/ # served at / (ETag, 304, binary-safe) └── data/
Each file exports a Page class with methods named after the HTTP verb (GET / POST / PUT / PATCH / DELETE), each taking a 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) } }
The mosaic tool — from scaffold to prod
Four commands cover the whole cycle. The tool installs once (curl … install.sh | bash) and runs from any project.
Scaffold (v0.4+)
mosaic new my-app drops a working project skeleton and auto-runs amc package add — mosaic dev produces a serving binary on the very next command.
DEV — watch + livereload (v0.2/0.3+)
Watches app/ and server.am, rebuilds + restarts the server on every save. WebSocket daemon on :35729 → the browser refreshes after each build.
Hot-reload supervisor (v0.5+)
The freshly-built worker is swapped behind a TCP shim: in-flight WebSocket connections survive code edits. No disruption for connected clients.
PROD — one-shot build
Full pipeline: regenerate _routes.am, run amc then gcc, produce the binary. Reads amalgame.lock to resolve package archives. You ship a native binary.
PROD — config-driven server (v0.7+)
mosaic serve mosaic.toml runs the whole stack from a config file — N static sites, TLS + ACME, per-site middleware, and reverse-proxy / load-balanced hosts. A prebuilt binary, no Amalgame code (nginx/Caddy style).
L7 security + auth built-in — already shipped
L3/L4 defense stays with the operator (kernel sysctl, firewall, CDN). Everything application-layer is in the framework, opt-in via .With*() and orchestrated by WebApp in the right pipeline order:
- ✓WithSecurityHeaders (v0.4) —
StrictHtml()/StrictApi(): HSTS, CSP, X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy, COOP/COEP - ✓WithCsrf (v0.7) — double-submit cookie, 256-bit token,
Secure+SameSite=Lax - ✓WithCors (v0.5) — deny by default,
AllowAll/Strictpresets, OPTIONS preflight handling - ✓WithRateLimit (v0.6) — per-IP fixed window, 429 +
Retry-Afteron overflow - ✓WithStatic (v0.13 · caching v0.27) — file serving, strong ETag +
Last-Modified,304(If-None-Match / If-Modified-Since), Range 206 / 416, pre-compressed.gzselection, path-traversal guard, ~35 MIME types - ✓WithCompress (v0.26) — on-the-fly gzip, negotiated via
Accept-Encoding(text/*, JSON, JS, SVG, XML), setsVary; viaamalgame-compress - ✓Multipart uploads (v0.25) —
ctx.Multipart()→UploadedFile(binary-safe,SaveTo/Bytes/Text) + text fields, capped bymax_body_bytes - ✓Template engine (v0.24) — Mustache-like
Template, HTML auto-escape by default (closes the.Html()XSS hole),ctx.Render(path, data);{{{x}}}for raw - ✓BasicAuth (v0.15, RFC 7617) · JwtAuth (v0.16, HS256) · OAuth2Client (v0.17, GitHub/Google presets, authorization-code flow)
- ✓Sessions —
MemorySessionStore,SignedCookieSessionStore(HMAC-SHA-256, stateless),RedisSessionStore - ✓WithLogging (v0.8.2) — structured access logs via
amalgame-logging, driven by[logging] - ✓Anti-slowloris timeouts via
HttpServerConfig— header/body/idle timeouts,max_body_bytes,max_header_bytes
Automatic HTTPS via native RFC 8555 ACME
One AcmeNative.EnsureCert(domain, email, dir, server) call and Mosaic provisions the certificate via the http-01 challenge, on a pure-Amalgame ACME state machine (ES256 account key) — no more certbot subprocess. Certs are written to <dir>/<domain>/{fullchain.pem, privkey.pem}. For embedded renewal: AcmeNative.NeedsRenewal(certPath, days) + CertDaysRemaining(certPath) in a thread. Let's Encrypt staging via AcmeNative.LeStaging(). (The legacy certbot wrapper Acme.EnsureCertMulti stays available for multi-SAN until native multi-SAN lands.)
RFC 6455 WebSocket — built-in server
The WebSocket server lives in amalgame-net-http: Ws.Serve in cleartext, Wss.Serve over TLS. The handler receives a connection and loops on WsConn. The *With variants apply the same socket timeouts as the HTTP server.
import Amalgame.Net.Http let handler = conn => { while (!WsConn.IsClosed(conn)) { let msg: string = WsConn.ReceiveText(conn) if (String_Length(msg) == 0) { return 0 } // disconnected WsConn.SendText(conn, "echo @ " + msg) } return 0 } Ws.Serve(8080, handler) // ws:// — Wss.Serve(port, cert, key, handler) for wss://
Subprotocol negotiation (v0.17) — Ws.ServeWithProtocols(port, "v2.json,v1", handler) echoes back the first Sec-WebSocket-Protocol the server supports; WsConn.Subprotocol(conn) reads it. Phoenix-style multi-client pub/sub (channels, topics, broadcast) is not yet a framework primitive: build it on top of WsConn, or wait for a dedicated version (see roadmap).
Server-Sent Events — one-way push
For live push (progress, notifications, tickers) without the weight of a WebSocket: app.Sse("/events", sc => { ... }) (v0.28). The handler holds the connection and pushes frames via SseConn — Send(data) (false when the client leaves), SendEvent(event, data, id), Comment (heartbeat), Retry. Browser side: new EventSource("/events").
Concurrency: thread pool or fibers, your choice
Go net/http model — blocking I/O, native multi-core, no async/await contaminating your code. WebApp exposes 7 Serve modes; pick based on load.
- ✓Serve / ServeWith — serial HTTP/1.1 loop, simple, for dev or low load
- ✓ServeMt / ServeMtWith — one thread per connection (~8 MB lazy stack), prod default for CPU-bound handlers
- ✓ServeAsync (v0.12) — 1 thread, N fibers (~64 KB/conn), Linux epoll — ideal for I/O-bound handlers, thousands of connections
- ✓ServeHttps / ServeHttpsMt (v0.14) — in-process TLS termination, no reverse proxy required
- ✓HTTP/1.1 keep-alive — enabled whenever
idle_timeout_sec > 0 - ✓Graceful shutdown — SIGTERM/SIGINT handler, drains in-flight requests, clean exit
Roadmap — where we are
The stack started from scratch in mid-May 2026 and shipped, in a few weeks, an end-to-end usable web stack. Here's the real state.
The core
Filesystem routing, in-process HTTPS, native RFC 8555 ACME, full security pack (CSRF/CORS/headers/rate-limit), auth (Basic/JWT/OAuth2), sessions (memory/signed cookie/Redis), static files, fiber async, hot-reload supervisor, HTTPS client.
Rendering, uploads & real-time
Auto-escaping template engine (anti-XSS), binary-safe multipart uploads, negotiated gzip compression, static Last-Modified + Range 206 + .gz selection, Server-Sent Events (WebApp.Sse), WebSocket subprotocol negotiation. (June 2026.)
HTTP & TLS polish
sendfile(2) zero-copy for static. Native multi-SAN ACME, multi-cert SNI, OCSP stapling, dns-01 challenge + wildcards. PKCE + OIDC/id_token + RS256 JWT. Async I/O for H2/HTTPS/WS (gated on TLS-side fibers).
WebSocket & real-time
Binary + continuation frames, wss:// client-side, per-message-deflate. Phoenix-style pub/sub channels primitive (topics + broadcast), multi-node via Redis Pub/Sub.
Ops & breadth
Prometheus /metrics, OpenTelemetry tracing, /healthz + /readyz, systemd Type=notify. Typed validation, template list iteration. amalgame-email SMTP, amalgame-queue, amalgame-cron, amalgame-openapi.
Beyond HTTP — nginx "front door" ambition
Beyond HTTPS listening, Mosaic aims at the nginx/apache front-door role: reverse proxy, load balancing, raw TCP/UDP proxying. Inventory in the beyond-http.md proposal. Reverse proxy and load balancing have shipped; the raw-stream and relay bricks remain roadmap.
HTTP/HTTPS reverse proxy
amalgame-net-proxy — proxy in front of N upstreams: longest-prefix routing, X-Forwarded-For, hop-by-hop stripping. Configure it in one [[proxy]] block of mosaic serve. HTTP/2 + transparent WebSocket forwarding still roadmap.
Load balancing
Weighted round-robin, least-connections, IP-hash — a pool per route via upstreams = [...] + strategy. Active health checks, outlier detection and sticky sessions are still roadmap.
TCP/UDP raw proxy
amalgame-net-stream — nginx stream {} or HAProxy TCP mode equivalent. Fronting Postgres, Redis Sentinel, MQTT, UDP servers.
SFTP / SMTP relay
amalgame-net-ssh (libssh2 binding, SFTP automation) and amalgame-net-smtp (receive + forward, basic v0.1 relay).
Follow the progress
Mosaic is public and usable. The spec is the source of truth — phases advance through PRs tracked on the repos.