{"id":24321148,"url":"https://github.com/pipelight/boulette","last_synced_at":"2025-04-05T07:01:46.331Z","repository":{"id":271390982,"uuid":"913206452","full_name":"pipelight/boulette","owner":"pipelight","description":"A terminal confirmation prompt that prevents you from accidentally damaging remote hosts.","archived":false,"fork":false,"pushed_at":"2025-01-18T19:16:02.000Z","size":1124,"stargazers_count":159,"open_issues_count":2,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-29T06:01:37.198Z","etag":null,"topics":["rust","shutdown","ssh"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pipelight.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-07T08:34:24.000Z","updated_at":"2025-03-25T13:57:14.000Z","dependencies_parsed_at":"2025-01-31T20:53:12.083Z","dependency_job_id":"22e5bdd9-347c-4902-9690-6388f657e4c9","html_url":"https://github.com/pipelight/boulette","commit_stats":null,"previous_names":["pipelight/boulette"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pipelight%2Fboulette","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pipelight%2Fboulette/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pipelight%2Fboulette/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pipelight%2Fboulette/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pipelight","download_url":"https://codeload.github.com/pipelight/boulette/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299828,"owners_count":20916190,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rust","shutdown","ssh"],"created_at":"2025-01-17T16:32:04.594Z","updated_at":"2025-04-05T07:01:46.272Z","avatar_url":"https://github.com/pipelight.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Boulette - A terminal confirmation prompt.\n\n_It's late._ 🥱\n\n_You finish your night coding session by typing `shutdown -h now` in a terminal._\n\n_But nothing happens._\n\n_Because it's the wrong terminal._\n\n_And suddenly your production server is unreachable._\n\n\u003cimg src=\"https://github.com/pipelight/boulette/blob/main/public/images/oh_la_boulette.jpg\" width=\"200\"\u003e\n\nProtect you from yourself.\nHop on the boulette train!\n\n**Boulette prevents you from accidentally damaging remote hosts**\nby raising a warning prompt on dangerous commands.\nThe prompt simply asks for user confirmation,\nand can also enforce a challenge resolution to decide whether to resume(or abort) the command.\n\n## Usage\n\nPrefix a critical command with `boulette` and a confirmation prompt will show up.\n\nAs an example we will use the `shutdown` command\nhowever every command can be **bouletteproofed**.\n\n```sh\nboulette \"shutdown -h now\"\n```\n\nCreate an alias to replace the command with the **bouletteproof** one.\nSee the [Write aliases](#write-aliases) section.\n\nAnd then safely use `shutdown` 😌.\n\n![boulette prompt](https://github.com/pipelight/boulette/blob/main/public/images/example_shutdown.png)\n\n### Challenge types\n\nIn order to execute the provided command you can choose between some challenges to be resolved:\n\n- **ask**, which is the default (`--challenge ask`). You have to type 'y' or 'n' to resume commande execution.\n\n  ![boulette prompt](https://github.com/pipelight/boulette/blob/main/public/images/ask_challenge.png)\n\n- **hostname**, enable with `--challenge hostname`. You must type the host name to resume command execution.\n\n  ![boulette prompt](https://github.com/pipelight/boulette/blob/main/public/images/hostname_challenge.png)\n\n- **numbers**, with `--challenge numbers` You must type a random 6 number sequence to resume command execution.\n\n  ![boulette prompt](https://github.com/pipelight/boulette/blob/main/public/images/numbers_challenge.png)\n\n- **characters**, with `--challenge chars` You must type a random 6 character string (Lower case 'a' to 'z' [a-z]) to resume command execution.\n\n  ![boulette prompt](https://github.com/pipelight/boulette/blob/main/public/images/chars_challenge.png)\n\n### Over ssh only\n\nBoulette confirmation prompt can be triggered inside **ssh session only** thanks to the `--ssh-only` option.\n\nWhen aliasing a command `\u003ccmd\u003e` with `boulette \u003ccmd\u003e`, typing `\u003ccmd\u003e` will execute transparently in a local terminal,\nand will only raise a prompt when executed from inside an ssh session.\n\n```sh\nalias off='boulette \"shutdown -h now\"' --ssh-only\n```\n\n![boulette prompt](https://github.com/pipelight/boulette/blob/main/public/images/example_ssh.png)\n\n## Write aliases\n\nThe idea is to enforce a prompt on your most dangerous commands.\nWe can do so by creating aliases of those commands\nand **prefixing them with boulette**.\n\n### Single command alias\n\nFor example, setting the following alias,\n\n```sh\nalias off='boulette \"shutdown -h now\"' --ssh-only\n```\n\nwill prompt you whenever you type `off`.\n\nHere are the one-liners I use the most frequently.\n\n```sh\nalias off='boulette \"shutdown -h now\" --ssh-only --challenge hostname'\nalias sus='boulette \"systemctl suspend\" --ssh-only --challenge hostname'\n```\n\n### Mutliple command alias\n\nYou can also enable boulette on a command and its every subcommands.\n\nLet's say you want to protect yourself from `shutdown` command ant its\nevery options.\nThis way `shutdown -r`, `shutdown -h now` and others will also raise a warning prompt.\n\nCreate a shell function to wrap the command call.\n\n- for bash and zsh shells\n\n  ```sh\n  # bash\n  shutdown () {\n    boulette \"shutdown $@\" --ssh-only --challenge hostname\n  }\n  ```\n\n- for fish shell\n\n  ```fish\n  # fish\n  function shutdown;\n    boulette \"shutdown $argv\" --ssh-only --challenge hostname\n  end\n  ```\n\n### Safeguard sudo\n\nIf you really are reckless and scroll, eye shuts,\nthrough your shell history.\nYou are more likely to pase a command prefixed with `sudo`.\n\nThe following alias is a safeguar for the `sudo \u003ccmd\u003e`\nversion of your dangerous command.\n\n- for bash and zsh shells\n\n  ```sh\n  # bash\n  sudo () {\n    args=\"$*\"\n    if [[ $args =~ ^(shutdown|reboot).* ]]; then\n      cmd='boulette \"sudo $args\" --ssh-only --challenge hostname'\n      eval $cmd\n    else\n      cmd='$SHELL -c \"sudo $args\"'\n      eval $cmd\n    fi\n  }\n  ```\n\n- for fish shell\n\n  ```fish\n  # fish\n  function sudo\n    set args \"$argv\"\n    set -l res $(string match -r \"^(shutdown|reboot).*\" $args)\n    # If there is a match\n    if set -q res[1]\n      command boulette \"sudo $args\" --ssh-only --challenge hostname\n    else\n      command sudo $argv\n    end\n  end\n  ```\n\nThen you can safely inadvertently type `sudo`.\n\n![boulette prompt](https://github.com/pipelight/boulette/blob/main/public/images/example_sudo.png)\n\n## Install\n\n### Cargo:\n\n```sh\ncargo install --git https://github.com/pipelight/boulette\n\n```\n\n### Try in a nix shell:\n\n```sh\nnix-shell -p https://github.com/pipelight/boulette\n\n```\n\n\u003cdetails close\u003e\n\u003csummary\u003e\u003ch3\u003e Nixos Module (Flakes) \u003c/h3\u003e\u003c/summary\u003e\n\nAdd the flake url to your inputs.\n\n```nix\ninputs.boulette.url = \"github:pipelight/boulette\";\n```\n\n```nix\nimports = [\n  inputs.boulette.nixosModules.default\n  # or\n  inputs.boulette.hmModules.default\n];\n```\n\nTweak the following options to your needs.\n\n```nix\n# default.nix AND/OR home.nix\n\nservices.boulette = {\n  enable = true; # Will enable and install `boulette` to your path.\n  enableZsh = true; # Optional: Will add guards for `shutdown` and `reboot` commands to your `zsh` interactive shell sessions.\n  enableBash = true; # Optional: Will add guards for `shutdown` and `reboot` commands to your `bash` interactive shell sessions.\n  enableFish = true; # Optional: Will add guards for `shutdown` and `reboot` commands to your `fish` interactive shell sessions.\n  enableSudoWrapper = true; # Optional\n  commands = [\"shutdown\" \"reboot\"]; # Optional\n  challengeType = \"hostname\"; # Optional: Defaults to hostname. One of \"ask\" \"hostname\", or \"numbers\".\n  sshOnly = true # Boolean, default is`true`. Optional: Boulette confirmation prompts will be triggerd inside ssh session only. Only effects the enable{zsh,bash,fish} options.\n};\n```\n\n\u003c/details\u003e\n\n## Help\n\nYou can display a useful help message with minimal examples.\n\n```sh\nboulette --help\n```\n\nGreatly inspired by [Molly-guard](https://salsa.debian.org/debian/molly-guard).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpipelight%2Fboulette","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpipelight%2Fboulette","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpipelight%2Fboulette/lists"}