Network Egress & Offline Mode¶
Jarvis is local-first. A Jarvis Automation server with its default settings makes no connections to the internet on its own: voice commands, transcription, speech, memory and device control all run on your own hardware, and the server talks only to your own nodes, phones and devices.
Everything that reaches the internet is either something you start (downloading a model, running jarvisd upgrade) or a setting that defaults to off. One client setting is on by default: the desktop agent updates itself from GitHub releases (see On nodes and phones).
Default posture
A fresh install with default settings is effectively offline once its models are downloaded. To reach the internet, you turn on a specific capability below.
Local vs. external¶
| Treated as local (always allowed) | Treated as external (you opt in) |
|---|---|
| Nodes, phones and desktop agents on your network talking to your server (ports) | Model and runtime downloads (huggingface.co, GitHub releases) |
| mDNS announcements on your local network | Update checks (api.github.com) |
| LAN device families (Hue, Kasa, LIFX, Z-Wave, HomeKit) | Web search and the reader proxy (r.jina.ai) |
| The model runtimes the server starts on the same machine | Push providers (Expo → Apple APNs / Firebase FCM) |
| The Pantry package store, vendor clouds, cloud model providers |
What can reach the internet¶
Settings that are off by default¶
Change these in the admin web app (Settings), or household settings in the mobile app as a household admin.
| Capability | Reaches | Setting (scope) |
|---|---|---|
| Server update checks | api.github.com, GitHub releases |
updates.enabled (server; or JARVIS_ALLOW_UPDATES in jarvisd.env) |
| Node update checks | api.github.com |
updates.allow_check (server) |
| Web search (quick answers, deep research) | DuckDuckGo and the pages it finds | web_search.enabled (household) |
| External reader proxy (deep-research fallback) | r.jina.ai |
web_scraping.allow_external (household) |
| Pantry package store | pantry-api.jarvisautomation.io (pantry.base_url) |
pantry.enabled (household) |
| Push delivery | your relay (such as relay.jarvisautomation.io) → Expo |
relay.url (server; or RELAY_URL); empty means off |
With updates.enabled off, the server makes no update request at all. With relay.url empty, push notifications are not sent; the inbox still works. With pantry.enabled off, package installs are refused.
Things you start¶
| Action | Reaches |
|---|---|
| Installing a model in the setup wizard or on the Models page | huggingface.co for the model; GitHub releases for the runtime that runs it (llama.cpp, whisper.cpp) |
| Installing a Hugging Face directory model (MLX, vLLM or Transformers) | Also GitHub releases for uv and a Python build, and PyPI (pypi.org, files.pythonhosted.org) for the engine's packages |
jarvisd upgrade |
api.github.com and the release's files on GitHub |
Adding a cloud model provider (an OpenAI-compatible endpoint or api.anthropic.com) |
That provider, with the conversations sent to it |
| Setting up phone calls (Twilio) | api.twilio.com |
| Installing a command package or a vendor-cloud device family | Whatever that integration needs |
Runtimes are checked against checksums built into jarvisd before they run. The download addresses are settings too: point llm.hf_endpoint (env HF_ENDPOINT), llm.engine_base_url (llama.cpp) and stt.engine_base_url (whisper.cpp) at a mirror on your network to install models without the internet. uv and the Python engines have no mirror setting: they always download from their pinned defaults.
On nodes and phones¶
| Capability | Reaches | Setting | Default |
|---|---|---|---|
| Desktop agent self-update (also the experimental Go Pi node, which is the same program) | api.github.com, GitHub releases |
auto_update in the agent's agent.json, or --no-auto-update |
On |
| Python node self-update | GitHub releases | allow_updates in the node's config.json (or JARVIS_ALLOW_UPDATES) |
Off |
| Wake-word model download (Python node) | GitHub (openWakeWord) | wake_word_model_autodownload_enabled in the node's config.json |
Off |
| Mobile push notifications | Expo (exp.host) → APNs/FCM |
Mobile app → Settings | Off |
The desktop agent checks the server's version daily and, when it differs, downloads that release from GitHub and installs it. It also accepts an update sent to it from the server. See Updates and settings.
Fully offline checklist¶
- Install the server and download your models while connected, or from a mirror on your network.
- Leave every server and Python-node setting in the tables above off (the default).
- Run desktop agents with
--no-auto-update, or set"auto_update": falseinagent.json: their self-update is on by default. Update them by hand instead. - Leave
relay.urlempty and mobile push off: no push payloads leave the phone or the server. - Don't add a cloud model provider.
- Upgrade by hand: download a release on another machine and run
jarvisd upgradewithJARVISD_RELEASE_BASEpointing at the folder (CLI). - Remember that a command package or a vendor-cloud device family (Nest, Ecobee, Govee cloud) adds outbound traffic for that feature by design. Installing it is the opt-in.
Verifying
On the server, list open connections with sudo lsof -i -nP | grep ESTABLISHED (Linux, macOS) or Get-NetTCPConnection -State Established (Windows), or check your router's logs. With everything above off, you should see only connections inside your network, not to github.com, huggingface.co, exp.host or r.jina.ai.
Command packages & device families¶
- Command packages (
jarvis-cmd-weather,-news,-spotify, ...) are external integrations by nature. Installing one from the Pantry is your consent for the traffic it needs; the package's code runs on your node, not in the cloud. - LAN device families (Hue, Kasa, LIFX, Z-Wave, HomeKit) stay entirely on your network.
- Vendor-cloud device families (Nest/Google, Govee cloud, Resideo, SimpliSafe) need the vendor's cloud to work. Adding one is the opt-in.
Enabling updates¶
Updates are off by default so a local-only server never phones home.
Turn on update checks on the admin web app's Updates page (the updates.enabled setting), or set JARVIS_ALLOW_UPDATES=1 in jarvisd.env and restart. The server then checks GitHub for new releases and can install one from the admin web app.
jarvisd upgrade on the command line works either way: running it is the opt-in. See Upgrade.
A node update needs two switches:
- On the server:
updates.allow_check, which lets the server look up the newest node release onapi.github.com. -
On the node: allow it to download and apply a release:
or set
JARVIS_ALLOW_UPDATES=truein the node's environment.
You start the update from the mobile app. Installing an explicit version skips the GitHub lookup, but the node still needs allow_updates to fetch the release.
Updates require internet
Any update path connects to GitHub. Leave them off for an air-gapped install and upgrade by hand instead.
See also: Security model: what leaves your network for every outbound connection and its default.