# freeqsay Cowsay with your FreeqWorld face. Written in Nim — a port of the [Clojure/Jolt implementation](https://gitlab.com/nandithebull/freeqsay), which is itself a port of the original TypeScript. ```bash freeqsay ``` Resolves a Bluesky handle → DID, derives the deterministic `avatar-v1` character, and prints a cowsay balloon above truecolor ANSI art. ## Usage ```bash freeqsay jay.bsky.team hello from the timeline freeqsay @pfrazee.com "ships free software" echo "piped in" | freeqsay jay.bsky.team # wrap width, thought bubble, scale freeqsay someone.bsky.social -W 20 short lines freeqsay jay.bsky.team --think hmm freeqsay jay.bsky.team --scale 3 big face says hi # raw DID (no network) freeqsay did:plc:ewvi7nmzuoqusbcablsm7c4h offline ok ``` Without a built binary, run it straight from the source tree: ```bash nim r -d:ssl src/freeqsay.nim jay.bsky.team hello world ``` ## Library ```nim import freeqsay echo freeqsay("jay.bsky.team", "hello").output ``` Modules: `freeqsay/say` (balloon + face), `freeqsay/avatar` (traits and the 16×24 sprite), `freeqsay/ansi` (truecolor half-block rendering), `freeqsay/png` (RGBA → PNG), `freeqsay/resolve` (handle → DID), `freeqsay/balloon`, `freeqsay/sha256` (SHA-256 / HMAC / HKDF). Importing `freeqsay` re-exports all of them. ## Development ```bash just build # optimized binary → ./freeqsay just static # fully static binary → ./freeqsay-static just test # test suite just run # run from source, no build just shot # gapless termshot png just --list # everything else ``` `just static` produces a binary with no dynamic dependencies at all — musl libc and OpenSSL linked in, nothing dlopen'd — so it runs on any Linux. It builds through [nimstatic](https://rickub.com/nandi/nimstatic), pinned in `tools/nimstatic` as a [DotSlash](https://dotslash-cli.com) file: the first run fetches that exact build, checks its hash and caches it, and the URL names a commit so it cannot move. A static binary carries no CA store, so set `SSL_CERT_FILE` when resolving handles on a host without one at the default path. `nimble build` and `nimble test` work too. `-d:ssl` is set in `config.nims`, since handle resolution goes over HTTPS through the stdlib client. Everything but the HTTP call and JSON parsing is pure Nim with no dependencies: SHA-256, HMAC and HKDF live in `freeqsay/sha256`, and the PNG encoder writes stored deflate blocks. ## How it works ``` handle → resolveHandle → did did → HKDF-SHA256(avatar-v1) → traits → 16×24 sprite → ANSI message → cowsay balloon ``` `fixtures/avatar-conformance.json` pins the cross-implementation contract: canonical seed, traits, and a sprite hash over all four facings. The suite replays it, so this port derives byte-identical characters to the Clojure and TypeScript implementations — including the sfc32 PRNG, reproduced on `uint32` lanes so trait selection matches the JavaScript original bit for bit.