Skip to content

fix(start-cli): stop scaffolding a host that breaks every command - #3752

Open
MattDHill wants to merge 2 commits into
masterfrom
fix/no-placeholder-host-in-workspace-config
Open

fix(start-cli): stop scaffolding a host that breaks every command#3752
MattDHill wants to merge 2 commits into
masterfrom
fix/no-placeholder-host-in-workspace-config

Conversation

@MattDHill

Copy link
Copy Markdown
Member

s9pk init-workspace scaffolds host.default: https://dev-vm.local for the packager to replace. That placeholder is not inert — it breaks every start-cli command in a fresh workspace, including ones that never open a socket.

$ start-cli s9pk list-ingredients
Network Error: Failed to resolve hostname: dev-vm.local

Why a wrong default host stops local commands

CliContext::init calls pin_mdns_host(&mut url)? before the command runs. For a .local host that resolves the name eagerly through getent — musl's getaddrinfo can't do mDNS, so start-core shells out to the system resolver — and pins the address into the URL. The ? propagates a failed lookup, aborting context construction.

dev-vm.local resolves on nobody's network, so context construction always fails. s9pk pack and s9pk list-ingredients are collateral: neither wants a host, and both are what s9pk.mk calls, so make cannot run in a freshly scaffolded workspace until the packager edits config.yaml.

Non-.local hosts return Ok(()) without resolving, which is why this is specific to the placeholder rather than to having a default at all.

The documented order walks into it

Quick Start has you scaffold, then make x86. Environment Setup has you set host.default in the Install to StartOS step — after that build. So a packager following the guide in order hits this on their first make, with an error naming neither the placeholder nor the file it came from.

The fix

The host block ships commented out, with a line saying what to do with it. An unset host falls back to http://localhost, which is not .local and so is never resolved.

Repointing the placeholder at some address would fix the DNS failure equally well, but it would assert a default that is wrong for everyone — there is no address that is right. Shipping none is the honest state, and it is also what the file now says.

Environment Setup is updated to match, and the scaffold test — which asserted a non-empty host map — now asserts the opposite, so a placeholder can't come back unnoticed.

Existing workspaces are untouched: write_if_absent never rewrites a config.yaml that exists. A packager already on 1.1.0 picks this up by commenting out or repointing host.default themselves, which the changelog entry says.

Verification

A/B against the released 1.1.0 binary — same command, same directory, only .startos/config.yaml differing:

scaffold result
new (host commented out) reaches list_ingredients and fails on the missing manifest — the command ran
old (dev-vm.local) Network Error: Failed to resolve hostname: dev-vm.local — dead before doing anything

The new constant also parses to exactly {schema, registry} with three registry entries and no host key.

Note

I did not run cargo test -p start-core locally — it compiles the whole backend from cold, which my box doesn't tolerate well. The one test I changed (scaffolded_config_parses_as_workspace) is a pure assertion swap against the same constant, but it wants CI's eyes rather than mine.

Found via

Helix hit this after Start9Labs/helix#72 gave its repo pool a real packaging workspace: signing started working and make promptly failed on DNS instead. Start9Labs/helix#76 drops the block on that box; this is the same fix where it belongs.

Left alone deliberately

The deeper question is whether pin_mdns_host should abort context construction at all for commands that never open a socket — that is what turns a wrong default into a total outage rather than a connection error at first use. Deferring the pin means resolving at each use site, since rpc_url and registry_url are derived from base_url at construction, so it is a real refactor with its own trade-offs. Worth deciding separately; this PR removes the trigger, not the mechanism.

@MattDHill
MattDHill force-pushed the fix/no-placeholder-host-in-workspace-config branch from 6113403 to ab5c947 Compare August 19, 2026 00:41
# The StartOS devices you install to. Uncomment and set `default` to your own
# box's address — shown in its web interface — to enable `make install`.
# host:
# default: https://adjective-noun.local

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't do adjective-noun anymore, this is a bad placeholder

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, and swept the rest — it was in two books, three sets of web fixtures, and two scripts as well.

server-name wherever it stands in for a name the reader substitutes, per @MattDHill.

One that wasn't a placeholder: setup.rs said --hostname "defaults to a random adjective-noun pair", which is a claim rather than a stand-in — and a wrong one, since generate_hostname() produces startos-<4 hex digits> and the ADJECTIVES/NOUNS lists it names are still compiled in but referenced by nothing. That's a clap doc comment, so it was wrong in --hostname's help text too. I first swapped in the real format, then reconsidered on Matt's point that the field is user-settable and changeable: naming any scheme is what went stale, so it now just says the name is generated if unset and can be changed later.

