Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/brew-nix-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Brew/Nix smoke

# Validate the Homebrew formula and the Nix flake on real runners (the dev host has
# neither). Each installs the published 0.10.0 standalone binary and asserts
# `clauster --version`. Manual + on changes to the packaging files. Non-required.
on:
workflow_dispatch:
pull_request:
paths:
- Formula/clauster.rb
- flake.nix
- flake.lock
- .github/workflows/brew-nix-smoke.yml

permissions:
contents: read

concurrency:
group: brew-nix-smoke-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
brew:
name: Homebrew (macos-arm64)
runs-on: macos-latest
timeout-minutes: 20
Comment on lines +24 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pin an explicit ARM macOS runner to keep the brew job valid.

Line 25 uses macos-latest, but the formula under test is arm-only on macOS (on_arm in Formula/clauster.rb). If macos-latest ever resolves to Intel, this smoke test fails for infra reasons rather than formula correctness. Pin a known ARM label (for example, macos-14) for deterministic architecture.

Suggested change
-    runs-on: macos-latest
+    runs-on: macos-14
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: Homebrew (macos-arm64)
runs-on: macos-latest
timeout-minutes: 20
name: Homebrew (macos-arm64)
runs-on: macos-14
timeout-minutes: 20

steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: brew install via a local tap + verify
run: |
set -euo pipefail
# Modern Homebrew refuses a bare local formula path — it must live in a tap.
# Stage our formula into an ephemeral local tap (mirrors real consumption via
# a homebrew-<x> repo) and install from there.
brew tap-new schubydoo/local --no-git
tap_dir="$(brew --repository schubydoo/local)"
mkdir -p "$tap_dir/Formula"
cp ./Formula/clauster.rb "$tap_dir/Formula/clauster.rb"
brew install --formula schubydoo/local/clauster
clauster --version
clauster --version | grep -q '^clauster'

Comment on lines +42 to +44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Strengthen version assertions to catch wrong-binary regressions.

Line 43 and Line 64 only assert the output starts with clauster, which can pass even when the wrong version is installed. Since this workflow is validating 0.10.0 packaging, assert the exact expected version banner.

Suggested change
-          clauster --version | grep -q '^clauster'
+          clauster --version | grep -q '^clauster 0\.10\.0$'
...
-          ./result/bin/clauster --version | grep -q '^clauster'
+          ./result/bin/clauster --version | grep -q '^clauster 0\.10\.0$'

Also applies to: 63-65

