From 0189316cced70b4176aa4e668646453ff1254f04 Mon Sep 17 00:00:00 2001 From: Alec Fong Date: Sun, 8 Mar 2026 15:34:10 -0700 Subject: [PATCH 1/3] feat: make brev exec fast with SSH multiplexing and fire-first approach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip upfront API status checks — just fire SSH immediately with a 5s connect timeout. If SSH succeeds (especially via a reused multiplexed connection), return instantly. If it fails, then check instance status and give helpful error messages suggesting brev ls. Add ControlMaster/ControlPath/ControlPersist to all SSH config templates so subsequent connections reuse the master socket (~0.6s vs ~3s). Suppress noisy SSH output (Agent pid, PTY warnings, known hosts warnings). --- pkg/cmd/exec/exec.go | 124 +++++++++++++++++++++++++++------------ pkg/ssh/ssh.go | 3 + pkg/ssh/sshconfigurer.go | 6 ++ 3 files changed, 96 insertions(+), 37 deletions(-) diff --git a/pkg/cmd/exec/exec.go b/pkg/cmd/exec/exec.go index 52200d55f..9993febdd 100644 --- a/pkg/cmd/exec/exec.go +++ b/pkg/cmd/exec/exec.go @@ -176,61 +176,110 @@ func parseCommand(command string) (string, error) { const pollTimeout = 10 * time.Minute func runExecCommand(t *terminal.Terminal, sstore ExecStore, workspaceNameOrID string, host bool, command string) error { - s := t.NewSpinner() - workspace, err := util.GetUserWorkspaceByNameOrIDErr(sstore, workspaceNameOrID) - if err != nil { - return breverrors.WrapAndTrace(err) + // Determine SSH alias: use the workspace name directly (with -host suffix if needed) + sshName := workspaceNameOrID + if host { + sshName = workspaceNameOrID + "-host" + } + + // Fire SSH immediately with a short timeout — skip all status checks for speed. + // SSH multiplexing (ControlMaster) in the config means subsequent + // calls reuse an existing connection and are near-instant. + // Use a 5-second connect timeout so we fail fast if the instance is down. + err := runSSHWithTimeout(sshName, command, 5) + if err == nil { + // Success — fire analytics in background and return + go trackExecAnalytics(sstore, workspaceNameOrID) + return nil } - if workspace.Status == "STOPPED" { // we start the env for the user - err = util.StartWorkspaceIfStopped(t, s, sstore, workspaceNameOrID, workspace, pollTimeout) + // SSH failed — now check what's going on with the instance + fmt.Fprintf(os.Stderr, "Connection failed, checking instance status...\n") + + workspace, lookupErr := util.GetUserWorkspaceByNameOrIDErr(sstore, workspaceNameOrID) + if lookupErr != nil { + return breverrors.WrapAndTrace(fmt.Errorf( + "ssh connection failed and could not look up instance %q: %w\nPlease check your instances with: brev ls", + workspaceNameOrID, err)) + } + + if workspace.Status == "STOPPED" { + s := t.NewSpinner() + startErr := util.StartWorkspaceIfStopped(t, s, sstore, workspaceNameOrID, workspace, pollTimeout) + if startErr != nil { + return breverrors.WrapAndTrace(startErr) + } + err = util.PollUntil(s, workspace.ID, "RUNNING", sstore, " waiting for instance to be ready...", pollTimeout) + if err != nil { + return breverrors.WrapAndTrace(err) + } + // Refresh SSH config so the host entry is up to date + refreshRes := refresh.RunRefreshAsync(sstore) + if err = refreshRes.Await(); err != nil { + return breverrors.WrapAndTrace(err) + } + + localIdentifier := workspace.GetLocalIdentifier() + if host { + localIdentifier = workspace.GetHostIdentifier() + } + sshName = string(localIdentifier) + + err = util.WaitForSSHToBeAvailable(sshName, s) if err != nil { return breverrors.WrapAndTrace(err) } + _ = writeconnectionevent.WriteWCEOnEnv(sstore, workspace.DNS) + err = runSSH(sshName, command) + if err != nil { + return breverrors.WrapAndTrace(err) + } + go trackExecAnalytics(sstore, workspaceNameOrID) + return nil } - err = util.PollUntil(s, workspace.ID, "RUNNING", sstore, " waiting for instance to be ready...", pollTimeout) - if err != nil { - return breverrors.WrapAndTrace(err) + + if workspace.Status != "RUNNING" { + return breverrors.WrapAndTrace(fmt.Errorf( + "instance %q is in state %q — please check with: brev ls", + workspaceNameOrID, workspace.Status)) } - refreshRes := refresh.RunRefreshAsync(sstore) - workspace, err = util.GetUserWorkspaceByNameOrIDErr(sstore, workspaceNameOrID) - if err != nil { + // Instance is RUNNING but SSH failed — maybe still booting, do the wait + s := t.NewSpinner() + refreshRes := refresh.RunRefreshAsync(sstore) + if err = refreshRes.Await(); err != nil { return breverrors.WrapAndTrace(err) } - if workspace.Status != "RUNNING" { - return breverrors.New("Instance is not running") - } localIdentifier := workspace.GetLocalIdentifier() if host { localIdentifier = workspace.GetHostIdentifier() } + sshName = string(localIdentifier) - sshName := string(localIdentifier) - - err = refreshRes.Await() - if err != nil { - return breverrors.WrapAndTrace(err) - } err = util.WaitForSSHToBeAvailable(sshName, s) if err != nil { - return breverrors.WrapAndTrace(err) + return breverrors.WrapAndTrace(fmt.Errorf( + "could not connect to instance %q: %w\nPlease check with: brev ls", + workspaceNameOrID, err)) } - // we don't care about the error here but should log with sentry - // legacy environments wont support this and cause errrors, - // but we don't want to block the user from using the shell _ = writeconnectionevent.WriteWCEOnEnv(sstore, workspace.DNS) err = runSSH(sshName, command) if err != nil { return breverrors.WrapAndTrace(err) } - // Call analytics for exec - userID := "" - user, err := sstore.GetCurrentUser() + go trackExecAnalytics(sstore, workspaceNameOrID) + return nil +} + +func trackExecAnalytics(sstore ExecStore, workspaceNameOrID string) { + workspace, err := util.GetUserWorkspaceByNameOrIDErr(sstore, workspaceNameOrID) if err != nil { - userID = workspace.CreatedByUserID - } else { + return + } + userID := workspace.CreatedByUserID + user, err := sstore.GetCurrentUser() + if err == nil { userID = user.ID } data := analytics.EventData{ @@ -241,19 +290,16 @@ func runExecCommand(t *terminal.Terminal, sstore ExecStore, workspaceNameOrID st }, } _ = analytics.TrackEvent(data) - - return nil } -func runSSH(sshAlias string, command string) error { - sshAgentEval := "eval $(ssh-agent -s)" - +func runSSHWithTimeout(sshAlias string, command string, connectTimeoutSecs int) error { // Non-interactive: run command and pipe stdout/stderr // Escape the command for passing to SSH escapedCmd := strings.ReplaceAll(command, "'", "'\\''") - cmd := fmt.Sprintf("ssh %s '%s'", sshAlias, escapedCmd) + // -T disables pseudo-terminal allocation (no "Pseudo-terminal will not be allocated" warning) + // ssh-agent stdout is suppressed to /dev/null to hide "Agent pid NNNNN" + cmd := fmt.Sprintf("eval $(ssh-agent -s) > /dev/null && ssh -T -o ConnectTimeout=%d -o LogLevel=ERROR %s '%s'", connectTimeoutSecs, sshAlias, escapedCmd) - cmd = fmt.Sprintf("%s && %s", sshAgentEval, cmd) sshCmd := exec.Command("bash", "-c", cmd) //nolint:gosec //cmd is user input sshCmd.Stderr = os.Stderr sshCmd.Stdout = os.Stdout @@ -265,3 +311,7 @@ func runSSH(sshAlias string, command string) error { } return nil } + +func runSSH(sshAlias string, command string) error { + return runSSHWithTimeout(sshAlias, command, 10) +} diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go index a13c58f48..3532da3a1 100644 --- a/pkg/ssh/ssh.go +++ b/pkg/ssh/ssh.go @@ -38,6 +38,9 @@ const workspaceSSHConfigTemplate = `Host {{ .Host }} RequestTTY yes ForwardAgent yes AddKeysToAgent yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m RemoteCommand cd {{ .Dir }}; $SHELL ` diff --git a/pkg/ssh/sshconfigurer.go b/pkg/ssh/sshconfigurer.go index 7539cba3f..33f9ebee0 100644 --- a/pkg/ssh/sshconfigurer.go +++ b/pkg/ssh/sshconfigurer.go @@ -233,6 +233,9 @@ const SSHConfigEntryTemplateV2 = `Host {{ .Alias }} AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m {{ if .RunRemoteCMD }} RemoteCommand cd {{ .Dir }}; $SHELL {{ end }} @@ -250,6 +253,9 @@ const SSHConfigEntryTemplateV3 = `Host {{ .Alias }} AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port {{ .Port }} {{ if .RunRemoteCMD }} RemoteCommand cd {{ .Dir }}; $SHELL From 82c267fae5f1742aefec8910905f92e0f0ae5c92 Mon Sep 17 00:00:00 2001 From: Alec Fong Date: Mon, 9 Mar 2026 11:31:01 -0700 Subject: [PATCH 2/3] fix: update SSH config test expectations with multiplexing settings --- pkg/ssh/sshconfigurer_test.go | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/pkg/ssh/sshconfigurer_test.go b/pkg/ssh/sshconfigurer_test.go index f38d16af4..a3a17d227 100644 --- a/pkg/ssh/sshconfigurer_test.go +++ b/pkg/ssh/sshconfigurer_test.go @@ -150,6 +150,9 @@ Host %s AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPersist 10m Port 22 Host %s-host @@ -164,6 +167,9 @@ Host %s-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPersist 10m Port 22 Host %s @@ -178,6 +184,9 @@ Host %s AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPersist 10m Port 22 Host %s-host @@ -192,6 +201,9 @@ Host %s-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPersist 10m Port 22 `, somePlainWorkspaces[0].GetLocalIdentifier(), somePlainWorkspaces[0].GetLocalIdentifier(), @@ -310,6 +322,9 @@ func Test_makeSSHConfigEntryV2(t *testing.T) { //nolint:funlen // test AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 20 Host testName2-host @@ -324,6 +339,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 2022 `, @@ -359,6 +377,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 Host testName2-host @@ -373,6 +394,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 `, @@ -409,6 +433,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 2022 Host testName2-host @@ -423,6 +450,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 `, @@ -457,6 +487,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m `, }, @@ -490,6 +523,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m `, }, @@ -527,6 +563,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Host testName2-host IdentityFile "/my/priv/key.pem" @@ -540,6 +579,9 @@ Host testName2-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m `, }, @@ -656,6 +698,9 @@ Host testName1 AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 Host testName1-host @@ -670,6 +715,9 @@ Host testName1-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 `, @@ -716,6 +764,9 @@ Host testName1 AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 Host testName1-host @@ -730,6 +781,9 @@ Host testName1-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 `, @@ -747,6 +801,9 @@ Host testName1 AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 Host testName1-host @@ -761,6 +818,9 @@ Host testName1-host AddKeysToAgent yes ForwardAgent yes RequestTTY yes + ControlMaster auto + ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPersist 10m Port 22 `, From 79a6f004f343cf8829a33817ec08560c7c9bb1f9 Mon Sep 17 00:00:00 2001 From: Alec Fong Date: Mon, 9 Mar 2026 12:25:59 -0700 Subject: [PATCH 3/3] fix: address PR feedback - ControlPath separator and ssh-agent reuse - Use `-` instead of `:` in ControlPath to avoid issues on filesystems that don't support colons (SMB mounts, WSL edge cases) - Only spawn ssh-agent if SSH_AUTH_SOCK is not already set, preventing orphaned agent processes on rapid repeated exec calls --- pkg/cmd/exec/exec.go | 5 +++-- pkg/ssh/ssh.go | 2 +- pkg/ssh/sshconfigurer.go | 4 ++-- pkg/ssh/sshconfigurer_test.go | 40 +++++++++++++++++------------------ 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/pkg/cmd/exec/exec.go b/pkg/cmd/exec/exec.go index 9993febdd..3e6d2ad82 100644 --- a/pkg/cmd/exec/exec.go +++ b/pkg/cmd/exec/exec.go @@ -297,8 +297,9 @@ func runSSHWithTimeout(sshAlias string, command string, connectTimeoutSecs int) // Escape the command for passing to SSH escapedCmd := strings.ReplaceAll(command, "'", "'\\''") // -T disables pseudo-terminal allocation (no "Pseudo-terminal will not be allocated" warning) - // ssh-agent stdout is suppressed to /dev/null to hide "Agent pid NNNNN" - cmd := fmt.Sprintf("eval $(ssh-agent -s) > /dev/null && ssh -T -o ConnectTimeout=%d -o LogLevel=ERROR %s '%s'", connectTimeoutSecs, sshAlias, escapedCmd) + // Only start ssh-agent if one isn't already running (avoids orphaned agent processes) + agentCmd := `if [ -z "$SSH_AUTH_SOCK" ]; then eval $(ssh-agent -s) > /dev/null; fi` + cmd := fmt.Sprintf("%s && ssh -T -o ConnectTimeout=%d -o LogLevel=ERROR %s '%s'", agentCmd, connectTimeoutSecs, sshAlias, escapedCmd) sshCmd := exec.Command("bash", "-c", cmd) //nolint:gosec //cmd is user input sshCmd.Stderr = os.Stderr diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go index 3532da3a1..c9169198a 100644 --- a/pkg/ssh/ssh.go +++ b/pkg/ssh/ssh.go @@ -39,7 +39,7 @@ const workspaceSSHConfigTemplate = `Host {{ .Host }} ForwardAgent yes AddKeysToAgent yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m RemoteCommand cd {{ .Dir }}; $SHELL diff --git a/pkg/ssh/sshconfigurer.go b/pkg/ssh/sshconfigurer.go index 33f9ebee0..d4ed50d0a 100644 --- a/pkg/ssh/sshconfigurer.go +++ b/pkg/ssh/sshconfigurer.go @@ -234,7 +234,7 @@ const SSHConfigEntryTemplateV2 = `Host {{ .Alias }} ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m {{ if .RunRemoteCMD }} RemoteCommand cd {{ .Dir }}; $SHELL @@ -254,7 +254,7 @@ const SSHConfigEntryTemplateV3 = `Host {{ .Alias }} ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port {{ .Port }} {{ if .RunRemoteCMD }} diff --git a/pkg/ssh/sshconfigurer_test.go b/pkg/ssh/sshconfigurer_test.go index a3a17d227..de0dabe8e 100644 --- a/pkg/ssh/sshconfigurer_test.go +++ b/pkg/ssh/sshconfigurer_test.go @@ -151,7 +151,7 @@ Host %s ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPath ~/.ssh/brev-control-%%r@%%h-%%p ControlPersist 10m Port 22 @@ -168,7 +168,7 @@ Host %s-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPath ~/.ssh/brev-control-%%r@%%h-%%p ControlPersist 10m Port 22 @@ -185,7 +185,7 @@ Host %s ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPath ~/.ssh/brev-control-%%r@%%h-%%p ControlPersist 10m Port 22 @@ -202,7 +202,7 @@ Host %s-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%%r@%%h:%%p + ControlPath ~/.ssh/brev-control-%%r@%%h-%%p ControlPersist 10m Port 22 @@ -323,7 +323,7 @@ func Test_makeSSHConfigEntryV2(t *testing.T) { //nolint:funlen // test ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 20 @@ -340,7 +340,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 2022 @@ -378,7 +378,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -395,7 +395,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -434,7 +434,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 2022 @@ -451,7 +451,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -488,7 +488,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m `, @@ -524,7 +524,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m `, @@ -564,7 +564,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Host testName2-host @@ -580,7 +580,7 @@ Host testName2-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m `, @@ -699,7 +699,7 @@ Host testName1 ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -716,7 +716,7 @@ Host testName1-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -765,7 +765,7 @@ Host testName1 ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -782,7 +782,7 @@ Host testName1-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -802,7 +802,7 @@ Host testName1 ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22 @@ -819,7 +819,7 @@ Host testName1-host ForwardAgent yes RequestTTY yes ControlMaster auto - ControlPath ~/.ssh/brev-control-%r@%h:%p + ControlPath ~/.ssh/brev-control-%r@%h-%p ControlPersist 10m Port 22