Skip to content

[Bug]: SSH host suggestions omit aliases from wildcard Include directories #35745

Closed

Description

@coygeek

Summary

Configured SSH hosts disappear from the desktop host suggestions when an Include pattern has a wildcard in a directory component. OpenSSH accepts the same configuration and resolves the host normally. This was reproduced against the production discovery API with isolated configuration files and a native OpenSSH control.

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/desktop

Steps to reproduce

  1. Create a temporary SSH home with .ssh/teams/one/hosts.conf containing Host fixture, HostName 192.0.2.10, and User developer.
  2. Write .ssh/config as Include teams/one/*.conf; call discoverSshHosts({ homeDir }) and verify the alias appears.
  3. Change only the Include to teams/*/*.conf; compare native ssh -G -F with the same production discovery call.

The isolated library reproduction is deterministic and failed in both recorded runs. The desktop UI was not launched; the UI consequence is source-only through the production discovery/IPC path.

Save the script below as repro-include.mjs and run bun repro-include.mjs /path/to/t3code from any directory. Prerequisites: a checkout of the stated commit with its locked dependencies installed, Bun, Git and OpenSSH. The script imports the production discovery API, creates every configuration input in an isolated temporary home, sets HOME/TMPDIR/T3CODE_HOME there, removes that home on exit, and leaves the checkout unchanged. It makes no SSH connection. Exit 1 means the defect reproduced, exit 0 means the expected behavior held, and exit 2 means setup or a control failed.

Reproduction script: repro-include.mjs
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { resolve, join } from "node:path";
import { createRequire } from "node:module";
import { pathToFileURL } from "node:url";
import { execFileSync, spawnSync } from "node:child_process";

