Security and verification
Supported versions
Security fixes are provided for the newest published release, currently v0.4.1. The unreleased main branch is tested continuously but is not a supported release channel. Pin production installations to an immutable tag and verify the checksum and build provenance. greppy has no self-updater: you upgrade deliberately, through verified release artifacts.
How a release is cut
A release requires all of the following to pass on the exact release commit:
- CI;
- CodeQL;
- the security audit of the Rust dependencies;
- the reproducibility audit of the task bank;
- the quality gate for the summaries;
- code signing and notarization.
Every package comes with an SBOM (*.spdx.json). Packages, SBOMs, build environments and the Qwen model's training evidence carry a SLSA build provenance attestation from GitHub. Agent benchmarks run continuously as diagnostics and never block the release under test. See Benchmarks and paper.
Verify a release
The expected identity is the repository metric-space-ai/greppy, the signer workflow .github/workflows/release.yml and the commit the release tag points to. Download the checksums, the release manifest and the package for your platform into an empty directory:
version=v0.4.1
asset=greppy-macos-arm64.pkg # or greppy-linux-x86_64.deb / greppy-linux-x86_64.rpm
mkdir "greppy-$version" && cd "greppy-$version"
gh release download "$version" --repo metric-space-ai/greppy \
-p SHA256SUMS -p RELEASE-ASSETS.json -p "$asset"
1. Checksum
shasum -a 256 -c --ignore-missing SHA256SUMS # Linux: sha256sum --check --ignore-missing SHA256SUMS
RELEASE-ASSETS.json: OK
greppy-macos-arm64.pkg: OK
SHA256SUMS covers all 15 other assets: the packages, their SHA-256 files and SBOMs, the manifest RELEASE-ASSETS.json, the build environments, the model provenance report and the Qwen training evidence. --ignore-missing checks the files you downloaded.
2. Manifest
RELEASE-ASSETS.json is the machine-readable contract for the exact file names, their roles and their SHA-256 values. Check that your package is listed as the package of this tag:
jq -r --arg a "$asset" '.tag, (.assets[] | select(.name == $a) | "\(.role) \(.sha256)")' RELEASE-ASSETS.json
v0.4.1
package 1040391f1e23a71d7a21c33eb5d2e9343d002fc6f44743e466fa086ee8295d86
3. Build provenance
The attestation binds the package's SHA-256 to the repository, the workflow and the source commit. --source-digest requires exactly the commit the release tag points to; RELEASE-ASSETS.json names the same commit as git_commit.
commit=$(gh api "repos/metric-space-ai/greppy/commits/$version" --jq .sha)
gh attestation verify "$asset" \
--repo metric-space-ai/greppy \
--signer-workflow metric-space-ai/greppy/.github/workflows/release.yml \
--source-digest "$commit" \
--deny-self-hosted-runners
Loaded digest sha256:1040391f1e23a71d7a21c33eb5d2e9343d002fc6f44743e466fa086ee8295d86 for file://greppy-macos-arm64.pkg
Loaded 1 attestation from GitHub API
The following policy criteria will be enforced:
- Predicate type must match:..................... https://slsa.dev/provenance/v1
- Source Repository Owner URI must match:........ https://github.com/metric-space-ai
- Source Repository URI must match:.............. https://github.com/metric-space-ai/greppy
- Source repo digest digest must match:.......... 7e6e6ed7d95a48dc7637ba5d406e7a724e589d77
- Subject Alternative Name must match regex:..... ^https://github.com/metric-space-ai/greppy/.github/workflows/release.yml
- OIDC Issuer must match:........................ https://token.actions.githubusercontent.com
- Action workflow Runner Environment must match : github-hosted
✓ Verification succeeded!
…
With greppy-linux-x86_64.deb, the same command also ends with ✓ Verification succeeded!.
Do not install a release if any of these checks fails: manifest, checksum, signature or attestation.
The release web page loads its asset list after the fact, so a plain HTML fetch shows nothing but the "Source code" links. The API and RELEASE-ASSETS.json are authoritative:
gh release view v0.4.1 --repo metric-space-ai/greppy --json assets --jq '.assets[].name'
macOS: signature and notarization
The .pkg is signed with a Developer ID Installer certificate and notarized by Apple; the notarization ticket is stapled. Gatekeeper rates it as "Notarized Developer ID".
pkgutil --check-signature "$asset"
spctl --assess --type install --verbose=4 "$asset"
xcrun stapler validate "$asset"
$ pkgutil --check-signature greppy-macos-arm64.pkg
Package "greppy-macos-arm64.pkg":
Status: signed by a developer certificate issued by Apple for distribution
Notarization: trusted by the Apple notary service
Signed with a trusted timestamp on: 2026-10-07 16:46:15 +0000
Certificate Chain:
1. Developer ID Installer: Michael Welsch (2HS27B8739)
…
2. Developer ID Certification Authority
…
3. Apple Root CA
…
$ spctl --assess --type install --verbose=4 greppy-macos-arm64.pkg
greppy-macos-arm64.pkg: accepted
source=Notarized Developer ID
$ xcrun stapler validate greppy-macos-arm64.pkg
…
The validate action worked!
The installed CLI carries a Developer ID signature with hardened runtime. After an installation with the installer, it lives under ~/.local/share/greppy/versions/v0.4.1/bin/, and with --system under /Applications/GreppyWorkspaceFS.app/Contents/Resources/bin/:
cd ~/.local/share/greppy/versions/v0.4.1/bin
codesign --verify --strict --verbose=2 greppy
codesign --display --verbose=4 greppy 2>&1 | grep -E '^(Authority|TeamIdentifier)='
greppy: valid on disk
greppy: satisfies its Designated Requirement
Authority=Developer ID Application: Michael Welsch (2HS27B8739)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
TeamIdentifier=2HS27B8739
A single Mach-O file cannot carry a notarization ticket; the ticket sits on the .pkg. That is why spctl --assess --type execute rejects the CLI with "the code is valid but does not seem to be an app". This is expected and not a defect.
The GitHub attestation proves the identity of the repository and the workflow. The Apple check independently proves platform trust. The displayed certificate name is a diagnostic and does not replace the repository-bound attestation.
Report a vulnerability
Report vulnerabilities privately through GitHub's Security › Report a vulnerability flow in the greppy repository. Do not open a public issue for a suspected vulnerability that affects repository contents, cache paths, local privilege boundaries, the loading of models or backends, or the daemon transport.
Include the affected version and platform, the steps to reproduce, the expected impact and whether local access is required. The maintainers acknowledge a complete report within five business days and coordinate disclosure as soon as a fix is ready. The full policy is in SECURITY.md.
Security boundaries
- greppy processes local source code and stores indexed spans in a local SQLite cache. Code navigation sends neither code nor model prompts to a network service.
- The ordinary grep passthrough calls the real system
grep. It opens no index, loads no model and changes no greppy cache. - The Qwen summaries are untrusted navigation hints and are missing on any inference or validation error.
- Models and embedded CUDA artifacts are extracted into private, content-addressed cache paths and nowhere else, and they are verified before loading. Release builds support no external backend libraries.
- greppy installs no drivers, toolkits, updates or other software.
- Agent workspaces are local private workspaces, not a security boundary between the agent and your repository contents. Nothing reaches your repository except a verified final proposal.
- The agent's tool subprocesses are confined when they write (Seatbelt on macOS, Landlock on Linux). Reads and network stay open, and greppy strips environment variables that hold credentials.
- The sockets of the inference daemons have mode
0600in a per-user directory with0700(/tmp/greppy-daemon-<uid>/). - Any process running as your user can reach the agent's remote-control socket, including the agent's tool subprocesses. The boundary is your user account, not a privilege boundary between agents.
Browser tool hardening (beta)
- The embedded engine honors its HSTS preload list from the first navigation on.
SubtleCryptois disabled while the engine depends on a timing-vulnerable RSA path (RUSTSEC-2023-0071).crypto.getRandomValuesandcrypto.randomUUIDremain available. A regression test fails as soon as it is turned back on.- Sessions use the
researchprofile (public web) by default; theprojectprofile also allows loopback. LAN and cloud-metadata addresses stay blocked. - You pass secrets with
--from-envor--value-stdin, never on the command line. Page text is fenced as untrusted input.
Sensitive repositories
Set GREPPY_STORE_DIR to an encrypted or ephemeral, user-private location when repository contents need extra protection at rest. You audit stored paths with greppy cache status --json, and you remove managed data with greppy cache clear --root DIR --yes or greppy cache clear --all --yes. See Local data, privacy and uninstall.