fix: classify Tailscale CGNAT (100.64.0.0/10) as LAN, not public - #89
PhilipEriksson wants to merge 1 commit into
Conversation
classifyHostScope only recognized RFC1918 + loopback, so a spark whose
lanIp is a Tailscale 100.x address fell through to 'public'. Combined
with an unauthenticated LLM endpoint this produced a red 'Open · Public'
(danger) badge even though the address is only reachable inside the
tailnet. Treat 100.64.0.0/10 as LAN so the badge reflects reality
('Open · LAN', warn). Add unit tests for the CGNAT block and its edges.
|
Hi @MikeGibbsOnyx @MiaAI-Lab — small nudge. One-line fix + tests: Tailscale 100.64.0.0/10 addresses classify as "public", producing a false "Open · Public" danger badge. Verified live — badge flips to "Open · LAN". Happy to adjust if you'd rather handle CGNAT differently. |
ACK — Tailscale CGNAT classified as LANProduction Readiness: GO
The boundary is right: Suite on your branch: 317/317 + 23/23; merges clean against current — Wei |
|
Landed via #107 (Batch B ACK merge onto |
Problem
A spark whose
lanIpis a Tailscale address (100.64.0.0/10) shows a red "Open · Public" (danger) LLM-posture badge, even though the endpoint is only reachable inside the tailnet.classifyHostScope()inserver/validate.jsonly recognizes loopback and the RFC1918 private ranges (10/8, 172.16/12, 192.168/16) plus link-local. Tailscale's CGNAT range is none of those, so any100.xaddress falls through to the finalreturn "public".The posture badge is
auth×scope:auth = open(the LLM API answers unauthenticated — no key configured)scope = public(the Tailscale IP misclassified)open+public→level = "danger"→ the red "Open · Public" badge. That's a false alarm: the 100.x address is not on the public internet, it's a private tailnet interface.Why it shouldn't show as "public" on Tailscale addresses
Tailscale assigns every node a stable IP from 100.64.0.0/10 (RFC 6598 shared-address space, the same CGNAT block carriers use). Traffic to that IP is only routable by peers in the same tailnet — it is never exposed to the public internet. Classifying it as "public" misrepresents the exposure and triggers the danger badge for a setup that is, in practice, as private as a LAN.
Fix
Treat 100.64.0.0/10 as
laninclassifyHostScope():A Tailscale-hosted spark now reports "Open · LAN" (warn) instead of "Open · Public" (danger) — accurate, since the endpoint is unauthenticated but confined to the tailnet.
Note: the "Open" part is intentionally unchanged. It still correctly reflects that the LLM API accepts unauthenticated requests; the fix only corrects the scope (network reachability), not the auth posture. If you want the badge to drop below "warn", set an API key on the endpoint and it becomes "API key · LAN".
Tests
Added a unit test in
server/collectors/__tests__/LlmProbe.posture.test.jscovering the CGNAT block and its edges:100.64.0.1,100.89.38.120,100.127.255.254→lan100.63.0.1,100.128.0.1(just outside the block) →publicAll 13 tests pass (
node --test server/collectors/__tests__/LlmProbe.posture.test.js).Verified in production
Applied the same one-line change to a live sparkDash install (v1.8.5,
node --watchauto-reload). A spark withlanIp: 100.89.38.120(Tailscale) flipped fromOpen · Public(danger) toOpen · LAN(warn) on the next probe cycle, confirmed via the live WebSocket snapshot.