From 5edb1800dc23dfa57153be00b22874d8068a1105 Mon Sep 17 00:00:00 2001 From: elwrcl Date: Tue, 25 Aug 2026 02:27:01 +0300 Subject: [PATCH] first commit --- .envrc | 1 + .github/workflows/update.yml | 114 +++++++++++++++ .gitignore | 1 + LICENSE | 21 +++ README.md | 211 +++++++++++++++++++++++++++ flake.lock | 61 ++++++++ flake.nix | 25 ++++ modules/flake-module.nix | 7 + modules/home-manager.nix | 183 +++++++++++++++++++++++ modules/nixos.nix | 21 +++ modules/nixpkgs.nix | 11 ++ modules/outputs.nix | 60 ++++++++ modules/package.nix | 232 ++++++++++++++++++++++++++++++ modules/sources.nix | 25 ++++ scripts/prefetch-nix-extension.sh | 44 ++++++ 15 files changed, 1017 insertions(+) create mode 100644 .envrc create mode 100644 .github/workflows/update.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 modules/flake-module.nix create mode 100644 modules/home-manager.nix create mode 100644 modules/nixos.nix create mode 100644 modules/nixpkgs.nix create mode 100644 modules/outputs.nix create mode 100644 modules/package.nix create mode 100644 modules/sources.nix create mode 100755 scripts/prefetch-nix-extension.sh diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml new file mode 100644 index 0000000..9be27cd --- /dev/null +++ b/.github/workflows/update.yml @@ -0,0 +1,114 @@ +name: update-helium + +on: + schedule: + - cron: "0 6 * * *" + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: update-helium + cancel-in-progress: false + +jobs: + update-helium: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + - name: Bump sources.nix to the latest upstream release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + SOURCES_FILE="modules/sources.nix" + + # Anchor on the leading whitespace and the opening quote so this only + # matches the `versions` block and never the `x86_64-linux = versions:` + # attribute names further down the file. + get_current_version() { + grep -E "^[[:space:]]+$1 = \"" "$SOURCES_FILE" | head -1 | sed 's/.*= "\(.*\)";/\1/' + } + + get_latest_release() { + curl -fsSL -H "Authorization: Bearer $GH_TOKEN" \ + "https://api.github.com/repos/$1/releases/latest" | jq -r .tag_name + } + + # prefetch-file reports an SRI hash directly, so there is no separate + # `nix hash convert` step that could drift out of sync. + fetch_sri_hash() { + nix store prefetch-file --json "$1" | jq -r .hash + } + + update_version() { + sed -i "s|$1 = \"$2\";|$1 = \"$3\";|" "$SOURCES_FILE" + } + + # Every hash sits on the line directly below the url it belongs to. + update_hash() { + sed -i "/$1/{n; s|hash = \"[^\"]*\";|hash = \"$2\";|}" "$SOURCES_FILE" + } + + echo "--- Linux ---" + CURRENT_LINUX=$(get_current_version linux) + LATEST_LINUX=$(get_latest_release imputnet/helium-linux) + + if [ "$LATEST_LINUX" != "$CURRENT_LINUX" ]; then + echo "$CURRENT_LINUX -> $LATEST_LINUX" + BASE="https://github.com/imputnet/helium-linux/releases/download/$LATEST_LINUX" + X86_HASH=$(fetch_sri_hash "$BASE/helium-$LATEST_LINUX-x86_64_linux.tar.xz") + ARM_HASH=$(fetch_sri_hash "$BASE/helium-$LATEST_LINUX-arm64_linux.tar.xz") + update_version linux "$CURRENT_LINUX" "$LATEST_LINUX" + update_hash "x86_64_linux\.tar\.xz" "$X86_HASH" + update_hash "arm64_linux\.tar\.xz" "$ARM_HASH" + else + echo "already at $LATEST_LINUX" + fi + + echo "--- macOS ---" + CURRENT_DARWIN=$(get_current_version darwin) + LATEST_DARWIN=$(get_latest_release imputnet/helium-macos) + + if [ "$LATEST_DARWIN" != "$CURRENT_DARWIN" ]; then + echo "$CURRENT_DARWIN -> $LATEST_DARWIN" + BASE="https://github.com/imputnet/helium-macos/releases/download/$LATEST_DARWIN" + X86_HASH=$(fetch_sri_hash "$BASE/helium_${LATEST_DARWIN}_x86_64-macos.dmg") + ARM_HASH=$(fetch_sri_hash "$BASE/helium_${LATEST_DARWIN}_arm64-macos.dmg") + update_version darwin "$CURRENT_DARWIN" "$LATEST_DARWIN" + update_hash "x86_64-macos\.dmg" "$X86_HASH" + update_hash "arm64-macos\.dmg" "$ARM_HASH" + else + echo "already at $LATEST_DARWIN" + fi + + echo "HELIUM_LINUX=$(get_current_version linux)" >> "$GITHUB_ENV" + echo "HELIUM_DARWIN=$(get_current_version darwin)" >> "$GITHUB_ENV" + + # A bad hash or a reshuffled upstream tarball should fail here rather than + # land on main and break everyone who tracks this flake. + - name: Verify the flake still builds + run: nix build --no-link --print-build-logs .#helium + + - name: Commit and push + run: | + set -euo pipefail + + if [ -z "$(git status --porcelain)" ]; then + echo "No changes to commit." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add modules/sources.nix + git commit -m "chore: update helium (linux: $HELIUM_LINUX, darwin: $HELIUM_DARWIN)" + git push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92b2793 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.direnv diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..88b3a05 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 ntgn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8bb93e5 --- /dev/null +++ b/README.md @@ -0,0 +1,211 @@ +# heliux + +Nix flake for the [Helium browser](https://helium.computer/), 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-linux` is actively tested. +> +> Also this started as a copy of [gitlab.com/ntgn/helium-flake](https://gitlab.com/ntgn/helium-flake), +> which in turn was based on [schembriaiden/helium-browser-nix-flake](https://github.com/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 `enableWidevine` build flag and a ready-made `helium-widevine` package, so + Netflix, HBO and other DRM sites play. + +## Quick start + +```sh +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: + +```nix +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: + +```nix +# 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: + +```nix +{ 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 — see "Obtaining extensions" below + { + id = "cjpalhdlnbpafiamejdnhcphjbkeiagm"; + hash = "sha256-FIbmYVj8cmXce7Vq4h7d2nOjmk4RkCnABmC4y5NDyGk="; + } + ]; + + # 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 | `[]` | List of extensions to install `{ id, hash }`. | +| `extraFlags` | list of strings | `[]` | Command line arguments passed to the wrapper. | +| `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. | + +## 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](https://chromeenterprise.google/policies/). + +Common useful policies: + +- `BrowserSignin`: Set to `0` to disable account sign-in. +- `BookmarkBarEnabled`: Set to `true` to 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. + +```nix +{ + 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; + }; +} +``` + +## Obtaining extensions + +> [!WARNING] +> Be wary that if you are rate-limited that the file will be empty and the build will fail + +Use the provided binary `prefetch-nix-extension` to obtain the nix code you need. + +You can copy the IDs from the URL in the chrome web store: + +```ascii +https://chromewebstore.google.com/detail/bitwarden-password-manage/nngceckbapebfimnlniiiahkandclblb + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ THIS PART +``` + +Example Usage: + +```console +./prefetch-nix-extension.sh 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..helium` | Helium browser package | +| `packages..helium-widevine` | Helium with the Widevine CDM bundled | +| `apps..helium` | `nix run` entry point | +| `homeModules.helium` | home-manager module | +| `nixosModules.helium` | nixos module | +| `devShells..default` | Shell with Helium available | +| `formatter.` | `nixfmt-tree` (`nix fmt`) | +| `checks..build` | Build check (`nix flake check`) | diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6a12a3e --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1775087534, + "narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1775710090, + "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4c1018dae018162ec878d42fec712642d214fdfa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1774748309, + "narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "333c4e0545a6da976206c74db8773a1645b5870a", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..bd60536 --- /dev/null +++ b/flake.nix @@ -0,0 +1,25 @@ +{ + description = "A Nix flake for the Helium browser"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + }; + + outputs = + inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + imports = [ + ./modules/nixpkgs.nix + ./modules/outputs.nix + ./modules/flake-module.nix + ]; + }; +} diff --git a/modules/flake-module.nix b/modules/flake-module.nix new file mode 100644 index 0000000..6b80aa2 --- /dev/null +++ b/modules/flake-module.nix @@ -0,0 +1,7 @@ +{ self, ... }: +{ + flake = { + homeModules.helium = import ./home-manager.nix { inherit self; }; + nixosModules.helium = import ./nixos.nix; + }; +} diff --git a/modules/home-manager.nix b/modules/home-manager.nix new file mode 100644 index 0000000..3192509 --- /dev/null +++ b/modules/home-manager.nix @@ -0,0 +1,183 @@ +{ self }: +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.programs.helium; + + fetchExtension = + { id, hash }: + let + os = if pkgs.stdenv.isDarwin then "mac" else "linux"; + arch = if pkgs.stdenv.isAarch64 then "arm64" else "x64"; + os_arch = if pkgs.stdenv.isDarwin then "arm64" else "x86_64"; + in + pkgs.fetchurl { + name = "${id}.crx"; + url = "https://clients2.google.com/service/update2/crx?response=redirect&os=${os}&arch=${arch}&os_arch=${os_arch}&nacl_arch=x86-64&prod=chromiumcrx&prodchannel=stable&prodversion=120.0.0.0&acceptformat=crx3&x=id%3D${id}%26installsource%3Dondemand%26uc"; + inherit hash; + }; + + unpackExtension = + { id, hash }: + pkgs.runCommand "helium-ext-${id}" + { + nativeBuildInputs = [ pkgs.unzip ]; + src = fetchExtension { inherit id hash; }; + } + '' + mkdir -p $out + unzip -q $src -d $out || true + + # Remove the system-reserved metadata folder that causes the load error + rm -rf $out/_metadata + ''; + + resolvedExtensions = map (spec: { + inherit (spec) id; + unpacked = unpackExtension { inherit (spec) id hash; }; + }) cfg.extensions; + + # We add the IDs to the Allowlist policy so Helium doesn't disable them for being "unverified" + policyAttrs = { + ExtensionInstallAllowlist = map (ext: ext.id) cfg.extensions; + } + // cfg.extraPolicies; + + loadExtensionFlag = + if resolvedExtensions != [ ] then + [ "--load-extension=${lib.concatStringsSep "," (map (ext: "${ext.unpacked}") resolvedExtensions)}" ] + else + [ ]; + + heliumWithFlags = pkgs.symlinkJoin { + name = "helium-configured"; + paths = [ cfg.package ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/helium \ + ${lib.concatMapStringsSep " \\\n " (f: "--add-flags ${lib.escapeShellArg f}") ( + [ + "--disable-component-update" + "--allow-file-access-from-files" + ] + ++ loadExtensionFlag + ++ lib.optionals pkgs.stdenv.isLinux [ + "--ozone-platform-hint=auto" + "--enable-features=WaylandWindowDecorations,NativeNotifications,SystemNotifications" + ] + ++ cfg.extraFlags + )} + ''; + }; + +in +{ + options.programs.helium = { + enable = lib.mkEnableOption "Helium browser"; + package = lib.mkOption { + type = lib.types.package; + default = self.packages.${pkgs.stdenv.hostPlatform.system}.helium; + }; + extensions = lib.mkOption { + type = lib.types.listOf ( + lib.types.submodule { + options = { + id = lib.mkOption { type = lib.types.str; }; + hash = lib.mkOption { type = lib.types.str; }; + }; + } + ); + default = [ ]; + }; + extraFlags = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + extraPolicies = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + default = { }; + }; + defaultBrowser = lib.mkOption { + type = lib.types.bool; + default = false; + }; + + finalPolicyJson = lib.mkOption { + type = lib.types.str; + internal = true; + default = builtins.toJSON policyAttrs; + }; + + preferences = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + default = { }; + description = '' + Chromium preferences to set in the Default profile. + These are merged into ~/.config/net.imput.helium/Default/Preferences. + Type: 'helium://prefs-internals/' to search for the json keys and values + ''; + example = lib.literalExpression '' + { + "browser"."show_home_button" = true; + } + ''; + }; + }; + + config = lib.mkIf cfg.enable { + home = { + packages = [ + heliumWithFlags + pkgs.jq + pkgs.coreutils + ]; + + activation = lib.mkIf (cfg.preferences != { }) { + heliumPreferences = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + prefs_dir="$HOME/.config/net.imput.helium/Default" + prefs_file="$prefs_dir/Preferences" + nix_prefs='${builtins.toJSON cfg.preferences}' + + run mkdir -p "$prefs_dir" + + if [ -f "$prefs_file" ]; then + merged=$(${pkgs.jq}/bin/jq -s '.[0] * .[1]' "$prefs_file" - <<< "$nix_prefs") + if [ -n "$merged" ]; then + printf '%s\n' "$merged" > "$prefs_file" + fi + else + printf '%s\n' "$nix_prefs" > "$prefs_file" + fi + ''; + }; + }; + + xdg.mimeApps = lib.mkIf cfg.defaultBrowser { + enable = true; + defaultApplications = { + "text/html" = "helium.desktop"; + "x-scheme-handler/http" = "helium.desktop"; + "x-scheme-handler/https" = "helium.desktop"; + }; + }; + xdg.desktopEntries.helium = lib.mkIf cfg.defaultBrowser { + name = "Helium"; + exec = "${heliumWithFlags}/bin/helium %U"; + icon = "helium"; + terminal = false; + categories = [ + "Network" + "WebBrowser" + ]; + mimeType = [ + "text/html" + "x-scheme-handler/http" + "x-scheme-handler/https" + ]; + }; + }; +} diff --git a/modules/nixos.nix b/modules/nixos.nix new file mode 100644 index 0000000..d3d0544 --- /dev/null +++ b/modules/nixos.nix @@ -0,0 +1,21 @@ +{ config, lib, ... }: +let + # finding all home-manager users that have helium enabled + enabledUsers = lib.filterAttrs (_: user: user.programs.helium.enable or false) ( + config.home-manager.users or { } + ); + + # generate etc files per user to support multi-user systems + policyFiles = lib.mapAttrs' ( + name: user: + lib.nameValuePair "chromium/policies/managed/helium-${name}.json" { + text = user.programs.helium.finalPolicyJson; + mode = "0644"; + } + ) enabledUsers; +in +{ + config = lib.mkIf (enabledUsers != { }) { + environment.etc = policyFiles; + }; +} diff --git a/modules/nixpkgs.nix b/modules/nixpkgs.nix new file mode 100644 index 0000000..7b66b7f --- /dev/null +++ b/modules/nixpkgs.nix @@ -0,0 +1,11 @@ +{ inputs, ... }: +{ + perSystem = + { system, ... }: + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + config.allowUnfree = true; + }; + }; +} diff --git a/modules/outputs.nix b/modules/outputs.nix new file mode 100644 index 0000000..1e48faa --- /dev/null +++ b/modules/outputs.nix @@ -0,0 +1,60 @@ +{ ... }: +{ + perSystem = + { pkgs, system, ... }: + let + sources = import ./sources.nix; + + version = if pkgs.stdenv.isDarwin then sources.versions.darwin else sources.versions.linux; + + src = pkgs.fetchurl ( + (sources.srcs.${system} or (throw "Unsupported system: ${system}")) sources.versions + ); + + helium = pkgs.callPackage ./package.nix { inherit version src; }; + + # same build with the proprietary Widevine CDM bundled, so DRM-protected + # streaming works out of the box. Kept as a separate output rather than a + # default so the plain `helium` package stays free-software only. + helium-widevine = helium.override { enableWidevine = true; }; + + app = { + type = "app"; + program = "${helium}/bin/helium"; + meta = { + inherit (helium.meta) + description + homepage + license + platforms + ; + }; + }; + in + { + packages = { + default = helium; + inherit helium; + } + // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux { inherit helium-widevine; }; + + apps = { + default = app; + helium = app; + }; + + devShells.default = pkgs.mkShell { + packages = [ + helium + pkgs.nix-update + ]; + }; + + formatter = pkgs.nixfmt-tree; + + checks = { + build = helium; + }; + }; + +} diff --git a/modules/package.nix b/modules/package.nix new file mode 100644 index 0000000..71bbf5b --- /dev/null +++ b/modules/package.nix @@ -0,0 +1,232 @@ +{ + stdenv, + lib, + makeWrapper, + autoPatchelfHook, + copyDesktopItems, + makeDesktopItem, + _7zz, + # runtime/build deps + alsa-lib, + at-spi2-atk, + at-spi2-core, + atk, + cairo, + cups, + dbus, + expat, + fontconfig, + freetype, + gdk-pixbuf, + glib, + gtk3, + kdePackages, + libdrm, + libgbm, + libGL, + libpulseaudio, + libva, + libvdpau, + libx11, + libxcb, + libxcomposite, + libxcursor, + libxdamage, + libxext, + libxfixes, + libxi, + libxkbcommon, + libxrandr, + libxrender, + libxscrnsaver, + libxshmfence, + libxtst, + libuuid, + mesa, + nspr, + nss, + pango, + pipewire, + systemd, + vulkan-loader, + wayland, + widevine-cdm, + # bundle the "proprietary" Widevine CDM so DRM streaming ( Netflix, HBO, ..) + # Requires `nixpkgs.config.allowUnfree = true` in the consuming config. + enableWidevine ? false, + version, + src, +}: +let + # Chromium flags applied on all platforms to disable update machinery. + commonFlags = [ + "--disable-component-update" + "--simulate-outdated-no-au='Tue, 31 Dec 2099 23:59:59 GMT'" + "--check-for-update-interval=0" + "--no-first-run" + "--enable-features=StorageAccessAPI" + "--restore-last-session" + ]; + + addFlags = lib.concatMapStringsSep " \\\n " (f: "--add-flags \"${f}\""); + + # Libraries that must appear on LD_LIBRARY_PATH at runtime on Linux. + linuxRuntimeLibs = [ + libGL + libvdpau + libva + pipewire + alsa-lib + libpulseaudio + ]; +in +stdenv.mkDerivation { + pname = "helium"; + inherit version src; + + nativeBuildInputs = [ + makeWrapper + ] + ++ lib.optionals stdenv.isLinux [ + autoPatchelfHook + copyDesktopItems + ] + ++ lib.optionals stdenv.isDarwin [ _7zz ]; + + buildInputs = lib.optionals stdenv.isLinux [ + alsa-lib + at-spi2-atk + at-spi2-core + atk + cairo + cups + dbus + expat + fontconfig + freetype + gdk-pixbuf + glib + gtk3 + libdrm + libgbm + libGL + libpulseaudio + libx11 + libxcb + libxcomposite + libxcursor + libxdamage + libxext + libxfixes + libxi + libxkbcommon + libxrandr + libxrender + libxscrnsaver + libxshmfence + libxtst + libuuid + mesa + nspr + nss + pango + pipewire + systemd + vulkan-loader + wayland + kdePackages.qtbase + ]; + + # QT libraries are bundled; suppress autoPatchelf warnings for them. + autoPatchelfIgnoreMissingDeps = lib.optionals stdenv.isLinux [ + "libQt6Core.so.6" + "libQt6Gui.so.6" + "libQt6Widgets.so.6" + "libQt5Core.so.5" + "libQt5Gui.so.5" + "libQt5Widgets.so.5" + ]; + + dontWrapQtApps = stdenv.isLinux; + + unpackCmd = lib.optionalString stdenv.isDarwin "7zz x $src"; + + installPhase = + if stdenv.isDarwin then + '' + runHook preInstall + + mkdir -p $out/Applications/Helium.app + cp -r . $out/Applications/Helium.app + + mkdir -p $out/bin + makeWrapper $out/Applications/Helium.app/Contents/MacOS/Helium $out/bin/helium \ + ${addFlags commonFlags} + + runHook postInstall + '' + else + '' + runHook preInstall + + mkdir -p $out/bin $out/opt/helium + cp -r * $out/opt/helium +${lib.optionalString enableWidevine '' + cp -r ${widevine-cdm}/share/google/chrome/WidevineCdm $out/opt/helium/WidevineCdm +''} + + makeWrapper $out/opt/helium/helium $out/bin/helium \ + --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath linuxRuntimeLibs}" \ + --add-flags "--ozone-platform-hint=auto" \ + --add-flags "--enable-features=WaylandWindowDecorations" \ + ${addFlags commonFlags} + + mkdir -p $out/share/icons/hicolor/256x256/apps + cp $out/opt/helium/product_logo_256.png \ + $out/share/icons/hicolor/256x256/apps/helium.png + + runHook postInstall + ''; + + # helper utility for getting extensions + postInstall = '' + mkdir -p $out/bin + cp ${../scripts/prefetch-nix-extension.sh} $out/bin/prefetch-nix + chmod +x $out/bin/prefetch-nix + ''; + + desktopItems = lib.optionals stdenv.isLinux [ + (makeDesktopItem { + name = "helium"; + exec = "helium %U"; + icon = "helium"; + desktopName = "Helium"; + genericName = "Web Browser"; + categories = [ + "Network" + "WebBrowser" + ]; + terminal = false; + mimeTypes = [ + "text/html" + "text/xml" + "application/xhtml+xml" + "x-scheme-handler/http" + "x-scheme-handler/https" + ]; + }) + ]; + + meta = { + description = "Private, fast, and honest web browser based on ungoogled-chromium"; + homepage = "https://helium.computer/"; + license = lib.licenses.gpl3Only; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + mainProgram = "helium"; + }; +} diff --git a/modules/sources.nix b/modules/sources.nix new file mode 100644 index 0000000..c35b07d --- /dev/null +++ b/modules/sources.nix @@ -0,0 +1,25 @@ +{ + versions = { + linux = "0.15.7.1"; + darwin = "0.15.7.1"; + }; + + srcs = { + x86_64-linux = versions: { + url = "https://github.com/imputnet/helium-linux/releases/download/${versions.linux}/helium-${versions.linux}-x86_64_linux.tar.xz"; + hash = "sha256-MQKJLN69/g1R46Y44ADo1UvKAXxS2vKsq/XlAwBO+58="; + }; + aarch64-linux = versions: { + url = "https://github.com/imputnet/helium-linux/releases/download/${versions.linux}/helium-${versions.linux}-arm64_linux.tar.xz"; + hash = "sha256-CYHUDYEMwe+2//4cDPMS2VqcRfbRJD0USDxDJ7kKu6Q="; + }; + x86_64-darwin = versions: { + url = "https://github.com/imputnet/helium-macos/releases/download/${versions.darwin}/helium_${versions.darwin}_x86_64-macos.dmg"; + hash = "sha256-vQ3CkgEQHCPDvlxW24zNVkFM42OkfHIHHgmvcehU0ic="; + }; + aarch64-darwin = versions: { + url = "https://github.com/imputnet/helium-macos/releases/download/${versions.darwin}/helium_${versions.darwin}_arm64-macos.dmg"; + hash = "sha256-QQT3dtBKQvnGi3ySsgaVeXGqKIJ/iys5fDApZnDlt5E="; + }; + }; +} diff --git a/scripts/prefetch-nix-extension.sh b/scripts/prefetch-nix-extension.sh new file mode 100755 index 0000000..1d1e81a --- /dev/null +++ b/scripts/prefetch-nix-extension.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# check if at least one ID was provided +if [ $# -eq 0 ]; then + echo "Usage: fetchext [ ...]" + echo "Example: fetchext nngceckbapebfimnlniiiahkandclblb" + exit 1 +fi + +UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" + +# print opening for easy copy-pasting +echo "extensions = [" + +for id in "$@"; do + # fetch CRX to calculate the hash + url="https://clients2.google.com/service/update2/crx?response=redirect&os=linux&arch=x64&os_arch=x86_64&nacl_arch=x86-64&prod=chromiumcrx&prodchannel=stable&prodversion=120.0.0.0&acceptformat=crx3&x=id%3D${id}%26installsource%3Dondemand%26uc" + + tmpfile=$(mktemp) + + # -L follows the redirect from Google's service to the actual .crx file + if curl -fsSL -A "$UA" "$url" -o "$tmpfile"; then + size=$(stat -c%s "$tmpfile") + + if [ "$size" -gt 1000 ]; then + # generate SRI hash + hash=$(nix-hash --type sha256 --flat --base32 "$tmpfile") + sri=$(nix hash convert --to sri --hash-algo sha256 "$hash") + + # output the Nix code block + echo " { id = \"$id\"; hash = \"$sri\"; }" + else + echo " # FAILED: ID $id returned an empty or invalid file (check ID)." + fi + else + echo " # FAILED: Could not reach Google Update servers for ID $id." + fi + + rm -f "$tmpfile" + # sleep to avoid rate limiting if fetching many at once + sleep 0.5 +done + +echo "];"