A shell safety layer powered by LLMs. Every command you type is checked before it runs — risky ones require confirmation.
$ rm -rf /var/log/* guard-sh: Deletes all files in /var/log recursively. Are you sure? [Y/n]
- You press Enter on a command.
- guard-sh intercepts it and sends it to an LLM.
- If the LLM considers it safe, the command runs normally.
- If not, you see a one-line warning and a
[Y/n]prompt. - Press
Yto proceed, anything else to cancel.
Commands in your whitelist skip the LLM entirely.
- Gemini (free tier available at https://aistudio.google.com)
- DeepSeek (https://platform.deepseek.com)
- OpenAI (https://platform.openai.com)
- Claude (https://console.anthropic.com)
- Ollama (local, no API key required — https://ollama.com)
- Go 1.22+
- bash, zsh, or fish
- API key(s) for one or more of the cloud LLMs listed above, or a running Ollama instance for fully local operation.
git clone https://github.com/berdanakyurek/guard-sh.git
cd guard-sh
bash install.shThis will:
- Build the
guard-shbinary and install it to~/.local/bin/ - Copy the default config to
~/.config/guard-sh/config.yaml - Copy the default prompt to
~/.config/guard-sh/prompt.txt - Add shell integration to the rc file of your active shell (bash →
.bashrc, zsh →.zshrc, fish →config.fish)
Download the binary for your platform from the releases page, place it in ~/.local/bin/, then run:
chmod +x ~/.local/bin/guard-sh
guard-sh setupThis will create the config dir, write the shell integration scripts, and add them to the rc file of your active shell.
bash install.sh --without-shellinstall.sh and guard-sh setup only add integration for your active shell (detected via the parent process). If you use multiple shells, run the command once from each:
# In bash
bash install.sh
# In zsh
bash install.sh
# In fish
bash install.shOr use guard-sh setup after installing the binary:
# In bash
guard-sh setup
# Switch to zsh, then:
guard-sh setupbash uninstall.shThis removes the binary, shell integration from all rc files, and the config dir.
To keep your config (providers, whitelist, cache):
bash uninstall.sh --keep-configguard-sh uninstall # removes shell integration and shell scripts, keeps config
guard-sh uninstall --purge # also removes the config dir (~/.config/guard-sh)After running guard-sh uninstall, the binary itself is not removed (a running program cannot delete itself reliably). The command prints the path to remove manually:
next remove the binary manually:
rm ~/.local/bin/guard-sh
Config file: ~/.config/guard-sh/config.yaml
- Providers can be added interactively with
guard-sh provider add— see Providers. - The whitelist can also be managed with
guard-sh whitelistcommands — see Whitelist. - Cache settings can also be managed with
guard-sh cache— see Cache.
Or you can edit the config file manually.
An example config file is as follows:
# Providers are tried in order. If one fails, the next is used.
provider_order:
- ollama
- gemini
- deepseek
- openai
- claude
providers:
ollama:
host: http://localhost:11434/api/chat # optional, this is the default
model: llama3.2 # optional, this is the default
gemini:
api_key: YOUR_GEMINI_KEY
model: gemini-3.1-flash-lite-preview # optional, this is the default
deepseek:
api_key: YOUR_DEEPSEEK_KEY
model: deepseek-chat # optional, this is the default
openai:
api_key: YOUR_OPENAI_KEY
model: gpt-4o-mini # optional, this is the default
claude:
api_key: YOUR_ANTHROPIC_KEY
model: claude-haiku-4-5-20251001 # optional, this is the default
# Commands always considered safe — LLM is never called.
# Only the base command name is needed: "ls" covers "ls -la", "ls /tmp", etc.
# For chained commands (&&, ||, ;, |), ALL parts must be whitelisted.
command_whitelist:
- ls
- cat
- echo
- pwd
- cd
- clear
# How long to wait for an LLM response, in seconds. Defaults to 10 if not set.
timeout_seconds: 10
# Include the working directory in the command sent to the LLM.
# Allows the LLM to assess risk based on context (e.g. chmod in /etc vs ~/projects).
# Cache keys include the working directory when this is enabled.
# Set to false to send only the command string.
send_working_directory: true
# Cache LLM responses to avoid repeated API calls for the same command.
cache_enabled: true
cache_max_size: 1000The LLM prompt is stored at ~/.config/guard-sh/prompt.txt. Edit it to adjust what guard-sh considers risky, change the output format, or add your own rules. Changes take effect immediately — no rebuild needed.
By default, guard-sh includes the current working directory when querying the LLM. This gives the LLM context to make better decisions — for example, chmod 777 config.yaml in /etc is riskier than in ~/projects/.
The LLM receives a structured two-line query:
Working directory: /etc Command: chmod 777 config.yaml
The working directory is also included in cache keys, so the same command in different directories is evaluated independently.
To disable:
send_working_directory: falseguard-sh can redact sensitive values from commands before sending them to any LLM provider. Two methods are supported: pattern-based and Shannon entropy-based. Matches are replaced with [REDACTED].
Redaction is configured globally under redaction: in your config, and can be overridden per-provider under providers.NAME.redaction.
redaction:
pattern_based:
enabled: true
patterns:
- '(?i)(password|passwd|secret|api[_-]?key|token|auth)\s*[=:]\s*\S+'
- '(?i)--password\s+\S+'
- 'AKIA[0-9A-Z]{16}'
shannon_entropy_based:
enabled: false
threshold: 4.5 # bits/char — typical English ~3.5, secrets typically >4.5
min_length: 20 # ignore tokens shorter than thisUses Go regular expressions. Any token matching a pattern is replaced with [REDACTED]. On by default for all providers.
You can add your own patterns:
redaction:
pattern_based:
enabled: true
patterns:
- '(?i)(password|passwd|secret|api[_-]?key|token|auth)\s*[=:]\s*\S+'
- '(?i)--password\s+\S+'
- 'AKIA[0-9A-Z]{16}'
- 'ghp_[a-zA-Z0-9]{36}' # GitHub personal access tokens
- 'xox[baprs]-[a-zA-Z0-9-]+' # Slack tokensToggle per-provider:
guard-sh redact list # list all global redaction patterns
guard-sh redact pattern on # interactively enable for a provider
guard-sh redact pattern off # interactively disable for a providerAutomatically detects high-entropy strings (API keys, random tokens, private key material) without requiring explicit patterns. Any whitespace-delimited token above the entropy threshold is replaced with [REDACTED]. Off by default.
Toggle per-provider:
guard-sh redact entropy on # interactively enable for a provider
guard-sh redact entropy off # interactively disable for a providerA provider can override any part of the global redaction config:
providers:
ollama:
host: http://localhost:11434/api/chat
redaction: # optional — overrides global for this provider
pattern_based:
enabled: false # disable pattern redaction for local model
gemini:
api_key: YOUR_KEY
redaction:
shannon_entropy_based:
enabled: true
threshold: 4.0 # more sensitive than global default
min_length: 16Fields not specified in the per-provider block fall back to the global value.
To use a different prompt for a specific provider, create prompt_PROVIDERNAME.txt in the same directory:
~/.config/guard-sh/prompt.txt # default, used by all providers
~/.config/guard-sh/prompt_ollama.txt # used only when ollama is queried
~/.config/guard-sh/prompt_gemini.txt # used only when gemini is queriedIf a provider-specific file exists, it takes precedence over prompt.txt for that provider.
guard-sh is designed to run as a shell integration — it intercepts every command you type automatically. Once installed, you don’t need to invoke it manually.
You can test it directly with:
guard-sh check "rm -rf /"This is useful for testing your configuration, but normal usage happens transparently through the shell hook.
guard-sh helpLists all commands and their usage.
guard-sh on # enable for the current session
guard-sh off # disable for the current sessionguard-sh on --global # auto-enable in every new terminal
guard-sh off --global # don't auto-enable in new terminalsoff --global does not remove guard-sh from your shell — it only stops it from auto-enabling. You can still use guard-sh on in any terminal at any time.
guard-sh healthcheckChecks all configured providers without spending any tokens. Reports:
- API key and model validity for cloud providers; server connectivity for Ollama
- Response latency per provider
- Shell integration status (
.bashrc/.zshrc/config.fish) - Config errors (e.g. unknown provider names in
provider_order)
guard-sh healthcheck providers ollama llama3.2 ● ok (12ms) gemini gemini-3.1-flash-lite-preview ● ok (184ms) deepseek deepseek-chat ✗ HTTP 401: Authentication Fails openai gpt-4o-mini ● ok (759ms) claude claude-haiku-4-5-20251001 ● ok (510ms) shell bash ~/.bashrc ● present zsh ~/.zshrc ○ not found fish ~/.config/fish/config.fish ○ not found
guard-sh statusShows session and global state, prompt path, timeout, cache stats, active providers, redaction patterns, shell integration status, and whitelist.
guard-sh
session ● on
global ● on
config ~/.config/guard-sh/config.yaml
prompt ~/.config/guard-sh/prompt.txt
timeout 10s
work dir ● on
cache ● on
1000 max entries
42 entries, 8.3 KB
providers
1 gemini
model gemini-3.1-flash-lite-preview
pattern redaction ● on
entropy redaction ○ off
2 ollama
model llama3.2
pattern redaction ○ off
entropy redaction ○ off
3 deepseek
model deepseek-chat
pattern redaction ● on
entropy redaction ○ off
redaction
1 (?i)(password|passwd|secret|api[_-]?key|token|auth)\s*[=:]\s*\S+
2 (?i)--password\s+\S+
3 AKIA[0-9A-Z]{16}
shell
bash ~/.bashrc ● present
zsh ~/.zshrc ○ not found
fish ~/.config/fish/config.fish ○ not found
whitelist
1 ls
2 cat
3 echo
4 pwd
5 cd
6 clear
7 man
8 help
9 history
10 which
+3 more (to see all, run "guard-sh whitelist")
guard-sh check "<command>" --debugPrints a step-by-step trace to stderr: whitelist result, per-provider cache check, redaction status, response time, and the final LLM response.
command "rm -rf /"
whitelist ○ miss
providers
openai pattern ○ off unchanged
entropy ○ off unchanged
✓ ok (1315ms)
result "This command will recursively delete everything from root."
With redaction enabled, the redacted form is shown before the query is sent:
providers
openai pattern ● on → "rm -rf [REDACTED]"
entropy ○ off unchanged
✓ ok (891ms)
On a cache hit, the provider is not queried at all:
providers openai cache ● hit → "This command will recursively delete everything from root."
If a provider fails, guard-sh moves on to the next and shows why:
providers
openai pattern ○ off unchanged
entropy ○ off unchanged
✗ HTTP 429: Rate limit exceeded (234ms), trying next
gemini pattern ○ off unchanged
entropy ○ off unchanged
✓ ok (401ms)
guard-sh setupCreates ~/.config/guard-sh/ with the default config and prompt, writes shell integration scripts (bash, zsh, fish), and adds them to your rc file based on your current shell. Existing files are not overwritten. This is the only step needed after downloading a pre-built binary.
guard-sh versionPrints the current version (e.g. 1.0.0).
guard-sh provider add # interactively add a provider
guard-sh provider remove # interactively remove a provider
guard-sh provider order # interactively reorder providersprovider add walks you through selecting a provider, picking a model, and entering an API key (or a full chat endpoint URL for Ollama, e.g. http://localhost:11434/api/chat). The provider is saved to your config and added to provider_order automatically.
provider remove lists your configured providers and removes the one you select.
provider order opens an interactive list. Use arrow keys to navigate, Enter to grab a provider, arrow keys to move it, Enter again to place it. s saves and exits, q exits without saving.
Commands in the whitelist are always safe. The LLM is never called.
command_whitelist:
- ls
- gitls,ls -la,ls /tmp→ all pass instantlyls && rm -rf /→rmis not whitelisted, so the full command goes to the LLM- For chained commands, all parts must be whitelisted to skip the LLM
You can manage the whitelist from the command line:
guard-sh whitelist # list all whitelisted commands
guard-sh whitelist add git # add a command
guard-sh whitelist remove git # remove a commandguard-sh caches LLM responses locally so the same command is never sent to the API twice. The cache is stored at ~/.config/guard-sh/cache.json.
When the cache is full, the least recently used entries are evicted first.
cache_enabled: true # set to false to disable caching
cache_max_size: 1000 # max number of cached responsesYou can also toggle caching from the command line:
guard-sh cache on # enable caching
guard-sh cache off # disable caching
guard-sh cache size 500 # set max cache size
guard-sh cache clear # delete all cached responses| Shell | Mechanism |
|---|---|
| bash | shopt -s extdebug + DEBUG trap |
| zsh | ZLE widget bound to Enter |
| fish | bind \n~/\r~ mapped to custom function |
GNU General Public License v3.0 — see LICENSE.