ChromeOS Ash shell. On NixOS. It's bad.

This repo takes a prebuilt ChromiumOS Chrome binary, puts it in front of X11, and teaches regular Linux to answer the ChromeOS D-Bus APIs Ash expects.
The result: the Ash desktop shell — system tray, app launcher, shelf, Terminal SWA, the works — running as your Linux desktop.
Ash binary (prebuilt ChromiumOS Chrome)
│
└── D-Bus (system bus)
├── org.chromium.flimflam ←→ [shill-bridge] → NetworkManager
├── org.chromium.cras ←→ [cras-bridge] → PipeWire/PulseAudio
├── org.chromium.PowerManager ←→ [power-bridge] → UPower
└── org.chromium.SessionManager ←→ [session-bridge]
└── + org.chromium.UserDataAuth (compat)
Each bridge is a small Go daemon that:
nix build --impure .#ash-vm # full VM image
./result/bin/run-nixos-vm
To run as a NixOS desktop, add the module to your flake:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
ash.url = "github:vitalyavolyn/chromeos-linux";
};
outputs = { nixpkgs, ash, ... }: {
nixosConfigurations.my-machine = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
ash.nixosModules.default
({ pkgs, ... }: {
services.chromeos-linux = {
enable = true;
user = "vitalya" # replace with your username
};
})
];
};
};
}
The desktop comes up and is usable as a very strange Linux shell:
These work enough to be useful, but they are not Chromebook-grade:
| Area | State |
|---|---|
| Network/audio/battery tray data | Real bridge data exists, but very limited. |
| Linux app launcher integration | .desktop files are written into Crostini prefs, but they may not launch (most do, though.) |
| Terminal emulation | Basic shell speaks just enough VT100 to render fastfetch. Just use your linux terminal app. |
| Audio devices | Volume works, but CRAS nodes are hardcoded Speaker/Headphone/Mic entries rather than real PipeWire node discovery. |
| Managed Linux app rendering | X11 apps on DISPLAY=:0 get hardware Vulkan. Wayland-managed app windows use the wl_shm path. |
CHROMEOS_RELEASE_NAME=Chrome OS in /etc/lsb-release flips Ash from built-in
fallback clients to real D-Bus ones — but also enables ChromeOS Mojo IPC mode, which
requires a /run/mojo/service_manager.sock peer. Our mojo-stub does the
ACCEPT_INVITEE handshake and drains messages, but it's basic.
The Chrome binary gets a few small patches:
chrome.terminalPrivate.openVmshellProcess(...) with eval(localStorage.t||"") so we can inject a terminal implementation.'wasm-unsafe-eval' → 'unsafe-eval' so the Terminal page can eval injected JS--database arg (fixes a crash)All patches fail loudly if the binary changes.
1 commits
Go
61.3%
Nix
32.9%
JavaScript
5.8%
ChromeOS Ash shell. On NixOS. It's bad.

This repo takes a prebuilt ChromiumOS Chrome binary, puts it in front of X11, and teaches regular Linux to answer the ChromeOS D-Bus APIs Ash expects.
The result: the Ash desktop shell — system tray, app launcher, shelf, Terminal SWA, the works — running as your Linux desktop.
Ash binary (prebuilt ChromiumOS Chrome)
│
└── D-Bus (system bus)
├── org.chromium.flimflam ←→ [shill-bridge] → NetworkManager
├── org.chromium.cras ←→ [cras-bridge] → PipeWire/PulseAudio
├── org.chromium.PowerManager ←→ [power-bridge] → UPower
└── org.chromium.SessionManager ←→ [session-bridge]
└── + org.chromium.UserDataAuth (compat)
Each bridge is a small Go daemon that:
nix build --impure .#ash-vm # full VM image
./result/bin/run-nixos-vm
To run as a NixOS desktop, add the module to your flake:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
ash.url = "github:vitalyavolyn/chromeos-linux";
};
outputs = { nixpkgs, ash, ... }: {
nixosConfigurations.my-machine = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
ash.nixosModules.default
({ pkgs, ... }: {
services.chromeos-linux = {
enable = true;
user = "vitalya" # replace with your username
};
})
];
};
};
}
The desktop comes up and is usable as a very strange Linux shell:
These work enough to be useful, but they are not Chromebook-grade:
| Area | State |
|---|---|
| Network/audio/battery tray data | Real bridge data exists, but very limited. |
| Linux app launcher integration | .desktop files are written into Crostini prefs, but they may not launch (most do, though.) |
| Terminal emulation | Basic shell speaks just enough VT100 to render fastfetch. Just use your linux terminal app. |
| Audio devices | Volume works, but CRAS nodes are hardcoded Speaker/Headphone/Mic entries rather than real PipeWire node discovery. |
| Managed Linux app rendering | X11 apps on DISPLAY=:0 get hardware Vulkan. Wayland-managed app windows use the wl_shm path. |
CHROMEOS_RELEASE_NAME=Chrome OS in /etc/lsb-release flips Ash from built-in
fallback clients to real D-Bus ones — but also enables ChromeOS Mojo IPC mode, which
requires a /run/mojo/service_manager.sock peer. Our mojo-stub does the
ACCEPT_INVITEE handshake and drains messages, but it's basic.
The Chrome binary gets a few small patches:
chrome.terminalPrivate.openVmshellProcess(...) with eval(localStorage.t||"") so we can inject a terminal implementation.'wasm-unsafe-eval' → 'unsafe-eval' so the Terminal page can eval injected JS--database arg (fixes a crash)All patches fail loudly if the binary changes.
1 commits
Go
61.3%
Nix
32.9%
JavaScript
5.8%