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 — about seventy lines of plain shell, nothing minified. Here is what it does, in order.
It checks for macOS first — on anything else it points you to the in-browser version at chanio.com/try and stops — and then for macOS 14 or newer, which Ollama needs.
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 50 KB — into ~/chanio/bin, makes its two programs executable, and adds one PATH line to .zshrc (and to .bash_profile, if you have one), 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 Ollama 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 a minute.
Model not pulled yet? ollama pull llama3.2:3b, the small AI (about 2 GB), 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, plus a sample week so your first question has something to find; captures, briefs, and logs get created empty; config.json records the model, the hour, the bigger AI it would send to (claude-sonnet-5 by default), and a daily spending limit you can change.
Unless you passed --no-launchd, it writes a real .plist to ~/Library/LaunchAgents set for 05:00 and registers it immediately. Then it tells you to type chanio door, and lists the commands ask, capture, brief, and status.
ask: how retrieval works, honestly
chanio ask “question” reads every .md and .txt file under notes, captures, and briefs (where the morning summaries go), 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 notes, 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 note, and reply with exactly the word HOLD if the notes don’t say enough — no guessing. Two checks in code back it up: if nothing retrieved is close to the question, the small AI isn’t asked at all, and if its answer wanders beyond your notes, the answer isn’t shown. Either way, the question is kept for tonight: appended to held.md and picked up by that night’s run.
capture and hold: save a paste, keep a question
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 keeps a question for tonight directly, for when you already know it deserves a bigger AI’s attention; no round-trip through the small AI first.
nightly: a pass on your Mac, then one batch
chanio nightly is the job launchd fires at 05:00, and it is really two passes.
The first 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 — up to 24,000 characters in all, split roughly evenly — and asks the small AI to fill four fixed sections: What mattered, Open threads, Questions worth asking (at most three), and Where your notes disagree, which says “none” unless the day’s notes genuinely contradict each other. That becomes the morning summary, its token counts written straight into its frontmatter.
The second pass only runs if held.md has kept questions waiting — none kept, no second pass, key or no key. A kept question that looks private (a health detail, a date of birth, a record number) never leaves: the small AI answers it on your Mac, under its own heading in the summary, and flags any answer that isn’t from your notes. For the rest, it looks for your Claude key, ANTHROPIC_API_KEY, in your environment or in ~/chanio/.env, where the door’s Settings save it. With a key, and a batch that can’t pass your daily spending limit: up to four passages per kept question, keys and passwords redacted and any passage that looks private left out, every open question bundled into one request to claude-sonnet-5 by default, each answered under its own heading. The reply lands in the morning summary, the real cost is computed from the token counts and written to the meter, and each question gets checked off. Without a key, or if the batch could pass the limit: nothing is sent anywhere — the morning summary says why, and that the questions are ready to paste into whatever AI you already use; chanio kept copies them, and chanio answer files the reply back in when you paste it. In the door, the same two steps are buttons just below the summary.
The meter
Every metered action — an answer on your Mac, a kept question, a saved paste, the nightly pass on your Mac, a send or a nightly batch to a bigger AI — 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, with costs kept to six decimal places.
chanio meter totals the log two ways, today and all-time: answers on your Mac and their tokens always show a zero cost, because nothing left the machine. Sends and batches to a bigger AI show their measured total, to four decimal places, computed from the token counts Anthropic returns and the price table at the moment each one ran. Estimates, like what the answers on your Mac would have cost at Claude’s list price, sit on their own line, labelled as estimates.
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 bigger AI 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 morning summary may still be being written 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 minute or so of the small AI’s attention while you are already reaching for your coffee.
And the starter is not the whole system. It’s a complete loop on its own — answers on your Mac, a morning summary, questions kept for tonight, an honest meter — but it isn’t the system its builder runs for himself, which captures every AI session automatically and compiles it each night with a much larger model, on the same kind of machine.
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 you can read line by line. Nothing above was the pitch. It’s just what’s in the file.