onho-badge — dynamic SVG badges with MoonBit + onho
A Shields.io-style badge service written in MoonBit on top of
anccnuer/onho, compiled to a WASI
executable and deployed as a WCGI app on Wasmer.
Endpoints
GET / landing page (browser fetches GitHub, server renders)
GET /gh/:user/:repo/:metric?value=… GitHub metric badge (stars, forks, issues, license, watchers, …)
GET /badge/:label/:message?color=… any custom label/value pair
GET /health ok
color / labelColor accept shields color names (red, brightgreen,
blueviolet, …) or 3/6-digit hex without the leading hash. Without value
the badge renders unknown in grey, so every repo URL is a valid image.
Markdown drop-in:

Build
moon test
moon build --target wasm --release
The CGI guest lives in cmd/wcgi/main.mbt; the output is
_build/wasm/release/build/cmd/wcgi/wcgi.wasm.
Run locally
One instance serves one request, CGI-style (run from Git Bash with
MSYS2_ARG_CONV_EXCL='*' so paths are not rewritten):
printf '' | wasmer run _build/wasm/release/build/cmd/wcgi/wcgi.wasm \
--env REQUEST_METHOD=GET \
--env PATH_INFO=/gh/moonbitlang/moon/stars \
--env QUERY_STRING=value=4200 \
--env CONTENT_LENGTH=0
Deploy to wasmer.io
wasmer deploy
wasmer.toml declares the module and the WCGI runner (dialect = "rfc-3875");
app.yaml points the app at the package. Wasmer's edge invokes the guest per
request with the CGI environment and turns stdout back into the HTTP response.
Architecture note
The WCGI sandbox cannot open outbound TLS connections, so the landing page
fetches api.github.com in the browser and passes the numbers to
/gh/…?value=…; the server's job is to render SVG for every request.