What happens to your code
No company wants to hand its codebase to a third party. This page is the mechanism, in detail, including the parts that are not reassuring.
Your code does not come to us
Stated first, because everything else on this page depends on it. A private repository is surveyed by the collector, which runs on your machine. Borehole never clones, receives or stores your source.
That includes machines we cannot read. A confidential VM in our own cloud project would stop us looking at your code, and your code would still be on our infrastructure. We do not run one. "We never receive it" is a simpler promise than "we hold it and cannot read it", and it is the one we make.
pip install borehole export BOREHOLE_TOKEN=bh_… from your account page borehole collect . --dry-run prints the entire bundle, sends nothing about it borehole collect . sends it, returns a report
What does cross
Facts about the repository, computed where it sits. The whole list:
- Commit hashes, dates, subjects, and insertion and deletion counts.
- Which files changed in which commit (and, from collector 0.4.0, whether each was a binary file), the list of tracked file paths, and tags with the commit each points at and its date.
- From collector 0.4.0: the name and version of each dependency your lockfiles pin from a public registry, its ecosystem, and whether the lockfile marks it as shipping. Packages from a private registry, a git URL or a local path are left out, because their names can describe your own code. Our server looks each package's ecosystem, name and version up in OSV, a public vulnerability database. OSV receives nothing else: no repository name, no path, no code.
- The remote's host and path, or the directory name if there is no remote.
- Where each detection pattern matched — a path and a line number — and up to 120 characters of the matched text. Except a credential: a match that looks like a key or token crosses as its location and kind only, never its text.
- From collector 0.5.0, committed
.envfiles, notebooks and app manifests are read too. A.envfile is read only by the credential patterns: what crosses is where a key-shaped value sits and what kind it is, never the value, and no other line of the file. - Secrets committed at any point in the history. The collector walks every added line on your machine and sends, for each credential-shaped match, the commit, the path and the kind of pattern. Never the text.
- Two counts about the tree: how many files, how many lines.
- Author names and email addresses, hashed on your machine. The same person is one identity within a survey and not across surveys.
File contents do not cross. Not compressed, not sampled, not hashed.
The collector's source is readable, and one file in it,
extract.py, lists everything it can compute.
Check it before you send it
Run --dry-run with your token set. It
prints the exact bundle a real run would send, including the quoted
lines, and sends nothing about your repository. The detection patterns
are not in the package; they are fetched for each run, and that fetch is
the only request a dry run makes. Its body is empty.
You should not have to take our word for what leaves your machine, and with that flag you do not have to.
Or run it in your own cloud
The collector can also run inside a Confidential VM in your own Google Cloud project, where Google signs the exact facts it sends. That proves the facts were not edited on the way, which the ordinary collector cannot. Your source still never leaves your project, and we never receive access to it. How to run it, and what it does not prove.
What happens to the facts
The bundle is assessed on our servers: the thresholds, the bands and the wording of every finding live there and not in the package. The report is stored, and it contains pointers — commit, path, line range — not your source. It records that it came from the collector, which version, and that no source reached us.
Public repositories are different
A public repository is cloned onto our infrastructure and read there, because it is already readable by anyone. The clone uses no credential, and the working copy is deleted when the scan ends, including when it fails. The hosted scanner holds no credential for a private repository and refuses any job that carries one.
What we structurally cannot do
These are not promises. They are consequences of how the thing is built, and each one is asserted by a test that fails the build.
- The hosted worker refuses a private repository. A job for one is failed before anything is cloned, however it was queued, and any access token it carries is destroyed unused.
- The web tier cannot clone. There is no
gitbinary in its image. Continuous integration checks this on every build. - The worker runs as nobody. Non-root, read-only filesystem, every Linux capability dropped.
- A public working copy is deleted in a
finally. It goes when the scan raises, not only when it succeeds. - Reports hold no contributor identities. Authorship is counted, never named — a report contains no email addresses.
What you control
- Who can read it. A private report is readable by the account that paid, by anyone GitHub says controls the repository, and by nobody else. It is never listed, never generates a shareable card, and never generates a badge.
- Whether anyone else can. You can mint a share link for a counterparty with no GitHub account. Anyone holding it can read the report — the page says so — and you can revoke it or replace it.
- The quoted lines. Three checks quote text because in each case the text is the evidence: a TODO's wording, a commit subject, an unpinned dependency. One click removes them from the stored report. The pointers stay, so you can still find the line yourself.
What we still cannot prove
That the facts were not edited. A report is built from what your machine sent. We cannot tell a bundle the collector produced from one somebody changed afterwards, so a collected report is as good as the hands that ran it — unless the collector ran in Confidential Compute in your own cloud, where Google signs the facts it sent.
That a hashed address stays anonymous. The hash is salted with the repository's head commit, which is in the bundle. So anyone holding a bundle can test whether a guessed address belongs to an author. It is a pseudonym, not anonymity. We do not do it, and a report stores authorship only as counts.
What a quoted line says. Three checks quote the line they matched, because the line is the evidence. If somebody wrote a colleague's name in a TODO, that text crosses. The dry run shows it first, and redaction removes it afterwards.
If a page about trust will not say that plainly, nothing else on it is worth much.
If that is not enough
For some codebases even facts should not leave, and we would rather say so than win the argument. Tell us what you need — we would rather route you somewhere honest than sell you something that does not fit.
See also the Privacy Policy, which lists every processor and what each one holds, and the Terms.