docs, hub, installer: say Server + Desktop makes the home IP public

Server + Desktop publishes services under the operator's own domain, and
the DNS record for that domain points at the home connection, so anyone
can look up the home IP address. None of the places that offer Server +
Desktop said so.

- README: new section "Server + Desktop and your home IP address" (what
  becomes public, what does not, the alternatives, and what happens
  technically), plus a note on the role table and in the security
  overview.
- SECURITY.md: a matching section, the consequence noted next to "Public
  web services exposed only when enabled by the operator", and the
  supported versions row no longer pins 1.0.x.
- ISO installer: the Server + Desktop role card ends with the warning.
- Hub: the domain setup text (onboarding, feature setup and domain
  reconfiguration share renderDomainNeedsHtml) and the upgrade dialog
  carry the same notice.
- Add tests/test_exposure_guards.py. It fails if one of these places
  loses the notice or the README anchor stops resolving.
This commit is contained in:
Arena.ai Agent
2026-10-01 21:43:47 -05:00
committed by naturallaw777
parent 2d777450e1
commit 34cfba4282
6 changed files with 162 additions and 5 deletions
+64 -2
View File
@@ -202,7 +202,7 @@ Bitcoin and self-hosting infrastructure runs on the machine.
|---|---|---| |---|---|---|
| **Desktop** | Everyday users and computers with modest hardware | Sparrow, Bisq, and Bisq 2 for self-custody and peer-to-peer Bitcoin use | | **Desktop** | Everyday users and computers with modest hardware | Sparrow, Bisq, and Bisq 2 for self-custody and peer-to-peer Bitcoin use |
| **Node** | People ready to verify and operate their own Bitcoin infrastructure | Everything in Desktop, plus the full Bitcoin stack: Bitcoin Core, Electrs, LND, Ride The Lightning, BTCPay Server, and wallet-to-node connections | | **Node** | People ready to verify and operate their own Bitcoin infrastructure | Everything in Desktop, plus the full Bitcoin stack: Bitcoin Core, Electrs, LND, Ride The Lightning, BTCPay Server, and wallet-to-node connections |
| **Server + Desktop** | Bitcoiners who want the same sovereignty over their communications, cloud, passwords, and web services | The complete Node stack, plus the private self-hosted services | | **Server + Desktop** | Bitcoiners who want the same sovereignty over their communications, cloud, passwords, and web services | The complete Node stack, plus the private self-hosted services. **Makes your home IP address public:** [read this first](#server--desktop-and-your-home-ip-address) |
**Desktop: start with your keys.** Desktop is not a reduced or Bitcoin-free **Desktop: start with your keys.** Desktop is not a reduced or Bitcoin-free
edition. It is a complete, private everyday computer with a clean GNOME edition. It is a complete, private everyday computer with a clean GNOME
@@ -237,6 +237,66 @@ communications, identity, and services.
> provider allows port forwarding. Most home routers and providers already > provider allows port forwarding. Most home routers and providers already
> support this. If you are unsure, a quick search for your router model and > support this. If you are unsure, a quick search for your router model and
> "port forwarding" will usually turn up a step-by-step guide. > "port forwarding" will usually turn up a step-by-step guide.
>
> **This mode also makes your home IP address public.** Read
> [what that means](#server--desktop-and-your-home-ip-address) before you
> choose it.
### Server + Desktop and your home IP address
> **⚠️ Server + Desktop makes your home IP address public.**
> Public services need a domain name that points at your home internet
> connection. When you finish the guided domain setup, Sovran_SystemsOS puts
> your home's public IP address in a Dynamic DNS record at
> [Njal.la](https://njal.la) and keeps it up to date, and you forward ports 80
> and 443 on your router to this computer. From then on:
>
> - **Anyone can look up your domain and see your home IP address.** An IP
> address typically reveals your internet provider and your approximate
> location, and it ties everything you publish on that domain to your home
> connection.
> - **Domain privacy does not hide it.** Registrar privacy protects the
> registrant's identity, not the IP address in your DNS records.
> - **Your connection is open to the whole internet on those ports.** Scanners
> and bots constantly probe public IP addresses, so expect automated probing
> and login attempts against every service you publish.
> - **Your service names are discoverable.** Public HTTPS certificates are
> listed in public Certificate Transparency logs, so hostnames such as
> `vault.yourdomain.com` can be found, and then resolved to your IP address,
> even if you never share them.
Nothing is published until you finish domain setup and port forwarding, but that
setup is the point of this mode, so assume your IP address will be public.
**Desktop** publishes nothing. **Node** publishes nothing unless you turn on a
feature that needs a domain: *Put BTCPay Server Online* or *Lightning Wallet
Connections*.
**If you do not want your home IP address to be public,** choose Desktop or
Node. Advanced users can put a VPS, reverse proxy, or tunnel in front of their
services so DNS points there instead of at their home. Sovran_SystemsOS does not
set this up for you, and the Hub's domain checks currently expect DNS to point
at your home IP address.
<details>
<summary><strong>What happens technically</strong></summary>
- You create a **Dynamic** DNS record at Njal.la and paste its update command
into the Hub. The Hub only accepts `njal.la` update URLs.
- The `sovran-ddns-update` timer asks Njal.la to point your record at the
address the request came from. It does this right after you save a domain,
two minutes after boot, and then every 15 minutes.
- Njal.la reports that address back, and Sovran_SystemsOS keeps it for Element
calling and the Hub. Nothing else looks up your public IP address: no STUN
server, public DNS resolver, or "what is my IP" service is involved. See
`modules/core/njalla.nix`.
- Once a service that needs a domain is turned on, the firewall opens TCP and
UDP ports 80 and 443 for Caddy, which requests public HTTPS certificates for
the domains you configure. See `modules/core/caddy.nix`.
- Optional features can need more ports. Element calling, for example, needs
TCP 7881 and UDP 3478, 7882, and 40000–40099. The Hub lists the ports each
feature needs.
</details>
--- ---
@@ -730,7 +790,9 @@ Sovran_SystemsOS uses layered controls:
- Separate service users, systemd sandboxing, and loopback bindings where practical - Separate service users, systemd sandboxing, and loopback bindings where practical
- Tor enforcement for supported Bitcoin services - Tor enforcement for supported Bitcoin services
- Restricted, time-limited support access with scoped `sudo` - Restricted, time-limited support access with scoped `sudo`
- Operator-controlled public service exposure - Operator-controlled public service exposure (Server + Desktop
[makes your home IP address public](#server--desktop-and-your-home-ip-address)
once you set up a domain)
See [`SECURITY.md`](SECURITY.md) for the threat model, limitations, reporting, See [`SECURITY.md`](SECURITY.md) for the threat model, limitations, reporting,
and operator guidance. No operating system can protect funds after recovery and operator guidance. No operating system can protect funds after recovery
+20 -2
View File
@@ -4,7 +4,7 @@
| Release | Supported | | Release | Supported |
|---|:---:| |---|:---:|
| Latest `1.0.x` stable release | Yes | | Latest stable release | Yes |
| `main` / `staging-dev` | Development only | | `main` / `staging-dev` | Development only |
| Older than `1.0.0` | No | | Older than `1.0.0` | No |
@@ -35,6 +35,23 @@ external networks and are outside a “fully offline” model.
The local Hub currently uses HTTP. Authentication does not encrypt local network The local Hub currently uses HTTP. Authentication does not encrypt local network
traffic, so use a trusted LAN and avoid public or guest Wi-Fi. traffic, so use a trusted LAN and avoid public or guest Wi-Fi.
### Public services and your home IP address
Server + Desktop publishes services under your own domain. The Dynamic DNS
record at Njal.la then points at your home's public IP address, which anyone
can look up (domain privacy does not hide it), and ports 80 and 443 are open
to the whole internet. Public HTTPS certificates also list your service
hostnames in Certificate Transparency logs. Desktop publishes nothing. Node
publishes nothing unless *Put BTCPay Server Online* or *Lightning Wallet
Connections* is on. See
[Server + Desktop and your home IP address](README.md#server--desktop-and-your-home-ip-address)
for what this means and the alternatives.
Sovran_SystemsOS does not ask a STUN server, public DNS resolver, or “what is
my IP” service for your address. The DDNS update asks Njal.la to use the
address the request came from, and the address Njal.la reports back is the one
Element calling and the Hub use.
### Bitcoin stack ### Bitcoin stack
Bitcoin and Lightning modules are maintained in the standalone Bitcoin and Lightning modules are maintained in the standalone
@@ -64,7 +81,8 @@ change both the system and local configuration.
- Separate service users and systemd sandboxing where supported - Separate service users and systemd sandboxing where supported
- Administrative service ports bound to loopback where practical - Administrative service ports bound to loopback where practical
- Tor enforced for supported Bitcoin traffic and onion services - Tor enforced for supported Bitcoin traffic and onion services
- Public web services exposed only when enabled by the operator - Public web services exposed only when enabled by the operator (this makes
your home IP address public)
Tor reduces network exposure for configured Bitcoin services. It is not a Tor reduces network exposure for configured Bitcoin services. It is not a
guarantee against every IP leak, application bug, or traffic-analysis attack. guarantee against every IP leak, application bug, or traffic-analysis attack.
@@ -43,6 +43,11 @@ function renderDomainNeedsHtml(opts) {
+ '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>' + '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>'
+ " and connecting your services. Just follow the steps below.</p>"; + " and connecting your services. Just follow the steps below.</p>";
} }
// Every variant above ends with a domain that points at the user's home
// connection. Say what that publishes (README: "Server + Desktop and your
// home IP address").
html += "<p>⚠️ <strong>Heads-up:</strong> your domain points at your home internet connection, "
+ "so anyone can look up your home IP address. Domain privacy does not hide it.</p>";
return html; return html;
} }
@@ -476,6 +476,10 @@
<li>To make your services available outside your home, complete one router task: forward ports <strong>80 and 443</strong> to this computer</li> <li>To make your services available outside your home, complete one router task: forward ports <strong>80 and 443</strong> to this computer</li>
</ul> </ul>
</div> </div>
<p class="support-desc">
⚠️ <strong>Heads-up:</strong> your domain points at your home internet connection,
so anyone can look up your home IP address. Domain privacy does not hide it.
</p>
<p class="support-desc"> <p class="support-desc">
The Hub guides you through every step. The Hub guides you through every step.
</p> </p>
+1 -1
View File
@@ -471,7 +471,7 @@ class InstallerWindow(Adw.ApplicationWindow):
# Role cards # Role cards
roles = [ roles = [
("Server + Desktop", ("Server + Desktop",
"Full sovereignty: host your own websites, cloud, chat, passwords, and Bitcoin services instead of relying on Big Tech. Sovran_SystemsOS walks you through getting your domain from Njal.la and connecting everything. One router task is required: forward ports 80 and 443 to this computer.", "Full sovereignty: host your own websites, cloud, chat, passwords, and Bitcoin services instead of relying on Big Tech. Sovran_SystemsOS walks you through getting your domain from Njal.la and connecting everything. One router task is required: forward ports 80 and 443 to this computer. Heads-up: this makes your home IP address public.",
"Server+Desktop"), "Server+Desktop"),
("Desktop Only", ("Desktop Only",
"A beautiful, easy-to-use desktop without the background server applications.", "A beautiful, easy-to-use desktop without the background server applications.",
+68
View File
@@ -0,0 +1,68 @@
"""Guards for the home-IP warnings.
Server + Desktop publishes the home IP address (the domain points at it), so every
place that offers Server + Desktop must say so, and the README section they point
at must exist.
These read the shipped source files like the nix-file checks in test_security.py:
nothing is run and nothing touches the network.
"""
import os
import re
import unittest
_ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))
_PHRASE = "home ip address"
def _read(*parts):
with open(os.path.join(_ROOT, *parts), encoding="utf-8") as f:
return f.read()
def _github_slug(heading):
slug = re.sub(r"[^\w\- ]", "", heading.strip().lower())
return slug.replace(" ", "-")
class HomeIpWarnings(unittest.TestCase):
def test_installer_role_card(self):
src = _read("iso", "installer.py")
card = re.search(r'\("Server \+ Desktop",\s*"((?:[^"\\]|\\.)*)"', src, re.S)
self.assertIsNotNone(card, "Server + Desktop role card not found")
self.assertIn(_PHRASE, card.group(1).lower())
def test_hub_domain_setup_text(self):
# domain-prereqs.js is the single source for onboarding, feature setup
# and domain reconfiguration; the notice must follow every variant.
js = _read("app", "sovran_systemsos_web", "static", "js", "domain-prereqs.js")
body = js[js.index("function renderDomainNeedsHtml"):]
body = body[:body.index("\n}\n")]
self.assertIn(_PHRASE, body.lower())
self.assertGreater(body.lower().index(_PHRASE), body.rindex("} else {"),
"the notice must come after the last variant, not inside one")
def test_hub_upgrade_dialog(self):
html = _read("app", "sovran_systemsos_web", "templates", "index.html")
dialog = html[html.index('id="upgrade-modal"'):html.index("Security Reset overlay")]
self.assertIn(_PHRASE, " ".join(dialog.lower().split()))
def test_readme_and_security_policy(self):
self.assertIn(_PHRASE, _read("README.md").lower())
self.assertIn(_PHRASE, " ".join(_read("SECURITY.md").lower().split()))
def test_links_to_the_readme_section_resolve(self):
readme = _read("README.md")
slugs = {_github_slug(m.group(2))
for m in re.finditer(r"^(#{1,6})\s+(.+?)\s*$", readme, re.M)}
links = re.findall(r"\]\(#(server--desktop[^)]*)\)", readme)
links += re.findall(r"README\.md#(server--desktop[^)\s]*)", _read("SECURITY.md"))
self.assertTrue(links, "expected links to the home-IP section")
for anchor in links:
self.assertIn(anchor, slugs)
if __name__ == "__main__":
unittest.main()