Installing EONYX: build, run, and configure
EONYX is a terminal AI agent for working with code, written in Go.
It connects straight to any OpenAI-compatible API.
OpenAI, OpenRouter, DeepSeek, Moonshot.
Or a local Ollama, llama.cpp or LM Studio server.
No telemetry, no OAuth.
One process, one binary.
1 📦 Requirements and installation
1.1 What you need
- Go 1.26+ to build from source. A prebuilt binary needs nothing.
- An API key for any OpenAI-compatible provider. A local model server needs no key at all.
- A terminal: Windows Terminal, PowerShell, any Linux shell, macOS Terminal.
- A licence. One key binds one machine, and it is verified at every startup.
1.2 Installation options
git clone https://github.com/eonyx-core/eonyx
cd eonyx
make build # builds bin/eonyx with the version from the git tagOr install into $GOPATH/bin:
make installThe version is baked in at build time from git describe --tags.
Nothing is hardcoded in the code:
eonyx --versionv* tag.Grab the archive for your platform from the Releases page and put the binary on your
PATH.Linux, macOS and Windows are covered.
1.3 First run
cd my-project
eonyxThe interactive TUI opens.
With no config at any level, EONYX writes a working eonyx.json into the global directory itself.
It ships two DeepSeek models and one local Ollama one.
All that is left is the key:
export DEEPSEEK_API_KEY=sk-... # or your own provider, see belowrun does the task without the TUI and exits:
eonyx run "describe what this repository does"
eonyx run -m deepseek-v4-pro --yolo "fix the failing test"It works in a pipe too.
The argument beats stdin, so when piping do not repeat the prompt as an argument.
Otherwise stdin is ignored:
{ echo "explain the error:"; cat error.log; } | eonyx runThe answer streams to stdout.
Tool activity and token stats go to stderr.
So redirecting into a file or a pipe is safe.
Flags:
--model/-m— the model--session/-s <id>— continue a specific session--continue/-c— continue the most recent one--yolo— disable tool confirmations--json— events as NDJSON instead of text--system-prompt— replace the system prompt
Exit codes tell outcomes apart for CI:
0— success2— usage error3— API error4— the task finished, but a tool was blocked by the permission gate5— hit themax_stepslimit130— interrupted with Ctrl+C
1.4 The remaining subcommands
| Command | Purpose |
|---|---|
eonyx | interactive TUI chat |
eonyx run "…" | headless agent run |
eonyx sessions | list of saved sessions |
eonyx info | configuration, data, and project paths |
eonyx config | the final (merged) configuration |
eonyx clean | remove its own configuration files |
1.5 Keys and commands in the TUI
| Key | Action |
|---|---|
Enter | send the message |
Ctrl+J | line break in the input field |
↑ / ↓ | history of your messages |
Esc | interrupt the running turn |
/ | command palette (on empty input) |
@ | file path autocompletion (Tab inserts) |
? | help (on empty input) |
Ctrl+P | command palette |
Ctrl+N | switch model |
Ctrl+S | session manager |
Ctrl+K | skills and MCP servers (on/off) |
Ctrl+O | expand/collapse tool output |
Ctrl+R | expand/collapse the model’s reasoning |
! <cmd> | run a shell command |
Ctrl+C | quit |
Slash commands: /new, /clear, /add [path], /rename <title>, /fork, /export, /yolo, /plan, /timestamps, /model, /sessions [query], /help, /quit.
! command is written into the conversation.The model then sees what you ran and what changed, such as the directory or an environment variable.
2 🧭 Configuration
EONYX reads eonyx.json from three levels and merges them top down.
Each level overrides the one before it.
- Global config
- Windows —
%LOCALAPPDATA%\eonyx\eonyx.json - Linux/macOS —
$XDG_CONFIG_HOME/eonyx/eonyx.json(~/.config/…by default)
- Windows —
- Global data
- Windows — the same path
- Linux/macOS —
$XDG_DATA_HOME/eonyx/eonyx.json(~/.local/share/…by default)
- Project config
eonyx.jsonor.eonyx.json, found by walking up from the current directory
Wherever the project config is found becomes the project root.
A service folder .eonyx for exports and logs appears inside it.EONYX_GLOBAL_CONFIG, EONYX_GLOBAL_DATA and EONYX_CACHE_DIR override the paths.eonyx info and eonyx config show what was loaded and from where.
EONYX refuses to start rather than quietly ignore a typo.
An unknown field is reported with its name and line number.
A wrong type or broken JSON gets a generic message.
2.1 A full eonyx.json example
This is the default config written on the first run.
It carries every supported top-level key:
{
"providers": {
"deepseek": {
"type": "openai",
"base_url": "https://api.deepseek.com",
"api_key": "$DEEPSEEK_API_KEY"
},
"local": {
"type": "openai",
"base_url": "http://localhost:11434/v1",
"api_key": ""
}
},
"models": [
{"id": "deepseek-v4-flash", "provider": "deepseek", "context_window": 1000000, "input_price": 0.14, "output_price": 0.28},
{"id": "deepseek-v4-pro", "provider": "deepseek", "context_window": 1000000, "input_price": 0.435, "output_price": 0.87},
{"id": "qwen2.5-coder", "provider": "local", "context_window": 32768, "input_price": 0, "output_price": 0}
],
"default_model": "deepseek-v4-flash",
"max_steps": 50,
"permissions": {
"allowed_tools": []
},
"skills": [],
"mcp": [],
"hooks": {},
"report": {
"database_url": ""
},
"system_prompt": "",
"log_file": "",
"retry": {}
}2.2 Providers and models
A provider is a way to reach an API.
There is one type so far, "openai", and it covers any OpenAI-compatible endpoint:
"providers": {
"openrouter": {
"type": "openai",
"base_url": "https://openrouter.ai/api/v1",
"api_key": "$OPENROUTER_API_KEY"
}
}api_key and base_url may reference environment variables such as $OPENAI_API_KEY.
They expand after the layers merge, so no secret sits in the file.
A model binds an id to a provider:
| Field | Purpose |
|---|---|
id | the model name as the API understands it |
provider | a key from the providers block |
context_window | context size in tokens — triggers auto-summarization of history |
input_price | price per 1M input tokens (USD), for the cost counter in the header |
output_price | price per 1M output tokens (USD) |
max_steps caps the tool-call rounds inside one turn.
The default is 50.
2.3 Permissions
The safe tools are always allowed: read, ls, glob, grep, git_status, git_diff, git_log, todo_write.
Everything else needs permission: writing, editing, bash, commits, MCP tools.
"permissions": {
"allowed_tools": ["bash", "write", "edit"],
"sandbox": true
}allowed_tools— tools that run without asking.sandbox— locksread,write,edit,multi_editandlsinside the project root. Off by default.--yolo, or/yoloin the TUI — drops the gate for the whole session.
The TUI adds an interactive layer on top.
A tool outside the allowlist raises an Allow once / Allow always (this session) / Deny dialog.
A denial does not kill the turn: the model gets a tool error and adapts.
/plan switches on a stricter gate.Only read tools stay available, so the agent studies the code and proposes a plan.
When the plan is ready EONYX asks: implement it, allow edits, or keep planning.
2.4 Hooks
Hooks are shell commands around tool calls.
The format is tool name → before / after:
"hooks": {
"write": {
"before": "echo \"writing: $EONYX_INPUT\" >> .eonyx/audit.log",
"after": "gofmt -l ."
},
"bash": {
"before": "[ \"$EONYX_CWD\" = \"$HOME\" ] && exit 1 || exit 0"
}
}Rules:
beforeruns first. A non-zero exit code blocks the call.afterruns on success. Its output is appended to the tool result, so the model sees it. An error there does not break the result.- Context arrives through environment variables, never substituted into the command text. Injection through the input is impossible.
EONYX_TOOL— the tool nameEONYX_PHASE—before/afterEONYX_CWD— the working directoryEONYX_INPUT— the JSON inputEONYX_RESULT— the result, only inafter
- Each hook gets 30 seconds, in the same shell session the
bashtool uses.
2.5 Other keys
| Key | Purpose |
|---|---|
skills | additional skill directories (see the Skills article) |
mcp | the list of MCP servers (see the MCP article) |
report.database_url | PostgreSQL for the read-only reporting tools empty — the tools are not registered |
system_prompt | a full replacement for the generated system prompt |
log_file | a log path instead of the standard one; supports $ENV |
retry | max_retries — 3 by defaultbase_delay_ms — 500, doubles up to 30 sresponse_header_timeout_sec — 60 |
3 📋 Summary
- Installation
make buildfrom source or a binary from GitHub Releases- check:
eonyx --version
- Running
eonyx— interactive TUIeonyx run "task"— headless, stdout/stderr separated
- Configuration
- the
eonyx.jsoncascade: global → data → project - strict schema,
$ENVreferences for secrets
- the
- Models
- any OpenAI-compatible API
context_windowtriggers auto-summarization- prices — the cost counter in the header
- Permissions
- reading is always allowed
- everything else — via
allowed_tools, the TUI dialog,--yolo, or/plan
- Hooks
before/afteraround tools- context through
EONYX_*variables beforecan block