Installation Guide
This guide covers installing GarraIA on various platforms.
Prerequisites
- Rust 1.97+ (if building from source)
- FFmpeg (for voice mode)
- Linux (prebuilt binaries): glibc ≥ 2.35 — Ubuntu 22.04+, Debian 12+.
Older distros and musl-based systems (Alpine) must build from source.
(Since v0.3.4 OpenSSL is statically vendored into the binaries: no system
libsslrequired.)
Quick Install
Linux/macOS
curl -fsSL https://garraia.org/install.sh | sh
Minimum glibc (Linux): the release binaries are built on Ubuntu 22.04 and require glibc ≥ 2.35. The installer probes
ldd --versionand aborts early with instructions if the system is older (the raw loader error would beversion `GLIBC_2.xx' not found). Keep this note in sync with thebuild-linux-x86_64runner in.github/workflows/release.ymlandMIN_GLIBCininstall.sh.
Two names, one binary. The installer leaves garraia (the release asset)
and garra (a relative symlink to it) in the install directory --
~/.local/bin when that is on your PATH, otherwise /usr/local/bin, and
$PREFIX/bin on Termux -- so every garra ... command in these docs and in the
CLI's own hints works as written. A pre-existing garra that is a regular file
(say, a from-source build you copied by hand) is left untouched with a warning;
a stale symlink is repointed. garra update keeps working through the alias:
it resolves the link first and replaces garraia, the one binary it points
at, so the two names never drift apart.
macOS, upgrading from v0.4.3 or older: run
garraia update(notgarra update) once. Those binaries took the path macOS hands them (_NSGetExecutablePath, which may be the link itself) and wrote the new version overgarra, leavinggarraiabehind; from the first version that resolves the link onward, either name is fine. If it already happened (garrais a regular file andgarraia --versionis older), deletegarraand rerun the installer. Linux and Termux were never affected (/proc/self/exeis kernel-resolved), nor is Windows (garra.cmdrunsgarraia.exedirectly).
The same script (auto-synced) is published through alternative channels — the release CDN is the most robust against per-IP rate limits (HTTP 429 is common on cloud pods whose egress IP is shared by many users):
# Official mirror — GitHub release CDN (no aggressive per-IP limits):
curl -fsSL https://github.com/michelbr84/GarraRUST/releases/latest/download/install.sh | sh
# Repository main branch (raw) and community CDN mirror:
curl -fsSL https://raw.githubusercontent.com/michelbr84/GarraRUST/main/install.sh | sh
curl -fsSL https://cdn.jsdelivr.net/gh/michelbr84/GarraRUST@main/install.sh | sh
Windows
irm https://garraia.org/install.ps1 | iex
install.ps1 is the Windows sibling of install.sh and behaves the same way:
it detects the platform, resolves the latest release, downloads the binary,
verifies it against the release's SHA256SUMS, installs it as garraia.exe
next to a garra.cmd shim ("%~dp0garraia.exe" %* -- so garra works too,
and keeps working if you move the folder), adds the directory to your user
PATH, and then chains into garraia init and garraia start. It never needs
administrator rights. A pre-existing garra.exe, or a garra.cmd the installer
did not write, is left untouched with a warning.
Same mirrors as the shell installer:
# Repository main branch (raw) and community CDN mirror:
irm https://raw.githubusercontent.com/michelbr84/GarraRUST/main/install.ps1 | iex
irm https://cdn.jsdelivr.net/gh/michelbr84/GarraRUST@main/install.ps1 | iex
# GitHub release CDN (no aggressive per-IP limits) - see the caveat below:
irm https://github.com/michelbr84/GarraRUST/releases/latest/download/install.ps1 | iex
Release-CDN caveat.
install.ps1landed after thev0.3.3tag, soreleases/latest/download/install.ps1returns 404 until the next release is cut.release.ymlalready uploads it, so this resolves itself fromv0.3.4onward. Until then, usegarraia.org, raw, or jsDelivr on Windows. The shell installer is unaffected --install.shhas shipped as a release asset for several versions.The
Install endpointsworkflow probes every URL on this page daily, so a broken mirror surfaces without anyone having to notice by hand.
Passing options. irm | iex evaluates the script with no arguments -- there
is no PowerShell equivalent of sh -s --. To pass flags, turn the downloaded
text into a scriptblock and invoke that:
& ([scriptblock]::Create((irm https://garraia.org/install.ps1))) -SkipSetup
Environment variables work in both forms and are usually simpler in automation:
$env:GARRAIA_SKIP_INIT=1; $env:GARRAIA_SKIP_START=1
irm https://garraia.org/install.ps1 | iex
| Flag | Environment variable | Effect |
|---|---|---|
-SkipSetup | GARRAIA_SKIP_INIT=1 + GARRAIA_SKIP_START=1 | Install only. |
-SkipInit | GARRAIA_SKIP_INIT=1 | Skip the setup wizard. |
-SkipStart | GARRAIA_SKIP_START=1 | Skip starting the gateway. |
-NoLocal | GARRAIA_BOOTSTRAP_LOCAL=0 | Skip the GPU/Ollama prompts. |
-Version <tag> | GARRAIA_VERSION=<tag> | Pin a release instead of latest. |
-InstallDir <dir> | GARRAIA_INSTALL_DIR=<dir> | Install elsewhere. |
-NoModifyPath | GARRAIA_NO_PATH=1 | Do not touch the user PATH. |
An environment variable set by the caller always wins over the matching flag, so existing automation keeps its behavior.
Where it installs. %LOCALAPPDATA%\Programs\GarraIA by default -- a
per-user location, so no UAC prompt. The installer adds that directory to your
user PATH and to the current session, so garraia works immediately in the
terminal you ran it from; other terminals that were already open need a
restart. Windows system directories are refused outright.
SmartScreen. The published binaries and installers are not code-signed
(there is no certificate configured for this project). Windows SmartScreen will
show "Windows protected your PC" the first time you run the desktop installer,
and you need More info -> Run anyway. Windows Defender may also flag a
low-reputation unsigned binary. This is expected, not a sign of tampering --
verify the SHA256SUMS entry if you want certainty. Removing the warning
requires an OV/EV code-signing certificate, which is tracked separately.
Windows ARM64. From v0.3.4 a native ARM64 build
(garraia-windows-aarch64.exe, best-effort) is published, and the installer
selects it automatically on ARM64 hosts. Pinning GARRAIA_VERSION to anything
older than v0.3.4 on ARM64 fails with a 404 -- those releases only served
ARM64 via the x86_64 binary under Windows 11's x64 emulation, which you can
still download by hand if you need an older version.
Desktop app (optional). Releases also carry
garraia-desktop-windows-x86_64.msi (and, when the bundler produces it, a NSIS
-setup.exe) -- a tray application that bundles the CLI as a sidecar. It is
built best-effort, so a given release may ship without it. The MSI is not a
prerequisite for the CLI; the one-liner above is the supported path.
Android (Termux)
From v0.3.6 the installer has an Android branch. Run it inside the
Termux app — the F-Droid
or GitHub build; the Play Store fork is a different app that removed the
RUN_COMMAND permission and is not supported.
pkg install curl termux-exec
curl -fsSL https://garraia.org/install.sh | bash
garraia doctor # platform, dirs, config, providers, daemon + a Termux block
garraia chat # a cloud provider, or --url http://PC-ON-LAN:8080
uname -s reports Linux inside Termux, so the installer detects Android
through $TERMUX_VERSION (exported by every Termux shell) or a *com.termux*
$PREFIX. On that branch it:
- downloads
garraia-android-aarch64(bionic,aarch64-linux-android, API 21+) instead of the glibcgarraia-linux-aarch64, which does not run on Android; - skips the glibc preflight — Termux has no glibc loader to interrogate;
- installs into
$PREFIX/bin, which is onPATHand writable withoutsudo(/usr/local/binwould be created outsidePATH, a silent trap); - writes
$PREFIX/bin/garra-mcp-serverand$PREFIX/bin/garra-mcp-server-linker, two wrappers for external MCP hosts — the second execs through the Android loader, for hosts that filter the environment hard enough to break the first (see MCP servers under Termux below).
garra update resolves the same asset from inside Termux, so self-update works
normally.
Keep the session alive. Android's phantom process killer and battery
optimization stop long-running background processes. Run termux-wake-lock,
exempt Termux from battery optimization, and prefer keeping the gateway in the
foreground of the Termux session that started it. garraia start -d works, but
Android may still reap it; a real "always on" needs the companion app on the
v1 rung of ADR 0016.
A shell for the agent (bash) on Termux. Android has no kernel support
for containers, so Docker/Podman never exist there — and in the default
standard profile (ADR 0024) the runtime
does not register the bash tool without a docker/podman sandbox. Out of
the box, then, a Termux install has no shell tool; garraia doctor says so in
its Termux block (Tool bash) with the step below. The supported way to get
one is the allowlist-only host mode (#1272): declare the commands you
trust in agent.bash_allowlist, and bash is registered on the host where
only those patterns run.
agent:
bash_allowlist:
- "git status" # exact match
- "git log *" # prefix match: '*' only at the end
- "pkg list-installed"
- "ls *"
In this mode the allowlist is a boundary, not a confirmation bypass:
anything outside it is refused. A chained command (;, |, ||, &&) is
split into segments and every segment must match a pattern of its own
(#1592), so git log | grep foo needs both git log * and grep * declared,
and git log | sh stays refused while sh is not on the list. Command
substitution ($(...), backticks), redirections (<, >) and a bare &
(background escapes the timeout) never match a pattern at all. The denylist and
the risky-command tier still apply on top. There is no sandbox — garraia doctor,
/api/diagnostics (tools.bash) and garra_status all say
"SEM sandbox, em modo allowlist-only" so the degraded mode is always visible.
Keep the list to commands you would let the agent run unattended on the phone.
An empty list (the default) keeps bash off. See
execution profiles for the full policy.
Static musl builds are deliberately not shipped. A static musl binary
breaks DNS on Android: there is no /etc/resolv.conf, musl's internal resolver
never reaches dnsproxyd, and LD_PRELOAD cannot intercept a static binary.
Garra Desktop on Linux
From v0.3.5 releases also carry the desktop app (parrot overlay + chat bar)
for Linux, built best-effort by the Tauri bundler:
garraia-desktop-linux-x86_64.deb—sudo apt install ./garraia-desktop-linux-x86_64.debgarraia-desktop-linux-x86_64.AppImage—chmod +xand run; needslibfuse2on Ubuntu 22.04+ (sudo apt install libfuse2), or run with--appimage-extract-and-run
These are different packages from the CLI ones: garraia-linux-x86_64.deb
installs only the terminal CLI (/usr/bin/garraia); the desktop .deb
installs the tray app plus the same CLI as its bundled sidecar, also at
/usr/bin/garraia. Because both own that path, the desktop package declares
Provides/Conflicts/Replaces: garraia — installing it replaces the CLI
package (you keep the garraia command either way). Pick one:
| You want | Install |
|---|---|
| Terminal only | garraia-linux-x86_64.deb (or the one-liner above) |
| Parrot + chat bar + CLI | garraia-desktop-linux-x86_64.deb |
Wayland caveat. On Wayland sessions (Ubuntu's default) the window manager does not honor always-on-top/skip-taskbar for regular windows, so the parrot and the chat bar behave as normal windows. X11 sessions behave as documented. The app itself works on both.
Garra Desktop on macOS
From v0.4.9 releases also carry the desktop app (parrot overlay + chat bar)
for macOS, built best-effort by the Tauri bundler:
garraia-desktop-macos-x86_64.dmg— open the DMG and drag to Applicationsgarraia-desktop-macos-aarch64.dmg— Apple Silicon, same install path
Like the Linux and Windows desktop packages, the macOS app bundles the CLI as a
sidecar; it is a different distribution from the CLI binaries
(garraia-macos-x86_64), which install only the terminal command. Pick one:
| You want | Install |
|---|---|
| Terminal only | the garraia-macos-* binary from the one-liner above |
| Parrot + chat bar + CLI | garraia-desktop-macos-*.dmg |
Unsigned on macOS. As with Windows, the desktop app is not
code-signed or notarized (no Apple Developer certificate is configured for this
project), so Gatekeeper will block the first launch. Right-click the app and
choose Open, or clear the quarantine attribute with
xattr -dr com.apple.quarantine /Applications/garraia-desktop.app. Both the
CLI and the desktop app behave the same way here; verify against SHA256SUMS
if you want certainty about what you downloaded.
CLI vs Desktop — what each one supports
The CLI and the Desktop app share the same gateway, channels, providers and plugins. The difference is the shell around them:
| Capability | CLI (garraia) | Desktop (tray app) |
|---|---|---|
| Terminal commands, chat, wizard | yes | yes (sidecar) |
| Gateway on port / API | yes | yes (spawned) |
| Parrot overlay + chat bar | no | yes |
| Tray icon / always-on-top window | no | yes (X11 only; see Wayland caveat) |
Auto-update via latest.json | no (use install.sh / install.ps1) | yes (Tauri updater, when a signing key is configured) |
| Package formats | .deb/.rpm/AppImage (Linux), .msi/NSIS (Windows), plain binary (macOS) | .deb+AppImage (Linux), MSI+NSIS (Windows), .dmg (macOS) |
Both are built best-effort on some platforms, so a given release may ship without a desktop package. The CLI one-liner is always the supported path.
Build from Source
Prerequisites
# Install Rust 1.97+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update stable
# Install FFmpeg (for voice mode)
# Ubuntu/Debian:
sudo apt install ffmpeg
# macOS:
brew install ffmpeg
# Windows:
# Download from https://ffmpeg.org/download.html
Build
# Clone the repository
git clone https://github.com/michelbr84/GarraRUST.git
cd GarraRUST
# Build release
cargo build --release
# Or with plugin support
cargo build --release --features plugins
Build on Termux (fallback)
Building on the device is a fallback, not the supported path — the release asset above is. Use it when you need an unreleased commit, or on a device the published binary does not cover.
pkg install rust clang pkg-config openssl ca-certificates git
# Termux ships its own CA bundle; without this, TLS fails with a generic error
# during the build (crates.io fetches) rather than anything that names certs.
export SSL_CERT_FILE=$PREFIX/etc/tls/cert.pem
# LTO is the difference between "slow" and "impossible" here. The workspace
# release profile sets `lto = true` + `codegen-units = 1`, and on a mid-range
# phone the linker gets OOM-killed -- the symptom is a bare `Signal 9`, with no
# error message pointing at memory. Override both:
CARGO_PROFILE_RELEASE_LTO=false \
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 \
cargo build --release --package garraia
install -m 755 target/release/garra "$PREFIX/bin/garraia"
Budget for it: on a device with under 4 GB of RAM expect 30-60 minutes and
set up swap first. Even with LTO off, the link step is the peak. Keep the
session in the foreground (termux-wake-lock) — a build backgrounded long
enough gets killed by the phantom process killer, which also reads as an
unexplained Signal 9.
Note that a source build does not produce $PREFIX/bin/garra-mcp-server
nor $PREFIX/bin/garra-mcp-server-linker; both wrappers are written by
install.sh. See MCP servers under Termux for how
to create them by hand.
Install
# Copy to PATH
sudo cp target/release/garra /usr/local/bin/
# Or use cargo install
cargo install --path crates/garraia-cli
Initial Setup
1. Initialize
garraia init
This wizard will (plan 0126):
-
Detect the environment (OS, root, RunPod hints, systemd, NVIDIA GPU via
nvidia-smi, Ollama install/running state, and whether the well-known ports3888,8080,11434,7860,9090are free) and print a one-line summary. -
Preserve an existing
config.yml: if one is already present, the wizard prompts you to backup-and-overwrite (renames the old file toconfig.yml.bak-YYYYMMDD-HHMMSS), merge/update (keeps your values and only adds missing keys), or cancel. Non-interactive runs (e.g.garraia initin CI) print the legacy hint and exit 0 without touchingconfig.yml. -
Offer a provider mode (issue #1180 — cloud-first on every machine):
- Cloud-first (OpenRouter +
z-ai/glm-5.3-flashprimary + Ollama fallback) — the default, GPU or not. On this path the "install Ollama?" confirm defaults to no and the local model picker lands on "skip the download", so nothing is pulled unless you say so. - Local-first (Ollama on this GPU primary + cloud fallback) — the
second option, offered when an NVIDIA GPU is detected and
GARRAIA_BOOTSTRAP_LOCALis not set to0. Here the local prompts stay preselected, since local is what you just asked for. - Cloud-only (no local stack) — the only mode offered on CPU / no-GPU machines.
- Cloud-first (OpenRouter +
-
On the cloud branch, let you pick the provider — OpenRouter (the project default), OpenAI, or Anthropic — and prompt for that provider's API key (each preset names its own env var, default model, and key-creation URL).
-
On GPU machines (and only after explicit confirmation), install Ollama via the official upstream script and pull the local model you pick (
qwen3.8:latest, ~18 GB, is the preselected row on the local-first path; lighter tags and "skip" are offered). NVIDIA drivers and CUDA are never installed by the wizard — ifnvidia-smiworks, the wizard assumes the GPU runtime is already usable. -
Offer to enable voice (Chatterbox TTS @
:7860+ faster-whisper STT @:9090). Endpoints are written intoconfig.yml; install instructions for both servers are printed for copy-paste (auto-install of those Python stacks is deferred — see voice.md). -
Offer a primary messaging channel (#1430). The step is a single list, and the first row — no channel for now — is the default, so pressing Enter without reading still connects nothing:
- Telegram, via a bot token from
@BotFather(the prompt that was already here). - WhatsApp, your personal number, by QR. The row says on its face that
this is the linked device path through an unofficial client; picking
it hands off to the very same
garraia whatsapp linkflow, with its consent screen (default no), the QR and the "who may talk to me?" question unchanged — see whatsapp.md. - Both, which runs Telegram now and WhatsApp at the end.
The WhatsApp hand-off runs after
config.ymlis written, because the link writes into the samechannels.whatsapp_linkedsection. A link that fails (no Node.js, QR not scanned, you changed your mind) does not failgarraia init: the rest of the configuration is already saved and the wizard just tells you to rungarraia whatsapp linklater. Non-interactive runs never reach this step at all. - Telegram, via a bot token from
-
Store API keys and bot tokens in the encrypted vault.
-
Mint
gateway.api_keywhen the machine is server-like (root or a RunPod pod) orHOSTis not loopback, and print how to expose the gateway (HOST=0.0.0.0 garraia start). Since v0.4.5 the wizard no longer writesgateway.host/gateway.portand removes them on a re-run: those keys are deprecated and never fed the bind, which comes from--host/HOSTand--port/PORT(#1261, auth-config.md §5.1). On a non-loopback bindgarraia startrefuses to boot without a gateway credential (gateway.api_keyorGARRAIA_GATEWAY_API_KEY).
Skip toggles:
GARRAIA_BOOTSTRAP_LOCAL=0— suppress the GPU/local-stack prompts even when a GPU is present (useful when you want to use the GPU for something else and run Garra in cloud-only mode).GARRAIA_SKIP_INIT=1— when running via thecurl | shinstaller (plan 0127, PR-B), skip the auto-run ofgarraia initand leave configuration for later. The installer falls back to printing next-steps and exits 0.GARRAIA_SKIP_START=1— same flow but skips the foregroundgarraia startaftergarraia initcompletes. Both toggles set together is equivalent to the pre-PR-B installer behavior.
The installers also skip both steps on their own, printing
Non-interactive install ... and the next-steps hint and exiting 0, when
nobody can answer the wizard: install.sh when /dev/tty cannot actually
be opened (a container, a CI runner, docker build — the device node is
there but has no controlling terminal behind it), install.ps1 when stdin
has no console behind it; both whenever CI is set to a non-empty value,
since a CI job that provides a pseudo-terminal still has no one to type.
2. Configure
garraia init writes this for you. To edit it by hand, first find the file the
gateway actually reads — the startup banner prints both the directory (Config)
and the filename (File).
The config directory is resolved in this order:
$GARRAIA_CONFIG_DIR, when set — the supported way to keep everything under a single directory of your choosing.~/.config/garraia(XDG), when it exists. This is the default for new installs.~/.garraia, when it exists and the XDG path does not (legacy).
Within that directory, config.yml wins over config.toml; if both exist the
TOML file is silently ignored.
gateway:
host: "127.0.0.1"
port: 3888
llm:
main:
provider: openai
model: gpt-4o
api_key: "sk-..."
channels:
telegram:
enabled: true
bot_token: "YOUR_BOT_TOKEN"
The API key is resolved per provider in the order credential vault → this
file → environment variable (OPENAI_API_KEY, OPENROUTER_API_KEY, …). If
none of the three yields a key, the provider is skipped at startup and the
gateway comes up unable to answer.
The credential vault needs a passphrase on every start. If you store keys in the vault, the gateway can only open it when
GARRAIA_VAULT_PASSPHRASEis present in its environment — the wizard cannot arrange that for you. Export it from your shell profile or a systemdEnvironmentFile. This is whygarraia initnow defaults to writing the key intoconfig.yml, which it creates with mode0600.
Run garraia config check to verify: it reports which file is in force and
fails with an explicit error for any provider whose key resolves nowhere.
3. Start
# Start in foreground
garraia start
# Or as daemon
garraia start --daemon
# With voice mode
garraia start --with-voice
Running under systemd
If you put the gateway in a systemd unit, restart it through systemd, not through the CLI:
systemctl --user restart garraia.service # user unit
sudo systemctl restart garraia.service # system unit
garraia restart stops whatever holds the port and starts a new process
outside the unit. Under an active unit that leaves an orphan daemon on the
port and the unit itself in a restart loop against Address already in use.
Since #1542 the CLI detects this (it reads the port holder's cgroup) and
refuses with the right command instead of doing it — exit code 78. To run
the CLI with its own flags, take the daemon out of the supervisor first
(systemctl --user stop garraia.service, then garraia start). The escape
hatch, if you really want the old behaviour, is
GARRAIA_ALLOW_SYSTEMD_RESTART=1 (exactly 1).
Docker Installation
Using Docker Compose
# Clone and start
git clone https://github.com/michelbr84/GarraRUST.git
cd GarraRUST
docker-compose up -d
Manual Docker
# Runtime-only image: build the binary first with `cargo build --release -p garraia`
# (Rust 1.97+). The repo's own Dockerfile does the multi-stage build for you.
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates ffmpeg \
&& apt-get clean
# cargo names the binary `garra`
COPY target/release/garra /usr/local/bin/garra
ENTRYPOINT ["garra"]
CMD ["start", "--host", "0.0.0.0"]
Pre-compiled Binaries
Download from GitHub Releases:
| Platform | Architecture | Bare binary | Archive |
|---|---|---|---|
| Linux | x86_64 | garraia-linux-x86_64 | garraia-linux-x86_64.tar.gz |
| Linux | aarch64 (ARM64) | garraia-linux-aarch64 | garraia-linux-aarch64.tar.gz |
| macOS | x86_64 | garraia-macos-x86_64 | garraia-macos-x86_64.tar.gz |
| macOS | aarch64 (Apple Silicon) | garraia-macos-aarch64 | garraia-macos-aarch64.tar.gz |
| Windows | x86_64 | garraia-windows-x86_64.exe | garraia-windows-x86_64.zip |
| Windows | aarch64 (ARM64, from v0.3.4) | garraia-windows-aarch64.exe | garraia-windows-aarch64.zip |
| Android (Termux) | aarch64 (from v0.3.6) | garraia-android-aarch64 | garraia-android-aarch64.tar.gz |
Plus, on Windows only, the desktop installers
garraia-desktop-windows-x86_64.msi and
garraia-desktop-windows-x86_64-setup.exe (both best-effort -- a release may
ship without them).
Linux packages (.deb / .rpm / AppImage)
From v0.3.4 releases also carry Linux packages (all best-effort, packaged by
the package-linux job from the same binaries as above -- see ADR 0015):
# Debian / Ubuntu (needs glibc >= 2.35, i.e. Ubuntu 22.04+ / Debian 12+):
sudo apt install ./garraia-linux-x86_64.deb
# Fedora / RHEL / openSUSE:
sudo rpm -i garraia-linux-x86_64.rpm
# Portable AppImage (x86_64 only for now):
chmod +x garraia-linux-x86_64.AppImage
./garraia-linux-x86_64.AppImage --version
Both packages install /usr/bin/garraia, the /usr/bin/garra symlink to it
(the short name the docs use), plus LICENSE/README.md under
/usr/share/doc/garraia/. aarch64 variants (garraia-linux-aarch64.deb /
.rpm) exist whenever the best-effort aarch64 binary was built.
garraia update under a package install. The self-updater swaps the
binary file in place, and a package installs it root-owned in /usr/bin -- so
run sudo garraia update, or simply download and install the next release's
package. The packages are not GPG-signed (there is no hosted apt/dnf
repository); verify downloads against the release's SHA256SUMS.
Bare binary or archive? They contain the same program. The archive adds
LICENSE and README.md and unpacks to a single directory whose executable is
named plainly garraia / garraia.exe, which is what you want for a manual
install or for repackaging. The bare binaries exist because garraia update
and both installers resolve assets by exact name -- they are the compatibility
surface and will not be renamed or removed.
From
v0.2.1(2026-05-14) aarch64 binaries match Rust'sstd::env::consts::ARCH, sogarraia updateselects the right asset automatically. Every asset ships with a sibling<name>.sha256, and all of them are listed inSHA256SUMS.
Verification
Check installation:
garraia --version
Run health check:
curl http://127.0.0.1:3888/api/health
Uninstall
garra uninstall removes the CLI from the machine: the binary itself, the
garra alias, the Termux wrappers (garra-mcp-server*) and any .old/.new
backups the updater left behind. It always shows the full list of targets and
asks for confirmation — --yes skips the prompt (required when stdin is not a
terminal, e.g. in scripts).
garra uninstall # install artifacts only; config and data stay
garra uninstall --yes # same, no confirmation prompt
garra uninstall --yes --purge # also removes config dir, data dir and the legacy ~/.garraia
What --purge covers: the config directory (~/.garraia or
$GARRAIA_CONFIG_DIR), the resolved data_dir from config.yml, and a legacy
~/.garraia left in $HOME when the config directory lives elsewhere.
What is never removed, on purpose:
- The desktop app — uninstall it with the platform installer (Add/Remove
Programs on Windows,
apt remove/ the.AppImagefile on Linux). - systemd units — if the daemon is running under a unit,
garra uninstallrefuses (exit 78) and tells you tosystemctl stop/disablethe unit first; removing the binary from under a managed unit leaves it crash-looping. - Ollama and other providers, project skills, and
~/.garra(sessions and skills of the Learning Agent) — remove those by hand if you want them gone. - On Windows, the
PATHentry in the registry is left alone; edit Environment Variables if you want it gone.
Other flags: --all-binaries also scans your PATH and the system
directories for other garraia/garra binaries (from older manual installs)
and removes them too.
Exit codes: 0 done (or nothing to do), 1 cancelled at the prompt, 64 no
terminal and no --yes, 70 a removal failed (the item is named on stderr;
system dirs may need sudo), 78 the daemon belongs to a systemd unit.
Troubleshooting
Old binary in PATH masks the new install
If garraia --version still prints the old version after installing a
package (.deb, etc.), an older copy earlier in PATH (typically a local
build in ~/.local/bin) is answering. Run which -a garraia garra, remove
the stale copy or fix the PATH order, then hash -r. garraia doctor
detects this and prints both paths (binary in doctor --json).
curl | sh fails with HTTP 429
raw.githubusercontent.com (which serves install.sh) and api.github.com
enforce per-IP rate limits. Cloud pods (RunPod etc.) share their egress IP
across many users, so a brand-new pod can be over the quota before you run
anything. Retry in a few minutes, or use one of the alternative channels:
# Official mirror — GitHub release CDN:
curl -fsSL https://github.com/michelbr84/GarraRUST/releases/latest/download/install.sh | sh
# Community CDN mirror of main:
curl -fsSL https://cdn.jsdelivr.net/gh/michelbr84/GarraRUST@main/install.sh | sh
On Windows the same mirrors serve install.ps1:
irm https://github.com/michelbr84/GarraRUST/releases/latest/download/install.ps1 | iex
irm https://cdn.jsdelivr.net/gh/michelbr84/GarraRUST@main/install.ps1 | iex
Inside the installer itself, downloads retry automatically and the release
tag is resolved from the github.com redirect (not the API), so the 429
surface is limited to that very first fetch of the script.
Termux: the downloaded binary does not run
The symptom is that the installer downloads, the checksum verifies, and running
garraia fails anyway. That means an install.sh older than v0.3.6 ran: it
had no Android branch, so uname -s reporting Linux made it fetch the glibc
garraia-linux-aarch64, which Android's bionic loader cannot run.
https://garraia.org/install.sh is a static copy in a separate repository,
synced on a daily cron and published manually — so it can lag behind main by
a day or more. If you hit this, fetch from a channel that tracks the source
directly:
curl -fsSL https://raw.githubusercontent.com/michelbr84/GarraRUST/main/install.sh | bash
Confirm before running: grep -c TERMUX_VERSION install.sh must be non-zero.
Termux: TLS, SSL_CERT_FILE and certificates
SSL_CERT_FILE does not affect the CLI's HTTPS traffic. garra talks to
LLM providers, GitHub and every other endpoint through reqwest with the
rustls-tls feature, whose roots are the webpki (Mozilla) set compiled into
the binary. No system trust store is read, so nothing to point at and nothing
to configure — TLS works on a fresh Termux install with no setup.
Two real exceptions:
- Postgres. The
sqlxdriver uses native roots, so TLS to a Postgres server does read the system store. ExportSSL_CERT_FILE=$PREFIX/etc/tls/cert.pem(pkg install ca-certificates).garraia doctorraises this only when aGARRAIA_*_DATABASE_URLis set. - Building from source.
cargofetching crates.io is a separate process with its own trust store — see the build section above.
If you are debugging a rustls-platform-verifier panic (Expect rustls-platform-verifier to be initialized, android.rs:90): that crate needs
a JNI Context that does not exist outside the JVM, and it is not in the
garra binary. Verify with
cargo tree -p garraia --target aarch64-linux-android --invert rustls-platform-verifier,
which reports "did not match any packages"; CI asserts it stays that way. A
panic with that message comes from a different binary on the device.
MCP servers under Termux
Two distinct failures, with distinct fixes.
1. Garra as an MCP server, spawned by an external host. MCP hosts spawn
their servers with a filtered environment (env -i PATH=… HOME=…), which drops
LD_PRELOAD. On Android the ELF exec resolves through the termux-exec shim, so
without it the exec fails before garraia runs at all — nothing inside the
binary can recover from that. Point the host at the wrapper install.sh
installs:
{ "mcpServers": { "garra": { "command": "/data/data/com.termux/files/usr/bin/garra-mcp-server" } } }
Equivalent, if you would rather configure the host directly:
{ "mcpServers": { "garra": {
"command": "garraia", "args": ["mcp-server"],
"env": { "LD_PRELOAD": "/data/data/com.termux/files/usr/lib/libtermux-exec.so" }
} } }
After a source build the wrapper does not exist; recreate it with:
cat > "$PREFIX/bin/garra-mcp-server" <<EOF
#!$PREFIX/bin/sh
# \$PREFIX is baked in as a default on purpose: the host that runs this
# wrapper is the one filtering the environment, so PREFIX may be unset too.
PREFIX="\${PREFIX:-$PREFIX}"
if [ -f "\${PREFIX}/lib/libtermux-exec.so" ] && [ -z "\${LD_PRELOAD:-}" ]; then
LD_PRELOAD="\${PREFIX}/lib/libtermux-exec.so"
export LD_PRELOAD
fi
exec "$PREFIX/bin/garraia" mcp-server "\$@"
EOF
chmod +x "$PREFIX/bin/garra-mcp-server"
1b. The host filters the environment so hard the wrapper itself will not
run. Some hosts spawn with a completely stripped environment
(env -i PATH=… HOME=…). Two things can then fail, and they look alike:
# the host cannot exec the wrapper script at all
timeout: failed to run command 'garra-mcp-server': Permission denied
# or the wrapper runs, and the inner exec of the ELF fails
garra-mcp-server: 8: exec: /data/data/…/garraia: Permission denied
The Android dynamic loader sidesteps both: it maps the ELF directly, with no
termux-exec shim and no LD_PRELOAD anywhere. install.sh writes a second
wrapper for the inner-exec case:
{ "mcpServers": { "garra": { "command": "/data/data/com.termux/files/usr/bin/garra-mcp-server-linker" } } }
If even that fails to start — the wrapper is a script too, so the first failure above still applies to it — point the host at the loader itself, with nothing in between. This is the configuration verified end to end on Android 13 with a 100% filtered environment (issue #920):
{ "mcpServers": { "garra": {
"command": "/system/bin/linker64",
"args": ["/data/data/com.termux/files/usr/bin/garraia", "mcp-server"]
} } }
/system/bin/linker64 exists on every 64-bit Android;
/apex/com.android.runtime/bin/linker64 is the equivalent on Android 10+ if
the first is missing. Use the 64-bit loader — the release asset is
aarch64-only.
After a source build the loader wrapper does not exist either; recreate it with:
cat > "$PREFIX/bin/garra-mcp-server-linker" <<EOF
#!$PREFIX/bin/sh
for garra_linker in /system/bin/linker64 /apex/com.android.runtime/bin/linker64; do
if [ -x "\$garra_linker" ]; then
exec "\$garra_linker" "$PREFIX/bin/garraia" mcp-server "\$@"
fi
done
exec "$PREFIX/bin/garraia" mcp-server "\$@"
EOF
chmod +x "$PREFIX/bin/garra-mcp-server-linker"
2. Garra as an MCP client, spawning npx/python servers. Packages
installed manually through npm/pip carry /usr/bin/env shebangs, a path Termux
does not have, and the child dies with
env: 'node': No such file or directory. Run pkg install termux-exec — from
v0.3.7 the gateway injects LD_PRELOAD into MCP children on Android by
itself, but the shim still has to be installed. For a binary you installed by
hand, termux-fix-shebang <file> rewrites the shebang in place.
garraia doctor checks the shim, both wrappers, $PREFIX/bin on PATH and
the trust store in one pass, and prints the next step for whatever is missing —
including the ready-to-paste linker64 command and args for your install.
Port already in use
# Find what's using the port
lsof -i :3888
# Use a different port
garraia start --port 3889
Permission denied
# Make executable
chmod +x garraia
Database issues
# Remove database and start fresh
rm -rf ~/.garraia/data/
garraia start
Update GarraIA
garraia update
# If update fails, rollback
garraia rollback