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.

EpisodeOne 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.
EntityA person, project, system, vendor or place the model found in episodes, with a running summary. "Storage rollout", "Elena", "the CMEK key" are entities.
FactA 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

Tools and skills

The MCP server exposes the graph. The ones you will see the model use:

search_memory_factsFacts by similarity, with optional valid_at_after / valid_at_before for time windows
search_nodesEntities, with summaries; can rank around a known entity
get_episodeThe full original text behind a fact
add_memoryStore something you tell it, or a decision taken in the session
summarize_sagaA 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

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/hemisphere/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/hemisphere-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.

Content is data

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 Remote access card of the console shows them 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 hemisphere https://<host>/mcp

What to expect, in order:

  1. 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.
  2. A consent page naming the app (Claude, or a program on this computer when Claude Code connects) and where the browser goes back to.
  3. A device name (mac, iphone, work laptop): how the connection shows in the Remote access card 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 in Remote access 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:

WhatWhere
Tools: recall, the memory tools, the channel toolsEverywhere: claude.ai, Claude Desktop, Cowork, Claude Code
Prompts the instance servesEverywhere the app lists MCP prompts
Automatic recall before each prompt, session memory (hooks)Claude Code and Claude Desktop with the plugin (hemi client); a connector 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.

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.

ProviderSourcesSign in withNotes
Microsoft 365o365_mail, o365_calendar, teamshemi auth o365One sign-in for the three. Uses the tenant's own Office client, so nothing to register.
Googlegmail, google_calendar, gdrivehemi auth googleOfficial 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.
Slackslack, slack_dms, slack_team, slack_incidents, slack_help, slack_announcementshemi auth slackUser token. Workspaces that block the app fall back to a browser session.
Jirajira, jira_teamhemi auth jiraJira 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.
Confluenceconfluencehemi auth jiraPages changed recently, with an excerpt; the same Atlassian token as Jira. spaces narrows it to the spaces you care about.
Shortcutshortcuthemi 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.
GitHubgithub_teamhemi auth githubGitHub REST API with a personal access token; hemi auth github import-gh takes the one gh already holds. Commits come from local clones.
WhatsAppwhatsapp, whatsapp_work, plus _media variantshemi auth whatsappOpt-in. Unofficial multi-device client with an account-ban risk. Scans a QR from the phone.
Google Cloudgcp_supporthemi auth gcpSupport cases with the corporate Google account.
Voice memosvoice_memosnoneApple Voice Memos, transcribed on the Mac with whisper.cpp.
Plaudplaudhemi 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.
Plaudplaudhemi 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.
OneDrive / SharePointonedrivehemi auth o365Documents 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 directorydirectoryhemi auth o365Title, department and manager of the people around you, so the graph knows who is who. Re-ingested only when something changes.
Local folderslocal_filesconsole: Sources → authDocuments 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.
Telegramtelegramhemi auth telegramTelegram'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 contactsgoogle_contactssame 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.aimemAPI key in Settings
Claude Codethe plugin hookshemi clientNot 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

System status has a Remote access card. 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 token in the card 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 the card 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). On battery the cadences stretch threefold. 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.

The hemi command

CommandWhat it does
hemi initAsks for the gateway, models, owner id and embeddings, writes the configuration and generates the tokens. Rerun it to change an answer.
hemi up / down / restartStart, stop or restart the four launchd agents. down leaves the data alone.
hemi statusEach agent with its port and health, the graph counts, when ingestion last ran and what it did.
hemi doctorChecks 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 ingestRun ingestion now. --source, --dry-run, --force.
hemi sourceslist, 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 clientConnects 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 / showCorporate 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 updateMove 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 versionInstalled 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, previousSymlinks the update flips
~/.hemisphere/channelThe channel you track
/opt/homebrew/bin/hemiLink 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
PortAgent
6379FalkorDB, the graph database
8000MCP server, what Claude Code talks to
8081Embeddings (llama-server on Metal), when local
8095hemisphere-api and the console
8180WhatsApp bridge, when enabled

All of them bind to 127.0.0.1.

Privacy

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 hemisphere.almansa.dev 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.

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

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:

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:

SymptomLikely cause and fix
hemi: command not foundInstalls 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 listUsually an expired sign-in. hemi auth <provider> again. Certificate errors behind a corporate proxy: hemi ca sync.
Ingestion says it skippedThe previous run was recent. hemi ingest --force.
Agents are down after a rebootThey start at login. hemi up if you stopped them by hand before.
Gateway errors in the logsKey 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 statusmissing: hemi license activate <key>. revoked: ask Pablo. stale: more than 48 h without reaching hemisphere.almansa.dev; connect to a network that does (a phone hotspot works) and run hemi license status.
hemi update fails with HTTP Error 403Versions 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 backThe 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 graphThe 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.