fix(remote): a brokered pane replaces a kernel that predates the broker's model surface

8b2a05c51 moved a brokered pane's default model, picker and rebuilds onto
the broker, but only a kernel carrying it behaves that way. The pane
floor stayed at 2.0.0, so a host already holding a 2.18.0 kernel kept
reusing it after the window upgraded, and #10448 stayed broken for
exactly the machine that reported it.

A connect that publishes a broker now needs 2.18.1, the first version
that can carry the fix; a host on its own credentials keeps the 2.0.0
floor. PaneFloor is the one place both the connect and its probe read
the floor from, so a probe cannot call a kernel usable that the connect
would then replace.
This commit is contained in:
esengine committed 2026-09-17 02:12:06 -07:00
1 parent f352dfbecc
commit 437b982b59
4 files changed
+42 -3

No files matched your search

+7 -2
View File
@@ -142,7 +142,7 @@ const brokerForwardName = "provider-broker"
// reports the address a kernel there should call. Empty when this pool has no
// broker to publish, which is what leaves that host on its own credentials.
func (p *Pool) broker(l *link) (string, error) {
if !p.opts.Broker.configured() || l.provider == config.RemoteProviderRemote {
if !p.brokers(l) {
return "", nil
}
l.brokerOnce.Do(func() {
@@ -159,6 +159,11 @@ func (p *Pool) broker(l *link) (string, error) {
return l.brokerAddr, l.brokerErr
}
// brokers reports whether a connect to l publishes this machine's broker.
func (p *Pool) brokers(l *link) bool {
return p.opts.Broker.configured() && l.provider != config.RemoteProviderRemote
}
// HostState is what a frontend shows for one machine: where its link stands,
// and — while a first connect is still running — which step it reached. The
// pool records it whether or not a caller asked to watch, because the window
@@ -396,7 +401,7 @@ func (p *Pool) serve(ctx context.Context, l *link, s *space, workspace string, c
ProductVersion: p.opts.Version,
FetchBinary: p.opts.FetchBinary,
ResolveDownload: p.opts.ResolveDownload,
MinVersion: bootstrap.MinPaneVersion,
MinVersion: bootstrap.PaneFloor(brokerAddr != ""),
Progress: func(step, detail string) {
p.record(l, func(st *HostState) { st.Step, st.Detail = step, detail })
if call.Progress != nil {
+1 -1
View File
@@ -45,6 +45,6 @@ func (p *Pool) Probe(ctx context.Context, host string) (bootstrap.Report, error)
LocalGOARCH: runtime.GOARCH,
ProductVersion: p.opts.Version,
FetchBinary: p.opts.FetchBinary,
MinVersion: bootstrap.MinPaneVersion,
MinVersion: bootstrap.PaneFloor(p.brokers(l)),
})
}
+15
View File
@@ -47,6 +47,21 @@ const (
// asks nothing a 1.x kernel cannot do, so those callers pass none.
const MinPaneVersion = "2.0.0"
// MinBrokeredPaneVersion is the oldest kernel a pane can be driven by when its
// models resolve through the broker. An older one starts sessions on its own
// default_model and lists its own providers, which the broker then refuses.
const MinBrokeredPaneVersion = "2.18.1"
// PaneFloor is the version floor for a pane's kernel, by whether the connect
// publishes a broker to it. A connect and a probe of it both read it here, so
// neither can call usable a kernel the other would replace.
func PaneFloor(brokered bool) string {
if brokered {
return MinBrokeredPaneVersion
}
return MinPaneVersion
}
// Broker points a bootstrapped serve at the provider broker on the machine
// starting it: Addr is the remote loopback address an -R forward publishes it
// on, and Token authenticates to it. A zero Broker leaves that host resolving
@@ -268,6 +268,25 @@ func TestUsableTakesTheFloorAndForgivesAnUnreadableVersion(t *testing.T) {
}
}
// A kernel from before the broker owned a pane's model surface still drives a
// pane on its own credentials, and is replaced for one that resolves at home.
func TestPaneFloorRisesOnlyForABrokeredPane(t *testing.T) {
c := candidate{path: "/usr/bin/reasonix", version: "2.18.0", flags: map[string]bool{}}
for _, f := range LaunchFlags(true) {
c.flags[f] = true
}
if !c.usable(PaneFloor(false), LaunchFlags(false)) {
t.Fatal("a 2.18.0 kernel was refused for a pane on its own credentials")
}
if c.usable(PaneFloor(true), LaunchFlags(true)) {
t.Fatal("a 2.18.0 kernel was accepted for a brokered pane")
}
c.version = MinBrokeredPaneVersion
if !c.usable(PaneFloor(true), LaunchFlags(true)) {
t.Fatal("a kernel at the brokered floor was refused")
}
}
// probeConn answers the locate probe and nothing else: choosing between the
// binaries a machine holds needs no file layer.
type probeConn struct{ out string }