Mosaic 🏗️ In progress — foundation shipped

Mosaic

Amalgame's web framework. A productive HTTPS server in the spirit of Next.js (filesystem routing), Phoenix (WebSocket channels), Caddy (automatic HTTPS via ACME). 100% Amalgame stack on top of OpenSSL 3.x and nghttp2 — not a toy IoT library.

amc package add web Mosaic GitHub ↗ Full spec ↗

Three-package architecture

Three composable packages, following the Amalgame convention (small, focused). You can import just TLS for a custom protocol, just HTTP for a low-level client/server, or Mosaic for the full stack.

v0.2.0 shipped

amalgame-tls

OpenSSL 3.x binding (LibreSSL also supported). TLS 1.2/1.3, ALPN, SNI. ACME via certbot wrapper until the native pure-AM impl lands.

v0.4.1 shipped

amalgame-net-http

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

v0.2.1 shipped

amalgame-web

Router (:param + *splat), WebContext, MemorySessionStore. Pure runtime library — the mosaic build tool lives in its own repo.

HTTPS Hello world — one ACME call, the binary handles the rest

namespace App
import Amalgame.Web

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

Filesystem routing — Next.js convention

Every .am file under app/ becomes an endpoint. File names with [id] capture a parameter, [...slug] captures a catch-all. The router is generated automatically on every build.

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

Each file exports Get / Post / Put / Patch / Delete functions. The HTTP verb is inferred from the name:

// 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)
}

Three execution modes, one command each

mosaic dev

DEV — hot reload

Filewatcher on app/ and lib/. Every change triggers an incremental recompile + browser livereload via WebSocket. Dev TLS optional, ACME disabled to avoid Let's Encrypt rate limits.

mosaic build --modular

PROD modular — .so hot-swap

Mosaic binary + reloadable app.so. Shipping a patch = replace app.so + SIGHUP. Filewatcher disabled in prod for security.

mosaic build --mono

PROD mono — single binary

Statically-linked binary named after your project. Static assets embedded. Ideal FROM scratch Docker, or zero-config VPS deploy.

L7 security built-in

L3/L4 defense stays with the operator (kernel sysctl, firewall, CDN). But everything application-layer is in the framework, opt-in via .With*():

  • WithSecurityHeaders — HSTS, X-Frame-Options=DENY, X-Content-Type-Options=nosniff, strict Referrer-Policy, basic CSP
  • WithCsrf — double-submit cookie + per-form token
  • WithCors — deny by default, explicit allow list
  • WithRateLimit — per-IP token bucket, configurable rps + burst
  • Slowloris protection — header timeout 10s, body timeout 30s, idle keep-alive 60s
  • Backpressure — when the worker queue is full, immediate 503 (never OOM)
  • Trusted-proxy allow-listX-Forwarded-For only trusted from whitelisted IPs
  • Safe-by-default cookies: Secure + HttpOnly + SameSite=Lax

Automatic HTTPS via ACME / Let's Encrypt

One ListenAcme(domain, email) call and Mosaic handles everything: bind :80 + :443, ACME v2 negotiation, in-process tls-alpn-01 challenge, cert caching, renewal 30 days before expiry. Multi-domain via SAN. Let's Encrypt staging opt-in via ACME_STAGING=1.

WebSocket with Phoenix-style pub/sub channels

WebSocket endpoints live on the same router as HTTP routes. The channel system is lightweight and single-process in v0.x (multi-node via Redis Pub/Sub planned for v1.x).

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

app.WebSocket("/chat", fn(ws, ctx) {
    chat.Subscribe(ws)                                // ws receives broadcasts
    while (ws.IsConnected()) {
        let msg = ws.RecvText()
        chat.Broadcast(msg)                           // sent to all subscribers
    }
})

Concurrency: thread pool, not event loop

Simplified Go net/http model — one thread per active request, bounded worker pool, blocking I/O. No async/await (no "function colors" contaminating all your code). Multi-core native. The MPSC queue guarantees backpressure: if it fills up, immediate 503.

  • N workers default 2 × NumCpus, configurable via mosaic.toml
  • Bounded queue at N × 4 — full = immediate 503 (no OOM)
  • Built on amalgame-threading (pthread + Win32, GC-safe)
  • Graceful shutdown — acceptor stops, workers drain, clean exit
  • ~100 KB per request — 256 active workers ≈ 25 MB for concurrency itself

Roadmap — 4 phases

The stack was assembled in two intensive days (2026-05-18 and 2026-05-19): nothing → HTTPS + HTTP/2 + WebSocket + filesystem routing + livereload, end-to-end. Here's what's left, in four phases:

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

Blocking for any serious prod use. Security pack (CSRF/CORS/headers/rate-limit), ACME wrapper injection fix, CRLF/SSRF/open-redirect anti-injection, slowloris timeouts, concurrent worker pool, HTTP/1.1 keep-alive, IPv6 dual-stack, graceful SIGTERM shutdown.

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

amalgame-crypto (Argon2id, RSA, Ed25519, CSPRNG) → then auth basic + bearer + JWT + WebAuthn + TOTP. SQLite + Postgres DB bindings + migration framework. amalgame-template with auto-escape (closes the default-XSS hole). Typed validation. Multipart upload. JsonFileSessionStore and RedisSessionStore.

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

Structured JSON/CLF access logs. Prometheus /metrics. OpenTelemetry tracing. /healthz + /readyz. systemd Type=notify. mosaic new <template> scaffold. dlopen hot-swap for reload without breaking WS. mosaic test integration harness.

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

Pure-AM ACME (replaces certbot wrapper). 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 — nginx-equivalent capabilities

Beyond HTTPS listening, Mosaic aims at the nginx/apache "front door" role: reverse proxy, load balancing, raw TCP/UDP proxying. Inventory planned in the beyond-http.md proposal.

prio HIGH

HTTP/HTTPS reverse proxy

amalgame-net-proxy — proxy in front of N upstreams. HTTP/1.1 + HTTP/2 + transparent WebSocket forwarding. ~400 LoC for the base.

prio HIGH

Load balancing

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

prio HIGH

Static file serving

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

prio MED

TCP/UDP raw proxy

amalgame-net-stream — nginx stream {} or HAProxy TCP mode equivalent. Fronting Postgres, Redis Sentinel, MQTT, UDP game servers. ~250 LoC.

prio LOW

SFTP via libssh2

amalgame-net-ssh — libssh2 binding. SFTP remains recommended for automation file transfer. ~500 LoC of glue.

prio LOW

SMTP relay

amalgame-net-smtp — receive + forward. Prod surface (DKIM, SPF, bounce, anti-spam) is massive; v0.1 targets basic relay, no antispam.

Follow the progress

Mosaic is public and in progress. The spec is the source of truth — phases advance through PRs tracked on the repos.