nix:
name: Nix flake (x86_64-linux)
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install Nix, build + run the flake
run: |
set -euo pipefail
# Single-user install (no daemon/systemd needed in CI).
sh <(curl -L https://nixos.org/nix/install) --no-daemon

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the full context of the workflow file
cat -n .github/workflows/brew-nix-smoke.yml

Repository: schubydoo/clauster

Length of output: 3015


🏁 Script executed:

# Search for Nix-related GitHub Actions or official installers
rg -i "nix.*action|nixos.*action" --type yaml

Repository: schubydoo/clauster

Length of output: 44


🌐 Web query:

Nix GitHub Actions official installer pinned commit

💡 Result:

There is no single "official" installer for Nix on GitHub Actions, as multiple community-maintained actions exist [1][2][3][4]. Consequently, there is no single "pinned commit" that covers all users [5][3]. Instead, you should choose a specific action and pin it to a commit hash or release tag for security and reproducibility [3][6]. Commonly used Nix installers for GitHub Actions include: 1. cachix/install-nix-action: This is a widely used community action [4][7]. While it uses semantic versioning (e.g., v31, v31.9.1) for releases, you can pin it to a specific git commit hash in your workflow file to ensure absolute reproducibility [5][6]. Example usage: - uses: cachix/install-nix-action@2126ae7 2. DeterminateSystems/determinate-nix-action: This action is maintained by Determinate Systems and supports explicit version pinning for their installer [3][8]. It is automatically tagged for every release [3]. Example usage: - uses: DeterminateSystems/determinate-nix-action@v3.17.2 3. NixOS/nix-installer-action: An experimental action associated with the official NixOS organization [1]. It is currently in a testing phase and primarily tracks the main branch [1]. To pin any of these actions securely: - Find the desired tag or commit on the action's GitHub repository "Releases" or "Commits" page [5][9]. - Replace the version tag (e.g., @v31) in your workflow file with the specific commit hash (e.g., @2126ae7fc54c9df00dd18f7f18754393182c73cd) [6]. - Use tools like Dependabot to manage and update these pinned versions automatically [2][10].

Citations:


🌐 Web query:

curl sh pattern security GitHub Actions CI environment risks

💡 Result:

The curl | sh (or similar pipe-to-shell) pattern is considered a high-risk practice in CI/CD environments, including GitHub Actions, due to its susceptibility to supply chain attacks and lack of integrity verification [1][2][3]. Primary Risks: 1. Arbitrary Code Execution: The pattern downloads and executes remote code in a single, unverified step. If the host, CDN, DNS, or network path is compromised, an attacker can serve malicious code that executes with the full privileges of the CI runner [1][2][3]. 2. Secret Exfiltration: Because the script runs in the CI environment, it has access to workflow secrets, environment variables, and tokens. Compromised scripts are frequently used to exfiltrate these credentials [3]. 3. Man-in-the-Middle (MitM) Attacks: Piping directly to a shell bypasses the ability to inspect the script's contents or verify cryptographic signatures before execution [3]. 4. Unintended Content: Remote servers may intermittently fail or serve unexpected content (e.g., HTML error pages) that can cause unpredictable or dangerous behavior when piped into a shell [4]. Recommended Secure Practices: 1. Use Vetted Actions: Replace ad-hoc shell scripts with official, well-maintained GitHub Actions. When using third-party actions, pin them to a specific commit SHA rather than a tag or branch [1][2]. 2. Download and Verify: If a script must be fetched, split the process into distinct steps: - Download the file to a local path [1][2]. - Verify its integrity using cryptographic hashes (e.g., SHA-256) or GPG signatures provided by the distributor [1][5][3]. - Execute the verified file only after the integrity check passes [1][2][3]. 3. Version Control: Whenever possible, vendor the script into your repository under version control to ensure the code remains immutable and audit-trailed [5]. 4. Use Security Tools: Employ automated security scanners (e.g., Semgrep, Poutine, or Pipefort) to detect and block unsafe pipe-to-shell patterns in your pipeline definitions [6][2][4]. Runtime security tools (e.g., kntrl-action) can further monitor and block suspicious process chains or unauthorized network access during workflow execution [7]. The critical takeaway is to treat all remote, unverified content as untrusted code [3][4]. Always verify the provenance and integrity of external scripts before execution [1][3].

Citations:


Replace inline shell installer with pinned GitHub Action for Nix.

Line 57 executes curl ... | sh to install Nix directly in the workflow. Standard practice is to use a vetted, commit-SHA pinned GitHub Action instead:

  • cachix/install-nix-action (widely used, pinnable to commit hash)
  • DeterminateSystems/determinate-nix-action (official, supports version pinning)
  • NixOS/nix-installer-action (experimental, official NixOS action)

This eliminates supply-chain attack surface, prevents silent failures from transient fetch issues, and aligns with how this workflow already pins actions/checkout to a commit SHA.

. "$HOME/.nix-profile/etc/profile.d/nix.sh"
export NIX_CONFIG="experimental-features = nix-command flakes"
# --no-update-lock-file uses the committed flake.lock and errors if it's
# stale (rather than silently regenerating it), asserting lock/flake match.
nix build .#clauster -L --no-update-lock-file
./result/bin/clauster --version
./result/bin/clauster --version | grep -q '^clauster'
nix run .#clauster --no-update-lock-file -- --version
36 changes: 36 additions & 0 deletions Formula/clauster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Homebrew formula for the signed standalone clauster binary.
#
# brew install --formula ./Formula/clauster.rb
#
# Covers the published 0.10.0 targets: macOS arm64 (Apple Silicon) and Linux
# x86_64 (Linuxbrew). macOS Intel and Linux arm64 binaries are not built yet
# (those install via pip/uv/uvx for now).
class Clauster < Formula
desc "Self-hosted web UI for spawning and managing Claude Code remote-control bridges"
homepage "https://github.com/schubydoo/clauster"
version "0.10.0"
license "Apache-2.0"

on_macos do
on_arm do
url "https://github.com/schubydoo/clauster/releases/download/v0.10.0/clauster-0.10.0-macos-arm64"
sha256 "be06f18dae69680bcffbf7b167faed6fec6cc81bf50329fbb39a20d9d8b3efa2"
end
end

on_linux do
on_intel do
url "https://github.com/schubydoo/clauster/releases/download/v0.10.0/clauster-0.10.0-linux-x86_64"
sha256 "c1052df6ee5bf0519e33c2981c522b59143281bea2f7cb9075c62f07aa4c8c87"
end
end

def install
# The release asset downloads under its versioned name; install it as `clauster`.
bin.install Dir["clauster-*"].first => "clauster"
end

test do
assert_match "clauster #{version}", shell_output("#{bin}/clauster --version")
end
end
27 changes: 27 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 82 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
description = "clauster — self-hosted web UI for Claude Code remote-control bridges";

inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

outputs =
{ self, nixpkgs }:
let
version = "0.10.0";

# The published 0.10.0 standalone binaries, keyed by Nix system. macOS Intel
# and Linux arm64 are not built yet (use pip/uv there for now).
assets = {
"x86_64-linux" = {
file = "clauster-${version}-linux-x86_64";
sha256 = "c1052df6ee5bf0519e33c2981c522b59143281bea2f7cb9075c62f07aa4c8c87";
};
"aarch64-darwin" = {
file = "clauster-${version}-macos-arm64";
sha256 = "be06f18dae69680bcffbf7b167faed6fec6cc81bf50329fbb39a20d9d8b3efa2";
};
};

systems = builtins.attrNames assets;
forAllSystems = nixpkgs.lib.genAttrs systems;

packageFor =
system:
let
pkgs = nixpkgs.legacyPackages.${system};
asset = assets.${system};
in
pkgs.stdenvNoCC.mkDerivation {
pname = "clauster";
inherit version;

src = pkgs.fetchurl {
url = "https://github.com/schubydoo/clauster/releases/download/v${version}/${asset.file}";
inherit (asset) sha256;
};

dontUnpack = true;

# The Linux binary is a PyInstaller one-file ELF; patch its loader/RPATH to
# the Nix store. Darwin needs no patching.
nativeBuildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.autoPatchelfHook ];
buildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [
(pkgs.lib.getLib pkgs.stdenv.cc.cc)
pkgs.zlib
];

installPhase = ''
runHook preInstall
install -Dm755 "$src" "$out/bin/clauster"
runHook postInstall
'';

meta = with pkgs.lib; {
description = "Self-hosted web UI for spawning and managing Claude Code remote-control bridges";
homepage = "https://github.com/schubydoo/clauster";
license = licenses.asl20;
platforms = systems;
mainProgram = "clauster";
sourceProvenance = [ sourceTypes.binaryNativeCode ];
};
};
in
{
packages = forAllSystems (system: rec {
clauster = packageFor system;
default = clauster;
});

apps = forAllSystems (system: rec {
clauster = {
type = "app";
program = "${self.packages.${system}.clauster}/bin/clauster";
};
default = clauster;
});
};
}
Loading