fix(felhomsshd): keep the persisted port unconditionally (no self-listen flip-flop)
On a continuous reconcile felhom-sshd itself listens on the claimed port, so re-probing isFree(persisted) found it 'busy' by our own daemon and thrashed to another candidate every tick. A persisted port is ours — keep it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -14,15 +14,18 @@ var ErrPortsExhausted = fmt.Errorf("felhomsshd: all candidate OOB ports are busy
|
||||
// for tests; production impl = probeFree (ss + net.Listen).
|
||||
type portProbe func(port int) bool
|
||||
|
||||
// claimPort returns the OOB port, mirroring the spike's shell algorithm:
|
||||
// - if a persisted port exists AND is still free → keep it (idempotent, no thrash),
|
||||
// - else the FIRST free candidate → persist + return,
|
||||
// - else ErrPortsExhausted (LOUD).
|
||||
// claimPort returns the OOB port:
|
||||
// - if a port is already PERSISTED (and != 22) → keep it unconditionally. It is OUR port; on a
|
||||
// continuous reconcile felhom-sshd is itself LISTENING on it, so re-probing with isFree would
|
||||
// (wrongly) find it "busy" by our own daemon and thrash to another candidate every tick. Once
|
||||
// claimed, the port is stable (the belt @ssh_port and the operator's known port depend on it).
|
||||
// - else the FIRST free candidate → persist + return (isFree = ss-empty AND a real bind succeeds).
|
||||
// - else ErrPortsExhausted (LOUD — never :22 or a random port).
|
||||
//
|
||||
// persist writes PortFile; readPersisted reads it. isFree is the probe. All injected for tests.
|
||||
// persist writes the port file; readPersisted reads it. isFree is the probe. All injected for tests.
|
||||
func claimPort(candidates []int, isFree portProbe, readPersisted func() (int, bool), persist func(int) error) (int, error) {
|
||||
if cur, ok := readPersisted(); ok && cur != 22 && isFree(cur) {
|
||||
return cur, nil
|
||||
if cur, ok := readPersisted(); ok && cur != 22 {
|
||||
return cur, nil // persisted = ours; keep it (no thrash — felhom-sshd holds it)
|
||||
}
|
||||
for _, p := range candidates {
|
||||
if p == 22 {
|
||||
|
||||
Reference in New Issue
Block a user