Skip to content

Security & privacy

Sankshep is built to run entirely on your machine. This page states exactly what it stores, where, and what — if anything — leaves the host, and the controls that back those claims.

Data handling: what leaves your machine

By default, nothing. No code, file paths, queries, or memory content is sent anywhere. Embeddings are computed locally via ONNX Runtime; vector search is local via sqlite-vec.

The only outbound network activity is:

Activity When What is sent
Embedding-model download First index_repo/search_code, unless side-loaded A checksum-verified request for the public model files — no repo content. Suppress with SANKSHEP_MODEL_OFFLINE=1 + SANKSHEP_MODEL_DIR.
Telemetry export Only if you set SANKSHEP_OTLP_ENDPOINT Counts and a low-cardinality repo tag (the folder name) — never file paths, queries, or code. To a collector you run.

There is no telemetry by default and no dollar/billing phone-home — Sankshep is never told which model you use or what you pay (ADR-0011, ADR-0017). No repository code or content ever leaves the machine; the model download is a one-time inbound fetch of public files, and it is disableable for air-gapped use.

SANKSHEP_PROMETHEUS=1 is a pull endpoint, not outbound

In HTTP mode it exposes a local /metrics endpoint that your Prometheus scrapes — the server never initiates that connection, so it is inbound and not listed above. Only SANKSHEP_OTLP_ENDPOINT (OTLP push) sends telemetry outbound.

Proven, not just asserted

A continuous-integration air-gap job runs the server on a no-egress network and confirms summarize_repo, index_repo, and search_code all work with outbound traffic physically blocked — and a socket-level test asserts get_context opens zero outbound connections.

Where state lives on disk

Per-repo state is written under <repo>/.sankshep/ (override the location with SANKSHEP_STATE_DIR):

File Written by Contents
facts.db remember Your remembered facts + their git-branch tags and optional source.
index.db index_repo Code chunks and their local embedding vectors.
stats.db get_context, compose_task_prompt Per-tool token counts (no code, no paths).

The embedding model is cached under SANKSHEP_MODEL_DIR (or a default per-user location). None of this is transmitted; deleting .sankshep/ resets a repo's index, memory, and stats.

Add .sankshep/ to your .gitignore

It's local state, not source. Keep facts.db/index.db/stats.db out of version control.

Network exposure (HTTP mode)

sankshep --http binds http://127.0.0.1:8080 — loopback only — by default, a DNS-rebinding defense. stdio mode has no network surface at all.

The HTTP server fails closed. If it is bound to a non-loopback interface (a container/Kubernetes ASPNETCORE_URLS=0.0.0.0) with authentication set to None, it refuses to start — you must either configure authentication or explicitly accept the exposure:

  • API-key auth (recommended for LAN/cluster): SANKSHEP_API_KEYS (bearer keys, compared in constant time).
  • OAuth 2.1 resource-server mode: SANKSHEP_OAUTH_*.
  • Explicit opt-in for a trusted, network-isolated deployment: SANKSHEP_ALLOW_UNAUTHENTICATED=1.

Health probes stay anonymous. The Helm chart is secure by default: it surfaces an auth block and will not serve unauthenticated on 0.0.0.0 unless you opt in.

Hardening & verified posture

These controls are enforced in code and covered by tests/CI:

