Environment variables¶
The single canonical reference for every variable Sankshep reads. Unless noted, a variable is unset by
default and the described behavior is off. HTTP/auth variables apply only to sankshep --http; stdio mode
ignores them.
There is no configuration file
Neither transport reads appsettings.json. Both take their content root from the installed binary's
directory and drop every JSON configuration source, so this page is the whole surface. The served
repository is input, never configuration: a file committed by anyone who can write to that repository
must not be able to change how the server runs.
In the repository itself the same table lives at docs/deploy/environment.md, and a test fails the
build if src/ reads a SANKSHEP_* name that is not registered, or if the table names one that no
longer exists.
Core & state¶
| Variable | Purpose | Default |
|---|---|---|
SANKSHEP_STATE_DIR |
Where per-repo state (facts.db, index.db, stats.db) is written. |
<repo>/.sankshep/ |
SANKSHEP_MODEL_DIR |
Directory to cache — or side-load — the embedding model (model.onnx + vocab.txt under <dir>/bge-small-en-v1.5/). |
per-user cache location |
SANKSHEP_MODEL_OFFLINE |
1 disables the model download entirely — the server fails closed if the model isn't already present. Pair with SANKSHEP_MODEL_DIR for air-gapped use. |
off (download on first use) |
SANKSHEP_WATCH |
1 enables a filesystem watcher that debounces changes and refreshes the index in the background. Off, freshness is maintained verify-on-read. |
off |
SANKSHEP_DISABLE_VEC0 |
1 forces the pure-C# brute-force vector store instead of the native sqlite-vec vec0 extension (test seam / ops escape hatch). |
off (native vec0 when available) |
SANKSHEP_WARM_MODEL |
Opt-out, unlike everything else here. 0 drops the embedding model from the readiness gate, so /health/ready returns 200 before the model is loaded. Only the exact string 0 disables it. |
on |
SANKSHEP_MAX_FILE_BYTES |
Source-file size cap. 0 disables the cap. A value that cannot be parsed falls back to the default, never to "no limit". |
2 MiB |
HTTP transport & authentication (--http)¶
| Variable | Purpose | Default |
|---|---|---|
ASPNETCORE_URLS |
Bind address(es). A non-loopback value (e.g. http://0.0.0.0:8080) triggers the fail-closed auth gate below. |
http://127.0.0.1:8080 (loopback) |
SANKSHEP_API_KEYS |
Comma-separated bearer keys (API-key auth). Clients send Authorization: Bearer <key>; keys are compared in constant time. SANKSHEP_API_KEY is a single-key alias. |
unset (auth mode None) |
SANKSHEP_OAUTH_AUTHORITY · SANKSHEP_OAUTH_AUDIENCE |
OAuth 2.1 resource-server mode (validate bearer tokens from your IdP). Both are required to select it; setting one alone does nothing. OAuth wins over API-key mode when both are configured. | unset |
SANKSHEP_OAUTH_SCOPES |
Scopes a caller's token must carry. Defaults to mcp:tools — leaving it unset is not the same as requiring nothing: a token without that scope is still rejected. |
mcp:tools |
SANKSHEP_OAUTH_RESOURCE |
The protected-resource URI. Required unless _AUDIENCE is an absolute URI — otherwise the server throws at startup rather than serving unprotected. |
derived from _AUDIENCE |
SANKSHEP_ALLOW_UNAUTHENTICATED |
1 explicitly permits an unauthenticated non-loopback bind. Without it — and without auth configured — a non-loopback bind refuses to start. Use only on a trusted, network-isolated deployment. |
off (server fails closed) |
SANKSHEP_ALLOWED_HOSTS |
Comma-separated Host-header allow-list, enforced in the pipeline so health probes stay reachable whatever it says. Entries are compared whole and a port is ignored. |
loopback names on a loopback bind; empty on any other bind, which accepts no Host |
SANKSHEP_ALLOWED_ORIGINS |
Comma-separated Origin allow-list for browser callers. Empty accepts no cross-origin request; a request with no Origin at all is unaffected. |
empty |
Fail-closed bind
On a non-loopback bind with auth mode None, the server refuses to start unless
SANKSHEP_ALLOW_UNAUTHENTICATED=1. Configure SANKSHEP_API_KEYS (or SANKSHEP_OAUTH_*) instead
whenever you can. SANKSHEP_ALLOWED_HOSTS is additional hardening — it does not satisfy the auth gate.
Telemetry (opt-in)¶
| Variable | Purpose | Default |
|---|---|---|
SANKSHEP_OTLP_ENDPOINT |
OTLP endpoint to push metrics to (a collector you run). Counts + a low-cardinality repo-folder tag only — never code, paths, or queries. | unset (no outbound) |
SANKSHEP_PROMETHEUS |
1 serves a /metrics scrape endpoint (inbound/pull, for your Prometheus). |
off |
SANKSHEP_FLEET_TEAM · SANKSHEP_FLEET_INSTANCE |
Optional labels (sankshep.team, service.instance.id) added to exported metrics. |
unset |
Logging¶
Everything Sankshep logs goes to stderr, on both transports. Under stdio that is required — stdout carries the JSON-RPC frames — and the HTTP host uses the same stream so the rule is a property of the process, not of one transport. Docker, Kubernetes, systemd and journald all capture both streams.
These are ASP.NET Core's own variables rather than Sankshep's, so they are not in the tables above, but they are the ones operators reach for.
| Variable | What it does |
|---|---|
Logging__LogLevel__Default |
Sankshep's own log level. Trace is safe — see the cap below. |
Logging__LogLevel__<category> |
One category, e.g. Logging__LogLevel__Sankshep=Debug. |
Logging__<provider>__LogLevel__<category> |
Scoped to one provider, e.g. Logging__Console__LogLevel__Default. |
Logging__Console__FormatterName |
simple (default, human-readable) or json, one object per line for a log shipper. |
Diagnostics are categorised by the type that emits them, so one noisy source can be quietened without silencing the rest:
| Category | What it says |
|---|---|
Sankshep.Minimizer.ContextMinimizer |
Paths skipped: too large, or linking outside the served root. |
Sankshep.Memory.SqliteVectorIndex |
Files skipped during indexing, and why. |
Sankshep.Memory.SavingsStatsStore |
token_report statistics degraded or reset. Warning. |
Sankshep.Server.Hosting.ReadinessWarmupService |
Why readiness is holding at 503. Error. |
Microsoft.AspNetCore |
The framework's per-request lines. Held at Warning by default. |
ModelContextProtocol |
The MCP SDK. Capped at Information, and not raisable. |
# Structured output for a log shipper.
Logging__Console__FormatterName=json
# Everything Sankshep says about what it skipped and why.
Logging__LogLevel__Sankshep=Debug
# Request logging back on while debugging routing or an ingress.
Logging__LogLevel__Microsoft.AspNetCore=Information
Raising the log level cannot make the server print your code — by design
The MCP SDK logs whole JSON-RPC payloads at Trace and Debug: every tool argument and result,
which for this product means source code, search queries and remembered facts. Before 2.0.0,
Logging__LogLevel__Default=Trace — the first thing anyone tries when an MCP server misbehaves —
echoed all of it to stderr under stdio, and to the log aggregator under HTTP.
Every ModelContextProtocol.* category is now held at Information in both hosts, against every
spelling. That matters because the rules compose: a rule naming a provider beats one that does not,
and a longer category beats a shorter one, so Logging__Console__LogLevel__Default=Trace and
Logging__LogLevel__ModelContextProtocol.Protocol=Trace would each re-open it if handled naively.
Both are handled specifically.
There is deliberately no variable that lifts the cap. Sankshep's own Trace logging is
unaffected, which is the point of capping the SDK rather than raising the floor for the whole process.
ASP.NET Core request logging is quiet by default. The framework emits about five Information lines
per request, and liveness and readiness probes run every ten to fifteen seconds for the life of a pod, so
an idle server's entire log was health checks. Microsoft.AspNetCore is held at Warning unless you set a
level for that category yourself; a failing request still speaks up. This is a default, not a cap — request
lines carry a method, a path and a status code, never a payload.
Under a service manager. The Windows Service host routes logs to the Event Log, whose provider starts
at Warning, so degradations are logged at Warning or above to be sure they arrive. Under systemd,
stderr is journald: journalctl -u sankshep.
TLS¶
| Variable | What it does |
|---|---|
Kestrel__Endpoints__Https__Url |
Serve TLS directly instead of behind a terminating proxy. |
Kestrel__Certificates__Default__Path · __Password |
The certificate to serve it with. |
ASPNETCORE_URLS is only one of the routes to a bind — Kestrel__Endpoints__<name>__Url reaches
Kestrel too and wins over it — which is why the startup guard reads the resolved endpoint set rather than
that variable. Sankshep warns at startup when a credential mode is configured on a routable plain HTTP
address, because the bearer token would then cross the network in the clear. It is a warning, not a
refusal: terminating TLS in front of Sankshep is a correct deployment it cannot see from the inside.
Development / eval harness¶
These are read by the benchmark eval harness (Sankshep.Evals), not the MCP server: SANKSHEP_SERVER_EXE
(path to the server binary under test) and the judge credentials ANTHROPIC_API_KEY / OPENAI_API_KEY
(plus optional *_BASE_URL / *_MODEL). See Benchmarks.
SANKSHEP_REQUIRE_MODEL and SANKSHEP_REQUIRE_VEC0 are test-suite knobs: they make a test run fail
instead of skip when the embedding model or the sqlite-vec native is absent. Nothing in the product reads
them.