Left alone: assets/adjectives.txt and assets/nouns.txt themselves. They appear to be dead — nothing references either static — but deleting compiled-in assets is a different change from retiring a placeholder, and it's your call whether something still wants them.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correcting myself above: I said the word lists "appear to be dead", which is misleading. Adjective-noun server names are a live feature — @MattDHill points out they are the re-rollable suggestions in the UI, which the user can cycle or free-form override.

They come from a TypeScript copy: shared-libs/ts-modules/shared/src/util/server-name-words.ts, via randomServerName(), used in the setup wizard's password page and the system → general server-name dialog. Untouched by this PR.

What is orphaned is only the Rust pair — hostname.rs's ADJECTIVES/NOUNS statics and the assets/*.txt they include_str! — which nothing in any .rs references. Still not deleting them here, and now for a better reason than "they look unused": the feature they're named after is alive elsewhere, so whether Rust ever needs its own copy is a question for someone who knows the plan, not a side effect of a placeholder sweep.

This also sharpens why setup.rs was wrong rather than merely stale. Those are two different fields:

/// Friendly server name
name: Option<InternedString>,
/// Hostname (LAN advertised) — defaults to a random adjective-noun pair
hostname: Option<InternedString>,

Adjective-noun is the name convention. hostname is generated by generate_hostname() as startos-<4 hex digits>. So that comment described the wrong field's behavior, in text that clap surfaces as --hostname help.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the orphaned Rust copy after all — @MattDHill pushed back on my leaving it dangling, and once the finding held up there was no reason to.

  • hostname.rs's lazy_static! block (both statics)
  • src/assets/adjectives.txt + nouns.txt — 9,072 lines, include_str!'d into every binary

They are private statics, so nothing outside the module could reach them even in principle, and nothing inside does. The live adjective-noun feature is the TypeScript copy — shared-libs/ts-modules/shared/src/util/server-name-words.ts via randomServerName() — which this PR does not touch. generate_hostname() has produced startos-<4 hex digits> for a while, so the Rust lists were serving a scheme that is gone.

assets/ held nothing else, so the directory goes with them. lazy_static stays a dependency — lib.rs, sound.rs, net/wifi.rs and net/ssl.rs still use it.

Note

Not compile-verified locally — a cold start-core build is more than my machine takes gracefully. The risk is about as low as a Rust deletion gets: removing a private unreferenced static cannot break anything that compiles today, and a stale include_str! is a hard compile error rather than a silent one, so CI will say so immediately if I have this wrong.

`s9pk init-workspace` wrote `host.default: https://dev-vm.local` as a
placeholder for the packager to replace. That is not inert.

`CliContext::init` pins a `.local` host eagerly, before the command runs:
musl's getaddrinfo cannot do mDNS, so start-core shells out to getent, and
the lookup's failure propagates and aborts context construction. Since
`dev-vm.local` resolves on nobody's network, every command in a fresh
workspace died on DNS — including `s9pk pack` and `s9pk list-ingredients`,
neither of which opens a socket. Those two are what `s9pk.mk` calls, so
`make` could not run at all until the file was edited.

Environment Setup has the packager set `host.default` in the *Install to
StartOS* step, after Quick Start has them run `make x86`. So the documented
order walks into it, and the error names neither the placeholder nor the
file it came from.

The `host` block now ships commented out, with a line saying what to do with
it. An unset host falls back to `http://localhost`, which is not `.local` and
so is never resolved. Repointing the placeholder at an address would work
equally well for the DNS failure but would assert a default that is wrong for
everyone; there is no address that is right.

The scaffold test asserted a non-empty host map. It now asserts the opposite
— no host at all — so a future placeholder cannot come back unnoticed.

Existing workspaces are untouched: `write_if_absent` never rewrites a config
that exists, so a packager on 1.1.0 picks this up by commenting out or
repointing `host.default` themselves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MattDHill
MattDHill force-pushed the fix/no-placeholder-host-in-workspace-config branch from ab5c947 to 024e685 Compare August 19, 2026 20:14
@MattDHill
MattDHill force-pushed the fix/no-placeholder-host-in-workspace-config branch from 024e685 to 32cf2cc Compare August 19, 2026 20:20
StartOS hasn't generated adjective-noun hostnames for a while —
`generate_hostname()` produces `startos-<4 hex digits>`, and the word lists
it used to draw from are still compiled in but referenced by nothing. The
placeholder outlived the scheme it was named after, across two books, three
sets of web fixtures, and two scripts.

`server-name` everywhere it stands in for a name the reader substitutes: it
reads as generic, which the old one stopped doing the moment it implied a
convention that no longer exists.

`setup.rs` was not a placeholder but a stale claim, on a clap doc comment
that becomes `--hostname` help text. It now says the name is generated if
unset and can be changed later, and names no scheme — the specificity is
what went stale, so a fresh specific claim would only do it again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MattDHill
MattDHill force-pushed the fix/no-placeholder-host-in-workspace-config branch from 32cf2cc to dd210f4 Compare August 19, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants