Install on a VM with systemd

One binary, one configuration directory, one data directory. No database to provision, no cache to run, no message queue. Fifteen minutes on a plain VM.

What is in the tarball

bin/searchcode                    the server and the CLI, one static binary
systemd/searchcode.service        the unit
systemd/oauth2-proxy.service      SSO in front of the UI, for auth.mode: proxy
systemd/oauth2-proxy.cfg          a worked oauth2-proxy configuration
config/searchcode.yaml            a starting configuration that validates as it stands
config/searchcode.yaml.annotated  every block that exists, documented
config/conf.d/repositories.yaml   where the repository list goes
AGENT-INSTALL.md                  this install, written for an LLM to execute
openapi.json                      the API contract for this exact build
codes.json                        every log code this build can emit, and where
sbom.json                         CycloneDX, listing what is actually in the binary
SHA256SUMS                        checksums for all of the above

If an agent is doing this install, hand it AGENT-INSTALL.md from the tarball — the same steps, self-contained, with an explicit verification after each one and a refusal list for the shortcuts an agent reaches for. It is docs/install/agent.md in the repository, and the tarball's copy is the one that matches the build you are installing.

Verify the checksums before you install anything:

sha256sum -c SHA256SUMS

codes.json is the one nobody thinks about until they need it. See Log codes.

Install

sudo useradd --system --home-dir /var/lib/searchcode \
             --shell /usr/sbin/nologin searchcode
sudo install -m0755 bin/searchcode /usr/local/bin/searchcode
sudo install -d -m0755 /etc/searchcode /etc/searchcode/conf.d
sudo install -m0644 config/searchcode.yaml /etc/searchcode/searchcode.yaml
sudo install -m0644 config/conf.d/repositories.yaml /etc/searchcode/conf.d/
sudo install -m0644 systemd/searchcode.service /etc/systemd/system/

git must be on the PATH. The product shells out to the real git binary deliberately rather than reimplementing the protocol, which is what makes the git-p4, git-svn and hg-git bridges work — see Source capability tiers.

Check the configuration before starting anything. It prints every problem, not the first, and exits non-zero on any of them:

sudo -u searchcode searchcode config validate

Then:

sudo systemctl daemon-reload
sudo systemctl enable --now searchcode

enable is what makes it come back after a reboot. start alone does not.

systemctl status searchcode
journalctl -u searchcode -f

The startup banner on stderr names the configuration it read and the addresses it bound. stdout is pure JSON — one object per line, with ts, level, msg, component and uniqueCode — and that is the schema to write SIEM rules against.

First sign-in

The shipped configuration uses auth.mode: local: built-in accounts and no identity provider. Nobody can sign in until you create somebody:

sudo -u searchcode searchcode user add --admin alice

The password is printed once and must be changed at first sign-in — no session is issued at all until it is replaced. There is no password-reset email and no SMTP configuration: this product sends no mail. Recovery is searchcode user passwd, run on the box, and it is audited.

Brute force is handled with backoff rather than lockout. Usernames appear in the commit history this product indexes, so account lockout would ship a working denial of service against any named engineer.

SSO instead

Set auth.mode: proxy and put oauth2-proxy in front of the UI listener only — port 8081 by default. systemd/oauth2-proxy.cfg is a worked configuration with the four values you must change marked.

For how the two listeners map onto DNS names and certificates, and what mcp.oauth.publicUrl and uiUrl then have to be set to, see Ports, names and what sits in front of each listener.

Do not put it in front of the API and MCP listener on 8080. Agents present a searchcode-issued credential your proxy did not issue and cannot validate, and the workaround people reach for next — a skip-auth route for /v1/mcp and /api/v1/* — is an authentication bypass. The two listeners are separate ports precisely so this cannot happen by accident.

Three things about proxy mode that get "fixed" by people who read them as oversights:

There is no native OIDC and there will not be one. Your proxy owns the identity provider conversation, which is what makes the egress claim true in its strongest form: the searchcode process never talks to an identity provider, in any mode.

Adding repositories

searchcode discover enumerates a code host into a conf.d/ fragment you review and commit. It is a CLI you run; the server never calls a code host.

searchcode discover --provider github --org yourorg \
  --out /etc/searchcode/conf.d/repositories.yaml

The repository list is hot-reloaded — no restart. Listen addresses, the auth mode, instance.dataDir and tracing are restart-only, and a reload carrying a change to one of them is rejected naming the field rather than half-applied. The full split is the Reload column in Configuration reference.

Upgrading

Replace the binary and restart. State lives in /var/lib/searchcode and is forward-compatible; nothing in the tarball needs to be re-run.

sudo systemctl stop searchcode
sudo install -m0755 bin/searchcode /usr/local/bin/searchcode
sudo systemctl start searchcode

Backup

/var/lib/searchcode/subjects.db is the only durable state that cannot be reconstructed. See Backup and restore.

If it will not start

searchcode doctor

runs the startup checks against a stopped instance and says which one failed and what usually causes it. searchcode doctor bundle prints a redacted diagnostic document to paste into a support ticket — see searchcode doctor.

Generated from the documentation that ships with searchcode enterprise. Questions: ben@boyter.org