const checkout = resolve(process.argv[2] ?? ".");
const scratch = mkdtempSync(join(tmpdir(), "ssh-include-repro-"));
let status = 2;
try {
  console.log(`Bun ${Bun.version}; ${process.platform} ${process.arch}`);
  console.log(execFileSync("git", ["--version"], { encoding: "utf8" }).trim());
  const sshVersion = spawnSync("ssh", ["-V"], { encoding: "utf8" });
  if (sshVersion.status !== 0) throw new Error("ssh version check failed");
  console.log(`${sshVersion.stdout}${sshVersion.stderr}`.trim());
  console.log(`Commit ${execFileSync("git", ["-C", checkout, "rev-parse", "HEAD"], { encoding: "utf8" }).trim()}`);
  process.env.HOME = scratch;
  process.env.TMPDIR = scratch;
  process.env.T3CODE_HOME = join(scratch, "t3");
  mkdirSync(join(scratch, ".ssh", "teams", "one"), { recursive: true });
  writeFileSync(join(scratch, ".ssh", "teams", "one", "hosts.conf"), "Host fixture\n HostName 192.0.2.10\n User developer\n");
  const require = createRequire(join(checkout, "packages/ssh/package.json"));
  const Effect = await import(pathToFileURL(require.resolve("effect/Effect")));
  const NodeServices = await import(pathToFileURL(require.resolve("@effect/platform-node/NodeServices")));
  const { discoverSshHosts } = await import(pathToFileURL(join(checkout, "packages/ssh/src/config.ts")));
  const run = () => Effect.runPromise(discoverSshHosts({ homeDir: scratch }).pipe(Effect.provide(NodeServices.layer)));
  const config = join(scratch, ".ssh", "config");
  writeFileSync(config, "Include teams/one/*.conf\n");
  const control = await run();
  console.log(`Literal-directory control: ${JSON.stringify(control.map(({ alias }) => alias))}`);
  if (!control.some(({ alias }) => alias === "fixture")) throw new Error("Control failed");
  writeFileSync(config, "Include teams/*/*.conf\n");
  const expected = execFileSync("ssh", ["-G", "-F", config, "fixture"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
  const hostname = /^hostname (.+)$/m.exec(expected)?.[1];
  console.log(`OpenSSH wildcard-directory hostname: ${hostname}`);
  if (hostname !== "192.0.2.10") throw new Error("OpenSSH contract control failed");
  const actual = await run();
  console.log(`T3 wildcard-directory aliases: ${JSON.stringify(actual.map(({ alias }) => alias))}`);
  status = actual.some(({ alias }) => alias === "fixture") ? 0 : 1;
} catch (error) {
  console.log(`Setup error: ${error.message}`);
} finally {
  rmSync(scratch, { recursive: true, force: true });
}
console.log(status === 0 ? "RESULT: expected behavior" : status === 1 ? "RESULT: defect reproduced" : "RESULT: inconclusive");
process.exitCode = status;

Expected behavior

Host suggestions should discover configured aliases from valid Include glob paths, including patterns in directory components. OpenSSH ssh_config(5), Include permits glob wildcards in each pathname. The native OpenSSH control resolves this exact pattern. The desktop delegates discovery to the shared implementation in apps/desktop/src/ssh/DesktopSshEnvironment.ts#L85-L87.

Actual behavior

With Include teams/*/*.conf and a matching file containing Host fixture, discoverSshHosts returns no hosts. Changing only the directory component to the literal directory (Include teams/one/*.conf) returns the alias. Native ssh -G -F resolves the wildcard configuration to the configured address without connecting.

Impact: P2 - Users organizing valid SSH configuration into wildcard-selected directories cannot find those configured hosts in the picker. The failure is bounded to discovery; manually entering the alias is a source-verified workaround via the manual target path, and replacing the wildcard directory with the tested literal directory restores discovery in the reproduction. No remote connection or authentication failure is claimed.

The observation establishes discovery output for the synthetic inputs below. NOT_ENOUGH_INFO: an integrated desktop execution and native Windows/Linux reruns were not performed; those runs would establish surface/platform behavior beyond the source path.

Impact

Minor bug or occasional failure

Version or commit

main @ 0bb6eaa (0bb6eaa). Latest published stable release checked: v0.1.9; packaged release behavior was not tested.

Environment

Bun 1.4.2, macOS (darwin) arm64, Git 2.56.0, OpenSSH_10.5p1 / LibreSSL 3.3.6. The production API was imported with the checkout's installed locked dependencies. The existing focused config test file passes all six tests under Vite+ (runner reports v5.0.5).

Logs or stack traces

Evidence

Observed output from running the exact script above:

Bun 1.4.2; darwin arm64
git version 2.56.0
OpenSSH_10.5p1, LibreSSL 3.3.6
Commit 0bb6eaa546c91915efbd3f092088c694f3949709
Literal-directory control: ["fixture"]
OpenSSH wildcard-directory hostname: 192.0.2.10
T3 wildcard-directory aliases: []
RESULT: defect reproduced

Source: packages/ssh/src/config.ts#L216-L224 skips the Include whenever the parent directory has an SSH pattern. packages/ssh/src/config.ts#L103-L129 only expands basename wildcards. This explains the observed empty result; the source is cited as diagnosis, not as a proposed repair.

All fixture aliases, addresses, users, host-key payloads, and filenames are synthetic; no real SSH configuration or key was read. Evidence provenance: observed (discovery output) and static (desktop entry-point propagation). Local verification: reproduced. Reproduction completeness: complete for discovery; NOT_ENOUGH_INFO: full desktop connection behavior was not exercised and would require an integrated desktop run.

Expected source: the contract and source links under Expected behavior. Failure source: the isolated production-API run and pinned discovery source links above. Missing fact: integrated desktop and other-platform execution are not established; discovery itself is directly reproduced.

Restoration check

The wildcard-directory fixture must return the alias and configured hostname while the existing literal-directory control continues to pass. The reproduction should then print RESULT: expected behavior and exit 0.

Workaround

Use a literal directory in the Include path; this restores discovery in the control. Manually entering the configured alias also bypasses host suggestions (source-verified manual target path; no desktop connection was run).

References

Searches covered open and closed issues/PRs for SSH Include, directory glob, SSH wildcard, host discovery, and current open SSH Include PRs. The related open filename-matcher PR was read and does not remove the parent-directory skip.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions