> For the complete documentation index, see [llms.txt](https://kaizo-1.gitbook.io/kaizo-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kaizo-1.gitbook.io/kaizo-docs/common-issues/nixos.md).

# NixOS

Kaizo’s normal install stamps file capabilities (`setcap`) so you can attach without staying on sudo. On NixOS that breaks library loading: capabilities set `AT_SECURE`, glibc drops `LD_*`, and [nix-ld](https://github.com/nix-community/nix-ld) never sees `libEGL.so.1`.

Use this page instead of the usual `setcap` / app-menu path.

{% hint style="success" %}
**Credit:** This workaround was written and shared by @yori. Thanks for tracing the capability vs nix-ld conflict and the launcher.
{% endhint %}

## Symptoms

* `error while loading shared libraries: libEGL.so.1: cannot open shared object file`
* `steam-run` cannot attach to Sober
* nix-ld still fails after you `setcap` the binary
* Baking libraries into `DT_RUNPATH` still fails once caps are on the file

{% hint style="warning" %}
Do not mix a sudo launch and a normal user launch. That is what caused the HWID mismatch. Stick to this wrapper every time.
{% endhint %}

## 1. Enable nix-ld

Add this to `configuration.nix`, then rebuild:

```nix
# configuration.nix
programs.nix-ld = {
  enable = true;
  libraries = with pkgs; [
    libglvnd mesa
    xorg.libX11 xorg.libXcursor xorg.libXi xorg.libXrandr xorg.libXrender
    libxkbcommon wayland freetype fontconfig dbus glib
  ];
};
```

```bash
sudo nixos-rebuild switch
```

## 2. Install Kaizo as usual

Follow How to run so the binary exists at `~/.local/share/kaizo/kaizo` (or set `KAIZO_BIN` to wherever you put it).

You can ignore a failed `setcap` on NixOS. The wrapper strips caps on purpose.

### 3. Run the script below

```
chmod +x kaizo-nixos.sh
./kaizo-nixos.sh
```

### kaizo-nixos.sh

```bash
#!/usr/bin/env bash
# ============================================================================
# kaizo NixOS fix — by @yori
# Self-contained launcher: fixes nix-ld loading, attach privileges, HWID
# stability, and overlay handoff when running kaizo (Roblox external) on
# NixOS. Tested against Sober.
#
# WHY THIS EXISTS (short version):
#  - kaizo ships with file capabilities. File caps => AT_SECURE=1 => glibc
#    drops LD_* env vars => nix-ld can't find libraries ("libEGL.so.1 not
#    found"). Caps and nix-ld are mutually exclusive on exec.
#  - kaizo's stamp->drop flow assumes Ubuntu-style file caps survive after
#    dropping to the user. On NixOS those caps are exactly what kills the
#    binary, and dropping mid-run also changes the privilege context
#    (HWID mismatch) and loses attach power over Sober's sandboxed Roblox.
#
# WHAT THIS SCRIPT DOES:
#  1. launches kaizo as root, re-injecting NIX_LD/NIX_LD_LIBRARY_PATH after
#     sudo (sudo's env_reset scrubs them)
#  2. strips caps at launch and shadows `setcap` with a stub, so the updater's
#     restamping no-ops and AT_SECURE stays 0 (binary stays pristine)
#  3. passes SUDO_USER/SUDO_UID/SUDO_GID through so the overlay handoff works
#  4. compiles+injects a tiny LD_PRELOAD shim that fakes success for
#     setuid/setgid-family calls (kaizo *thinks* it dropped; real creds stay
#     root: fingerprint stable, attach works, license validates normally --
#     no auth is bypassed, this only adapts the launch flow to NixOS)
#
# REQUIREMENTS:
#  - NixOS with programs.nix-ld.enable = true and the usual GUI libraries in
#    programs.nix-ld.libraries (mesa/libglvnd/xorg basics); see README/wiki
#  - X11 available (Xwayland fine); first run may fetch gcc via nix
#
# USAGE:
#   chmod +x kaizo-nixos.sh
#   ./kaizo-nixos.sh            # then launch Roblox via Sober
# ============================================================================
set -euo pipefail

BIN="${KAIZO_BIN:-$HOME/.local/share/kaizo/kaizo}"
FARM="/run/current-system/sw/share/nix-ld/lib"
FIXDIR="${KAIZO_FIXDIR:-$HOME/.local/share/kaizo/nixos-fix}"
SHIM_SRC="$FIXDIR/droprefuse.c"
SHIM_SO="$FIXDIR/droprefuse.so"

[ -f "$BIN" ] || { echo "kaizo binary not found at $BIN" >&2; exit 1; }
[ -d "$FARM" ] || {
  echo "nix-ld farm missing at $FARM" >&2
  echo "enable programs.nix-ld (+libraries) in your configuration.nix" >&2
  exit 1
}

# ---------------------------------------------------------------- shim build
mkdir -p "$FIXDIR"
if [ ! -f "$SHIM_SO" ] || [ "$SHIM_SRC" -nt "$SHIM_SO" ]; then
  cat > "$SHIM_SRC" <<'CEOF'
// droprefuse.c -- report the invoking user's identity, refuse privilege
// drops (fake success). Keeps kaizo's flow happy on NixOS while the process
// retains its real (root) credentials. No auth/licensing logic touched.
#define _GNU_SOURCE
#include <grp.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

static uid_t target_uid(void) {
    const char *s = getenv("KAIZO_DROP_UID");
    return s ? (uid_t)atoi(s) : (uid_t)1000;
}
static gid_t target_gid(void) {
    const char *s = getenv("KAIZO_DROP_GID");
    return s ? (gid_t)atoi(s) : (gid_t)100;
}

uid_t getuid(void)  { return target_uid(); }
uid_t geteuid(void) { return target_uid(); }
gid_t getgid(void)  { return target_gid(); }
gid_t getegid(void) { return target_gid(); }

int getresuid(uid_t *r, uid_t *e, uid_t *s) {
    uid_t u = target_uid();
    if (r) *r = u; if (e) *e = u; if (s) *s = u; return 0;
}
int getresgid(gid_t *r, gid_t *e, gid_t *s) {
    gid_t g = target_gid();
    if (r) *r = g; if (e) *e = g; if (s) *s = g; return 0;
}

int setuid(uid_t u)                        { (void)u; return 0; }
int seteuid(uid_t u)                       { (void)u; return 0; }
int setreuid(uid_t r, uid_t e)             { (void)r; (void)e; return 0; }
int setresuid(uid_t r, uid_t e, uid_t s)   { (void)r; (void)e; (void)s; return 0; }
uid_t setfsuid(uid_t u)                    { (void)u; return target_uid(); }

int setgid(gid_t g)                        { (void)g; return 0; }
int setegid(gid_t g)                       { (void)g; return 0; }
int setregid(gid_t r, gid_t e)             { (void)r; (void)e; return 0; }
int setresgid(gid_t r, gid_t e, gid_t s)   { (void)r; (void)e; (void)s; return 0; }
gid_t setfsgid(gid_t g)                    { (void)g; return target_gid(); }

int setgroups(size_t n, const gid_t *l)    { (void)n; (void)l; return 0; }
int initgroups(const char *u, gid_t g)     { (void)u; (void)g; return 0; }
CEOF
  echo "[*] building drop-refusal shim..."
  if command -v gcc >/dev/null 2>&1; then
    gcc -shared -fPIC -O2 -o "$SHIM_SO" "$SHIM_SRC"
  else
    nix --accept-flake-config shell nixpkgs#gcc -c gcc -shared -fPIC -O2 \
        -o "$SHIM_SO" "$SHIM_SRC"
  fi
fi

# ------------------------------------------------------- display permissions
# root draws windows on the user's session (Xwayland included)
UID_N="$(id -u)"
export DISPLAY="${DISPLAY:-:0}"
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$UID_N}"
if command -v xhost >/dev/null 2>&1; then
  xhost +SI:localuser:root >/dev/null 2>&1 || true
else
  nix --accept-flake-config shell nixpkgs#xorg.xhost \
      -c xhost +SI:localuser:root >/dev/null 2>&1 || true
fi

cd "$(dirname "$BIN")"

# ------------------------------------------------------------------- launch
exec sudo env DISPLAY="$DISPLAY" sh -c '
  BIN="'"$BIN"'"; FARM="'"$FARM"'"; SHIM_SO="'"$SHIM_SO"'"; UID_N="'"$UID_N"'"
  # keep AT_SECURE=0: strip caps now, neuter restamping during the run
  command -v setcap >/dev/null 2>&1 && setcap -r "$BIN" 2>/dev/null || true
  mkdir -p /tmp/.kaizo-nixos
  printf "#!/bin/sh\nexit 0\n" > /tmp/.kaizo-nixos/setcap
  chmod +x /tmp/.kaizo-nixos/setcap
  cd "$(dirname "$BIN")"
  exec env \
    PATH="/tmp/.kaizo-nixos:$PATH" \
    LD_PRELOAD="$SHIM_SO" \
    KAIZO_DROP_UID='"$UID_N"' \
    KAIZO_DROP_GID="$(stat -c %g "'"$HOME"'" 2>/dev/null || echo 100)" \
    NIX_LD="$FARM/ld.so" \
    NIX_LD_LIBRARY_PATH="$FARM" \
    HOME='"$HOME"' \
    USER="'$(id -un)'" \
    LOGNAME="'$(id -un)'" \
    XDG_RUNTIME_DIR="'"$XDG_RUNTIME_DIR"'" \
    '"${XAUTHORITY:+XAUTHORITY=$XAUTHORITY}"' \
    "$BIN" "$@"
' "$@"
~/scripts ❯ code kaizo-fix.sh 
~/scripts ❯ code kaizo-fix.sh 
~/scripts ❯ cat kaizo-fix.sh 
#!/usr/bin/env bash
# kaizo-fix: re-apply nix-ld RUNPATH + ptrace caps after every kaizo update.
# Why: file capabilities make the kernel set AT_SECURE=1, which makes glibc
# drop all LD_* env vars -> nix-ld's NIX_LD_LIBRARY_PATH never reaches the
# loader -> "libEGL.so.1 not found". Baking the lib dir into DT_RUNPATH
# survives secure mode because it lives in the ELF, not the environment.
set -euo pipefail
BIN="$HOME/.local/share/kaizo/kaizo"
RPATH="/run/current-system/sw/share/nix-ld/lib"
CAPS='cap_dac_override,cap_dac_read_search,cap_sys_ptrace,cap_setfcap=ep'

pkill -f 'share/kaizo/kaizo' 2>/dev/null || true  # avoid ETXTBSY
sleep 1

nix shell nixpkgs#patchelf -c patchelf --set-rpath "$RPATH" "$BIN"
sudo setcap "$CAPS" "$BIN"

echo "[+] rpath: $(nix shell nixpkgs#patchelf -c patchelf --print-rpath "$BIN")"
echo "[+] caps:  $(getcap "$BIN" | sed 's/.*://')"
echo "[+] kaizo patched - launch WITHOUT sudo"
```

## Related

* How to run
* Common issues
* Requirements


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://kaizo-1.gitbook.io/kaizo-docs/common-issues/nixos.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
