11 KiB
heliux
Nix flake for the Helium browser, with NixOS and home-manager modules for declarative extension, policy and settings management — plus an optional bundled Widevine CDM so DRM-protected streaming works.
Note
Only
x86_64-linuxis actively tested.Also this started as a copy of gitlab.com/ntgn/helium-flake, which in turn was based on schembriaiden/helium-browser-nix-flake. All credit for the original packaging work goes to them. :3
What this adds on top of upstream
- Tracks current Helium releases, updated daily by GitHub Actions.
- An
enableWidevinebuild flag and a ready-madehelium-widevinepackage, so Netflix, HBO and other DRM sites play.
Quick start
nix run github:elwrcl/heliux
DRM / Widevine
Helium is compiled with Widevine support, but upstream ships no CDM library and
the wrapper passes --disable-component-update, so the component updater can
never fetch one at runtime. The result is that DRM playback silently fails.
The helium-widevine package bundles the CDM next to the binary, which fixes
playback without re-enabling the update machinery:
programs.helium.package = helium.packages.${system}.helium-widevine;
The CDM is proprietary, so this output requires allowUnfree. Plain helium is
unchanged and stays free-software only.
NixOS + home-manager
Important
You need both modules. Policies only apply when they are written to
/etc/chromium/policies/managed/, which is what the NixOS module does; the home-manager module handles extensions, preferences and the wrapper.
Add the flake to your inputs:
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager.url = "github:nix-community/home-manager";
helium.url = "github:elwrcl/helium-flake";
};
}
Then configure it:
{ config, pkgs, helium, ... }:
{
imports = [
# Writes policies to /etc/chromium/policies/managed/
helium.nixosModules.helium
];
home-manager.users.${YOUR_USERNAME} = {
imports = [ helium.homeModules.helium ];
programs.helium = {
enable = true;
defaultBrowser = true;
# Bundled Widevine CDM, for DRM streaming
package = helium.packages.${pkgs.system}.helium-widevine;
extensions = [
# uBlock Origin
{ id = "cjpalhdlnbpafiamejdnhcphjbkeiagm"; }
];
# Added to the wrapper
extraFlags = [
"--force-dark-mode"
];
# Merged into the policy file in /etc
extraPolicies = {
HomepageLocation = "https://start.duckduckgo.com";
PasswordManagerEnabled = false;
MetricsReportingEnabled = false;
};
# See "Preferences" below
preferences = {
browser.show_home_button = true;
bookmark_bar.show_on_all_tabs = true;
};
};
};
}
Options Reference
The following options are available under programs.helium:
| Option | Type | Default | Description |
|---|---|---|---|
enable |
boolean | false |
Whether to enable the Helium browser module. |
package |
package | self.packages.helium |
The helium package to use. |
extensions |
list of submodules | [] |
Extensions to install: { id, hash } (hash only needed in unpacked mode). |
extensionInstallMode |
"policy" or "unpacked" |
"policy" |
How extensions are installed — see below. |
extraFlags |
list of strings | [] |
Command line arguments passed to the wrapper. Do not pass --enable-features here — use extraFeatures. |
extraFeatures |
list of strings | [] |
Chromium features merged into the single --enable-features switch. |
allowFileAccessFromFiles |
boolean | false |
Pass --allow-file-access-from-files. Lets any local file:// page read other files on disk — see below. |
extraPolicies |
attribute set | {} |
Raw Chromium policies to apply. |
preferences |
attribute set | {} |
Json that will be merged into XDG Config. |
defaultBrowser |
boolean | false |
Set Helium as the default browser in XDG mimeapps. |
Flags and features
Chromium stores one value per command line switch, so a second
--enable-features=… silently replaces the first instead of adding to it.
Both the package and the home-manager wrapper need to enable features, so they
build a single combined switch: the package exposes its list as
passthru.enabledFeatures, and the module merges its own additions and your
extraFeatures into it.
That is why features go in extraFeatures rather than extraFlags — a raw
--enable-features in extraFlags would come last and wipe out everything
else.
Warning
allowFileAccessFromFilesdrops the same-origin restriction forfile://pages. Any local HTML file you open — including one you just downloaded — can then read other files on your disk and send them anywhere. It is off by default; only turn it on if you specifically need local pages to load local resources.
Policies
Since Helium is based on Chromium, you can use any of the standard Chromium
policies in the extraPolicies block. You can find a full list of available
names and values at the Chrome Enterprise Policy List.
Common useful policies:
BrowserSignin: Set to0to disable account sign-in.BookmarkBarEnabled: Set totrueto force the bookmark bar to show.URLBlocklist: A list of URL patterns to block.
Preferences
These are usually what you imperatively choose in the Settings menu. You can
find all the json keys and values inside the helium browser by typing
helium://prefs-internals/ and searching for the values.
Important
The merge happens once, at activation time. Chromium owns this file while it runs and rewrites it from memory on exit, so close the browser before you activate or your settings get written back over. Preferences that Chromium protects with a MAC in
Secure Preferencescannot be set this way at all — it spots the outside edit and resets them. UseextraPoliciesfor anything that has to stick.
{
programs.helium.preferences = {
browser.show_home_button = false;
bookmark_bar = {
show_apps_shortcut = false;
show_managed_bookmarks = false;
show_on_all_tabs = false;
show_tab_groups = false;
};
helium.browser.layout = 1;
};
}
Extension install modes
extensionInstallMode = "policy" (the default) force-installs every entry in
extensions through the ExtensionInstallForcelist Chromium policy. The
browser downloads and installs each extension itself, so:
- the real Web Store ID and signature are kept — native messaging hosts such as KeePassXC-Browser match on that ID and only work this way,
- the install is recorded in the profile, so each extension's
onInstalledhandler runs once instead of on every launch, - extensions keep updating themselves.
This mode needs the NixOS module, which is what writes the policy file into
/etc/chromium/policies/managed/. No hash is required.
extensionInstallMode = "unpacked" pins the CRXs in the Nix store and hands
them to the browser with --load-extension. It is fully reproducible and needs
no NixOS module, but Chromium treats command-line extensions as freshly
installed on every start — every extension re-runs its onInstalled handler,
which for many extensions means a welcome tab on each launch — and it derives
the extension ID from the load path unless the manifest carries a key. Each
entry needs a hash in this mode; see below.
Obtaining extensions
Only needed for extensionInstallMode = "unpacked".
Use the prefetch-nix binary, which the package installs alongside the browser,
to obtain the nix code you need.
The Web Store decides which build of an extension to serve from the
prodversion, os and arch in the request, so a hash is only valid for the
exact URL it came from. prefetch-nix is wrapped by the home-manager module so
it already asks for the same URL the module will later fetch. If you run the
script straight out of the repo instead, override the defaults with
HELIUM_PRODVERSION, HELIUM_OS, HELIUM_ARCH, HELIUM_OS_ARCH and
HELIUM_NACL_ARCH to match your programs.helium.prodversion and target
platform, or the hash will not match at build time.
Note
If the store rate-limits you it answers with an error page rather than a CRX.
prefetch-nixchecks for the CRX3 magic and reports# FAILEDinstead of printing a hash for it, and the build refuses such a file too — so a rate-limited fetch can never end up as a silently empty extension.
You can copy the IDs from the URL in the chrome web store:
https://chromewebstore.google.com/detail/bitwarden-password-manage/nngceckbapebfimnlniiiahkandclblb
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ THIS PART
Example Usage:
prefetch-nix nngceckbapebfimnlniiiahkandclblb cjpalhdlnbpafiamejdnhcphjbkeiagm
# OUTPUT:
extensions = [
{ id = "nngceckbapebfimnlniiiahkandclblb"; hash = "sha256-XOVs2Tvay8hQ13SHz+728BDu2mMyQ0JxUuUI6FZ1NaM="; }
{ id = "cjpalhdlnbpafiamejdnhcphjbkeiagm"; hash = "sha256-FIbmYVj8cmXce7Vq4h7d2nOjmk4RkCnABmC4y5NDyGk="; }
];
Updating Helium
The update-helium GitHub Actions workflow checks upstream once a day, rewrites
modules/sources.nix with the new version and hashes, verifies the flake still
builds, and commits the bump. You can also trigger it by hand from the Actions
tab.
Note that this keeps the flake current — to pull an update into your system
you still run nix flake update helium and rebuild.
Note
GitHub disables scheduled workflows in repositories with no activity for 60 days. If updates stop arriving, re-enable the workflow in the Actions tab.
Flake outputs
| Output | Description |
|---|---|
packages.<system>.helium |
Helium browser package |
packages.<system>.helium-widevine |
Helium with the Widevine CDM bundled |
apps.<system>.helium |
nix run entry point |
homeModules.helium |
home-manager module |
nixosModules.helium |
nixos module |
devShells.<system>.default |
Shell with Helium available |
formatter.<system> |
nixfmt-tree (nix fmt) |
checks.<system>.build |
Build check (nix flake check) |