local_mode
local_mode
¶
Local-only mode is a contract — the single idiom for checking it.
Every outbound path must ask the same question, the same way, before it touches an API key or produces the data it is about to send. When the question is re-asked ad hoc at each call site, a path is always forgotten:
speech/_discovery.pyswallowed a local Whisper failure and quietly continued down the list to OpenAI and Deepgram.speech/llm_polish.pyresolved an engine without ever asking whether it was local.tools/screen_vision_tools.pywrote the screenshot to a temp file first and only then decided whether it was allowed to send it.
So the rule stops being copied:
from diapason.core.local_mode import local_only
if local_only(config):
# refuse, BEFORE reading a key or capturing anything
Order matters as much as the test. Nothing outbound may be prepared before this branch: in local-only mode a path must touch no credential and create no artifact. That is externally verifiable, which is what makes it a contract rather than an intention.
The contract's boundary — what is NOT an outbound path
local_only governs what DIAPASON sends: its API calls, its uploads, its
background fetches. Handing content to the user's own visible applications at
the user's explicit command sits OUTSIDE that boundary, by the same reasoning
that already lets dictation paste the user's words into Gmail-in-Chrome under
local-only. Opening « ouvre youtube et cherche X » in the USER'S browser is
the user browsing, with Diapason as the hand on the keyboard — the disclosure
IS the request. Blocking it would not protect the user from Diapason; it
would protect the user from themselves, which is not this contract's job.
Concretely exempt: tools/desktop_tools.open_in_browser and the
single-purpose fetch that completes a user-commanded browse of the same
destination (desktop/smart_intents.resolve_youtube_watch_url). Anything
the assistant initiates on its own, or that sends data anywhere the user did
not name out loud, stays guarded.
The user's own paired devices
A second boundary, decided deliberately: a device the user has PAIRED — cryptographically, by carrying a one-time code from one machine to the other — is no longer "elsewhere". It is the same person's other computer, holding the same fleet identity, and reaching it is the point of owning both.
This is narrower than it sounds, and the narrowness is what makes it safe:
- it applies only to devices in the mesh registry with trust TRUSTED — a revoked or unknown address gets no exemption at all;
- the address must be private (loopback or RFC1918): the LAN the user is standing on, never the open internet, which is why the mesh ships LAN-first;
- it covers mesh traffic only. The Succès sync relay stays fully gated
(
succes/relay.py), because a relay is a third party by construction.
The check lives in mesh/transport.py — assert_may_reach_device — so
this exemption cannot be claimed by any other code path merely by importing
something. Everything else about the contract is unchanged: turning
local_only on still means Diapason itself sends nothing outward.
Classes¶
LocalOnlyError
¶
Bases: RuntimeError
Raised when a remote call is refused because local-only mode is on.
Carries nothing_left_the_machine so callers can tell this apart from a
generic outage and say so — the distinction is precisely what the user
asked for by turning the mode on.
Functions:¶
host_is_local
¶
True when a URL or host designates this machine.
Reaching loopback is not leaving, so http://localhost:11434 (Ollama),
the internal FastAPI server and the Apple Foundation Models shim stay
available in local-only mode — they are the whole point of it.
A private-network address is NOT local here. 192.168.1.50 is someone
else's computer; sending a dictation there is still sending it away, and
the user did not consent to that by asking for local-only.
Anything unparseable is treated as remote: a host we cannot read is not a host we can vouch for.
Source code in src/diapason/core/local_mode.py
assert_may_leave
¶
assert_may_leave(
what: str,
*,
destination: str = "",
config: Optional["DiapasonConfig"] = None,
) -> None
Raise :class:LocalOnlyError when what may not leave this machine.
The single idiom every outbound path calls before reading a credential or producing the payload. Two things make it a contract rather than a style:
- it raises rather than returning a boolean, so a caller cannot forget to branch on the answer;
destinationshort-circuits it for loopback, so guarding a path costs nothing when that path was never leaving in the first place.
what is a short human description used in the message — "the dictated
text", "the screenshot". It must never contain the data itself.
Source code in src/diapason/core/local_mode.py
local_only
¶
Return True when nothing may leave this machine.
config is optional so call sites deep in a path need not thread it
through; when omitted the active configuration is loaded.
Fail-closed on any error. Getting this wrong in one direction costs a feature until the configuration is readable again; getting it wrong in the other sends the user's voice, screen or text to a third party. The costs are not symmetric, so the default must not be either.
Source code in src/diapason/core/local_mode.py
engine_is_local
¶
Best-effort verdict on whether an inference engine runs on this machine.
Trusts the explicit is_cloud flag of the engine protocol first, then
falls back to the engine id. An engine that answers neither is treated as
remote: an unknown backend is not a local one.