agentsclimarketplace

Machin deploy

Skill javimosch/machin/skills/machin-deploy

Ship a machin (MFL) web app to production — run it correctly and safely behind a reverse proxy (nginx / Caddy / Traefik / Cloudflare), with the machweb hardening + proxy-awareness knobs, a systemd unit, and a slim Docker image. Use when deploying or operationalizing a machin HTTP service: getting HTTPS via a proxy, fixing http→https redirects/cookies behind TLS termination, the real client IP, request size/time limits, access logs, and the run/restart story. Distilled from the deploy dogfood (machin v0.78) and the machin-deploy reference app.From its SKILL.md

Install
npx -y skills add javimosch/machin --skill machin-deploy

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 10 stars10 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.8 KB, as published. Nobody here has run it

Deploying a machin web app

A machin web service is one static-ish native binary (it links libc, OpenSSL, and maybe libsqlite3 — not fully static, but no runtime/interpreter). The standard production shape is that binary behind a reverse proxy that terminates TLS — nginx, Caddy, Traefik, or Cloudflare. You do not need machin to speak TLS itself; the proxy does HTTPS and forwards plain HTTP to your app. This skill is about making the app correct and safe in that setup.

serve_tls(port, certfile, keyfile, handler) (framework/machweb.src) lets machweb terminate HTTPS itself with no proxy in front — for a simple/internal service where you already have a cert+key file and don't want another moving part. It's not yet a replacement for the proxy setup below for anything public-facing: no ACME/Let's Encrypt auto-renewal (bring your own cert, renew it yourself), and res.is_hijack/res.is_stream (protocol upgrades, SSE) aren't supported over it yet — use serve behind a proxy for those. Reach for serve_tls when you want zero infra for a small/internal HTTPS service; reach for the reverse-proxy shape below for anything public production-facing.

Build the app with the machin-web / machin-backend skills; this is the operate it in production how-to.

Turn on the production knobs (in main)

All default off, so dev is unchanged. Enable what you need before serve:

func main() {
    harden(20 * 1024 * 1024, 15000)   // body cap 20 MB, 15 s read timeout, +trust-proxy,
                                       // +Secure cookies, +access log — the common set
    serve(8080, func(req) { return handle(req) })
}

harden(max_body_bytes, read_timeout_ms) is shorthand; the individual switches are:

CallEffect
set_trust_proxy(1)trust X-Forwarded-Proto / X-Forwarded-For (only behind a proxy you control)
set_secure_cookies(1)add ; Secure to every cookie (you're served over HTTPS)
set_max_body(n)reject a request body larger than n bytes with 413 — without buffering it
set_read_timeout(ms)cap a slow client's request read (anti slow-loris)
set_access_log(1)one JSON access-log line per request on stderr (fd 2)

Be proxy-correct

Behind a TLS-terminating proxy the socket is plain HTTP, so without help your app thinks every request is http:// and sees the proxy's IP. With set_trust_proxy(1):

  • scheme(req) → http/https from X-Forwarded-Proto. Use it so redirects, OAuth redirect_uris, and emailed links are https, not http.
  • base_url(req) → scheme://host — build absolute URLs that survive the proxy.
  • client_ip(req) → the real client (left-most X-Forwarded-For hop), for logging / rate-limiting / audit. req.remote is the raw socket peer (the proxy) if you need it.
  • set_secure_cookies(1) → sessions get Secure so they're never sent in clear.

Only trust these headers behind a proxy that sets them — a client can forge X-Forwarded-*. If the app is ever directly exposed, leave set_trust_proxy(0).

Reverse proxy snippets

nginx — forward the headers machweb reads, and don't buffer SSE:

location / {
    proxy_pass         http://127.0.0.1:8080;
    proxy_set_header   Host $host;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_buffering    off;          # let Server-Sent Events stream (machweb also sends X-Accel-Buffering: no)
}

Caddy — TLS + the forwarded headers are automatic:

app.example.com {
    reverse_proxy 127.0.0.1:8080
}

Run it: systemd

A single binary is a trivial unit. Type=simple, run as a non-root user, restart on failure, and rely on set_access_log(1) → journald:

[Unit]
Description=machin app
After=network.target

[Service]
ExecStart=/opt/app/server
Environment=PORT=8080 APP_SECRET=change-me
User=app
Restart=on-failure
RestartSec=2
# SIGTERM stops it; in-flight requests are short-lived goroutines.

[Install]
WantedBy=multi-user.target

listen sets SO_REUSEADDR, so a restart rebinds immediately (no TIME_WAIT wait).

Run it: Docker

By default the binary needs libc + OpenSSL (libssl/libcrypto, for sessions/crypto) and libsqlite3 (if you use SQLite) at runtime, so ship it on a slim base:

FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
      libssl3 libsqlite3-0 ca-certificates && rm -rf /var/lib/apt/lists/*
COPY server /usr/local/bin/server
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/server"]

(Multi-stage: build with machin build in a builder stage, copy just the binary.)

FROM scratch for a SQLite-only service: machin build --static compiles the SQLite amalgamation in (no libsqlite3); with CC=musl-gcc you get a libc-free ~1 MB binary that runs from an empty image — FROM scratch / COPY server / ENTRYPOINT. This works when the program uses no OpenSSL — i.e. no HTTPS client (https_*) and no crypto builtins (hmac_sha256/sha256/rand_bytes/…, which signed sessions use). Those still link OpenSSL (so the slim base above) until native TLS lands (issue #260).

Gotchas

  • Trust the proxy, not the client. set_trust_proxy(1) only behind a proxy that overwrites X-Forwarded-*. Directly exposed → keep it off, or a client spoofs its IP.
  • set_secure_cookies(1) requires HTTPS. On a Secure cookie set over plain http:// the browser drops it — turn it on only once TLS (via the proxy) is in front.
  • Access logs go to stderr so stdout stays a clean data/JSON stream; capture fd 2 in journald/Docker.
  • Body cap is on the declared Content-Length — an over-cap request is 413'd before its body is read (no OOM, no draining megabytes).
  • Graceful shutdown is SIGTERM = exit. Each request is its own short-lived goroutine, so kill is safe in practice; there's no long-drain coordinator yet.

Reference

  • machin-deploy — a production-ready machin service wired with all of the above: proxy-correct, hardened, a systemd unit, a slim Dockerfile, and nginx/Caddy configs. Clone it as a deploy template.

Keep looking

Skills are one crate of 628,853. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.