Skip to content

Consolidate Tailscale/SSH Keys/Password screens into a single Login screen#1

Merged
passcod merged 10 commits intomainfrom
login-screen-consolidation
Mar 5, 2026
Merged

Consolidate Tailscale/SSH Keys/Password screens into a single Login screen#1
passcod merged 10 commits intomainfrom
login-screen-consolidation

Conversation

@passcod
Copy link
Member

@passcod passcod commented Mar 4, 2026

Summary

Consolidates the three separate Tailscale, SSH Keys, and Password screens into a single Login hub screen with sub-screens, as described in docs/login-screen-plan.md.

Changes

New flow

Welcome -> DiskSelection -> VariantSelection -> [TpmToggle] -> Hostname
  -> Login -> Timezone -> NetworkResults -> Confirmation

Steps renumbered from 1/8..8/8 to 1/6..6/6.

Login screen (hub)

  • Inline password entry (password + confirm), same logic as the old Password screen
  • Below the password fields, keybind hints for sub-screens:
    • t -- Tailscale auth key (yellow * when a value is set)
    • s -- SSH authorized keys (yellow * when keys are present)
    • g -- Import SSH keys from GitHub (only shown when github.com is reachable)

Sub-screens

  • LoginTailscale: text input for auth key, Enter/Esc returns to Login
  • LoginSshKeys: growing list of individual key entry fields with Tab to add/cycle, Shift+Tab to cycle back, validation on exit
  • LoginGithub: GitHub username input, fetches keys and appends as individual entries

Data model change

ssh_keys_input: String replaced by ssh_keys: Vec<String> + ssh_key_cursor: usize. Keys are stored individually and filtered (empty/invalid removed) when leaving the SSH keys screen.

Network check

Added https://github.com/ to default_endpoints() (any HTTP response is a pass). The g keybind on Login is gated behind github_reachable().

Spec updates

  • installer.tui.network-check+4: added github.com endpoint
  • installer.tui.tailscale+2: rewritten for Login sub-screen
  • installer.tui.ssh-keys+2: rewritten for growing key field list
  • installer.tui.ssh-keys.github+2: rewritten for GitHub sub-screen
  • installer.tui.password+2: rewritten for inline password on Login
  • installer.tui.confirmation+3: updated step numbering to 6/6

Test results

All 238 unit tests and 68 integration tests pass. No stale tracey references. Clippy clean.

passcod added 10 commits March 5, 2026 12:06
Update installer.md spec items:
- installer.tui.network-check+4: add https://github.com/ endpoint
- installer.tui.tailscale+2: rewrite for Login sub-screen via t keybind
- installer.tui.ssh-keys+2: rewrite for growing key field list via s keybind
- installer.tui.ssh-keys.github+2: rewrite for GitHub sub-screen via g keybind
- installer.tui.password+2: rewrite for inline password on Login screen
- installer.tui.confirmation+3: update step numbering to 6/6
Add https://github.com/ (any HTTP response is a pass) to the network
check endpoint list. Update test counts from 5/6 to 6/7.

r[impl installer.tui.network-check+4]
Replace Screen::Tailscale, Screen::SshKeys, Screen::Password with
Screen::Login (hub with inline password), Screen::LoginTailscale,
Screen::LoginSshKeys, and Screen::LoginGithub sub-screens.

Key changes:
- Login screen shows password fields + keybind hints (t/s/g) with
  yellow * indicators when values are set
- ssh_keys_input: String replaced by ssh_keys: Vec<String> with
  ssh_key_cursor for growing key field list
- LoginSshKeys supports Tab (add/cycle), BackTab (cycle back),
  and filters invalid keys on exit
- GitHub sub-screen only shown when github_reachable() is true
- advance(): Hostname -> Login -> Timezone (sub-screens don't advance)
- go_back(): sub-screens return to Login; Timezone -> Login
- Step numbering changed from /8 to /6
- All unit and integration tests updated for new flow
Plain t/s/g keybinds would intercept password input on the Login screen.
Switch to Alt+t, Alt+s, Alt+g so typing passwords works normally.

Also add alt:<text> directive to the script parser for integration tests.
After a successful GitHub key fetch, navigate sideways to the SSH Keys
sub-screen (instead of back to Login) so the user can see the keys that
were just imported. The cursor is positioned on the first imported key.

Updated spec (ssh-keys.github +3 -> +4), implementation, and tests.
…-appended

Rework the SSH keys sub-screen behavior:

- There is always a trailing blank field at the end of the list.
- Tab and Shift+Tab always cycle forward/backward through fields
  (wrapping at boundaries), they never insert new fields.
- When the user types into the trailing blank field, a new blank
  field is automatically appended to maintain the invariant.
- filter_ssh_keys (on Enter/Esc) still removes empty/invalid entries.

This makes the interaction more predictable: Tab always navigates,
and adding a new key is done by simply selecting the blank field
at the end and typing.

Updated spec (ssh-keys +3 -> +4), implementation, render hints,
and tests (4 new unit tests for ensure_trailing_blank).
When importing GitHub keys, the ssh_keys list typically ends with a
trailing blank (e.g. ["existing-key", ""]). Previously, the fetched
keys were appended after this blank, leaving an empty entry in the
middle of the list. Now, trailing empty entries are stripped before
appending, then ensure_trailing_blank re-adds a single blank at the
end. This keeps the invariant that empty fields only appear at the
bottom of the list.
Non-empty SSH key entries that don't pass the validity check are now
rendered in red instead of yellow (when selected) or white (when
collapsed). This gives immediate visual feedback that the key will be
discarded when leaving the screen, rather than silently dropping it.

Empty entries remain gray, valid entries remain yellow/white as before.

Updated spec (ssh-keys +4 -> +5) and all tracey references.
In interactive mode, the password can no longer be left empty. If the
user presses Enter with both password fields empty, a 'Password is
required' error is shown inline (in red) and the screen does not
advance. This only applies to interactive mode; auto/prefilled modes
with a config-provided password or password-hash still work as before.

The intro text no longer mentions the option to leave the password
empty. A new test (scripted_empty_password_blocks_advance) verifies
the blocking behavior.

All existing tests updated to type a password ('pw') instead of
skipping with empty Enter+Enter. The interactive_empty_firstboot_is_null
test was renamed conceptually: since firstboot is no longer null when
only a password is set, it now verifies that password_set is true and
all other optional fields are absent.

Updated spec (password +3 -> +4) and all tracey references.
@passcod passcod merged commit 60ac9d4 into main Mar 5, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant