OpenHuman Keeps Your Memory Local and Reads It in the Cloud
The GitHub trending chart-topper ingests your inbox, calendar, and repos into a local Obsidian vault every twenty minutes. Local inference ships off by default, and a local model that stops responding falls back to the cloud without telling you.
The OpenHuman README says local-first in four different places. The documentation says something more precise: local_ai.runtime_enabled defaults to false, chat and reasoning and vision and speech-to-text and text-to-speech and web search all route to the cloud unless you change that, and if you do configure a local provider and it stops answering, "requests transparently fall back to the remote provider, no broken state."
Both of those things are true at once. The gap between them is the whole story of this project, and it is a gap most people reading a trending repo card will never see.
Why the gap matters here more than elsewhere
OpenHuman's entire value proposition is ingestion. Auto-fetch pulls from your connected accounts on a twenty-minute loop. Memory Tree compresses what it finds into scored Markdown trees in SQLite on your machine, mirrored as an Obsidian vault you can open and edit. SuperContext sweeps that memory and your files before the model reads your first message. A background loop the maintainers call the subconscious diffs your world and writes your morning briefing.
The pitch is that you skip the weeks other agents spend learning your stack. The mechanism is that a compressed representation of your email, calendar, repositories, documents, and chats becomes standing context for every turn.
So the question "where does inference happen" is not a side question. It is the question. The better this thing works, the more of your life is in the prompt.
I want to be fair about the framing before going further. OpenHuman's docs are unusually straight about all of this. They publish a table titled "What stays in the cloud by default." They name the source files. Most projects making a privacy claim do not hand you the config keys to check it. The problem is not concealment. The problem is that the README and the docs are written for different readers, and the README is the one with the star count.
The mechanism, in order
Data flows in four steps, and only the first three are local.
Ingest. The maintainers claim over 100 OAuth integrations, more than 5,000 MCP servers, and over 90,000 skills. Auto-fetch runs every twenty minutes.
Store. Scored Markdown trees in local SQLite, mirrored to an Obsidian vault. The maintainers credit Karpathy's LLM knowledgebase as the inspiration and describe the design as a deliberate rejection of what they call vector-soup black boxes. Whatever you think of the pitch, a memory store you can read in a text editor is a genuinely better debugging story than an opaque index.
Compress. A feature the maintainers call TokenJuice squeezes tool output before it reaches the model, which they say cuts token counts by up to 80 percent. Their README is blunt that a memory this large would be unaffordable otherwise.
Read. By default, a hosted model. This is where the local-first framing stops applying.
The three privacy modes, and the one that is real
The [privacy] config block defines three modes. standard is the default and permits managed cloud routing, bring-your-own-key providers, and local models. local_only refuses every external chat provider. sensitive is described as the foundation for an upcoming PII-aware tier and today behaves exactly like standard, which is worth knowing before you select it and feel safer.
The local_only implementation is the part I respect. It is not a policy the model is asked to follow. The check sits in the inference provider factory at src/openhuman/inference/provider/factory.rs, and under local_only the core refuses to construct an external provider at all. The error names the blocked provider and tells you how to fix it. That makes the guarantee independent of prompts, tools, and bugs upstream. Only Ollama, LM Studio, MLX, and local OpenAI-compatible endpoints pass.
Egress control also sits orthogonal to the autonomy tiers, which govern what the agent may do rather than where its inference goes. You can run a fully autonomous agent that never sends a byte off-device. Separating those two axes is the right design and a lot of tools conflate them.
The cost of flipping the switch is real. local_only refuses CLI delegates like Claude Code alongside the hosted providers, so any workflow that hands work to another agent stops. You give up frontier reasoning quality. And the README's own cost pitch, one subscription plus TokenJuice, is a standard-mode pitch that does not survive the change.
Two config surfaces, and only one of them wins
Here is the part that will bite people.
There are legacy local_ai.usage.* booleans, and there are unified *_provider fields. The docs state plainly that after migration, the provider fields are the source of truth. Setting embeddings_provider = "ollama:all-minilm" routes embeddings on-device even when local_ai.usage.embeddings = false. An unset, blank, or cloud value for embeddings_provider keeps embeddings in the cloud even when local_ai.usage.embeddings = true.
Read that second sentence again. If you picked the "embeddings only" preset in an older build and later migrated, the box you checked is not the thing deciding where your embeddings go.
Then there is the fallback. The provider router creates a health-gated local provider at startup, and when Ollama is unreachable, requests fall back to remote rather than failing. As engineering, that is a defensible availability choice and it prevents a broken app. As a privacy property, it means that in standard mode, "my local model is running" is an assumption you are making, not a guarantee the system enforces. The only structural defense is local_only, where the remote client cannot be built in the first place.
One thing the maintainers got right that most people get wrong: local vision. Ollama does not reject an image sent to a text-only model. It drops the image and answers from the prompt text, producing a fluent description of something the model never saw. OpenHuman runs a capability check and refuses to route the request, falling back to a vision-capable default and logging a warning. That failure mode eats people silently, and writing it into the docs is the kind of honesty that earns trust for the rest of the claims.
Put this into practice
Decide what you are protecting before you touch settings. Privacy Mode governs data egress. Autonomy tiers govern actions. Getting the wrong knob is the most common way people end up feeling protected and not being protected.
If you want the guarantee, set local_only and pay for it. Run it as a separate profile for sensitive work and keep a standard profile for everything else. Do not try to get both from one configuration.
If you stay in standard, set the provider fields by hand. chat_provider, reasoning_provider, embeddings_provider, agentic_provider, coding_provider, memory_provider, heartbeat_provider, learning_provider, and subconscious_provider all take a provider string:
chat_provider = "ollama:llama3.1:8b"
reasoning_provider = "ollama:qwen2.5:14b"
embeddings_provider = "ollama:bge-m3"
Anything unset, blank, cloud, or openhuman keeps that workload on the cloud route. Do not rely on the presets.
Verify with openhuman.inference_status rather than your own memory of what you configured. The desktop app surfaces Ollama reachability, model availability, and per-subsystem enablement live. Given the silent fallback, this is the only way to know.
Connect fewer accounts than you want to at first. Auto-fetch across every integration is what makes the memory good and also what sets the blast radius. Watch a few sync cycles and read what actually landed in the vault before you add your work email.
Budget the hardware if you go local. The docs list roughly 1.0 GB for gemma3:1b-it-qat, 1.2 GB for bge-m3, and another 1.7 GB if you add Moondream for vision, with 8 GB of RAM as a minimum and 16 GB preferred. The maintainers say a previous design put every modality on-device by default and produced a heavy, hardware-sensitive footprint, which is why the current version scopes local AI narrowly and ships it off.
Where this is soft
This is early beta by the maintainers' own badge, with the README telling you to expect rough edges. The repository page showed 114 open issues and 27 open pull requests when I read it.
The star counts disagree with each other. The GitHub repository page served 35.3k stars and 3.5k forks; the shields.io badge endpoint read 38k the same day. The README's claim of being the number one trending repository on GitHub for nine consecutive days in its launch week is the maintainers' claim, and I did not verify it.
The license is GPL-3.0. Fine for personal use. A real question if you were thinking about building on it commercially.
Everything I have said about the Rust-core enforcement comes from the documentation. I have not audited factory.rs. What makes the claim worth repeating is that it is specific enough for you to check, which is more than most privacy claims offer.
Some cloud paths remain even in a mostly-local setup, by design. Web search runs through a backend proxy so no API key sits on your machine, and hosted text-to-speech runs server-side. Those are separate from the chat model and separate from Privacy Mode's chat-provider enforcement, so read the trust model rather than assuming one switch covers everything.
I have read the code paths and the docs closely. I have not run this long enough to tell you whether the memory quality justifies handing it your inbox, and anyone who tells you otherwise two weeks after launch is guessing.
What to take from it
"Local-first" is a claim about the disk. It is being read as a claim about the network, and those are different properties with different failure modes.
OpenHuman is better than most projects here, not worse, because it wrote the boundary down in config keys you can grep instead of adjectives you have to trust. The Memory Tree in a readable Obsidian vault is a good idea. The provider-factory enforcement is a good idea. The vision capability check is a good idea.
Read local-ai.md before you connect your email, not after. The table titled "What stays in the cloud by default" is the paragraph that should have been in the README.
Sources: tinyhumansai/openhuman README on GitHub; OpenHuman docs, "Local AI (optional)"; OpenHuman docs, "Privacy Mode".