What leaves your machine, and when.
Who you are dealing with
Canopy is operated by The Banana Standard LLC. For anything on this page — a question, a deletion request, a correction — write to anthony@thebananastandard.xyz. We are small, so expect a human reply rather than a ticket number.
The short version
The desktop app is local-first. Your code, the workspace index and your agent transcripts sit on your machine and are not sent anywhere by Canopy. There is no telemetry, no analytics SDK and no crash reporter — not in the desktop app, not on this website. The one thing Canopy does on its own is check whether a newer version exists, and that request carries no identifier and can be switched off.
The exception is the sync hub, and you have to reach for it. If you share a thread, or join someone else’s, the transcript goes to a server — and a transcript is code. It contains the contents of files the agent read and the output of commands it ran. Today that server keeps it indefinitely.
On your machine
Canopy is a workspace manager and a terminal multiplexer. It launches the agent CLIs you already have installed, against folders you added yourself. What it writes locally:
- A SQLite database beside the app’s other data — projects, workspaces, tab state, your settings, and the cross-project search index (file paths, extracted symbols and text snippets from the projects you added).
- Four secrets in the operating system keyring, and no others: three AWS fields for a Bedrock provider setup, and the hub token this machine holds once you sign in. Nothing is written to that keyring under any other name.
- Nothing else about a session. Agent transcripts are written by the agent CLI itself, into its own directory in your home folder. Canopy reads those files to draw the session list; it does not copy them elsewhere.
A thread you never share never leaves the process it runs in. An open file tab is not thread content and has no route to the hub at all.
Everything that leaves your machine
This is the whole list. Each entry names the action that causes it, because with one exception none of them happen on their own.
- The update check. A GET of a static manifest file on this site. No query string, no identifier, no account, nothing about your machine — the only thing it carries beyond the request itself is the updater library’s own user-agent string, which names the library and not you or your version. This is the one that ships switched on; it runs shortly after launch and then about once a day, and the toggle is in Settings under Behaviour, labelled “Check for updates on launch”. Turning it off stops it entirely.
- Downloading an update. Only when you press Update. The artifact comes from the release host named in that manifest, which is GitHub. Nothing is downloaded before the click, nothing installs without it, and nothing restarts until you press Restart.
- Sharing a thread. Opens a WebSocket to the sync hub and publishes that thread’s transcript to it. See the next two sections — this is the one that matters.
- Joining a thread. Connects to the hub named in the invite link, presents the invite, and sends the display name you typed. You need no account to join.
- Signing in. Device-code pairing over HTTPS to the same hub: this machine asks for a code, you approve it in your own browser, and the hub hands this machine a long-lived token. The desktop app never holds a Clerk session token.
- Opening a link. A link you click inside Canopy is handed to your operating system, which opens it in your browser. Canopy does not fetch it.
- The agent itself. Canopy starts the agent CLI as a subprocess. That process then talks to its provider — Anthropic, for Claude Code — directly, over your own account and your own credentials. Canopy is not in that path: it does not proxy, inspect or hold those requests, and it never sees the provider credential. Whatever your agent sends is governed by that provider’s terms and privacy policy, not this one.
What the hub stores when you share
Sharing a thread writes rows into a Postgres database. There are exactly seven tables that hold anything about a person, and this is all of them:
- threads — the thread id, the title if it has one, when it was created, and the account id that created it.
- participants — for each person in a thread: the thread id, their participant id, their role, their display name, and when they joined.
- events — the transcript. Every message, in order, with the author’s participant id, kind and display name, a timestamp, and the event payload stored as JSON. This is the big one; the next section says what is in a payload.
- invite_tokens — each invite link’s token, the thread and role it grants, its expiry, who created it and when, and when it was revoked if it has been.
- users — created when you sign in: an account id we mint, which identity provider you used and your id at that provider, your email address if the sign-in session supplied one, your display name, your plan and where it came from, a Stripe customer id if you ever have one, when the account was created, and whether it has been disabled.
- device_codes — a pairing attempt in flight: a hash of the device code, the short code you read off the screen, its status, the account that approved it, the device label you gave the machine, timestamps, and how many times it has been polled. These rows are short-lived by design: a completed pairing is marked claimed rather than removed, and it is swept along with any expired rows the next time somebody starts a pairing. There is no timer, so a row can outlive its ten-minute window on an idle hub.
- device_tokens — one row per signed-in machine: a hash of the token, the account it belongs to, the label you gave the device, when it was created, roughly when it was last used, and when it was revoked. Only the hash is stored, so a copy of the database hands nobody a working credential.
No IP address is stored in any of those tables. The hub does use the requesting address as an in-memory key for rate limiting; it is not written to the database and not logged.
A transcript is code
This is the part worth reading twice, because “we store your messages” understates it. A thread event can be any of: a message you typed, the agent’s reply, its reasoning, a tool call with its arguments, a tool’s result, a permission request, or a turn summary with the token usage and cost. Two of those carry files verbatim:
- A tool call’s arguments are the shell command that was run, the path that was read, or the exact text of an edit.
- A tool’s result is that tool’s output — the contents of the file it read, the output of the command it ran, the diff it produced.
So if you share a thread in which an agent read a file, the hub now has that file. That is not a leak; it is what sharing a thread means, and it is why the decision is per-thread and why the app says so before the click rather than after it.
What a share changes on the way out
The copy that goes to the hub is not the copy you see. Three transforms run at the publishing boundary, and only one of them is off by default:
- The thread’s working directory is reduced to a folder name. Always, not a setting. It is an absolute path on your disk — it usually contains your operating-system account name and says how you organise your files — and only the project folder’s own name is published. Nobody chooses to disclose the rest, so it is not offered as a choice. Other paths are not rewritten. A path inside a tool call — the file an agent read or edited — is published as it stands, absolute and complete, unless you hide tool activity below. If your folder layout is itself sensitive, that is the setting that covers it.
- Likely secrets are redacted. On by default. Known credential shapes — cloud keys, provider tokens, JWTs, private-key blocks,
key=valuepairs whose key names a secret — plus a high-entropy backstop for long random-looking strings. Your own view is never redacted; only the wire copy is. Treat this as a safety net and not a guarantee. It is pattern matching, and a secret in a shape it does not know goes out intact. - Tool activity can be hidden. Off by default. With it on, guests still see that a tool ran and whether it failed, but the arguments and the output are replaced with a placeholder. It stops tool payloads — the files an agent read, the commands it ran — from reaching the hub. It does not cover the agent’s own prose: if the agent quotes a file back to you in its reply, that reply is still published.
One more thing that is easy to miss: an invite link is a credential. Anyone holding it can read the thread, with no account and nothing else of their own. Treat it like a password. Stop sharing revokes the links you handed out and disconnects anyone using one.
How long it is kept
Indefinitely. There is no retention window and no purge job. Thread events are append-only by design — the hub never updates or deletes one — and the only rows it deletes on its own are expired device codes. Stop sharing kills the links; it does not erase what was already sent. A retention policy is planned and does not exist yet, and this page will say so plainly until it does.
Deleting it
There is no self-serve delete button yet. To have your account and your threads removed, email anthony@thebananastandard.xyz from the address on the account, or tell us the thread. On the operator’s side this is genuinely small: the schema has no foreign keys, so removal is a handful of direct statements against the seven tables above, and the events go with them.
What you can do yourself today, without asking:
- Revoke a device. The account site lists the machines signed in to your account and can kill any of them. Signing out of the desktop app revokes that machine’s own token as well as forgetting it locally.
- Revoke invites. Stop sharing on a thread revokes every live link for it.
- Never share in the first place. Nothing about a local thread reaches the hub until you press Share.
Who else can see any of it
Canopy is small and runs on other people’s infrastructure. These are the companies involved and what each of them is exposed to:
- Clerk — sign-in. They hold your identity: the email address or social account you sign up with, and the session in your browser. Canopy’s account site is the only place Clerk keys exist; the hub only verifies a Clerk session, and it does so against a locally held key rather than by calling them. Nothing on the WebSocket path touches Clerk at all.
- Railway — hosts the hub process. Sees the traffic to it and holds its logs and environment.
- Neon — the managed Postgres behind the hub. Holds every table listed above, transcripts included, and its backups.
- Vercel — hosts this website, the read-only thread viewer and the account site. Sees the ordinary request logs of a web host.
- GitHub — hosts the release files. Sees the download request when you install or update, and counts it.
- Anthropic — whatever the Claude Code CLI itself sends. This is a direct relationship between you and them, under your own account. Canopy starts the process and is not in the request path.
Nothing is sold, and nothing is shared with anyone not on that list. There is no advertising and no third-party tracking of any kind.
Things that do not exist
Stated as absences because they are the questions people actually ask:
- No telemetry, and no usage or feature analytics.
- No analytics SDK, and no crash or error reporter.
- No cookies set by this website, which is a set of static files with no server behind it. The account site uses a Clerk session cookie, because signing in requires one.
- No account required to download, install or use the desktop app, and none required to open an invite link someone sent you.
Where this can be wrong
Canopy is early. If this page and the software disagree, the software is what is happening and this page is the bug — please report it. Any change here will be reflected in the date at the top of the page.