# Egress modes — what each one does > ⚠️ **Experimental.** The egress boundary is new and ships as > defense-in-depth, not an absolute guarantee. It blocks the known data-egress > paths under the scope you pick, but this is an early version — don't rely on > it as your *only* protection for highly sensitive data yet. See the threat > model and limitations linked just below. LDR's **egress scope** (Settings → *Egress Scope*, or the *Privacy & Egress* panel on the research form) controls **where your research traffic is allowed to go** — which search engines run, whether your LLM/embeddings may be cloud services, and which URLs may be fetched. It's the single switch for *"how much of this run is allowed to leave my machine?"* Pick a mode below. The default is **Adaptive**, which just follows your primary search engine, so most people never need to think about it. > This page explains the modes for everyday use. For the threat model, > guarantees, and limitations, see > [`SECURITY.md`](../SECURITY.md#egress-policy-module) and the technical > [egress package README](../src/local_deep_research/security/egress/README.md). ## At a glance | Mode | Search engines | LLM / embeddings | Best for | |---|---|---|---| | **Adaptive** *(default)* | follows your primary engine | forced local **only** when the run is private | "just do the sensible thing" | | **Unprotected** | any engine (no restriction) | any provider (cloud allowed) | escape hatch — **not recommended**; disables egress protection (hard SSRF / cloud-metadata blocking still applies) | | **Public only** | public web/academic engines only | your configured providers | public research; your local collections aren't touched | | **Private only** | local engines only (collections, local SearXNG/Ollama) | **forced local** — cloud blocked | sensitive work that must stay on the machine | | **Strict** | **only** your one primary engine | your configured providers | a single, exact source with zero expansion | --- ## 🔀 Adaptive *(default)* **Adaptive follows your primary search engine** and resolves to a concrete mode for each run: - Primary is a **public** engine (e.g. SearXNG pointed at a public instance, arXiv, PubMed) → behaves like **Public only**. - Primary is a **private** source (a local collection, your library) → behaves like **Private only** (and therefore forces local LLM + embeddings). Why it's the default: you choose a search engine anyway, and the privacy posture "just matches" it. If you make a **private collection** your primary, the whole run automatically stays local — nothing leaves the box. > **Note:** to use a cloud LLM on a private collection, mark the collection > **public** (see below) or add the provider to *trusted inference providers*; > to drop all restrictions for one run, use **Unprotected**. Adaptive > deliberately narrows to match the primary. ## 🔓 Unprotected **Not recommended — an escape hatch.** Egress-scope restrictions are disabled for the run: any engine, URL, and LLM/embeddings provider is permitted. The hard SSRF and cloud-metadata blocks still apply. A loud, non-dismissible banner shows while it is active. Prefer marking a collection **public**, or adding a **trusted destination** (`policy.trusted_inference_providers` / `policy.trusted_search_engines`) for the specific case, over disabling protection wholesale. > The older **Both** scope (blanket-permit any classified engine) has been > **retired**: existing saved `both` configurations are migrated to **Adaptive** > (which follows your primary engine and forces local inference for a private > primary). If you relied on `both` to use a private collection with a cloud > model, mark that collection **public**, or choose **Unprotected** to opt out > of protection explicitly. ## ☁️ Public only Only **public** web/academic engines run; your **local collections are excluded**. URL fetches are allowed to public hosts and blocked for private ones. Inference is whatever you configured (cloud allowed). Use it when you want public research and don't want your private documents queried at all. ## 🔒 Private only The privacy mode. **Only local engines** run (collections, library, a local SearXNG/Ollama). Crucially, it **forces local LLM and embeddings** — cloud providers (OpenAI, Anthropic, Google, OpenRouter, …) are blocked, so your query and your retrieved documents never reach a cloud model. Public URL fetches are blocked, and a process-wide socket guard blocks stray outbound connections. **Nothing leaves the machine.** > If you have no local LLM configured, a Private-only run will refuse rather > than silently fall back to the cloud — that's intentional (fail-closed). ## 🎯 Strict The tightest mode: **only your single primary engine** runs — no expansion to any other engine at all. At the URL layer it behaves like Private-only (private hosts allowed, public blocked), but it does **not** force local inference — set the *Require local* toggles if you also want local LLM/embeddings. --- ## Per-collection public/private Each RAG collection has a **public/private** flag (default **private**): - A **private** collection is excluded under *Public only* / *Adaptive-public*, and when used it forces local inference — its chunks never reach a cloud model. - Mark a collection **public** (the *Public collection* checkbox when creating it) only if its contents are non-sensitive and you're happy to process them with cloud inference / use them under public scope. ## The two local-inference toggles Independent of the scope, you can force local inference any time: - **Require local LLM endpoint** — refuse cloud LLM providers / non-local URLs. - **Require local embeddings** — refuse cloud embedders, and refuse a HuggingFace download for an uncached local model. Both are **implied automatically** under *Private only* (and Adaptive-private), which is why those toggles auto-check and lock when you select Private only. --- ## Per-research overrides The three primary controls — **Egress Scope**, **Require local LLM endpoint**, and **Require local embeddings** — also appear on the research-form page as per-run dropdown / checkbox overrides. Values set there apply **only to that research run** and do **not** persist to the settings database, so you can do a one-off private run without changing your defaults. ## Audit log Changes to any `policy.*` key, `llm.require_local_endpoint`, `llm.allowed_local_hostnames`, or `embeddings.require_local` emit a `policy_audit=True` log line so administrators can trace configuration changes. Those audit lines are deliberately filtered out of the WebSocket progress stream (they never reach browser subscribers). --- ### See also - [Configuration reference](CONFIGURATION.md#settings-list) — the exact setting keys (`policy.egress_scope`, `llm.require_local_endpoint`, `embeddings.require_local`, `llm.allowed_local_hostnames`) and their auto-generated `LDR_*` environment variables. - [`SECURITY.md`](../SECURITY.md#egress-policy-module) — threat model, guarantees, and caveats (including what this does **not** defend against).