A coding agent in one C file. ~800 KB, no runtime, no package manager.
Runs offline on a model you own, and on a phone.
site · releases · changelog · hako (models) · hako-edit · hako-studio · org

: opens the command menu under the line · / drives the model · every skill is /<skill>

:theme previews as you move; the default is your terminal's own colors

:model lists the provider's own catalog, narrowed with /
| Local first. Auto-defaults to any installed hako model ( hako-sho 3B or hako-koi 7B); it runs on your device, no key, no cloud. The agent detects it and just works. |
Single binary. One C file. Builds with gcc -lpthread. Curl on PATH for HTTP. Nothing else linked. |
14 cloud providers when you want them. Anthropic OAuth (Claude Pro/Max sub), GitHub Copilot, GH Models (free), OpenRouter, plus 9 more over OpenAI-compat. Set a key or run a one-line :login, your call. |
—— I ——
Every serious coding agent ships a runtime with it. Hermes pulls Python 3.11, Node, uv,
ripgrep and ffmpeg; on Windows it downloads a portable Git too. Claude Code and its
neighbors want Node and npm. They are good tools; they are also a language ecosystem you
have to keep alive on every machine you want to think on.
hako is the same idea built the other way. One C file. It compiles with
gcc hako.c -o hako -lpthreadand the result is a ~800 KB binary (the browser UI inside) that links libc and nothing else. It talks to fourteen cloud providers when you want them, and to a 3B model on your own disk when you do not. It runs on a laptop, on a FreeBSD box, on Windows, and in a 32-bit Linux emulator on an iPhone: the same source, the same behavior.
| hako | agents that ship a runtime | |
|---|---|---|
| install | one binary, ~800 KB | interpreter + package tree |
| runtime deps | libc, pthread, curl(1) |
Python and/or Node, plus packages |
| build from source | gcc hako.c -o hako -lpthread |
a package manager and a lockfile |
| works with no network | yes, on local weights | usually only as a client |
| your data leaves | only to the provider you chose | varies |
| license | GPL-3.0 | varies |
The wager is simple: an agent is mostly a loop, some JSON, and careful text handling. None of that needs a runtime. Keeping it in C is what makes it small enough to live anywhere, and being small enough to live anywhere is the whole point.
—— II ——
- Local models run on the native engine: no ollama, no server. The agent
starts the hako engine as a
hakm --servechild that keeps the model resident and the system prompt's KV prefix warm (on disk too, so a cold first turn costs seconds, not minutes). Answers stream token by token. No in-process link, no compile flag, no daemon, no socket. Install the engine once (curl -fsSL https://mithraeums.github.io/hakm.sh | sh, ormake hakmnext to a checkout),hakm pull hako-sho, andhakodefaults to the mithraeum provider on first launch: no key, no:login, no config. Small models speak their native Qwen<tool_call>dialect, and the harness repairs the shapes they get wrong. (ollama is also supported as a separate optional provider,:login ollama, for non-hako models.) - 14 cloud providers when you want them. Anthropic native (SSE + OAuth via Claude Pro/Max), ChatGPT sign-in (any ChatGPT plan, free included), OpenAI function-calling, GitHub Copilot, GitHub Models (free), OpenRouter (PKCE), Nous Portal (device code), and OpenAI-compat aliases for Gemini, Groq, Cerebras, DeepSeek, Mistral, Together, Fireworks, xAI/Grok, custom. Bare
:loginis a menu: sign-ins first, API keys one level down in:login api;:login <name>goes straight there. - An input bar that stays out of the way. Drawn in the normal flow of the terminal (no absolute cursor moves, no scroll region), so a resize or a phone keyboard reflows cleanly. Keep typing while the model works; lines queue and run in order. History,
^Rsearch, kill-word, bracketed paste,ctrl+gto finish the line in$EDITOR,ctrl+ofor the whole transcript in a pager,ctrl+vto paste an image (or drop its file in) for any provider that can see one. - Your terminal's colors by default, the suite's themes when you want them.
theme=terminaluses the profile's own 16 colors;:theme <name>swaps to one of the shared presets or a customtheme.<name>.<slot>theme from the rc, and bare:themeopens a picker that recolors as you move (enter keeps it, esc puts the old one back, and it fits even a phone-sized window). The pick is written to the rc, so hake and the studio see it too. Truecolor where supported, 256-color fallback otherwise. - HAKO.md project context. Per-project
<cwd>/.hako/HAKO.mdis auto-loaded into the system prompt, the CLAUDE.md equivalent for hako-code. Binary-safe + size-capped (200KB). - Trust-gated tools.
read_file,list_dir,grep,glob,write_file,edit_file(str_replace),edit_lines(line range),run_shell(120s, up to 600 per call),web_fetch,read_skill,delegate(a subagent with its own context: it reads only unless you are in auto mode, cannot start another, and stops after 10 minutes). Per-call[y]/[n]/[a]approval, and writes show a diff of what they are about to change before you answer. Untrusted dir = all tools refused;:trustonce per project. - Persistent sessions + Agent Skills. Per-cwd session id, 7-day resume, append-only JSONL history. Skills follow the open standard (a folder, a
SKILL.mdwithnameanddescription, loaded by progressive disclosure), so a skill written for any compatible agent works here. Read from./.hako/skills,./.claude/skills,./.agents/skills,~/.hako/skills,~/.claude/skillsand~/.agents/skills, nearest first; a skill in two places loads once. Install a whole repository of them with:skill install owner/repo. - Drop-in in a repo you already set up.
AGENTS.mdorCLAUDE.mdin the working directory is read as project context (after.hako/HAKO.md), and skills already sitting in.claude/skillsare picked up as-is. Nothing to duplicate, nothing to migrate. - Several models on one chat. Run hako in two panes,
:jointhe second to the first's chat, and each keeps its own model:@name <text>asks another (@completes the live ones), a line without@goes to your own unless:route <name>sends it elsewhere, and each model hears what the others said, as it is said. A model writes to another the same way, with a line that starts with@name, and three models in a row hand it back to you. Memory notes say which one saved them, a hako never writes over a file another changed since it read it, and:sayis for the people on the chat, not the models. - Scriptable.
hako -p "task" --output-format jsonprints one JSON object and exits 0 or 1;-p -takes the prompt on stdin. Enough for another tool to drive hako as a subprocess. - Measurable.
hako --bench tools/bench/tools-v1.jsonlscores tool-call reliability against any provider and prints JSON: well-formed (exactly one parseable call) and correct (right tool, right argument), including restraint tasks where calling nothing is the right answer. It runs the real parser on a real turn with execution stubbed, so the number is about hako rather than about the benchmark. - Asks when it should.
ask_userputs a real choice in front of you, rendered through the same arrow-key picker as:modeland:theme. Tool approvals use it too. - Self-update.
hako --updatepulls the latest GitHub release, verifies sha256, atomically replaces the binary. No reinstall, no rebuild. - Streaming everywhere. Anthropic SSE, every OpenAI-compatible provider and the local engine stream live tokens, including through tool calls. The spinner names the tool in flight (
reading main.c);anim_style=picks one of eight. - Context that does not run out. Token accounting per provider, and at 70% of the window the middle of the conversation folds into a digest while the opening request and recent turns stay verbatim.
:compactto force it,:usageto see it. - Skills as modes. Skills load as one-line descriptions and fetch their body on demand, so installing ten costs almost nothing.
:use <skill>promotes one to a mode: its full instructions go into every reply until:use none, and it survives a restart.
—— III ——
gcc hako.c -o hako -lpthread # one-liner
make # cross-OS Makefile (-O0: fast to build, the agent waits on I/O)
make RELEASE=1 # -O2, what releases ship
make UNIVERSAL=1 # macOS arm64 + x86_64 fat binary
make WEB=1 # embed hako studio, for `hako --serve`
make test # the gate: no model, no network, no credentials
make asan # ASan + UBSan build → ./hako_asanDeps: libc + pthread +
curl(1)on PATH. No third-party libraries linked. More gates:make test-tty(a pty harness with a fake engine; python3 + pyte),make test-serve,make fuzz(the parsers under ASan),make win-check,make wasm-check.
curl -fsSL https://mithraeums.github.io/hako.sh | sh # the agent
curl -fsSL https://mithraeums.github.io/install.sh | sh # the whole suite: agent, editor, engineDetects OS and arch, downloads the latest release, verifies the sha256 sidecar, drops hako into /usr/local/bin (or ~/.local/bin). Where there is no release binary (iSh is 32-bit x86), it builds from source instead (apk add build-base git curl first). No package manager. No daemon. No telemetry.
| macOS universal2 · arm64 + x86_64 |
Linux x86_64 · arm64 |
FreeBSD x86_64 |
Windows x86_64 · MinGW |
iSh i686 · built from source |
Local, zero config (recommended): the engine, one model, launch:
curl -fsSL https://mithraeums.github.io/hakm.sh | sh # the engine → ~/.hako/bin/hakm
hakm pull hako-sho # 3B weights, ~1.9 GB, resumable
hako # finds them and runs locally
> :trust # grant tool access in this project
> list files in this directoryOr wire a cloud provider:
hako
> :login anthropic # OAuth, uses your Claude Pro / Max subscription
> :login github-models # free for any GitHub account
> :login gemini # paste a free-tier API key
> :model gemini-3.8-flashOr one-shot:
hako -p "summarize README.md"Self-update:
hako --update # check + atomic-replace if newer—— IV ——
| Key | Action |
|---|---|
| ← → | move cursor |
| ↑ ↓ | history prev / next |
Home / End · ^A / ^E |
line start / end |
^U / ^K / ^W |
kill to start / end / word |
^R |
reverse-incremental history search |
^L |
clear screen |
| Backspace · Delete | delete back / forward at cursor |
^C |
cancel the line, or stop the turn in flight |
^D (empty) |
EOF / exit |
^G |
finish the line in $EDITOR (editor= in ~/.hakorc) |
^O |
the whole transcript, expanded, in a pager (q closes) |
| Shift+Tab | cycle ask → auto → plan |
| Tab | complete commands, names and paths; with the menu open, take the selected entry |
| Esc | close the command menu (the line stays); stop a reply being read aloud |
Bracketed paste: multi-line pastes land as one entry.
: runs hako itself; / drives the model. Typing either opens a menu under the line
(↑/↓ to move, Tab to take, Esc to close); the wrong prefix still works.
:help [cmd] :q :update :remote [on|off|pair] :mcp [reload] :memory :e :o
:name <chat> :color <name|off> :sessions [clear [all]] :resume <id> :session [new]
:history :clear :undo :edit :todo [clear]
:agents :join [hako or chat] :as <name> :route <name|off> :say <text> @name <text>
:theme [<name>] :mode ask|auto|plan :auto :tools :toolgate :toolmode :trust :mouse :editor :voice [on|off]
:model [<id>] :models [refresh] :provider [<name>] :pull <model>
:login [<prov>|api] :logout [<prov>] :accounts :usage
:skills [reload] :skill install <url> :skill uninstall <name> :use <skill|none>
/compact /retry /<skill> [text] (every installed skill is a / command)
Bare :provider / :model / :theme (no argument) open an arrow-key picker; give an argument to set directly. :help lists every command from the same table the menu, the editor's Rei pane and the studio use. A command that would race a running turn (/retry, :clear, :model) runs as soon as the turn ends.
—— V ——
~/.hako/state provider, model, settings (mode 0600, no secrets)
~/.hako/credentials per-provider api_key / oauth tokens, sealed (XChaCha20-Poly1305, 0600)
~/.hako/credentials.key its key where there is no keychain (0600; on a Mac the key is in the login keychain)
~/.hako/skills/ markdown behaviors (flat or dir-dispatcher)
~/.hako/input_history line editor history (last 500)
~/.hako/projects/<encoded-cwd>/state per-project session id, turn count
~/.hako/projects/<encoded-cwd>/trust sentinel: tools allowed in this cwd
~/.hako/projects/<encoded-cwd>/sessions/ append-only JSONL session history
~/.hakorc or ~/.config/hako/hakorc user config ($HAKORC first)
<cwd>/.hakorc per-project config, read once the folder is trusted
<cwd>/.hako/HAKO.md per-project context (auto-loaded into system prompt)
One file for the whole suite (hako, hake, the studio): the first that exists of $HAKORC,
~/.hakorc and ~/.config/hako/hakorc, then the project's own .hakorc once the folder is
trusted (it may not set ai_api_key, ai_endpoint, ai_auto_approve, editor or a
*_cmd). code.<key> is hako's own and beats <key>; edit. and studio. are the
others'. Unknown keys are left alone. The pickers write the one key they change, in place.
ai_provider=mithraeum # or anthropic, openai, gemini, ollama, groq, … (:providers)
ai_model=hako-sho
theme=terminal # the default: your terminal's own colors. code.theme=nord for hako only
theme.mine.base=nord # a custom theme: a preset to start from,
theme.mine.accent=#b89656 # then any slot as #rrggbb, ansi0-15 or default
voice=0 # 1 reads each finished reply aloud (voice_rate, voice_cmd)
update_check=1 # 0: never ask GitHub for a newer release
mouse=0 # shared with hake; default off here, it takes the scroll wheel
ai_auto_approve=0 # 0 = ask before tools (shift+tab cycles ask/auto/plan)
show_splash=0
anim_style=pump # braille dots bar pulse bounce ghost arrows blocks pump
editor=vim # for ctrl+g; else $EDITOR
shell_timeout=120 # run_shell deadline in seconds (max 600)
ai_max_tokens=4096
ai_stream=1
prompt_cache=1 # Anthropic prompt caching
context_window=0 # override the model's window (0 = from the catalog)
A key set here beats a stale value in ~/.hako/state; a project's own saved state still
wins inside that project.
Three auth paths:
- OAuth (subscription / account-bound):
:login anthropic(Claude Pro/Max),:login copilot(GitHub Copilot Pro/Business),:login github-models(free for any GitHub user),:login openrouter(PKCE, auto-issue an OR API key),:login nous(Nous Portal, device code). Inference bills against your subscription where applicable; no per-token API key needed. - API-key paste:
:login openai,:login gemini,:login groq,:login cerebras,:login deepseek,:login mistral,:login together,:login fireworks,:login xai,:login anthropic-api,:login openrouter-api,:login nous-api,:login custom. Opens provider console in your browser, prompts with input hidden, persists into the cred store. - Local: hako's own models need no login:
hakm pull hako-sho, then:provider mithraeum. Ollama works too::login ollama(ensureollama serveis running) or:login ollamacloud(paste an Ollama key).
Run :providers for the full grouped list with ◎ (active) and * (saved login) markers. :models lists the active provider's live catalog (its own listing endpoint, cached 24h, :models refresh refetches; installed .mlf2 weights for mithraeum), falling back to a built-in list when offline. Bare :model opens the picker, where / filters as you type. :accounts lists saved logins; :logout [<provider>] wipes one. Mid-chat :provider X swaps secrets and flattens wire-format-specific message bodies so the conversation survives.
Resolution order: HAKO_API_KEY env → <PROVIDER>_API_KEY env → ~/.hako/credentials (per provider). Legacy CLAW_* / HAKOC_* env names are read as fallback with a one-shot deprecation warning.
First run in any directory asks for trust. Untrusted = no tool access at all.
| Provider | id (:provider <id>) |
Auth | Cost | Wire format | Notes |
|---|---|---|---|---|---|
| Anthropic | anthropic · claude |
OAuth | sub | native + SSE | Claude Pro/Max subscription; live-tested |
| ChatGPT | chatgpt · codex |
OAuth | sub | OpenAI Responses | any ChatGPT plan, free too; device sign-in |
| Anthropic API | anthropic-api · claude-api |
paste | $/tok | native + SSE | regular API key, separate from sub |
| GitHub Copilot | copilot · github-copilot |
OAuth | sub | OpenAI-compat | Copilot Pro/Business; device flow |
| GitHub Models | github-models · ghmodels |
OAuth | free | OpenAI-compat | rate-limited; any GH account |
| OpenRouter | openrouter |
OAuth (PKCE) | $/tok | OpenAI-compat | auto-issues user-scoped key; :free tier |
| OpenRouter API | openrouter-api |
paste | $/tok | OpenAI-compat | paste existing key |
| Nous Portal | nous |
device code | $/tok | OpenAI-compat | many vendors' models; public catalog |
| Nous Portal API | nous-api |
paste | $/tok | OpenAI-compat | paste a key from portal.nousresearch.com |
| OpenAI | openai · gpt |
paste | $/tok | function-calling | |
| Gemini | gemini · google |
paste | free/$ | OpenAI-compat | generous free tier on AI Studio |
| Groq | groq |
paste | free | OpenAI-compat | fastest hosting for Llama family |
| Cerebras | cerebras |
paste | free | OpenAI-compat | ultra-fast inference |
| DeepSeek | deepseek |
paste | $/tok | OpenAI-compat | DeepSeek-Chat / Reasoner |
| Mistral | mistral |
paste | $/tok | OpenAI-compat | Mistral Large / Small / Codestral |
| Together | together |
paste | $/tok | OpenAI-compat | aggregator |
| Fireworks | fireworks |
paste | $/tok | OpenAI-compat | aggregator |
| xAI | xai · grok |
paste | $/tok | OpenAI-compat | Grok API at api.x.ai |
| Ollama (local) | ollama · local |
none | local | native | needs ollama serve running |
| Ollama Cloud | ollamacloud · ocloud |
paste | $/tok | native | ollama.com hosted |
| custom | custom |
paste | depends | OpenAI-compat | set ai_endpoint in .hakorc |
Three wire formats (native Anthropic, native Ollama, OpenAI-compat). Quickest paths: (a) Claude Pro/Max → :login anthropic. (b) ChatGPT (free or paid) → :login chatgpt. (c) Copilot Pro → :login copilot. (d) Free, no card → :login github-models, :login gemini, :login groq, or :login ollama. (e) Pay-as-you-go → any paste row.
Some smaller / non-tool-tuned models (Mistral 7B, Phi-4, DeepSeek-R1 distills, smaller Gemma / Llama variants) don't reliably emit OpenAI/Anthropic function-calling JSON. Toggle ReAct mode:
:toolmode prose # model emits <tool name="X">{...}</tool> blocks in prose (aliases: react, xml)
:toolmode native # back to native function-calling (default)ReAct mode injects a tool schema in the system prompt and parses <tool>...</tool> blocks from the model's response. Each tool call is executed and the result is appended as <observation tool="X">...</observation> for the next turn. Works with any instruct-tuned model.
—— VI ——
~/.hako/skills/
├── style.md flat skill, whole file injected
└── corp/ directory skill, SKILL.md is the dispatcher
├── SKILL.md
└── agents/
├── CEO.md
├── DEV.md
└── QA.md
Directory skills inject only SKILL.md plus a <files> manifest. The agent reads inner files on demand via read_skill(skill, path): no trust gate (skills are user-installed), path-traversal blocked.
:skill install mithraeums/skills # every skill in the repo, into ~/.hako/skills
:skills # what is loaded
:use brief # one of them, as a mode, until :use none
Browse the catalog: mithraeums/skills.
—— VII ——
v0.4.2 brings back the FreeBSD build that v0.4.1 lacked. Everything else is v0.4.1:
- Shared chats that hold together. Another hako's lines show as they are said, even mid-turn. Models talk to each other with
@namelines, and three in a row hand it back to you. Arrivals and leaves are said,:jointakes a chat's name, and each line is logged once. - Sealed sign-ins.
~/.hako/credentialsis encrypted (XChaCha20-Poly1305), its key in the login keychain on a Mac or a 0600 key file elsewhere. - Current models. ChatGPT sign-in works again (default
gpt-6-luna), and every provider's default, list, prices and context sizes are up to date. - A smoother screen. Every redraw is one frame: menus, the spinner and streaming no longer flicker or hop. In Rex the pane shows hako working, waiting on you or done.
- A question from the model no longer leaves a turn stuck.
Every earlier release, and the full detail, is in CHANGELOG.md.
—— VIII ——
If you share the belief that simplicity empowers creativity, feel free to contribute.
- Forking this repo
- Submitting a Pull Request
- Bug reports and feature requests
Please ensure your code follows the existing style.
This project started out of curiosity and as a branch of hako-edit, a C-based modal text editor. If you hit any issues, feel free to open an issue on GitHub. Pull requests, suggestions, or even thoughtful discussions are welcome.
— SEE LICENSE — · GPL-3.0
— deus sol invictus mithras —
