{"slug": "lying-to-zed-to-get-its-remote-server-running-on-freebsd", "title": "Lying to Zed to get its remote server running on FreeBSD", "summary": "A developer documented running Zed's remote server on FreeBSD by installing the linux_base-rl9 compatibility layer and placing a wrapper script at ~/.local/bin/uname that reports \"Linux x86_64\" only for the `-sm`/`-ms` queries, so Zed uploads its Linux server to ~/.zed_server/ instead of rejecting the host. The setup also requires FreeBSD commit ec6629628cb, present in main, stable/15 and releng/15.2, which fixes a directory-listing bug that otherwise leaves the project tree empty with \"Invalid argument (os error 22)\"; without the fix, a quiet variant of the bug dropped 112 of 1,002 files from the listing. The author, who disables Zed's AI features via `\"disable_ai\": true` and uses OpenCode for regression testing and edge-case work, called the uname wrapper \"a small lie, but a contained one\" and said it is the part he would most like to retire.", "body_md": "I’ve used VS Code since I was about 16, so for almost ten years and honestly it was mostly fine. But as of late it has become unbearable: slow, and increasingly weighed down by AI slop, on the other hand Zed is fast, and it has a native switch that turns all of its AI stuff off, which I absolutely adore. (It’s a toggle in Settings, or `\"disable_ai\": true` in your settings file.)\n\nThat’s not because I avoid LLMs. I just don’t want them trying to edit every line I write, or sitting in the corner judging me ominously. I still write most of my FOSS code myself: it keeps me sane, and I feel FOSS developers have a responsibility to keep code clean for LLMs to *steal* from lol. What I do hand off is the tiring part: regression testing, edge cases, and exploration beyond what my tiny 20-watt brain can hold. For that I use harnesses that work well with models, like [OpenCode](https://opencode.ai). Beyond auto-complete.. I’ve always found entire harnesses like claude code etc. very clunky in editors.\n\n## Getting it working\n\nZed has no FreeBSD remote server offering (yet), but FreeBSD can run Linux binaries in spades, so we use Zed’s Linux one. All you need is a FreeBSD server you can SSH into.\n\n### 1. Turn on Linux compatibility\n\n```\nsudo sysrc linux_enable=YES\nsudo service linux start\nsudo pkg install linux_base-rl9\nkldstat -m linux64    # should print a module\n```\n\n### 2. Make Zed pick the Linux server\n\nZed asks the host what platform it runs. FreeBSD says `FreeBSD amd64`, there’s no server for that, and Zed gives up. This wrapper says `Linux x86_64` to that one question and passes everything else to the real `uname`. Save it as `~/.local/bin/uname` and make it executable:\n\n``` bash\n#!/bin/sh\ncase \"$#:$*\" in\n  1:-sm|1:-ms) printf 'Linux x86_64\\n' ;;\n  *) exec /usr/bin/uname \"$@\" ;;\nesac\n```\n\nPut `~/.local/bin` on `PATH` for non-interactive SSH sessions. For zsh, add this to `~/.zshenv` (use your shell’s equivalent):\n\n```\nexport PATH=\"$HOME/.local/bin:$PATH\"\n```\n\nIt’s a small lie, but a contained one, and the part I’d love to retire.\n\n### 3. Connect.\n\nConnect over SSH from Zed and open a folder. The first connection uploads the Linux server to `~/.zed_server/`. To watch, run `tail -f ~/.local/share/zed/logs/server-workspace-*.log`.\n\n### 4. Run a kernel with the (FreeBSD) fix\n\nWithout it you’ll connect fine and get an empty project tree, with this in the server log:\n\n```\nerror processing entry failed to read directory entry\nCaused by:\n    Invalid argument (os error 22)\n```\n\nThe fix is [ec6629628cb](https://github.com/freebsd/freebsd-src/commit/ec6629628cbed2816f27aed8a6d5f9eb3521c1c1), in `main`, `stable/15` and `releng/15.2`, so 15.2-RELEASE will include it. If the error persists, check you booted the new kernel with `uname -v`.\n\n## The bug in layman terms\n\nTo show your project, Zed asks the server for the names of the files in each folder, a few at a time. Zed sets aside a small amount of space for each batch.\n\nPicture a teacher reading a class roll to a student who writes the names on a sticky note (I couldn’t come up with a better analogy, bare with me). The teacher crosses off each name as she reads it, and she never reads a crossed-off name again. The student’s note has room for 6 names. The teacher reads in groups of 4, a sensible habit that works fine almost every time. But 6 isn’t a whole number of groups, so she reads 8 names and crosses all 8 off. Only 6 fit on the note.\n\nNow she’s in a bind. Her slip was grabbing more than fit before checking. She can’t hand over the 6 that fit and keep the other 2 for later, because they’re already crossed off and she would never read them again. So she reports an error instead, which at least is loud. When I tried the quiet way, 112 of my 1,002 files vanished (from the listing) with no warning. A loud error beats a quiet loss I’m pretty sure. Zed saw the error and decided it couldn’t read the folder, and showed it up empty.\n\nThe fix is small: read no more names than fit on the note. Read 6, cross off 6, come back for the next 6, until the roll is done. In the real bug, the teacher is FreeBSD’s Linux translator (Linuxulator), the note is a 768-byte buffer, and a group of 4 names is a 512-byte block.\n\n## For the kernel nerds\n\nThis is a regression. A February 2026 fix for NFS directories returning bogus entries ([110f2567cb51](https://github.com/freebsd/freebsd-src/commit/110f2567cb51f1eeddbd5d9937000ad64f6dc746), PR [292282](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=292282)) changed how `sys/compat/linux/linux_file.c` sizes its native read, in both `linux_getdents()` and `linux_getdents64()`:\n\n``` php\n/* before February 2026 */\nbuflen = min(args->count, MAXBSIZE);\n\n/* after: every request rounded up to a 512-byte block */\nbuflen = min(roundup2(args->count, DEV_BSIZE), MAXBSIZE);\n```\n\nIf the caller’s buffer isn’t a multiple of 512 bytes, the native batch can outgrow it. The emulation layer converts records until one doesn’t fit and returns `EINVAL`, but `kern_getdirentries()` has already advanced the directory offset past the whole batch. That’s why returning a partial batch isn’t enough on its own.\n\nThe fix ([ec6629628cb](https://github.com/freebsd/freebsd-src/commit/ec6629628cbed2816f27aed8a6d5f9eb3521c1c1), reviewed in [D60760](https://reviews.freebsd.org/D60760)):\n\n``` php\nif (args->count >= DEV_BSIZE)\n\tbuflen = min(args->count, MAXBSIZE);\nelse\n\tbuflen = roundup2(args->count, DEV_BSIZE);\n```\n\nIt’s safe because Linux directory records are never larger than FreeBSD’s: for `dirent64` the header is 19 bytes against 24, and both round each record up to a multiple of 8. A native batch that fits in the caller’s buffer translates into one that fits too.\n\nLegacy `getdents` had the same bug for 32-bit and 64-bit callers. My first patch only fixed `getdents64`; fixing both took the test matrix from 86 failures to zero.\n\n## Acknowledgements\n\nI’ve had plenty of patches merged before this one, but this was among the quickest to land in `releng/15.2`: it didn’t sit in `stable` for long (thankfully!). The release engineering team (re@) runs a really cool process that I am yet to fully understand in depth. Thank you also to Kyle Evans (kevans@) and Mark Johnston (markj@) for reviewing the patch and for their notes along the way.", "url": "https://wpnews.pro/news/lying-to-zed-to-get-its-remote-server-running-on-freebsd", "canonical_source": "https://hayzam.com/blog/03-lying-to-zeds-remote-editor/", "published_at": "2026-11-13 22:54:47+00:00", "updated_at": "2026-11-13 23:20:50.682314+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Zed", "FreeBSD", "VS Code", "OpenCode", "linux_base-rl9"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/lying-to-zed-to-get-its-remote-server-running-on-freebsd", "markdown": "https://wpnews.pro/news/lying-to-zed-to-get-its-remote-server-running-on-freebsd.md", "text": "https://wpnews.pro/news/lying-to-zed-to-get-its-remote-server-running-on-freebsd.txt", "jsonld": "https://wpnews.pro/news/lying-to-zed-to-get-its-remote-server-running-on-freebsd.jsonld"}}