Skip to content

An alias is a declaration, not a connection - #69

Merged
sotashimozono merged 1 commit into
mainfrom
alias-is-a-declaration
Sep 14, 2026
Merged

sotashimozono merged 1 commit into
mainfrom
alias-is-a-declaration

Conversation

@sotashimozono

Copy link
Copy Markdown
Member

One alias that would not come up took the whole daemon with it. Measured before anything was written — Panza connecting fine, one host that does not resolve:

[[alias]] panza  = Panza                  ← connected
[[alias]] asleep = no-such-host-anywhere

ssh: Could not resolve hostname no-such-host-anywhere
Error: alias asleep -> ssh host no-such-host-anywhere

127.0.0.1:17405 -> 000    (nothing listening)

The reasons a host fails to answer are mostly not mistakes: maintenance, the wrong network, an agent with no key loaded, a jump box that is down. The stated reason for it being fatal was that an alias "was named for this run" — true of a name typed on the command line, not of three lines in a config file.

The shape

declared splits from sessions. declared is what the PAC routes and the dashboard lists; sessions is what is connected now. Startup dials all of them at once, serves what came up, names what did not. Only a command-line alias is still fatal.

A request is the retry — no timer, no background loop. A reader who reloads has asked for exactly one more attempt, and a host that is down does not get dialled every thirty seconds by a daemon nobody is using.

Reached carries four answers because there are four situations, and 502 / 503 / 404 keep them apart:

http://panza.ssh-browser/  -> 200
http://asleep.ssh-browser/ -> 502  asleep is not connected: ssh to no-such-host-anywhere: early eof
http://typo.ssh-browser/   -> 404

Reconnecting

SftpFs::is_alive is the channel to the driver task, which ends when the ssh pipes close — the same event, not a proxy for it. But it is racy from this side: the driver learns of a death when its own read fails, which is after a request went down the pipe. So alias checks liveness after an attempt too and retries once. Measured by killing the ssh under a live daemon against Panza:

connected -> 200 ; kill ssh ; reload -> 200     (×3)

Without the second check that reload was a 502 and only the one after it worked — a retry you have to do twice is not one.

The rule this bends

The README said a request must never open an ssh, because a page could name hosts in an <img src> and enumerate your ssh_config. That stays true for ssh_config hosts. declared is fixed at startup and is not ssh_config, so the furthest a page reaches is a host you already asked to have served, and one whose up-or-down it can already read off the status code.

What it must not get is the rate, so a failed dial is remembered for three seconds and requests inside that window are answered from the memory. A test with a five-minute window watches five requests dial once. README updated to say both halves.

Not silent

Startup lines, the dashboard (with what ssh said and a link that retries), and the front page all list what is declared and not connected. An alias in your config file appearing on none of those pages is the silent failure this project says it does not have.

222 Rust tests, 63 e2e checks against a real SSH host, fmt/clippy/tsc/permission-pin clean.

One alias that would not come up took the whole daemon with it. Measured: a
config naming Panza and a host that does not resolve, with Panza connecting
fine, and nothing listening on the port afterwards. A cluster in maintenance
meant no sites at all, and the reasons hosts fail to answer are mostly not
mistakes -- maintenance, the wrong network, an agent with no key loaded, a jump
box that is down.

The reason it was fatal was written in the code, next to the opposite rule for
`[[host]]`: an alias "was named for this run". That is true of a name typed on
the command line and not of three lines in a config file, which is what they
usually are.

So `declared` splits from `sessions`. `declared` is what the PAC routes and the
dashboard lists; `sessions` is what is connected now. Startup dials all of them
at once, serves what came up, and names what did not. Only a command-line alias
is still fatal.

A request is the retry. There is no timer and no background loop: a reader who
reloads has asked for exactly one more attempt, and a host that is down does not
get dialled every thirty seconds by a daemon nobody is using. `Reached` carries
four answers because there are four situations -- open, down with what ssh said,
stopped from the dashboard, never declared -- and 502/503/404 keep them apart.
Collapsing them is how a sleeping host used to read as a typo.

A connection that dies is replaced. `SftpFs::is_alive` is the channel to the
driver task, which ends when the ssh pipes close, so it is the same event rather
than a proxy for it. But it is racy from this side -- the driver learns of a
death when its own read fails, which is after a request went down the pipe -- so
`alias` checks liveness *after* an attempt too, and retries once. Measured
against a real host by killing the ssh under a live daemon: three kills, three
single reloads, all 200. Without the second check the first reload was a 502.

That makes a request able to open an ssh, which the README said it must never
be. It stays true for `ssh_config` hosts: `declared` is fixed at startup and is
not `ssh_config`, so the furthest a page reaches is a host you already asked to
have served. What it must not get is the rate, so a failed dial is remembered
for three seconds and requests inside that window are answered from the memory.
A test with a five-minute window watches five requests dial once.

Stopping a declared alias needs a stopped set, or the next request undoes it,
and a button that undoes itself is worse than no button. It says a restart
brings it back, because for a declared alias the config file is the durable
statement.

Not connected is reported everywhere it is true: startup lines, the dashboard
with what ssh said and a link that retries, and the front page. An alias in your
config file that appears on none of those pages is the silent failure this
project says it does not have.

222 tests, 63 e2e checks against a real host.

Signed-off-by: Souta <shimozono-sota631@g.ecc.u-tokyo.ac.jp>
@codecov

codecov Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.77170% with 122 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/ssh-browser/src/origin/mod.rs 60.06% 119 Missing ⚠️
crates/ssh-browser/src/main.rs 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@sotashimozono
sotashimozono merged commit 7a56964 into main Sep 14, 2026
20 checks passed
@sotashimozono
sotashimozono deleted the alias-is-a-declaration branch September 14, 2026 12:39
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