Quickstart
Install
Section titled “Install”Install script (Linux, macOS)
Section titled “Install script (Linux, macOS)”curl -fsSL https://letmoa.run/install.sh | shInstalls the latest release into /usr/local/bin if it is writable, otherwise
~/.local/bin. Override with MOA_INSTALL_DIR, or pin a version with
MOA_VERSION=v0.18.1. The script never uses sudo.
Homebrew (macOS, Linux)
Section titled “Homebrew (macOS, Linux)”brew install e-aleixandre/tap/moaManual download
Section titled “Manual download”Grab the archive for your platform from
GitHub Releases, verify it
against checksums.txt, extract it, and put moa somewhere on your PATH.
go install
Section titled “go install”go install github.com/e-aleixandre/moa/cmd/moa@latestBuilds from source, so you need Go 1.25+. Only works for releases newer than v0.18 (older tags predate the current module path).
Keeping it up to date
Section titled “Keeping it up to date”moa update --check # report the available versionmoa update # download, verify, and replace the binaryUpdates never restart anything — restart Moa yourself afterwards. See CLI Reference for the details and the Homebrew/Nix caveat.
Requirements
Section titled “Requirements”- A provider login: an API key (
ANTHROPIC_API_KEY,OPENAI_API_KEY,XAI_API_KEY, orMETA_API_KEY) or an interactive OAuth login (see Authenticate) — at least one provider - To build from source instead of installing a release: Go 1.25+ and Node.js/npm (the latter only to build the embedded web UI frontend via
make build)
Build from source
Section titled “Build from source”make fe-install # first time only: install frontend depsmake build# → ./bin/moaExamples below use
moa. If you built locally without installing, use./bin/moa.
Authenticate
Section titled “Authenticate”Moa needs credentials for at least one provider. They are stored in
~/.config/moa/auth.json (mode 0600), or under MOA_CONFIG_DIR if you set
it. An environment variable always wins over a stored credential for the same
provider.
Environment variables (simplest)
Section titled “Environment variables (simplest)”export ANTHROPIC_API_KEY="..."# orexport OPENAI_API_KEY="..."# Or use the separately metered xAI developer APIexport XAI_API_KEY="..."# Then select Grokmoa -model grok# Meta Model API (Muse Spark)export META_API_KEY="..."moa -model museOAuth / interactive login
Section titled “OAuth / interactive login”moa --login anthropicmoa --login openai# SuperGrok/X subscription (uses the Grok consumer proxy, not XAI_API_KEY billing)moa --login xai# Muse subscription (mints a Meta Model API key from the login)moa --login metaAnthropic (Claude Pro/Max) and OpenAI (ChatGPT Plus/Pro) use the same
browser flow: moa opens the provider’s authorization page (and prints the URL if
the browser does not open), you sign in, and the provider hands back a callback
URL. For Anthropic, paste the code#state value shown on Anthropic’s page (or
the full callback URL). For OpenAI, moa catches the redirect itself when it runs
on the same machine; otherwise paste the full address the browser opened,
starting with http://localhost:1455/auth/callback?. A bare code without its
state is refused. On success the credential is written to auth.json.
moa --login openai first asks which method you want: 1 for the ChatGPT
subscription OAuth flow above, 2 to paste a plain OpenAI API key instead (read
without echo).
XAI_API_KEY uses the metered api.x.ai developer API. It is separate from a
SuperGrok/X subscription. moa --login xai instead starts an OAuth device flow:
open the displayed verification URL, enter the code, and sign in with the X
account that has the applicable SuperGrok/X entitlement. This consumer route
uses a shared public Grok Build client and a private consumer proxy, so it is
best-effort rather than a promised public xAI API integration. If both are set,
XAI_API_KEY takes precedence over the stored xAI OAuth login.
In moa serve you can also sign in, replace an API key or switch between a
subscription and a key from Settings → Providers, in the browser or from a
paired phone (see Provider sign-ins). For
Anthropic you can keep a subscription and an API key: the key takes over
when the plan’s limit is reached and the plan resumes when it recovers.
An expired login does not lock you out of Serve: sessions still open and existing ones still reopen, and the failure appears when you send a message, naming the provider to sign in to again. Switching that session to a model from a provider that is still authenticated is enough to keep working. A one-shot CLI run reports the problem before it starts, since it has nothing to fall back to.
For voice in the web UI — dictation and live calls with a voice delegate — and for Pulse Realtime, store a plain OpenAI API key in a separate slot, so the agent itself can stay on an OpenAI OAuth subscription:
moa --login openai-transcribeRemove credentials:
moa --logout anthropicUse it
Section titled “Use it”# One-shotmoa -p "refactor the handler to use middleware"moa -p @prompt.md
# Web UImoa serve# → http://127.0.0.1:8080What the first moa serve needs
Section titled “What the first moa serve needs”- Credentials for one provider, as above. Serve opens sessions even without them; the error appears when you send the first message.
- A working directory.
moa serveuses the directory you start it in as the workspace root, and every new session defaults to it. Start it in the repository you want to work on, or pick another path per session in the session palette (⌘K). - Nothing else. Config files are optional: with no
~/.config/moa/config.jsonmoa runs in its defaultyolopermission mode with an unrestricted path scope. Tighten that with Configuration. - No authentication is on by default, and Serve binds to
127.0.0.1. Before exposing it anywhere else, read Web UI → Security.
Files moa creates on first use, all under ~/.config/moa/ (or MOA_CONFIG_DIR):
auth.json for credentials, sessions/ for history, config.json if you write
one. See Overview → Storage for the full list.
Troubleshooting
Section titled “Troubleshooting”no credentials for provider "x": set X_API_KEY or run --login — nothing is
stored for the provider of the model you asked for. Set the environment variable
or run the matching moa --login.
token refresh failed: … (run --login <provider> to re-authenticate) — the
stored OAuth credential expired and the refresh was rejected (invalid_grant
and similar come from the provider). Run moa --login <provider> again. In
Serve you can keep working meanwhile by switching the session to a model from a
provider that is still authenticated.
Anthropic rejects OAuth requests, complaining the Claude Code version is too
old — Anthropic requires OAuth clients to announce a recent Claude Code client
version, and raises the floor over time. Moa hardcodes that version, so the fix
is to update moa (moa update); nothing in your configuration affects it.
An MCP server never becomes ready — a new session’s first message waits up
to about 15 seconds for its MCP servers to start, then goes ahead without the
ones that are not ready. Open the session’s MCP page (from the session panel
or the status line): a server that failed shows its error there, and you can turn
it on or off or restart it. A remote server that needs a login shows needs
sign-in: press Connect and follow the steps. For a project .mcp.json,
also check the directory is trusted — an untrusted project’s servers are not
loaded at all (see Configuration).
Voice input does nothing — it needs moa --login openai-transcribe (or a
plain OPENAI_API_KEY), and browsers only grant microphone access over HTTPS or
on localhost. Without that key the composer shows no phone button (Talk live).
- CLI Reference — all flags and model aliases
- Web UI —
moa servefeatures - Configuration — config files and options