User manual
Everything after hemi init: how the graph is organised, how to get good answers out of it from Claude Code, how sources, updates and the console work, and what to do when something is off.
How to think about it
Hemilake stores three kinds of things.
| Episode | One unit of raw input: a mail, a meeting, a Slack thread, a Jira change, a voice memo, a turn of a Claude Code session. Stored as the original text, with the time it happened and the source it came from. Episodes are never modified. |
|---|---|
| Entity | A person, project, system, vendor or place the model found in episodes, with a running summary. "Storage rollout", "Elena", "the CMEK key" are entities. |
| Fact | A one-line relationship between two entities, extracted from an episode: "Elena approved 140 K€ for phase 1 of the storage rollout". Each fact records valid_at, when it became true, and invalid_at, when newer information superseded it. Each fact lists the episodes that support it. |
Two consequences shape how you use it. Facts are extractions, so they lose nuance; when the exact wording, a number or a date matters, the episode behind the fact has it verbatim. And the graph is your memory, not the world's: the absence of a fact means Hemilake never saw it, not that it is false.
Using it from Claude Code
hemi client installs a plugin in Claude Code with three hooks and an MCP server. You do not have to call anything by hand: the hooks work on every prompt, and the model reaches for the tools when a question is about your own work.
The recall block
Before the model sees your prompt, the plugin sends it to the graph and prints the most relevant valid facts above it, each with its date and the episode that supports it. If your prompt names a time window ("yesterday", "this week", "in August"), the lookup filters by date instead of similarity, so a broad question like "what happened this week" works.
When the block says the lake has nothing for a window, that means nothing was ingested for it. Check hemi sources list for the last sync time of the source you expected.
Asking well
- Name things the way your channels name them. The graph resolves "the rollout" to the right entity only if that phrase appears in what it ingested. Project codenames, ticket keys and people's first names work well.
- Ask for the timeline, not the summary. "What changed on X since June, in order" gets superseded facts with their dates. "Summarise X" gets the current state only.
- Ask who and when. Every fact has both. "Who moved the deadline and when" is a direct lookup.
- Ask for the source. "Show me the message where that was decided" makes the model open the episode and quote it.
- Prepare a meeting. "Everything with Elena since the last steering committee, newest first, and what is still open" pulls from mail, Teams, Slack and Jira together.
Tools and skills
The MCP server exposes the graph. The ones you will see the model use:
search_memory_facts | Facts by similarity, with optional valid_at_after / valid_at_before for time windows |
search_nodes | Entities, with summaries; can rank around a known entity |
get_episode | The full original text behind a fact |
add_memory | Store something you tell it, or a decision taken in the session |
summarize_saga | A running summary of a chain of related episodes, such as one long session |
The plugin also ships skills: hemisphere teaches the model how to query the lake (query it first, filter by date, read the episode when detail matters); writing applies your voice profile once you have one (below); and one per channel (gmail, o365-mail, teams, slack, whatsapp, telegram, …) for when you want the model to act on a channel directly, not on its memory of it.
What gets stored
- Each turn, once the answer is complete: your message and the answer become one episode, chained to the previous turns of the session. Short prompts, slash commands and turns whose answer is only an error are skipped. Answers are truncated.
- Each session, when it closes: a summary of the chain.
- What you tell it about yourself: preferences, people, dates. The skill instructs the model to store these at once.
- Never: secrets, tokens or passwords. Also anything from directories you exclude in the plugin configuration.
Claude Desktop
The same plugin, installed by hand. hemi client does three things on a Mac: it writes the connection settings the plugin reads (~/.config/hemilake/client.json, URL and token), it installs the plugin in Claude Code when claude is on PATH, and it writes the plugin zip to ~/Downloads/hemilake-plugin.zip. In Claude Desktop, open Customize → Plugins → Upload plugin, pick that file from Downloads, then quit and reopen Desktop. Recall, session memory and the graph tools work in the Cowork and Code tabs; the plain Chat tab loads the skills only, because Desktop runs hooks and MCP servers only in those two.
If Claude says Hemisphere "is not configured", hemi client was never run on that Mac: run it and reopen Desktop. Desktop keeps its own copy of the plugin: after an update whose notes mention the plugin, run hemi client again and upload the new zip.
Without the plugin, Claude Desktop can also use the instance as a connector, like claude.ai and Cowork (below): the tools, without the hooks.
What comes back from the graph is text you once received, and it could contain instructions aimed at a model. The skill tells the model to treat it as data and to flag anything that reads like a command. If it flags something, look at the episode.
Connecting the Claude apps
claude.ai, Claude Desktop, Cowork and Claude Code can use an instance as a connector: you paste its URL and sign in on the instance itself. No token changes hands. The instance has to be reachable from where the app runs: for claude.ai and Cowork that means published with hemi expose (or a hosted instance); for Claude Code on the same machine the local address works. hemi client connector prints these steps for your instance, with its URL filled in, and the console's Connect page walks through them, tool by tool, with copy buttons.
claude.ai and Claude Desktop (Cowork uses the same connectors): Settings › Connectors › Add custom connector, name it Hemilake, and paste the URL. Leave the OAuth client id and secret empty; the app registers itself.
https://<host>/mcp
Claude Code, on any machine; then /mcp in a session and pick hemisphere to sign in.
claude mcp add --transport http hemilake https://<host>/mcp
What to expect, in order:
- The instance's own login, a page asking for a one-time code.
hemi console(on a hosted instance,hemi enroll --console <slug>) opens the browser signed in and the page carries on where it was. On the same machine, on the API port, nothing is asked. - A consent page naming the app (Claude, or a program on this computer when Claude Code connects) and where the browser goes back to.
- A device name (mac, iphone, work laptop): how the connection shows under Connect › Devices & credentials and in
hemi client token list, where it is revoked. Approve.
The app then holds a token that lasts an hour and renews it while it stays connected. Revoke it under Connect › Devices & credentials or with hemi client token revoke <name>; the app asks to connect again. The plugin's own tokens (hemi client, hemi client token new) are unaffected.
What each surface gets:
| What | Where |
|---|---|
| Tools: recall, the memory tools, the channel tools | Everywhere: claude.ai, Claude Desktop, Cowork, Claude Code |
| Prompts the instance serves | Everywhere the app lists MCP prompts |
| Automatic recall before each prompt, session memory (hooks) | Claude Code and Claude Desktop with the plugin (hemi client, or the Hemilake plugin below); a connector alone cannot run hooks |
Skills (hemisphere, writing, one per channel) | The plugin only |
So on a machine where you work in Claude Code, install the plugin: it recalls before every prompt and stores the session. Everywhere else, the connector: the model reaches for the tools when a question is about your own work, and you can tell it to.
The Hemilake plugin for a Claude organisation or account
For hosted instances there is one plugin for everyone, with nothing of yours inside: its connector is the shared Hemilake gateway, and each person signs in to their own instance with their passkey. It is the plugin Anthropic's directory will list; until then it is a zip you upload, published with each release:
https://hemilake.com/stable/hemilake-plugin.zip
Once, by the Owner of a Claude Team or Enterprise organisation (on a personal plan, by you, for your own account): in claude.ai or Claude Desktop, Customize → Connectors → Add custom connector, name it Hemilake, URL https://mcp.hemilake.app/mcp, OAuth client id and secret empty; then Customize → Plugins → Upload plugin, pick the zip, and publish it to the organisation. Once the directory lists Hemilake, enabling it there replaces both steps.
Each person, once: press Connect on the Hemilake connector. The gateway's page asks for your handle (ana), your instance's name (ana.acme) or your email, and remembers it; your own instance then asks for your passkey and shows the consent page, as above. Nothing to install, no token to paste.
- claude.ai, Claude Desktop, the mobile apps: the tools and the prompts through the connector, the skills from the plugin.
- Claude Code on your computer: the plugin arrives with your claude.ai account (Claude Code 2.1.273 or later shows it as
hemilake@synced): skills, recall before each prompt and session memory, the tools through the connector;/mcpsigns in the first time. A machine wherehemi clientinstalled the plugin keeps that one. To try the zip for one session:claude --plugin-url https://hemilake.com/stable/hemilake-plugin.zip. - Cowork in the cloud: the tools through the connector; for recall and session memory there, download your personal plugin from the console's Connect › Plugins (
hemilake-hooks.zip, made for you, revoked there too) and upload it in Customize → Plugins. There is nothing to connect.
Gemini Enterprise, Spark and Antigravity
A hosted instance works in Gemini Enterprise through the same gateway as the Claude apps, mcp.hemilake.app. Gemini Enterprise does not register clients by itself, so an admin of the Gemini Enterprise app adds the connector once, with the values below. The assistant, the agents built in the app and Spark then share it, and each person reaches their own lake.
Once, by an admin: in the Gemini Enterprise app, Connected data stores › MCP Server, authentication OAuth 2.0.
| Field | Value |
|---|---|
| MCP Server URL | https://mcp.hemilake.app/mcp |
| Authorization URL | https://mcp.hemilake.app/oauth/authorize |
| Token URL | https://mcp.hemilake.app/oauth/token |
| Client ID | The gw1. id below |
| Client secret | Any non-empty value. The client is public and the secret is ignored. |
| Scopes | memory |
| PKCE | On |
| Use HTTP Basic Authentication | Off |
gw1.eyJuIjoiR2VtaW5pIEVudGVycHJpc2UiLCJyIjpbImh0dHBzOi8vdmVydGV4YWlzZWFyY2guY2xvdWQuZ29vZ2xlLmNvbS9vYXV0aC1yZWRpcmVjdCJdfQ
If saving fails on an organisation policy, constraints/discoveryengine.managed.disableCustomMcpServerConnector is enforced above the project, and an organisation policy admin has to allow custom MCP servers there. Gemini Enterprise reads the tool list when the data store is saved, so save it again after an update that adds tools.
Each person, once: the first time Gemini wants a Hemilake tool it shows Authorize. The gateway's page asks for your handle, your instance's name or your email, and your own instance asks for your passkey and shows the consent page, as with Claude.
Every tool carries MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint). Gemini asks before a tool that acts outside your lake, such as sending a mail, and runs a recall or an add_memory without asking. Gemini has no hooks, so recall before each prompt and session memory happen only when the model calls recall and add_memory; app or agent instructions that tell it to recall first and to save what it learns as it goes make the difference.
Google Antigravity (the app, the IDE and the agy CLI) gets a plugin with the same recall before each prompt, session memory, skills and tools as Claude Code. With hemi on the machine, run hemi client --platform antigravity (after hemi enroll for a hosted instance) and reopen Antigravity. Without it, download the Antigravity build from the console's Connect page, unzip it, move the hemilake folder into ~/.gemini/config/plugins/ and reopen Antigravity.
Sources
A source is a connector plus a block in ~/Library/Application Support/hemisphere/config/ingest.toml. All of them start disabled. Enabling one and authenticating its provider is what makes it ingest.
| Provider | Sources | Sign in with | Notes |
|---|---|---|---|
| Microsoft 365 | o365_mail, o365_calendar, teams | hemi auth o365 | One sign-in for the three. Uses the tenant's own Office client, so nothing to register. |
gmail, google_calendar, gdrive | hemi auth google | Official APIs, read-only plus send and events. Drive: files you created, edited or were shared, with an excerpt; a grant made before 0.7.7 needs hemi auth google once more. Workspace accounts may need an admin to allow the client. | |
| Slack | slack, slack_dms, slack_team, slack_incidents, slack_help, slack_announcements | hemi auth slack | User token. Workspaces that block the app fall back to a browser session. |
| Jira | jira, jira_team | hemi auth jira | Jira Cloud REST API with your site, email and an Atlassian API token (they expire, a year at most). The same form is in the console. |
| Confluence | confluence | hemi auth jira | Pages changed recently, with an excerpt; the same Atlassian token as Jira. spaces narrows it to the spaces you care about. |
| iCloud Calendar | apple_calendar | hemi auth apple (or the console) | CalDAV with your Apple ID and an app-specific password (Apple has no OAuth for calendars). Events of every calendar, the past day and the coming week; the apple_calendar_* tools create, change and delete events with the same credentials. |
| Shortcut | shortcut | hemi auth shortcut (or the console) | Shortcut REST API with a personal API token. Everything updated since the last run: stories with their comments and tasks, epics, iterations, objectives and Docs. |
| HubSpot | hubspot | hemi auth hubspot (or the console) | HubSpot CRM API with the access token of a private app of your account (read scopes for contacts, companies, deals and owners). Everything modified since the last run: contacts and companies with their emails and domains, deals with their stage, stage changes and owner, and the notes, tasks, calls and meetings logged on them. |
| GitHub | github_team | hemi auth github | GitHub REST API with a personal access token; hemi auth github import-gh takes the one gh already holds. Commits come from local clones. |
whatsapp, whatsapp_work, plus _media variants | hemi auth whatsapp | Opt-in. Unofficial multi-device client with an account-ban risk. Scans a QR from the phone. | |
| Google Cloud | gcp_support | hemi auth gcp | Support cases with the corporate Google account. |
| Voice memos | voice_memos | none | Apple Voice Memos, transcribed on the Mac with whisper.cpp. |
| Plaud | plaud | hemi auth plaud (or the console) | Recordings of a Plaud Note Pro or NotePin: the transcript with speakers and times, Plaud's summary and the moments you flagged with the button. A recording made during a calendar meeting becomes that meeting's notes on the Today page. |
| Plaud | plaud | hemi auth plaud (or the console) | Recordings of a Plaud Note Pro or NotePin: the transcript with speakers and times, Plaud's summary and the moments you flagged with the button. A recording made during a calendar meeting becomes that meeting's notes on the Today page. |
| Teams transcripts | teams_transcripts | hemi auth o365 | Transcripts Teams itself wrote of your meetings, yours or another organiser's, with speakers and times. Needs the tenant to grant transcript access to the sign-in; without it the source reads nothing. |
| OneDrive / SharePoint | onedrive | hemi auth o365 | Documents you opened or edited and the ones shared with you, with an excerpt of their text (Word, PowerPoint, Excel, PDF). Same sign-in as mail. |
| People directory | directory | hemi auth o365 | Title, department and manager of the people around you, so the graph knows who is who. Re-ingested only when something changes. |
| Local folders | local_files | console: Sources → auth | Documents in folders you choose, with the Finder chooser from the console or by path. For ~/Documents, ~/Desktop and ~/Downloads the background ingestion needs a Privacy & Security grant; the console page says which. |
| Telegram | telegram | hemi auth telegram | Telegram's own user API: phone number and the code Telegram sends, once. Chats and groups; channels are opt-in. No bridge, no QR, no ban risk. |
| Google contacts | google_contacts | same Google sign-in (run hemi auth google again on an older grant) | Your address book, so the people in your chats get their email, phone and company. Nothing is ingested as an episode. |
| mem.ai | mem | API key in Settings | |
| Claude Code | the plugin hooks | hemi client | Not in ingest.toml; always on once the plugin is installed. |
hemi sources list # on/off, last sync, last error per source
hemi sources enable slack_dms
hemi sources disable whatsapp
hemi sources sync # add blocks for sources this version knows and your file lacks, disabled
hemi sources prune # drop blocks of sources that no longer exist
hemi ingest --dry-run # each enabled source reports what it would pull
hemi ingest --source o365_mail # run one source now
hemi ingest --force # run even if the last tick was recent
People and the graph
People lists everyone the graph knows; open one to see what you know about them, the episodes you share, their details with the source of each value, and your own notes. What you type there becomes a fact with you as the source. Two nodes for one person merge from their page; one node for two people (a first name the extraction kept attaching to the same node) is split there too, episode by episode, with a proposal you correct before applying. Graph shows any entity's neighbourhood as of a date: facts as edges, superseded ones dashed, and the episodes behind them.
From another machine or your phone
The console's Connect page holds the device keys. Publish the instance with hemi expose cloudflare --url https://your-host --token <tunnel token> (a Cloudflare Tunnel you own) or hemi expose tailscale, then create a device key under Connect › Advanced and use it on the other machine: hemi client --server https://your-host --token <token> if hemi is installed there, or the claude mcp add line it prints for Claude Code alone. Only the MCP travels: the console and the admin token stay on the machine. Claude's web and mobile apps need OAuth, which is coming.
The scheduled agent ticks every 5 minutes while you are logged in and the Mac is awake; each source collects on its own cadence (30 minutes unless you set every under it) and what it collects reaches the graph in batches (process, 30 minutes). With battery_saver = true (off by default) the cadences stretch threefold on battery. Each source keeps a watermark, so a run only pulls what is new since the previous one. If the Mac sleeps for a weekend, Monday's first tick catches up.
Behind a corporate TLS proxy (Netskope, Zscaler), hemi ca sync builds the CA bundle every hemi process uses. hemi init does it once; rerun it when the proxy's root changes and connectors start failing with certificate errors.
The console
http://127.0.0.1:8095, loopback only, while hemi up is running. On a hosted instance it is https://<hostname> behind a passkey, and the first session starts on its own terms: until a passkey is registered every page comes back to the Passkeys page (the session the claim opened lasts seven days, and without a passkey the key file is the only way back in); then Getting started (/start, in the sidebar while a step is missing) lists what is done and what is not, and in order: connecting Claude, the plugin, the sources with their sign-ins, and the first ingest, after which Today fills in.
- Overview. Whether each agent is up, memory and disk of the graph, counts of episodes, entities and facts, and the gateway usage of the last days. Below it, every source with its switch, its last sync and error, a Run button, and a Sign in button that walks the OAuth flow in the browser (the WhatsApp one shows the QR).
- MCP. Every tool Claude can call through your instance, grouped by origin (memory, each channel, WhatsApp), with its parameters; the ones marked write act on your accounts. What is listed is exactly what
/mcpserves at that moment: a channel you have not authenticated still lists its tools, and they answernot_authenticateduntil you do. - Logs. The same as
hemi logs, per agent, following. - Settings. The values you gave
hemi init: gateway URL, models, embeddings, plus the optional per-provider OAuth overrides and the mem.ai key. Saving writes the server environment; a restart applies it.
The hemi command
| Command | What it does |
|---|---|
hemi init | Asks for the gateway, models, owner id and embeddings, writes the configuration and generates the tokens. Rerun it to change an answer. |
hemi up / down / restart | Start, stop or restart the four launchd agents. down leaves the data alone. |
hemi status | Each agent with its port and health, the graph counts, when ingestion last ran and what it did. |
hemi doctor | Checks dependencies, ports, permissions, the gateway and the graph; says what is wrong and, where it can, what to run. First thing to paste when asking for help. |
hemi ingest | Run ingestion now. --source, --dry-run, --force. |
hemi sources | list, enable, disable, sync, prune. Edits ingest.toml for you. |
hemi auth <provider> | Sign in to a provider: o365, google, slack, jira, github, gcp, whatsapp. Also mcp-token and admin-token to rotate the tokens; rerun hemi client after rotating the first. |
hemi client | Connects Claude on this machine: writes the settings the plugin reads, installs it in Claude Code, drops the zip for Claude Desktop in Downloads. connector prints the steps that add the instance as a connector in claude.ai, Claude Desktop, Cowork and Claude Code (above); token new <name> gives another device a revocable token for the plugin. |
hemi logs [agent] | Tail the logs. -f follows, --tail N. |
hemi voice <action> | collect, profile, blind-test. See below. |
hemi replay --since <ISO> | Re-queue episodes that were collected but never reached the graph (a restart with a full queue). Dry run unless --execute. |
hemi ca sync / show | Corporate TLS proxy root into the CA bundle every hemi process uses. |
hemi enroll <licence | url> | Hosted instance: claim it with this device's owner key (the licence from your invitation) or add this device to one you claimed (its URL); status lists the devices, --console opens its console signed in. The client install (install-client.sh, hemi and the plugin only) ends by running it. |
hemi export | Your lake as one encrypted file pair, for a backup or a new computer; --passphrase, --out, --move. See below. |
hemi update | Move to what your channel serves, verifying signature and checksum, rolling back if the new version does not come up. --rollback, --channel. |
hemi channel [name] | Show or switch the channel: stable, beta, edge. |
hemi version | Installed version and channel. |
hemi seal and hemi unseal exist for the server runtime, where the data directory is encrypted with a passphrase. On a Mac the seal mode is off: FileVault already encrypts the disk, and you would be typing a passphrase at every login for nothing.
Your writing voice
After a few days of ingestion the graph holds enough of your own messages to derive how you write, per channel and per audience: greetings, sign-offs, sentence length, how you deliver bad news, which words you never use.
hemi voice collect # gathers your sent messages per channel into a corpus
hemi voice profile # writes voice-profile.md from the corpus
hemi voice blind-test # 20 rounds: pick your real text from the generated one
The profile is accepted only if you pick your own text 66% of the time or less over the 20 rounds; above that, the generated text is still recognisably not you, and the profile goes back for another pass. Once accepted, the writing skill applies it whenever Claude Code drafts something in your name. The corpus never leaves the Mac.
Updates and channels
Releases are published on this site in three channels. stable is what the installer gives you; beta gets releases a week or two earlier; edge is built from every commit and is for us. Each channel is a signed manifest that names a version, the tarball, its SHA-256 and a hold flag we can set to stop clients updating when something is wrong.
hemi version # 0.7.1 (stable)
hemi update # take the current release of your channel
hemi channel beta # switch; the next update takes beta
hemi update --rollback # back to the version you had before
What hemi update does, in order: fetch the manifest and its signature, verify the signature against the public key built into your copy, refuse if the channel is on hold or the version is older than you have, download the tarball, check its SHA-256, unpack it next to the running version, point current at it, restart the agents and wait for them to be healthy. If they are not, it points current back at the previous version, restarts again and exits non-zero saying so.
Two versions are kept on disk, the current and the previous; older ones are removed. The graph, configuration and credentials live outside the versioned directory and no update touches them.
A client install (the owner of a hosted instance: hemi and the plugin, no services, marked by ~/.hemisphere/client-only) updates from the same channels with the same checks and the same floor; it unpacks only cli/ and plugin/, and the health check is that the new hemi runs. There hemi status says client-only and lists the instances enrolled on the machine, and the instance commands answer with a one-line hint.
Files, folders and ports
~/.hemisphere/versions/<v>/ | The code of each installed version |
|---|---|
~/.hemisphere/current, previous | Symlinks the update flips |
~/.hemisphere/channel | The channel you track |
/opt/homebrew/bin/hemi | Link to current/cli/hemi |
~/Library/Application Support/hemisphere/config/ | hemi.env, ingest.toml, the CA bundle |
…/hemisphere/secrets/ | Tokens and OAuth credentials, chmod 600 |
…/hemisphere/falkordb/ | The graph |
…/hemisphere/raw/ | Every episode as text, per source |
…/hemisphere/state/ | Per-source watermarks and the ingestion queue |
…/hemisphere/runtime/ | Python environments, the embeddings model, the WhatsApp bridge |
…/hemisphere/logs/ | One file per agent |
| Port | Agent |
|---|---|
| 6379 | FalkorDB, the graph database |
| 8000 | MCP server, what Claude Code talks to |
| 8081 | Embeddings (llama-server on Metal), when local |
| 8095 | hemisphere-api and the console |
| 8180 | WhatsApp bridge, when enabled |
All of them bind to 127.0.0.1.
Moving to a new computer
Your lake lives in one place. Copying the folders by hand to a second Mac leaves two installs with the same licence, the same sign-ins and the same WhatsApp session, each pulling the same mail into a graph of its own; they drift apart within a day. hemi export is the way to move: one encrypted copy, then the old install stops.
- On the old computer,
hemi export --out ~/Desktop. It pauses ingestion for a moment, asks the graph to write itself to disk, and leaves two files on the Desktop:<date>.hsnap(the lake, encrypted) and<date>.json(the wrapped key and a description of the install). Keep them together. The copy opens with the export key of this device'senroll-key.json; add--passphraseto also open it with a passphrase you choose, or use--passphrasealone when the new computer will not have the key file. - Move the two files and, unless you used a passphrase,
enroll-key.json(in~/.config/hemilake/) to the new computer, by AirDrop, a disk or a drive. The files are ciphertext; the key file is what opens them, so treat it as you treat a password. - When you are ready to switch, run the export once more with
--move:hemi export --out ~/Desktop --move. After writing the copy it stops the four agents, removes them from login, and marks the install as moved. From then onhemi upandhemi updaterefuse to start it, saying where the lake went;hemi up --forceis the way back if something went wrong, and the data is still there. - On the new computer, install Hemilake and run
hemi init, thenhemi import <date>.json <date>.hsnap(--key enroll-key.jsonwhen the file is not in its usual place,--passphrasefor an export made with one). Before it changes anything it prints one line per check: the embedding model (the new install must embed with the same model as the old one, or it refuses and says both names), each source and whether its sign-in works here, the install id, the database version and the free disk. Then it stops the agents, lays the lake in, starts them again and replays the last two hours from the raw files. Your sign-ins travel as they are between two Macs; the WhatsApp link comes along because you used--move(from a copy made without it, it is dropped and you link the phone again); the licence follows the install id, sohemi license statusrenews it, orhemi license activatewith the same key if it asks. If the new computer already has a lake, the import refuses until you add--replace, which keeps the old one underreplaced-<date>in the data folder.
The same two files are your backup: hemi export without --move whenever you want a copy, to a drive that is not this computer. What the .json says about you is the list of connected sources and the kind of sign-in each has, the model names and the version numbers; no token, password or message is in it. hemi import --verify-only opens a copy, lists what is in it and runs the checks without touching the install: a way to know a backup is good.
Taking your lake home
If your lake ran on a hosted instance, Download my lake on the console's Passkeys page gives you the same two files, and the same hemi import lays them into a Mac. Two things differ from a move between laptops. The hosted instance embeds with gemini-embedding-001 and a Mac with the local bge-m3 unless hemi init pointed it at a remote model: the graph's vectors only work with the model that made them, so the import lands the lake with a re-embedding pending and the Mac rewrites every vector with its own embedder over the following ingest ticks (hemi lake reembed --status says where it is); search by meaning is partial until it finishes. And the sign-ins do not come along: the hosted service signs Google in through Hemilake's web client and a Mac uses its desktop client, so Google, and Microsoft when the client differs, are listed as sources to sign in again; a WhatsApp link from a hosted copy is dropped too. Jira, GitHub and the other token sources work as they are. The install id and licence of the Mac stay the Mac's.
Privacy
- The data sits on your Mac, under your user, inside FileVault. We have no access to it and no way to reach the machine. The one thing
hemisends us is crash reports and a daily heartbeat with counts, described below;hemi telemetry offstops even that. - The text of your episodes goes to the LLM gateway you configured, and nowhere else. Pick one with no-training terms and be deliberate about which channels you point at it.
- Embeddings run locally by default and send nothing.
- Sources are off until you enable them. Disabling one stops new ingestion; what was already ingested stays until you delete it.
- Logs and the console carry counts and latencies, not content.
- The WhatsApp connector uses an unofficial client and carries a risk of the account being banned by WhatsApp. It is off by default for that reason.
Licence
The beta is private. A key from Pablo (HEMI-XXXX-XXXX-XXXX-XXXX) is bound to your Mac when you install (or with hemi license activate <key> on an install that predates licences), for up to two Macs per key. What you get back is a statement signed by the licence key, good for 48 hours; hemi renews it every six hours while hemilake.com answers, and verifies it offline the rest of the time. On a corporate network that blocks the domain nothing happens for two days; after that hemi up and the ingestion stop until you are on a network that reaches it once.
hemi license status # holder, good offline until, last renewal; renews now if it can
hemi license activate HEMI-XXXX-XXXX-XXXX-XXXX
hemi license deactivate # frees this Mac's slot on the key
What is checked: hemi up, restart, update, client, every scheduled ingestion tick, and hemisphere-api when it starts. Downloads (hemi update, the WhatsApp bridge) carry the key too. If the licence is revoked, the next renewal says so and hemi stops with "ask Pablo"; your data is untouched either way.
The console's Today page
The console (http://127.0.0.1:8095/) opens on your day: the meetings with what the lake knows about each one and a Prep me sheet, what you said you would do and have not, who is waiting on you, the facts that changed in the last 24 hours, four questions worth asking, and an Ask your hemisphere… box that answers from your memory in your language. The brief behind it is rebuilt by the pipeline after every ingest run that brought new episodes, with your small model, from today's calendar and the mail, chats, memos and agent sessions of the last three days. Only text already in the lake goes to the gateway that already extracts it; a build costs a few cents. hemi brief builds or shows it from a terminal; [brief] enabled = false in ingest.toml turns it off.
Telemetry
During the private beta hemi reports to us, so a broken install on your side shows up on ours before you have to write. It is on by default and one command turns it off:
Since 0.18 the daily pulse also carries an ontology block: how many nodes of each type, which relation names appear often, what shapes the untyped names have, how each source extracts, and what you corrected, all as counts. The only names it may carry are the entries of a public list shipped with the release (server/config/ontology-dictionary.txt: tools, vendors, places), with the type your graph gave them. Nothing you wrote or received leaves: no name of a person, no fact, no address. hemi telemetry show prints the exact body; hemi telemetry off stops it.
Since pulse schema 4 the install also sends hourly rows of counts to the same server, bound to your licence: for every closed hour, one row per source, step and model (calls, tokens, distinct episodes and a histogram of the cost per episode in fixed buckets; the price itself stays on your machine) and one row per source with the quality counters (episodes, entities and facts created, wasted calls, facts still valid a week and a month later, your corrections, facts recall served, brief items and their outcomes, decisions and their agreement). Every name in a row comes from a fixed list shipped with the release or is a model name; the label of an account and the identifier of an episode never leave. hemi telemetry usage --hours 24 prints the rows of the last day, hemi telemetry show prints what would leave now, hemi telemetry off stops them with everything else.
In return, what the fleet learns comes back to every install as hints: a small set of public names with their type and short extraction sentences, drafted from those counts, reviewed before publishing, delivered with the licence check and read by the extractor with every episode. The Telemetry page and hemi ontology hints show the set in force. No hint can describe a person or a fact: it is built from counts and a public list, never from anyone's episodes.
hemi telemetry status # on/off, install id, what is sent
hemi telemetry off
hemi telemetry on
hemi telemetry test # sends one test event and prints its id
hemi telemetry show # the exact pulse body and the hourly rows that would leave now
hemi telemetry usage --hours 24 # the hourly rows of the last day, from the journal, nothing sent
Since 0.11 the install also sends one pulse a day (at least: a pulse that could not be sent waits on your machine and goes out on a later tick), bound to your licence so Pablo can help with a slow ingestion or a cost jump without asking for screenshots. It carries: version and channel, whether the MCP is up, the size of the graph (episodes, entities, people, facts, episodes in the last 24 hours), which connectors are on and which report an error (the flag, never the message), tokens and estimated cost per model for today, 7 and 30 days, and CPU, memory and disk per component. Never episode text, people, addresses, environment or tokens. The console's Telemetry page and hemi telemetry show print the exact body that goes; hemi telemetry send sends one now.
What is sent, and when:
- When hemi crashes: the exception type and message, with your home directory replaced by
~, and the stack frames of hemi's own code (file, line, function). Nothing from other libraries, no local variables. - When hemi refuses to do something (the
[hemi] error:lines, such as launchd rejecting an agent or a checksum not matching): that message, paths cut the same way, and where in hemi it came from. - Once a day, from the scheduled ingestion: whether the API is answering and how many sources are enabled. A count, not their names.
- With every event: the hemi command that was running, version and channel, macOS version, architecture, Python version, and a random install id created on first use so one machine's events group together.
Never: environment variables, tokens or credentials, your user or host name, the names of your sources, and no text from any prompt, message or document. Every event prints a line with its id when it is sent, so nothing leaves silently. The server is ours, on the same machine that serves this site, and events are deleted after 90 days.
When something is off
Start with hemi doctor. Then:
| Symptom | Likely cause and fix |
|---|---|
hemi: command not found | Installs before 16 September 2026 linked hemi into ~/.local/bin, which is not on a fresh Mac's PATH. Run ln -sfn ~/.hemisphere/current/cli/hemi "$(brew --prefix)/bin/hemi" once, or add ~/.local/bin to your shell profile. |
| No recall block in Claude Code | /mcp in Claude Code should list hemisphere. If not, run hemi client again and follow the command it prints. If it is connected and the block is empty, nothing has been ingested yet: hemi sources list. |
A source shows an ERROR in hemi sources list | Usually an expired sign-in. hemi auth <provider> again. Certificate errors behind a corporate proxy: hemi ca sync. |
| Ingestion says it skipped | The previous run was recent. hemi ingest --force. |
| Agents are down after a reboot | They start at login. hemi up if you stopped them by hand before. |
| Gateway errors in the logs | Key expired or wrong URL. Settings in the console, or hemi init again, then hemi restart. |
Permission denied: …/Library/LaunchAgents/… | You cannot write to ~/Library/LaunchAgents: root owns it after a sudo run (macOS keeps your home under sudo), or a device-management profile locks it. ls -lde ~/Library/LaunchAgents shows which. For the first, sudo chown "$(whoami)" ~/Library/LaunchAgents. For the second, versions from 0.7.2 keep the agents in the data directory instead and you run hemi up after each login. Never use sudo with hemi on a Mac. |
licence missing / revoked / stale in hemi status | missing: hemi license activate <key>. revoked: ask Pablo. stale: more than 48 h without reaching hemilake.com; connect to a network that does (a phone hotspot works) and run hemi license status. |
hemi update fails with HTTP Error 403 | Versions up to 0.7.2 identified themselves as Python-urllib, which Cloudflare blocks. Run the install one-liner again: it downloads with curl, puts the current version in place and leaves your graph and settings alone. From 0.7.3 on, hemi update works. |
hemi update rolled back | The new version did not come up healthy on your machine. You are on the previous one and working. Send us hemi doctor and the logs. |
| Facts collected but missing from the graph | The MCP restarted with a full queue. hemi replay --since <date>, then --execute. |
When you write to us, include hemi version, hemi doctor and the relevant lines from hemi logs. Logs carry no content from your channels.