Trust
The posture in one page. Security questionnaire has the long answers; this is what to hand somebody who has ten minutes.
What it is
One process, on your hardware, that clones your repositories and answers questions about them — for humans in a browser and for agents over MCP and a REST API.
No SaaS component. No vendor account. No model, no GPU, no inference cost. No database to provision, no cache, no message queue.
What it stores
Everything lives under one directory, instance.dataDir:
| Clones of your repositories | Rebuildable — git clone again |
| The analysis store | Derived from the clones |
subjects.db |
Users, credentials, seats, sessions. The only durable state that cannot be reconstructed. |
That last one is the whole backup story. See Backup and restore.
What it sends
Nothing to us. The product never phones home — no telemetry, no licence check-in, no analytics, no update ping, on any tier, for any reason.
Egress, precisely
The claim people expect is "no egress", and the honest version is more useful than the slogan, because most of what looks like egress is not made by this process at all.
| Connection | Made by | When |
|---|---|---|
git clone, git fetch |
the git binary, in a subprocess |
Whenever a repository is ingested or refreshed |
| Container image pull | your container runtime | At deploy |
| SSO to your identity provider | your auth proxy | Every browser sign-in, in auth.mode: proxy |
| Code-host enumeration | searchcode discover, a CLI you run |
Only when you run it |
| Code-host enumeration | searchcode | Only if you declare a connections.sources entry, which names exactly one host |
| OTLP trace spans | searchcode | Only if you set observability.tracing.enabled: true |
| Audit records to a remote syslog | searchcode | Only if you set audit.output: syslog with a remote address |
The last three rows are the only outbound connections the searchcode process
itself ever makes, and each one exists because you configured it. A default
install configures none of them: there is no connections: block, tracing is
off, and audit.output defaults to stdout. The honest sentence is therefore
"no outbound connection unless you configure one, exactly three things can
configure one, and never the vendor" — not "no egress", which was true before
connections existed and is not the same claim.
observability.tracing.enabled, .endpoint and connections.egress are
restart-only, deliberately: opening one of the process's outbound
connections is not a change a file edit should make a second later without the
deployment noticing.
A connection to a code host inside your own network is not internet egress.
GitHub Enterprise Server, self-managed GitLab and Bitbucket Data Center live
behind the same firewall as this product, so the socket goes to
ghe.internal.example.com:443 and never leaves — and
connections.egress: internal-only refuses a connection naming a public code
host at config load, so it is a switch you can point at rather than a policy
somebody has to review. The generated NetworkPolicy is derived from the hosts
you declared, so what the process may reach is readable off the manifest.
A connection is bounded in three ways that are tests rather than promises: it names one host and the client is pinned to it, so a cross-host redirect is refused and the credential never travels to a host you did not declare; its token is metadata-only and nothing in this product reads, writes or mirrors your code host's access-control lists; and no domain we own is reachable from the process at all, in any mode, with no setting that permits it.
If you would rather the server never called a code host, do not declare a
connection: searchcode discover is a CLI you run,
whose output is a conf.d/ fragment you review and commit, and it is unchanged.
What it runs as
- uid 65532, non-root
- read-only root filesystem
- all Linux capabilities dropped,
no-new-privileges - every write goes to the data volume or a
/tmpemptyDir - images signed with cosign, keylessly
- CycloneDX SBOM attached to every release, generated from the built binary
Who can see what
One function answers "which repositories may this principal see", and every surface derives from it — search, files, blame, history, the API, MCP, analytics, aggregates. Filtering happens at query time, so a repository you cannot see is never read.
Access is granted by declarative bindings in configuration: identity groups on
one side, repository groups on the other. Allow-only, order-independent,
validated in CI. authz.defaultAccess defaults to deny.
/api/v1/explain-access answers "why can Bob see this?" with the binding that
granted it.
What is recorded
A separate audit stream that records denials as well as grants, unconditional on every tier — no licence check, no off switch. Application logs are a different stream: pure JSON on stdout, one object per line, with a unique code per emission site. See Log codes.
What the licence does
It sets two numbers: repository count and user count. No feature is licence-gated on any tier, there is no capability array in the payload, and verification is offline against a compiled-in public key.
An absent, tampered, expired or unknown licence is community mode — logged
loudly, reported on /api/v1/status, never a crash and never a lockout. Past grace
pauses indexing and nothing else.
Untrusted input
Every repository in the estate is untrusted input by definition — it is whatever your developers wrote, plus whatever their dependencies vendored.
Raw file bytes are served as the declared type with nosniff,
Content-Disposition: attachment and a default-src 'none'; sandbox CSP, with
no exception list. Any HTML or SVG among your code would otherwise be stored
cross-site scripting against every user of the instance.
api.rawContentOrigin optionally moves raw content to a second hostname; the
headers are the defence and the origin is an addition to it.
Syntax highlighting uses a text/scanner tokenizer rather than a regex lexer,
because a backtracking lexer on a minified bundle or a 40,000-character
generated line is a denial of service against the file view.
Where to go next
- Security questionnaire — the full answers, including offboarding
- OWASP Top 10 for Agentic Applications — control mapping — the OWASP Top 10 for Agentic Applications mapping
- Air-gapped install — how to prove the egress claim yourself