Skip to content

Let the daemon choose its own port - #48

Merged
sepehr-safari merged 1 commit into
mainfrom
daemon-picks-its-own-port
Aug 11, 2026
Merged

Let the daemon choose its own port#48
sepehr-safari merged 1 commit into
mainfrom
daemon-picks-its-own-port

Conversation

@sepehr-safari

Copy link
Copy Markdown
Contributor

The approval API was on a fixed 127.0.0.1:8787, and the GUI concluded it was talking to its own daemon because something answered there. Anything that reached the port first would be handed the unlock passphrase, or an imported nsec, by a GUI with no way to tell the difference.

The daemon now binds port 0 in managed mode and reports what the kernel gave it. A port nobody has chosen yet cannot be taken in advance, and it differs on every start.

It reports it on stdout, and that is not incidental. The SDK's line-mode spawn ignores a child's stderr outright (.stderr = if (output_mode == .collect) .pipe else .ignore), so the ordinary std.debug.print banner would have been dropped on the floor and the GUI would have waited forever for a port it was never told. Read out of the SDK rather than assumed.

The GUI sends nothing until it has been told where to send it. Until the port line arrives it stays in starting, which is the honest state and one the retry tick already knows how to leave. A restart clears it, since a restarted daemon gets a different port. Attached mode is unchanged: the address comes from the environment and is the operator's business.

What this does not do

It does not help against a process running as this user, which can read the token file as easily as the GUI can. It closes the case where something is simply sitting on a known port, waiting.

Verified by running it

  • the daemon reports a real ephemeral port and the API answers there
  • the port differs across restarts (60693, then 60715)
  • the GUI launched in managed mode reaches first-run setup, which it can only reach after an authenticated /info round trip to the port it was told about; its child daemon was listening on 61235, not 8787

The approval API was on a fixed 127.0.0.1:8787, and the GUI decided it
was talking to its own daemon because something answered there. Anything
that got to the port first would be handed the unlock passphrase, or an
imported nsec, by a GUI with no way to tell the difference.

The daemon now binds port 0 in managed mode and reports what the kernel
gave it. A port nobody has chosen yet cannot be taken in advance, and the
port differs on every start.

It reports it on STDOUT, which is not incidental: the SDK's line-mode
spawn ignores a child's stderr outright, so the ordinary banner would
have been dropped and the GUI would have waited forever for a port it
was never told. Read out of the SDK rather than assumed.

The GUI sends nothing until it has been told where to send it. Until the
port line arrives it stays in `starting`, which is the honest state and
one the retry tick already knows how to leave. A restart clears it,
because a restarted daemon gets a different port. Attached mode is
unchanged: the address comes from the environment and is the operator's
business.

This does not help against a process running as this user, which can
read the token file as easily as the GUI can. It closes the case where
something is simply sitting on a known port, waiting.

Verified by running it: the daemon reports a real ephemeral port and
serves there, the port differs across restarts, and the GUI launched in
managed mode reaches first-run setup, which it can only do after an
authenticated /info round trip to the port it was told about.
@sepehr-safari
sepehr-safari merged commit ae87710 into main Aug 11, 2026
3 checks passed
@sepehr-safari
sepehr-safari deleted the daemon-picks-its-own-port branch August 11, 2026 10:35
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