One line, one Mac: what the chanio starter actually does, step by step.
You’ve read the “run a local AI on your Mac” posts already. This one skips how it feels and just walks the file — what it checks, what it installs, what it downloads — before you pipe anything into sh.
The one line, and what it is allowed to do
curl -fsSL https://chanio.com/install.sh | sh
That’s the whole install command, and chanio.com/install.sh is worth reading before you run it — thirty-some lines of plain shell, nothing minified. Here is what it does, in order.
It checks for macOS first — anything else prints one line about Windows and Android landing in 2027, and stops.
Node and Ollama each get the same check, and neither needs an administrator password. If Node 18 or newer is already on your Mac, it uses that. If not, it downloads the official Node 22 build from nodejs.org, about 50 MB, into ~/chanio/node and uses it from there; nothing is added to your system folders. If Ollama is missing, it downloads the Ollama app, about 200 MB, into ~/Applications. Each download announces itself before it starts. If you already have either through Homebrew, it leaves that alone.
With both in place, it downloads the starter itself — a tarball of plain Node, about 10 KB — into ~/chanio/bin, makes the two files in it executable, and adds one PATH line to your shell’s rc file, only if it isn’t already there. Last, it hands off to chanio init.
What init does
chanio init is what the installer calls, and a command you can run again yourself — --model qwen3:14b for something bigger than the default llama3.2:3b, or --no-launchd to skip the 5am job entirely.
It checks for Ollama again — this run exits with an error if the binary truly isn’t there — then confirms it’s actually running, not just installed. Nothing answering, it tries opening the Ollama app, falls back to starting ollama serve in the background, and waits up to about 24 seconds.
Model not pulled yet? ollama pull llama3.2:3b runs right there in your terminal, once — every init after that skips straight past it.
Then the bookkeeping: notes gets a short README explaining it’s plain Markdown; captures, briefs, and logs get created empty; held.md is seeded with one starter question; config.json records the model, the hour, and that the frontier model defaults to claude-sonnet-5.
Unless you passed --no-launchd, it writes a real .plist to ~/Library/LaunchAgents set for 05:00 and registers it immediately. Then it prints four commands to get started: ask, capture, nightly, brief.
ask: how retrieval works, honestly
chanio ask “question” reads every .md and .txt file under notes, captures, and briefs, strips YAML frontmatter, and splits what’s left into roughly 700-character paragraph chunks.
It scores those chunks with keyword statistics — term frequency weighted by how rare each word is across your vault, the same family of math as classic tf-idf. No embedding model, no vector database: if your note says “tile options” and you ask about “the backsplash decision,” nothing matches unless that exact word appears somewhere. Say it plainly — this is not semantic search.
Ties go to whichever chunk you edited more recently — a small, deliberate lean toward whatever you were actually thinking about lately. The highest-scoring passages get sent to the model: up to 8 of them, 6,000 characters total, whichever limit hits first.
The instruction to the model is blunt: answer only from what’s in front of it, name the file, and reply with exactly the word HOLD if the notes don’t say enough — no guessing. When that happens, the question is appended to held.md and picked up by that night’s run.
capture and hold
chanio capture takes whatever you hand it — a file path, piped stdin, or by default your clipboard (it shells out to pbpaste) — and writes it into captures as a dated Markdown file, titled from its first line unless you pass --title, with frontmatter recording where it came from and when. Nothing to capture gets a message, not an empty file.
chanio hold “question” writes straight to that same held.md — one checkbox line with a timestamp. It’s the direct route for when you already know a question deserves the frontier model’s attention, no local round-trip needed first.
nightly: the local pass and the frontier pass
chanio nightly is the job launchd fires at 05:00, and it is really two passes.
The local pass costs nothing and nothing in it leaves your Mac. It gathers every file under notes and captures that changed in the last 24 hours — capped at 24,000 characters, split roughly evenly — and asks the local model to fill four fixed sections: What mattered, Open threads, Three questions worth holding, and Corrections, which says “none” unless the day’s notes genuinely contradict each other. That becomes tonight’s brief, tokens and a $0 cost written straight into its frontmatter.
The frontier pass only runs if held.md has open questions waiting — none held, no second pass, key or no key. It checks ~/chanio/.env for ANTHROPIC_API_KEY. With a key: up to four passages per held question, every open question bundled into one request to claude-sonnet-5 by default, each answered under its own heading. The reply lands in the brief, the real cost is computed from the token counts and written to the meter, and each question gets checked off. Without a key: nothing is sent anywhere — the same questions and passages go into a plain text file in briefs, formatted to paste into whatever AI you already use, and chanio answer files the reply back in when you paste it.
The meter
Every metered action — an on-device answer, a held question, a capture, a local nightly pass, a frontier batch — appends one line of JSON to meter.jsonl. The file doesn’t exist until the first thing happens; after that it’s one line per event, nothing rounded off before it’s written.
chanio meter totals the log two ways, today and all-time: on-device answers and tokens always show $0.00, because nothing left the machine. Rented frontier batches show their actual dollar total, computed from the token counts and the price table at the moment each batch ran — no rounding on the way to the screen.
The honest part
llama3.2:3b is a three-billion-parameter model, small on purpose — small enough to answer in a couple of seconds on a laptop, small enough to pull once and forget. It’s good at exactly what ask and the nightly pass ask of it: matching a passage to a question, summarizing what changed, noticing a recurring phrase — not reasoning through a genuinely hard tradeoff the way a frontier model does. That’s why HOLD exists as a real exit built into the prompt, instead of a small model bluffing through a question above its size.
The 05:00 job is a real launchd StartCalendarInterval, and it behaves the way launchd documents, not the way cron does: if your Mac is asleep at 5am, the job runs the next time the machine wakes. In practice that means the brief may still be compiling as you open the lid, and a laptop closed for three days gets one run on wake, not three. The run also needs Ollama to come up, which the starter handles, and a few seconds of the model’s attention while you are already reaching for your coffee.
And the starter is the free tier, not the whole system. It’s a complete loop on its own — on-device answers, a nightly brief, a held-questions queue, an honest meter — but it isn’t the system its builder runs for himself, which captures every AI session automatically and compiles it nightly on a much larger local model. Pro, which runs that fuller loop for you overnight, is a private beta right now, not something you can sign up for today.
Uninstall
chanio uninstall does one thing: it tells launchd to stop running the nightly job and deletes the .plist. It doesn’t touch anything inside ~/chanio — notes, captures, briefs, held.md, config.json, and meter.jsonl are all exactly where you left them. Want it all gone? That’s rm -rf ~/chanio, run by you, on purpose — the starter won’t take that step for you.
Now you know what it does before you run it.
One line, and every step behind it is something you can read yourself — a real launchd job, a plain Markdown folder, a meter that never rounds up. Nothing above was the pitch. It’s just what’s in the file.