### Motivation
This is the second of three PRs to add remote/collab support for the git
graph and is a follow-up to #54468. I'm adding remote support for the
search because it's not user accessible without the initial graph fetch
having remote support, so it allows us to merge this without having to
add full remote support. Collab guest support will be added in a
follow-up PR.
#### Summary
For large repos, searching can take a while to fully stream in all
matched results. For example, running a basic search on the Linux repo
took over 10s for me. Because of that, we want to stream search results
in chunks to downstream users to keep the time-to-first-match low. After
this change, the first chunk gets sent back after ~50ms on the Linux
repo from receiving the request.
In order to accomplish that, I added a new proto client API that allows
for a request to map to n responses. e.g.
```/dev/null/example.rs#L1-1
client.add_entity_stream_request_handler(Self::handle_search_commits);
```
Note: The proto API isn't supported over collab yet, that will be
another PR
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A
---------
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
Closes#45271
Zed hardcodes `ControlMaster=yes` with a `ControlPath` in a random temp
directory, so it can never find a ControlMaster session the user already
has open. This means you get prompted for credentials again even if
you're already authenticated.
This patch checks for an existing master before spawning a new one. It
runs `ssh -G` to resolve the user's configured `controlpath` (with `%h`,
`%p`, etc. already expanded), then `ssh -O check` to verify it's alive.
If it is, Zed skips askpass and uses the existing socket. If not, the
current behavior is unchanged. Both commands are local and don't hit the
network.
Also adds a `killed: AtomicBool` to `SshRemoteConnection` because
`has_been_killed()` was using `master_process.is_none()` as a proxy for
"connection is dead." When reusing an external master, `master_process`
starts as `None`, so the connection pool would discard it immediately.
Tested against a university SSH setup with `ControlMaster auto`
configured. The reuse path connects without prompting, and the fallback
path works normally when no master exists. Windows is unaffected.
Release Notes:
- Zed now reuses existing SSH ControlMaster sessions instead of
prompting for credentials again (#45271).
## Summary
- The cross-compile path for building `remote_server` via `cargo
zigbuild` was missing `.current_dir()`, causing it to inherit the
process cwd
- This fails with "cannot specify features for packages outside of
workspace" when Zed is launched from outside its source tree
- The native build path at the same location already sets
`.current_dir()` correctly — this aligns the cross-compile path
Closes#53950
## Test plan
- [x] Build Zed from source
- [x] Launch from `/tmp`: `cd /tmp && /path/to/zed some-project
--dev-container`
- [x] Verify dev container cross-compilation succeeds
Release Notes:
- Fixed dev container cross-compilation failing when Zed is launched
from outside its source directory
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
### Summary
Follow up on: https://github.com/zed-industries/zed/pull/53550
Threads in the sidebar are now filtered by remote connection in addition
to path list. Previously, two different machines with the same absolute
project paths (e.g. both at `/home/user/project`) would have their
threads merged into a single sidebar group. Now each remote host gets
its own group.
To support this, this PR introduces `RemoteConnectionIdentity`, a
normalized subset of `RemoteConnectionOptions` that includes only the
fields relevant for identity matching (e.g. SSH host + username + port,
but not nickname or connection timeout). All remote host comparisons in
the sidebar, thread metadata store, and workspace persistence now go
through `same_remote_connection_identity()` instead of raw `==` on
`RemoteConnectionOptions`.
#### Follow Up on remoting for sidebar
1. Make the neighbor in archive thread fallback to the main git worktree
instead of the next thread
2. Start implementing remote git operations that archive feature depends
on
3. Make remote connection pool equality use `RemoteConnectionIdentity`
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
---------
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A
This PR greatly improves our handling of remote threads in the sidebar.
One primary issue was that many parts of the sidebar were only looking
at a thread's path list and not its remote connection information. The
fix here is to use `ProjectGroupKey` more consistently throughout the
sidebar which also includes remote connection information.
The second major change is to extend the MultiWorkspace with the ability
to initiate the creation of remote workspaces when needed. This involved
refactoring a lot of our remote workspace creation paths to share a
single code path for better consistency.
Release Notes:
- (Preview only) Fixed remote project threads appearing as a separate
local project in the sidebar
---------
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
Release Notes:
- N/A
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Credit to Dario Weißer for bringing this to our attention.
Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- Fixed a bug where a cleverly crafted directory name could lead to
remote code execution
Closes#52943
## Summary
- Prefer SSH nicknames over raw hosts in remote connection display names
- Add regression tests for nickname and host fallback behavior
## Why
The `nickname` field is documented as the user-facing label for SSH
connections, but `RemoteConnectionOptions::display_name()` always
returned the raw host. That meant recent-projects UI surfaces kept
ignoring nicknames even when they were configured.
## Validation
- `cargo test -p remote ssh_display_name`
- `cargo test -p remote`
Release Notes:
- Fixed SSH recent-project labels to show configured nicknames instead
of raw hosts when available.
This PR adds tracking of project groups to the MultiWorkspace and
serialization/restoration of them. This will later be used by the
sidebar to provide reliable reloading of threads across Zed reloads.
Release Notes:
- N/A
---------
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
## Context
Closes#11473
In-house Zed implementation of devcontainers. Replaces the dependency on
the [reference implementation](https://github.com/devcontainers/cli) via
Node.
This enables additional features with this implementation:
1. Zed extensions can be specified in the `customizations` block, via
this syntax in `devcontainer.json:
```
...
"customizations": {
"zed": {
"extensions": ["vue", "ruby"],
},
},
```
2.
[forwardPorts](https://containers.dev/implementors/json_reference/#general-properties)
are supported for multiple ports proxied to the host
## How to Review
<!-- Help reviewers focus their attention:
- For small PRs: note what to focus on (e.g., "error handling in
foo.rs")
- For large PRs (>400 LOC): provide a guided tour — numbered list of
files/commits to read in order. (The `large-pr` label is applied
automatically.)
- See the review process guidelines for comment conventions -->
## Self-Review Checklist
<!-- Check before requesting review: -->
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Improved devcontainer implementation by moving initialization and
creation in-house
## Summary
- Fix SSH `-L` port-forward arguments to wrap IPv6 addresses in brackets
(e.g. `-L[::1]:8080:[::1]:80`), so SSH can correctly parse them
- Rewrite `parse_port_forward_spec` to support bracket-wrapped IPv6
tokens like `[::1]:8080:[::1]:80`
- Add diagnostic logging for stdin read failures in the remote server to
aid debugging connection issues
Closes#49009
## Test plan
- [x] New unit tests: `test_parse_port_forward_spec_ipv6`,
`test_port_forward_ipv6_formatting`,
`test_build_command_with_ipv6_port_forward`
- [x] Existing tests pass: `cargo test -p remote --lib
transport::ssh::tests` (6/6)
- [ ] Manual verification: connect via SSH to an IPv6 host with port
forwarding configured
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Closes https://github.com/zed-industries/zed/issues/49363
When an existing remote workspace's connection is dead (e.g. the server
died and reconnect failed, leaving the client in `ServerNotRunning`
state), `remote_connection()` returns `None`. Previously this caused an
error dialog, blocking reconnection — the user had to manually switch to
another project and back to recover.
Now the code falls through to establish a fresh connection instead,
matching the previous behavior where clicking "Reconnect" would just
work.
Release Notes:
- Fixed remote reconnect failing with an error when the server is not
running, now establishes a fresh connection instead.
Closes#15196, #46918
- fix: notebook_ui, use buffer so that notebooks open in remote/WSL
settings.
- fix: add musl in nix for cross-compilation, without this remote server
doesn't build inside NixOS
Release Notes:
- Implement WSL and SSH remote kernels (crates/repl/src/kernels/*) and
wire up spawn/kill kernel proto messages and client requests.
Replaces a bunch of `impl FnMut` parameters with `&mut dyn FnMut` for
functions where this is the sole generic parameter.
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Re-lands https://github.com/zed-industries/zed/pull/48891, which was
reverted due to conflicts with multi-workspace.
Before you mark this PR as ready for review, make sure that you have:
- [X] Added a solid test coverage and/or screenshots from doing manual
testing
- [X] Done a self-review taking into account security and performance
aspects
Release Notes:
- N/A
Here's some backstory:
* on macOS, @cole-miller and I noticed that since roughly Oct 2025, due
to some changes to latest macOS Tahoe, for any spawned child process we
needed to reset Mach exception ports
(https://github.com/zed-industries/zed/issues/36754 +
6e8f2d2ebe)
* the changes in that PR achieve that via `pre_exec` hook on
`std::process::Command` which then abandons `posix_spawn` syscall for
`fork` + `execve` dance on macOS (we tracked it down in Rust's std
implementation)
* as it turns out, `fork` + `execve` is pretty expensive on macOS
(apparently way more so than on other OSes like Linux) and `fork` takes
a process-wide lock on the allocator which is bad
* however, since we wanna reset exception ports on the child, the only
official way supported by Rust's std is to use `pre_exec` hook
* posix_spawn on macOS exposes this tho via a macOS specific extension
to that syscall `posix_spawnattr_setexceptionports_np` but there is no
way to use that via any standard interfaces in `std::process::Command`
* thus, it seemed like a good idea to instead create our own custom
Command wrapper that on non-macOS hosts is a zero-cost wrapper of
`smol::process::Command`, while on macOS we reimplement the minimum to
achieve `smol::process::Command` with `posix_spawn` under-the-hood
Notably, this changeset improves git-blame in very large repos
significantly.
Release Notes:
- Fixed performance spawning child processes on macOS by always forcing
`posix_spawn` no matter what.
---------
Co-authored-by: Cole Miller <cole@zed.dev>
Closes#46252
Uses the `remoteUser` property returned from the devcontainer CLI so
that settings are respected. Additionally, checks for a default shell in
`passwd` if `$SHELL` is not set.
Release Notes:
- Fixed remote_user and shell inconsistencies from within dev containers
Closes#46801
When connecting to WSL2 with fish as the default shell, Zed gets stuck
at "Starting proxy". The connection works fine when bash is the default
shell.
In `start_proxy()`, the WSL command was being invoked with:
```rust
let proxy_process = match wsl_command_impl(&self.connection_options, "env", &proxy_args, false)
```
Changing the last argument to true invokes the WSL command with `--exec`
flag which executes the command in the WSL environment without spawning
a new shell.
With above fix I can launch Zed from WSL (Arch) with fish.
## Summary
This PR extends the `always_allow` tool permission patterns to work with
Nushell, Elvish, and Rc shells. Previously, these shells were
incorrectly excluded because they don't use `&&`/`||` operators for
command chaining. However, brush-parser can safely parse their command
syntax since they all use `;` for sequential execution.
## Changes
- Add `ShellKind::Nushell`, `ShellKind::Elvish`, and `ShellKind::Rc` to
`supports_posix_chaining()`
- Split `ShellKind::Unknown` into `ShellKind::UnknownWindows` and
`ShellKind::UnknownUnix` to preserve platform-specific fallback behavior
while still denying `always_allow` patterns for unrecognized shells
- Add comprehensive tests for the new shell support
- Clarify documentation about shell compatibility
## Shell Notes
- **Nushell**: Uses `;` for sequential execution. The `and`/`or`
keywords are boolean operators on values, not command chaining.
- **Elvish**: Uses `;` to separate pipelines. Does not have `&&` or `||`
operators. Its `and`/`or` are special commands operating on values.
- **Rc (Plan 9)**: Uses `;` for sequential execution and `|` for piping.
Does not have `&&`/`||` operators.
## Security
Unknown shells still return `false` from `supports_posix_chaining()`, so
`always_allow` patterns are denied for safety when we can't verify the
shell's syntax.
(No release notes because granular tool permissions are still
feature-flagged.)
Release Notes:
- N/A
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
- Remove the newlines in the unzip command string, which made it not
work.
- Fix spawning the terminal and tasks. Unfortunately, the Windows
OpenSSH server has limitations that require rather ugly workarounds.
Release Notes:
- N/A
When I kill the remote proxy, it seems like `ssh` exits with code -1.
Since that's less than or equal to 0, this means we don't try to restart
the connection. The proxy really shouldn't exit for any reason, so let's
get rid of this check altogether.
Release Notes:
- N/A
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
The client has not yet setup its own side of things so the message will
always error as being unhandled anyways.
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Assimilates https://github.com/zed-industries/zed/pull/46856 and adds
support for reveal file manager in the project panel on wsl. Closes
https://github.com/zed-industries/zed/pull/46856
Release Notes:
- Fixed "Reveal in File Manager" not working for WSL remote connections
on Windows.
- Show "Reveal in File Manager" in the project panel context menu on WSL
---------
Co-authored-by: Max Malkin <maxim_malkin@outlook.com>
Follow up to https://github.com/zed-industries/zed/pull/47200, fixing an
edge case where if the heartbeat failure disconnects us we get stuck in
an unconnectable state
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Adds the ability to create a dev container definition from scratch.
Additionally, separates devcontainer logic out into its own crate, since
it was getting sufficiently complex to separate from the
`recent_projects` crate.
A screen recording of the modal experience:
https://github.com/user-attachments/assets/f6cf95e1-eb7b-4ca3-86c7-c1cbc26ca557
Release Notes:
- Added modal to initialize a dev container definition in the project
with `projects: initialize dev container`
- Added podman support for dev container actions with the `use_podman`
setting
- Improved devcontainer error handling
---------
Co-authored-by: Sam Coward <idoru42@gmail.com>
Closes #ISSUE
Moves the settings content definitions into their own crate, so that
they are compiled+cached separately from settings, primarily to avoid
recompiles due to changes in gpui. In that vain many gpui types such as
font weight/features, and `SharedString` were replaced in the content
crate, either with `*Content` types for font/modifier things, or
`String`/`Arc<str>` for `SharedString`. To make the conversions easy a
new trait method in the settings crate named `IntoGpui::into_gpui`
allows for `into()` like conversions to the gpui types in
`from_settings` impls.
Release Notes:
- N/A
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Release Notes:
- Opening bundled files, keymap, and local release notes now opens in
remote windows instead of opening a new local zed window
- Opening the settings files, keymap files, task files, debug files and
logs will now open within wsl windows instead of opening a new local zed
window