privacy
What leaves your machine.
Nothing does. Not on the first run and not on the thousandth, because the shipped binary has no code path to a socket. Every claim below names the file you can open to check it, so none of it has to be taken on trust.
The tool
ccx collects nothing and sends nothing. There is no account, no key of
ours, no usage ping, and no opt-out to hunt for, because there is nothing to
opt out of. It launches the official claude and
codex binaries already on your machine, under your own logins.
What those two send to Anthropic and OpenAI is between you and them, on terms
you already agreed to.
That is a checkable claim rather than a promise, and here is how to check it.
Every line of network code in the repository lives in one file,
src/share.mjs, and all of it funnels through one function there,
call(), which is the only place fetch is named
anywhere in src/ or bin/. The binary,
bin/claudecodex.mjs, imports six things from that file —
buildReceipt, writeReceipt,
renderReceipt, buildPayload,
previewLines and payloadBytes — and not one of them
can reach call(). The argument parser has no
--share, --shares, --unshare,
--sync or --licence to reach them with either.
The billing module, src/license.mjs, is imported by nothing
outside its own test file. test/license.test.mjs keeps it that
way: it copies the tool into a temporary directory, replaces that module with
a booby trap that writes a marker and throws, and runs seven real argvs
through the real binary. If any of them so much as loads licence code, the
suite goes red.
ccx --share-dry exists for the same reason. It does a full run,
prints the exact bytes an upload would carry with every redaction
rule applied, and then sends none of them.
What ccx writes to your disk
Four things, all under ~/.claudecodex — or
$CLAUDECODEX_HOME if you set it — and all written
0600, owner-only.
brain.json— which agent tends to win which kind of task, as counters and weights. Delete it and the router starts from nothing.prefs.json— your settings, such as how many review rounds you are willing to pay for.lessons.json— the review checklist the tool builds per repository. Fixed question text from a table insrc/lessons.mjs, plus counters for how often each one fired and caught something. No code and no task text.receipts/— only when you ask, with--receipt. One self-contained HTML file per run holding the conversation, the findings and the diff. It is written before anything is redacted or previewed, and it stays where it lands.
Nothing is written anywhere else, and nothing is written outside your home directory. There is no daemon, no launch agent and no background process.
This website
No analytics, no tag manager, no error reporting, no embedded video player,
no webfont. Every byte this site loads comes from this site. You can
watch that in a network panel, and it is the reason the type is a system
stack rather than something prettier fetched from a font CDN. The
Content-Security-Policy header in
site/vercel.json enforces it: a request to any other host is
blocked by your own browser, whether or not we meant to make it.
No cookies. The front page sets one key in localStorage,
ccx.seen, so the opening replay plays once instead of on every
visit. It is a flag, it never leaves your browser, and clearing site data
removes it.
The site is served by Vercel, which keeps ordinary access logs for its own operations. We do not read them and nothing is joined to them.
If you email me
Then I have your address and whatever you wrote, in a mailbox, until one of us deletes it. It is not added to a list, because there is no list.
The hosted tier, which is not live
None of this section is running. The Cloudflare Worker that would
serve it is written and sits in worker/ in the repository,
deployed nowhere. There is no account to make, no card to enter and nothing
stored on any server of ours today. It is described here in the future tense
so that the terms are public before the thing exists rather than after, and
this page changes before any of it goes live.
Four things would need a server. Here is what each would hold.
A run you choose to share
- Opt in, per run. Nothing is uploaded unless you ask for that run specifically. There is no default-on setting and no bulk sync of past runs.
- What goes up: the task text, the redacted diff, the reviewer's findings, and the run's metadata — domain, which agent drove, which reviewed, how the loop stopped, how many findings and files.
- Redacted on your machine first. The rules run locally, before a socket is opened: whole files that are secrets by name, named key shapes replaced in place, an assignment rule and an entropy sweep for the ones with no fixed shape, and identity scrubbing for repo paths, home paths, usernames and email addresses. The upload function refuses any payload that has not been through them.
- Shown to you before it is sent. The preview prints the byte count and what each rule caught, and the send waits on a yes.
- Where it lives: the full record in Cloudflare R2, an index row in Cloudflare D1. Both in Cloudflare's network, nowhere else.
- Who can see it: anyone with the link. The id is unguessable, share pages are served
noindex, nofollowand are listed in no directory, so a link you do not hand out is a page nobody finds. - How long: while the subscription is active, plus thirty days.
- How to remove it:
ccx --unshare <id>with either the licence or the delete token printed at share time. That deletes the object, not just the link. A cancelled subscriber can still delete, because a delete that requires a payment is a hostage situation.
Brain sync across your machines
- One snapshot per machine, merged server-side into per-domain counts and pass rates. Push the same snapshot twice and nothing moves.
- The server never sees a word of your task text. Token keys are hashed on your machine —
sha256(token + your account salt), truncated to 16 hex characters — and the Worker rejects any push whose keys are not exactly that shape. What arrives is hashes and integers. - Deleting your account deletes the snapshots and the merged view with everything else.
Paying
- Stripe handles the payment. Card details go to Stripe and never touch a machine of ours. We would never see a card number, and there is no field on our side to put one in.
- What we would store from a purchase: a random account id, your email address, which plan, which team you are on if any, the status, the period end, Stripe's customer id, and when the account was created. That is all eight columns of the
accountstable, and it is inworker/schema.sql. - The Worker never calls Stripe's API. It reads three signed webhooks and nothing else.
- Your licence is an Ed25519-signed statement you carry with you. Nothing licence-shaped is stored server-side at all, because the signature is the record.
Two things that stay true either way
- No model is ever called with your data on our side. Not ours, not anyone's. There is no inference on the server at all, which is the only reason charging $5 for it is defensible.
- One command deletes everything.
DELETE /v1/accountremoves the runs, the stored objects, the brain snapshots, the merged view, the quota rows and the account row. Nothing is kept back as a tombstone with your data in it.
If any of this changes
The date below changes with it, and the change will say what is new rather than quietly widening a sentence. Pricing lists the four things that could ever need a server and the public test each has to pass before it gets built.
These are drafts. They were written by the person who wrote the code,
to be accurate rather than to be safe, and no lawyer has read them. If you
need a reviewed policy before you can use ccx at work, say so and I will get
one done.
Last changed 31 July 2026. Questions:
angus@angusangus.com.
See also the terms.