From 2a3324c201e90b8222ab11319b47195b37888cbe Mon Sep 17 00:00:00 2001 From: Nathan Esquenazi Date: Sun, 12 Apr 2026 16:35:47 -0700 Subject: [PATCH] fix: allow onboarding from Docker bridge networks (closes #334) (#335) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expands the onboarding setup IP check from 127.0.0.1-only to any loopback or RFC-1918 private address. Docker containers connect via 172.17.x.x — previously blocked with a 403. Public IPs still blocked unless auth enabled. 791 tests pass. --- CHANGELOG.md | 4 ++++ api/routes.py | 15 ++++++++++++--- static/index.html | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc6768..c58107f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ --- +## [v0.50.9] Onboarding works from Docker bridge networks (PR #335, fixes #334) + +- **Docker users can now complete onboarding without enabling auth first** (closes #334): The onboarding setup endpoint previously only accepted requests from `127.0.0.1`. Docker containers connect via bridge network IPs (`172.17.x.x`, etc.), so the endpoint returned a 403 mid-wizard with no clear explanation. The check now accepts any loopback or RFC-1918 private address (`127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) using Python's `ipaddress.is_loopback` and `is_private`. Public IPs are still blocked unless auth is enabled. + ## [v0.50.8] Model dropdown deduplication — hyphen vs dot separator fix (PR #332) - **Model dropdown no longer shows duplicates for hyphen-format configs** (e.g. `claude-sonnet-4-6` from hermes-agent config): The server-side normalization in `api/config.py` now unifies hyphens and dots when checking whether the default model is already in the dropdown. Previously, `claude-sonnet-4-6` (hermes-agent format) and `claude-sonnet-4.6` (WebUI list format) were treated as different models, causing the same model to appear twice — once as a raw unlabelled entry and once with the correct display name. The raw entry is now suppressed and the labelled one is selected as default. diff --git a/api/routes.py b/api/routes.py index b0bfc38..b328e93 100644 --- a/api/routes.py +++ b/api/routes.py @@ -827,10 +827,19 @@ def handle_post(handler, parsed) -> bool: return j(handler, saved) if parsed.path == "/api/onboarding/setup": - # Writing API keys to disk - restrict to loopback unless auth is active + # Writing API keys to disk - restrict to local/private networks unless auth is active. + # In Docker, requests arrive from the bridge network (172.x.x.x), not 127.0.0.1, + # even when the user accesses via localhost:8787 on the host. from api.auth import is_auth_enabled - if not is_auth_enabled() and handler.client_address[0] != "127.0.0.1": - return bad(handler, "Onboarding setup is only available from localhost when auth is not enabled.", 403) + if not is_auth_enabled(): + import ipaddress + try: + addr = ipaddress.ip_address(handler.client_address[0]) + is_local = addr.is_loopback or addr.is_private + except ValueError: + is_local = False + if not is_local: + return bad(handler, "Onboarding setup is only available from local networks when auth is not enabled.", 403) try: return j(handler, apply_onboarding_setup(body)) except ValueError as e: diff --git a/static/index.html b/static/index.html index 51fae8a..a786255 100644 --- a/static/index.html +++ b/static/index.html @@ -526,7 +526,7 @@
System
Instance version and access controls.
- v0.50.8 + v0.50.9