Area Control
Egress No repo content leaves the host; socket-level test + no-egress air-gap CI prove it.
Telemetry Off by default; opt-in only; counts + folder-name tag, never paths/queries/code.
Path scope Containment covers all three path-taking tools — get_context, summarize_repo and index_repo — not just one. A ../.. escape is rejected, a rooted-but-not-fully-qualified path such as /etc is resolved against the repo root rather than taken literally, and a symlink or junction whose target leaves the root is skipped rather than followed. On the HTTP transport the absolute-path carve-out is refused outright.
SQL All SQLite access (facts, vectors, stats) uses bound parameters — no string-built queries.
Deserialization Typed YAML/JSON only; no polymorphic type resolution, no BinaryFormatter.
Untrusted repos .gitignore matching is ReDoS-safe — every pattern compiles with RegexOptions.NonBacktracking, which guarantees linear time in the input length. .docx/.pdf extraction is size-capped against decompression bombs, source files have their own byte cap, and directory walks are iterative and symlink-cycle-safe. A directory that cannot be read yields a partial result rather than failing the whole call. Nothing in a served repository is configuration: neither transport reads appsettings.json.
Auth Fails closed on unauthenticated non-loopback binds; API keys compared with a constant-time equality.
Secrets API keys / tokens are never written to logs; all logging is paths/errors/counts only.
Isolation The image runs as a non-root user. A read-only root filesystem and dropping all Linux capabilities are runtime settings, not image ones: the Helm chart sets both, and the docker run recipe on the deployment page passes them.

Security review — who did it, and what that is worth

Maintainer-run and AI-assisted. Not independent, and not a third-party audit. This page previously called it an "independent security review", which it has never been: the reviews are performed by the maintainer with AI assistance, and every finding is verified by the maintainer. No external firm has audited Sankshep. If you need one for a procurement process, you do not have one.

That is worth stating flatly, because the two things are not close in value and the word "independent" is the one a reader would rely on.

What the process actually is: findings are refuted before they are accepted, and confirmed issues are fixed before release. The v1.8.0 review covered ten dimensions — path traversal, injection, deserialization, egress, secrets, tool-input abuse, HTTP-tier auth, supply chain, denial of service, and command/crypto misuse — and found no critical or high-severity vulnerabilities. The medium and low hardening items it surfaced (the fail-closed auth bind, ReDoS-safe ignore matching, relative-path containment, document size limits and cycle-safe enumeration above) were remediated in v1.8.0.

A second and much harder review followed it. The 2026-09-05 production-readiness audit raised 189 findings across thirteen dimensions and scored v1.8.0 45/100 — not ready, including three critical defects producing silently wrong output on the default path.

All three criticals are fixed in v2.0.0. Of the 189 findings, 162 are closed, 23 were refuted on re-examination, and four ship deferred rather than quietly: one blocked by an SDK bug (NuGet lock files), two awaiting a decision rather than a fix (the sqlite-vec native's provenance, and a licensing question), and one needing the maintainer's hands (rotating a key out of a local benchmark script). The security-relevant one of those four is the sqlite-vec provenance question; it is stated here because a release that listed only its closures would be making the same kind of claim this remediation spent itself removing.

That audit is also the reason this page says "maintainer-run" instead of "independent": it is exactly the kind of claim a self-review is worst at checking about itself.

Supply chain

The NuGet package is published with OIDC Trusted Publishing — GitHub's OIDC token is exchanged for a short-lived NuGet key at release time, so no long-lived API key is stored anywhere. NuGet audit is enforced at build time: the real vulnerability codes (NU1901–NU1904) are treated as build errors, so a flagged advisory fails the build — that's how the OpenTelemetry 1.10.0 CVE was caught.

Container images are published to GHCR and run as a non-root user. A read-only root filesystem and dropped capabilities come from how you RUN the image, not from the image itself — nothing an image contains can enforce either. The Helm chart sets readOnlyRootFilesystem: true and drop: [ALL], and the docker run recipe on the deployment page passes --read-only --cap-drop=ALL; a bare docker run gets neither.

The image pins both its base images by digest, and it carries a full SLSA provenance statement and an SPDX SBOM, which docker buildx imagetools inspect will show you. The embedding-model download is SHA-256 verified and fail-closed. For the strictest posture, pin the image by digest and pre-bake or side-load the model.

Reporting a vulnerability

Please report suspected security issues privately — do not open a public issue, which would disclose the problem before a fix exists.

→ Report a vulnerability privately

This opens a GitHub private security advisory visible only to the maintainer, and you'll get a response there. (Sankshep's product source is a private repository, so vulnerability reports are received through this public project's advisory channel.) See also SECURITY.md.