Skip to content

feat(deploy): one-time setup page joins the tailnet in the browser (SPEC-605) - #551

Open
iret77 wants to merge 4 commits into
mainfrom
feat/605-setup-page
Open

iret77 wants to merge 4 commits into
mainfrom
feat/605-setup-page

Conversation

@iret77

@iret77 iret77 commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #550 once the live onboarding run is green.

Live onboarding test 2026-10-08: on an existing server with password login and a chat-only assistant, the only route left was the provider's web console plus hand-editing a Tailscale key into a long line. Owner decision: Home-Assistant-style setup page — no key, no terminal for the key, nothing secret through the chat.

What

  • v3/deploy/setup-page.py (SPEC-605): host-side, stdlib-only. Runs tailscale up without a key, serves http://<server>:8420/ — code form → "Connect with Tailscale" (the login link Tailscale printed) → "Done, palaia at http://:8420/". Wrong code: nothing, 1 s delay. Exits 0 when joined, 1 on 30-min timeout; port closed, tailscale up child terminated. Makes up and prints a code if PALAIA_SETUP_CODE is empty.
  • get-palaia.sh: no key and not on a tailnet → setup page (embedded verbatim; temporary ufw rule when ufw is active; stdin detached because the script itself arrives on stdin). PALAIA_TSKEY unchanged.
  • cloud-init.yaml: PALAIA_SETUP_CODE is the one thing to fill in; a filled TAILSCALE_AUTH_KEY still wins; guard matches the marker word, so the old "replace every placeholder" trap is gone. 20 KB (< Hetzner's 32 KiB; AWS's 16 KB is noted in the README).
  • Install skill: Steps 2–6 rewritten around the page; the assistant makes up the code; 3c first offers recreating an empty server with the setup file (no console at all); key path kept for technical users, with "palaia" as key description.
  • Host-side, not in the hub → no image change, no release; get.palaia.ai serves main.

Verified

  • pytest tests/deploy tests/clients tests/test_version_drift.py tests/docs_site green; docs vitest 20 passed; ruff clean.
  • test_setup_page.py runs the real script against a fake tailscale: no link without the code (page and status), link with it, done + exit 0 after "login", timeout exit 1 + port closed, already-joined skip, generated code format. Embeds byte-identical; guard picks page vs key (bash-executed); dash -n on the installer; heredoc round-trips through | dash.

Not verified (first live run is the onboarding test on the owner's VPS)

Open

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…PEC-605)

Live onboarding test: getting a Tailscale key onto an existing server meant
a provider console plus hand-editing a key into a long line. Owner decision
2026-10-08 (#550): Home-Assistant-style setup page instead — no key, no chat.

- v3/deploy/setup-page.py: stdlib-only host-side helper. Runs `tailscale up`
  without a key, shows its login link only after the one-time setup code,
  polls until the tailnet address answers, then exits; 30-min timeout.
  Makes up and prints a code (XXXX-XXXX-XXXX, no look-alikes) if none given.
- get-palaia.sh: no PALAIA_TSKEY and not yet on a tailnet -> setup page
  (embedded verbatim, temporary ufw rule if ufw is active, stdin detached).
- cloud-init.yaml: PALAIA_SETUP_CODE is the one thing to fill in; a filled
  TAILSCALE_AUTH_KEY still wins. Guard matches the marker word only.
- palaia-install skill: Steps 2-6 around the setup page; the assistant
  makes up the code; key path kept for technical users (incl. "palaia" as
  the key description); 3c offers recreating an empty server first.
- Tests: setup page run for real against a fake tailscale; embeds are
  byte-identical; guard picks page vs key; user-data size <= 32 KiB.
- SPEC-605 + IMPLEMENTATION row; deploy README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iret77 iret77 added the v3 v3: code under v3/ on main, active development label Oct 8, 2026
Second review (Codex, read-only) plus own check, all verified in the code:
- Pending login is dropped (`tailscale logout`) whenever the page ends
  without a join, so an old login link cannot pull the server into
  someone else's network later.
- SIGTERM/SIGHUP run the same clean-up; the `tailscale up` child is
  waited for and killed if needed.
- At most 32 concurrent connections, 10 s request timeout — a flood of
  idle connections cannot lock the owner out.
- cloud-init: an emptied TAILSCALE_AUTH_KEY counts as "no key" (it used
  to skip the code-protected page).
- Installer: ufw rule only added/removed when it did not exist before,
  removed on interrupt too (trap); the code goes through the environment
  (sudo --preserve-env), never as a command argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iret77

iret77 commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Second review (Codex gpt-6-astra, read-only, security focus): 5 findings, all verified against the code and fixed in d9408fc — unbounded connection threads (now 32 max, 10 s timeout), SIGTERM/SIGHUP skipping clean-up, emptied cloud-init key bypassing the code page, installer deleting a pre-existing ufw rule, setup code visible in sudo arguments. Own addition: the pending Tailscale login is dropped (tailscale logout) whenever the page ends without a join. New tests cover each. No code-bypass or login-link leak found.

iret77 and others added 2 commits October 8, 2026 17:27
…(SPEC-605)

Cloud-server steps 2/3, the after-boot note and the existing-server text
in all five onboarding languages now describe the setup code and the
one-time setup page; no key to create.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v3 v3: code under v3/ on main, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup page: join the tailnet in the browser — no key, no terminal, nothing through the chat

1 participant