| feat(start-os): the server's name is its .local hostname (#3791) * feat(start-os): the server's name is its .local hostname ServerInfo carried two spellings of one identity: a free-form display label shown in the browser tab, and the DNS label the .local address and the Linux system hostname are built from. The UI collected one and derived the other through normalize()/denormalize(), so the tab could read "My Cool Server" while every address the user typed said my-cool-server.local — and someone hunting for the second did not recognize the first. Drop ServerInfo.name and collapse ServerHostnameInfo into ServerHostname. The Server Name field now edits the .local hostname directly and surfaces ServerHostname::validate rather than silently normalizing input away, so the browser tab shows the address the user reaches the server by. server.set-hostname takes a hostname and nothing else, and setup execute drops --name. Removing normalize() means the field has to reject what it used to rewrite, so it applies the DNS label rules on top of validate()'s charset rule — a 63 character limit and no leading or trailing hyphen — and turns off autocapitalization, since a phone keyboard would otherwise capitalize the first letter of a name that must be lowercase. Those two rules stay in the form rather than in validate(): startd calls sync_hostname on every boot and ServerHostname::load does not validate, so tightening validate() would drop a server whose stored hostname predates the rule into diagnostic mode. The v0_4_0_2 migration drops the stored name; rolling back writes one back, title-casing the hostname the way the derivation it returns to would have. Restoring from a backup and transferring to a new drive both stop renaming the server: the wizard sent normalize("") for those flows, which is the "start9" fallback, and it overwrote the hostname the backup carried. * fix: enforce the DNS label rules where the operator supplies a hostname The 63-character and hyphen-edge rules lived only in the Angular form, so `server.set-hostname` would commit a hostname the kernel refuses: the DB write lands before `sync_hostname` runs, and `startd` calls `sync_hostname` unconditionally on every boot, so the server comes back into diagnostic mode with no way out but editing the database by hand. `ServerHostname::new_from_input` holds an operator-supplied hostname to the full rules and backs `set_hostname_rpc` and `new_opt`. `validate` and `new` keep the charset rule alone, so a hostname stored before a rule existed still loads and a legacy backup still restores. The form validator now trims, reports an empty value as `required`, and reports a bad character before a bad length, so pasting a name with a trailing space saves instead of failing on a character the user cannot see. Both forms submit the trimmed value. Also: the new CLI argument's help goes through `help.arg.hostname` in all five locales, the setup wizard's remaining validation messages are translated, the start-cli changelog records the two commands that changed, the transfer section of the device test plan no longer asks for a server name the flow does not offer, and `lan_address()` — dead on master and on this branch — is gone. * fix: cap the hostname at what the root CA's Common Name can carry The root CA is issued to `<hostname> Local Root CA`, and X.509 caps a Common Name at 64 characters, so a 51-character hostname aborts `AccountInfo::new` inside OpenSSL. That runs after the data drive is prepared, so a fresh setup died on a raw ASN.1 error and the operator had to start the install over. `MAX_LEN` is 50, and a test now asserts the branded name still fits, so the constant moves if the branding does. Also from review: `hostnameValidationErrors` carries the `required` message its validator emits, instead of each caller patching one in; the Server Name dialog marks an already-invalid hostname touched so `tui-error` explains why Save is disabled; the setup wizard's form moves to `NonNullableFormBuilder`; the start-cli changelog sections follow Keep a Changelog order; the transfer section of the device test plan points at the source device's name rather than the target's, which that section destroys; and the word list is `hostname-words.ts`, the last file still spelled for the display name. * fix: heal a hostname the kernel refuses, and say 50 everywhere The 0.4.0.2 release note still promised 63 characters after the cap moved to 50, so the notes announcing the change described a rule the code rejects. The `--hostname` help and the start-cli entry likewise named the length rule without the hyphen-edge one. An earlier version accepted a hostname longer than the kernel allows, and `sync_hostname` runs on every boot, so such a server comes up in diagnostic mode every time and the RPC that would rename it is out of reach. Diagnostic mode can still take an update, so `v0_4_0_2::up` is where it heals: `repair_hostname` keeps as much of the stored name as the rules allow and generates one when nothing usable remains. A previous round declined this on the grounds that a stranded server could not reach the migration; `diagnostic.rs` exposes `update`, so it can. The root CA test now builds a certificate instead of measuring a string, so it fails if anything inside `make_root_cert` grows rather than only the branding, and the hostname limit gets an entry under the root AGENTS.md coupled changes, since the number is restated by hand across Rust, TypeScript, five locales, five dictionaries and the docs. The two `.local` previews are signals rather than methods called on every change detection, the setup wizard declares the `Required` message its password field needs rather than borrowing it from the hostname helper, and the device test plan checks the hostname where the UI shows it. * fix: repair only a hostname the system refuses, not one the field would The repair added last round gated on `new_from_input`, which is the rule for a name an operator is typing now — at most 50 characters, no hyphen on either end. A server renamed on 0.4.0.1 could hold 63 characters: the UI allowed it, the kernel carries it, avahi publishes it, and the leaf certificate fits. Updating would have truncated that name and moved the `.local` address with it on the same boot, silently, breaking bookmarks and known_hosts on a server that was working. `repair_hostname` now returns a hostname untouched when `set_hostname` can carry it — in the character set and within `HOST_NAME_MAX` — and rewrites only what would fail on the boot path. It also strips hyphens before spending the length budget rather than after, so a name buried behind them survives. A backup restore builds its database through `Database::init`, which stamps the current version, so no migration ever runs over it; a backup taken from a server holding an unusable hostname would have restored straight into diagnostic mode. `recover_full_server` runs the hostname through the same repair. Also from review: the setup wizard's own validation messages come after the spread that was overwriting them, the start-cli entry names `setup execute --hostname` alongside `server set-hostname`, and the coupled-changes bullet drops a claim about a TS binding that does not exist, names both changelogs, and no longer separates itself from the list with a blank line. * fix: bound the untouched hostname band by what the server can actually serve Last round narrowed the repair's trigger to what the boot path refuses, on the grounds that a longer name still works — "the UI allowed it, the kernel carries it, avahi publishes it, and the leaf certificate fits". The last clause is false. A leaf certificate is issued to `<hostname>.local`, X.509 caps a Common Name at 64, and a hostname of 59 or more takes it past that: `make_leaf_cert` fails, `get_config` returns nothing, and the handshake ends in a fatal alert. So every HTTPS request to the `.local` name fails, for the StartOS UI and for every service binding, and the rename dialog that would fix it is served over the address that no longer works. rustls rejects a DNS label with a hyphen on either end, which costs a hostname its address the same way. `ServerHostname::is_usable` is that whole question in one place, and `repair_hostname` leaves a hostname alone only when it passes. The band this protects is 51 to 58 characters — a name the operator set, that serves fine, and whose address should not move under them. Above it there is no working address to protect, so the repair gives the server one. Both ceilings now derive from the Common Name limit they come from, and a test mints a real leaf certificate at the boundary, so `MAX_SERVED_LEN` moves if the `.local` suffix ever does. Also from review: the coupled-changes bullet credited the `server set-hostname` man page to a doc comment that `#[arg(help = ...)]` overrides. * fix(start-os): preserve hostname migrations after rebase Compose the hostname migration with the admin-port and ALPN changes already accumulated for 0.4.0.2, retaining every up and down path. Cover the combined migration plus restore and transfer hostname preservation. Raise start-cli to 2.0.0 for the removed and changed stable flags, refresh its lockfile and generated man pages, and normalize generated man-page whitespace at the source. * fix(start-os): cap server hostnames at 32 characters * docs(setup): restore state flow comments * refactor(start-os): scope hostname repair to migration | 8 天前 |