purple-ssh 3.2.0

Open-source terminal SSH manager and SSH config editor. Search hundreds of hosts, sync from 16 clouds, transfer files, manage Docker and Podman over SSH, sign short-lived Vault SSH certs and expose an MCP server for AI agents. Rust TUI, MIT licensed.
Documentation
{
  description = "Open-source terminal SSH manager and SSH config editor for macOS and Linux.";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    rust-overlay = {
      url = "github:oxalica/rust-overlay";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs =
    { self, nixpkgs, rust-overlay }:
    let
      supportedSystems = [
        "x86_64-linux"
        "aarch64-linux"
        "x86_64-darwin"
        "aarch64-darwin"
      ];

      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;

      # Memoized: each system's nixpkgs is imported once and reused
      # across packages, devShells, formatter and checks.
      pkgsFor = forAllSystems (system: import nixpkgs {
        inherit system;
        overlays = [ (import rust-overlay) ];
      });

      mkPurple = pkgs:
        let
          rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
          cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
        in
        pkgs.rustPlatform.buildRustPackage {
          pname = "purple-ssh";
          version = cargoToml.package.version;

          src = pkgs.lib.cleanSource ./.;

          cargoLock.lockFile = ./Cargo.lock;

          nativeBuildInputs = with pkgs; [
            rustToolchain
            pkg-config
          ];

          buildInputs = [ pkgs.openssl ];

          # Use system openssl. Cargo.toml only vendors openssl for
          # cfg(target_env = "musl"); Nix builds against glibc/darwin.
          OPENSSL_NO_VENDOR = 1;

          # Tests need $HOME, ~/.ssh, a working ssh binary and serialized
          # PATH manipulation (vault_ssh_tests::PATH_LOCK). The Nix
          # sandbox provides none of those, so the test suite runs in CI
          # instead. `nix flake check` still verifies the package builds.
          doCheck = false;

          meta = with pkgs.lib; {
            description = cargoToml.package.description;
            homepage = "https://getpurple.sh";
            license = licenses.mit;
            mainProgram = "purple";
            platforms = platforms.unix;
          };
        };
    in
    {
      packages = forAllSystems (system:
        let purple = mkPurple pkgsFor.${system};
        in {
          default = purple;
          purple-ssh = purple;
        });

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

      devShells = forAllSystems (system:
        let
          pkgs = pkgsFor.${system};
          rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
        in {
          default = pkgs.mkShell {
            inputsFrom = [ self.packages.${system}.default ];
            packages = with pkgs; [
              rustToolchain
              cargo-audit
              cargo-deny
            ];
            OPENSSL_NO_VENDOR = 1;
          };
        });

      formatter = forAllSystems (system: pkgsFor.${system}.nixfmt-rfc-style);

      checks = forAllSystems (system:
        let
          pkgs = pkgsFor.${system};
          rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
          src = pkgs.lib.cleanSource ./.;
        in {
          package = self.packages.${system}.default;

          fmt = pkgs.runCommand "purple-fmt-check"
            { nativeBuildInputs = [ rustToolchain ]; }
            ''
              cp -r ${src}/. .
              chmod -R u+w .
              cargo fmt --check
              touch $out
            '';
        });

      overlays.default = final: _prev: {
        purple-ssh = mkPurple final;
      };
    };
}