Skip to content

chore(build): reduce micasa binary size (~54MB) #1003

Description

@cpcloud

Problem

The micasa binary is ~54 MB stripped (go build -ldflags="-w -s" -trimpath ./cmd/micasa). That's heavy for an interactive TUI and bloats download archives and container layers.

On-disk breakdown (53.5 MB)

Section Size Notes
.text (machine code) ~22 MB
.gopclntab (PC→line tables) ~18 MB needed for stack traces; scales ~linearly with code
.rodata (strings, embedded data, type info) ~12 MB
.data / .noptrdata / .typelink / .itablink ~2 MB
.bss (33 MB) not on disk — the crypto/internal/fips140/drbg.memory 32 MiB scratch buffer, runtime-only

Biggest discretionary contributors

  1. Multi-provider LLM stackinternal/llm/client.go unconditionally imports 10 any-llm-go providers. The gemini provider drags in google.golang.org/genaigoogle.golang.org/grpc + google.golang.org/protobuf + cloud.google.com/go/auth (s2a-go, gax-go, …). anthropics/anthropic-sdk-go (~1.4 MB) and openai-go (~1.8 MB, shared by openai/groq/mistral/deepseek/llamacpp/llamafile) are also linked. Rough total incl. proportional gopclntab/rodata: ~12–16 MB.
  2. alecthomas/chroma/v2 lexers via glamour markdown rendering — code + dozens of embedded language lexers. ~2–4 MB.
  3. itchyny/gojq via internal/config/query.go (config query <jq> feature) — full jq engine + yacc parser tables. ~1.5 MB.
  4. brianvoe/gofakeit/v7 via internal/fake (demo data) — code + embedded word lists. ~1.5 MB.

Quick wins already identified

  • .goreleaser.yaml ldflags only sets -X main.version=…no -s -w, so released archives aren't even stripped (~3–5 MB larger than they need to be).
  • upx --best --lzma takes the stripped binary 53.5 MB → 12.5 MB (measured). Tradeoffs: small one-time decompress on launch, breaks go version -m, occasional Windows AV false positives, friction with macOS code-signing/notarization.

Options to weigh

  • A. Add -s -w to goreleaser ldflags (trivial, no downside).
  • B. Add a UPX post-build hook in goreleaser (4× smaller downloads; band-aid — same runtime memory + container size; macOS may need exclusion or post-pack signing).
  • C. Collapse / replace the any-llm-go multi-provider stack with thin direct HTTP clients (OpenAI-compatible chat-completions + Anthropic Messages cover everything micasa does — NL→SQL and summary calls). Real fix, ~10–15 MB, breaking refactor of internal/llm. Aligns with "LLM is opt-in, not a crutch."
  • C1 (lighter). Just drop the gemini provider import to shed genai/grpc/protobuf/cloud-auth (~6–10 MB), keep the rest.
  • D. Drop gojq from config querying (replace with a path selector or just config show) — ~1.5 MB.
  • E. Trim chroma lexers down to the few languages actually highlighted — needs a fork/vendored registry; brittle across chroma upgrades. ~2–3 MB.

Recommendation

Do A + B now for shipped artifacts (≈ 57 MB unstripped release → ≈ 12 MB), then C/C1 as a follow-up to cut the uncompressed footprint (UPX-decompressed memory and container layers still carry the full size).

Metadata

Metadata

Assignees

No one assigned

    Labels

    choreMaintenance and housekeepingllmLLM and chat featuresnixNix packaging and buildperformancePerformance optimization

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions