make all four network options work, including having no network at all

Option 1 no longer refuses. It assumes offscale is already running — installing
it is one command, documented on the site — and asks for its address and a key,
which is mechanically what option 2 does. The two share a branch because the
difference between them is what to say, not what to do: one is "you already have
a server", the other is "set one up first, here is where".

Option 4 is new: no private network. Presented as a real choice rather than a
failure to choose, with what it costs stated before it is taken and paged so it
is read rather than scrolled past:

  · anything reachable remotely has to be published deliberately and kept closed
    otherwise
  · TLS certificates are yours to obtain and renew
  · every exposed service needs its own authentication, since there is no longer
    a boundary in front of it
  · the machine will be found — anything on a public address is scanned within
    minutes

And the one that is specific to this platform rather than general advice:
ALLOW_ANY_ORIGIN defaults ON, which is deliberate and only defensible because
the tailnet is the perimeter. With no tailnet it must be set to false with an
HTTPS proxy in front, or Officer runs with a check disabled on an assumption that
is no longer true. The summary line says so, so it survives the run.

Declining option 4 redraws the menu rather than dropping to a bare prompt.
Tailscale is still installed when 4 is chosen, and the run says how to connect it
later.

Verified all four end to end with tailscale stubbed, plus the decline-and-choose-
again path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 20:32:27 +00:00
co-authored by Claude Opus 5
parent 06492c3297
commit c629a849d9
2 changed files with 71 additions and 21 deletions
+35 -6
View File
@@ -62,12 +62,13 @@ tailscale_network_menu() {
info "Which network should this machine join?"
echo ""
echo " [1] set up your own network — offscale"
echo " Your own coordination server, here on this machine. The protocol"
echo " on the wire is Tailscale's and the encryption is WireGuard's;"
echo " offscale changes neither — it runs headscale's open-source code."
echo " What changes is the work: one command to install, certificates"
echo " included, managed from an app rather than a terminal, and"
echo " enrolling a device is a link and a tap."
echo " Your own coordination server. The protocol on the wire is"
echo " Tailscale's and the encryption is WireGuard's; offscale changes"
echo " neither — it runs headscale's open-source code. What changes is"
echo " the work: one command to install, certificates included, managed"
echo " from an app rather than a terminal, and enrolling a device is a"
echo " link and a tap. Set it up first, then come back here with its"
echo " address and a key."
echo " https://officer.dev/infrastructure/offscale.html"
echo ""
echo " [2] use a network you already run — headscale or offscale"
@@ -79,6 +80,10 @@ tailscale_network_menu() {
echo " nothing to maintain, free for personal use; the trade is that"
echo " the list of your machines lives with them."
echo ""
echo " [4] no private network at all"
echo " This machine is reached over the open internet, or not at all."
echo " Everything the tailnet was doing becomes yours to do."
echo ""
echo " [?] what are tailscale, headscale and offscale?"
echo ""
}
@@ -180,6 +185,30 @@ TS_DEFAULT_CONTROL_URL="https://controlplane.tailscale.com"
# Moving a node between coordination servers is not something `up` will do while
# it is logged in to one. Logging out first is the documented way, and doing it
# unasked would be worse than saying so.
# What choosing "no private network" actually hands you, said before it is
# chosen rather than discovered afterwards.
tailscale_none_warning() {
echo " Without a tailnet, everything it was doing becomes yours:"
echo ""
echo " · Anything you want to reach remotely has to be published to the"
echo " open internet deliberately, and kept closed otherwise."
echo " · TLS certificates are yours to obtain and to keep renewed."
echo " · Every exposed service needs its own authentication, because"
echo " there is no longer a network boundary in front of it."
echo " · This machine will be found. Anything listening on a public"
echo " address is scanned within minutes and attacked continuously."
echo ""
echo " For Officer specifically, one setting stops being safe:"
echo ""
echo " ALLOW_ANY_ORIGIN defaults ON, which means origin checking is off"
echo " unless it is explicitly set to false. That default is deliberate"
echo " and it is only defensible because the tailnet is the perimeter."
echo " With no tailnet you must set ALLOW_ANY_ORIGIN=false and put an"
echo " HTTPS reverse proxy in front of the platform, or it is running"
echo " with a check disabled that was disabled on the assumption you"
echo " are making false."
}
tailscale_needs_logout() {
local current="$1" target="$2"
[[ -n "$current" && -n "$target" && "$current" != "$target" ]]
+36 -15
View File
@@ -446,21 +446,25 @@ if ! skip; then
TS_LOGIN_SERVER=""
TS_PLANE=""
while [[ -z "$TS_PLANE" ]]; do
if ! read -rp " Which one? (1/2/3/?): " TS_PLANE_CHOICE; then
if ! read -rp " Which one? (1/2/3/4/?): " TS_PLANE_CHOICE; then
echo ""
fail "No answer."
fi
case "$TS_PLANE_CHOICE" in
1)
# TODO(pastilhas): what setting up your own offscale actually does.
warn "Setting up your own network is not built yet."
echo " Pick 2 if you already run one, or 3 for tailscale.com."
;;
2)
# No default offered, deliberately. A coordination server URL is
# somebody's private infrastructure, and a machine that joins the
# wrong one has joined a stranger's network.
read -rp " URL of your server (e.g. https://headscale.example.com): " TS_LOGIN_SERVER || fail "No answer."
1 | 2)
# Both end in the same place: a coordination server somebody runs.
# The difference is only whether they already have one, which changes
# what to say, not what to do.
if [[ "$TS_PLANE_CHOICE" == "1" ]]; then
echo ""
echo " Install offscale first — one command, per"
echo " https://officer.dev/infrastructure/offscale.html — then give"
echo " this machine its address and a key."
fi
# No default offered. A coordination server URL is somebody's private
# infrastructure, and a machine that joins the wrong one has joined a
# stranger's network.
read -rp " Address of your server (e.g. https://headscale.example.com): " TS_LOGIN_SERVER || fail "No answer."
if [[ "$TS_LOGIN_SERVER" =~ ^https?:// ]]; then
TS_PLANE="self-hosted"
else
@@ -468,16 +472,32 @@ if ! skip; then
fi
;;
3) TS_PLANE="tailscale" ;;
"?")
4)
echo ""
tailscale_networks_help | page
warn "No private network — do this at your own risk."
echo ""
tailscale_network_menu
tailscale_none_warning | page
echo ""
if confirm "Continue with no private network?" n; then
TS_PLANE="none"
else
echo ""
tailscale_network_menu
fi
;;
*) warn "Pick 1, 2, 3 or ?." ;;
*) warn "Pick 1, 2, 3, 4 or ?." ;;
esac
done
if [[ "$TS_PLANE" == "none" ]]; then
warn "no private network — Tailscale is installed but not connected"
echo " Connect it later with: sudo tailscale up"
echo " Remember ALLOW_ANY_ORIGIN=false and an HTTPS proxy in front of Officer."
SUMMARY+=("Tailscale: NOT connected by choice — no private network, ALLOW_ANY_ORIGIN must be set false")
TS_CONNECT=false
fi
if [[ "$TS_CONNECT" != false ]]; then
echo ""
info "How should this machine authenticate?"
if [[ "$TS_PLANE" == "tailscale" ]]; then
@@ -587,6 +607,7 @@ if ! skip; then
ERRORS+=("Tailscale: up did not complete")
SUMMARY+=("Tailscale: NOT connected")
fi
fi
fi
fi
step_ok