https://github.com/hyper0x/cmdguard
A safety net for dangerous shell commands — intercept rm, mv, chmod with configurable protection levels, automatic backup, and undo.
https://github.com/hyper0x/cmdguard
Last synced: 4 days ago
JSON representation
A safety net for dangerous shell commands — intercept rm, mv, chmod with configurable protection levels, automatic backup, and undo.
- Host: GitHub
- URL: https://github.com/hyper0x/cmdguard
- Owner: hyper0x
- Created: 2026-06-06T06:54:00.000Z (24 days ago)
- Default Branch: main
- Last Pushed: 2026-06-19T07:19:17.000Z (11 days ago)
- Last Synced: 2026-06-19T08:29:12.789Z (11 days ago)
- Language: Go
- Size: 215 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# cmdguard
**Command protection tool** — guards `rm`, `mv`, `chmod` with confirmation, automatic backup, and undo.
[](https://goreportcard.com/report/github.com/hyper0x/cmdguard)
[](LICENSE)
[简体中文](README.zh.md)
---
## Overview
cmdguard wraps dangerous commands (`rm`, `mv`, `chmod`) to prevent accidental data loss.
- 🚫 **Four protection levels** — reject, confirm_double, confirm, warn
- 💾 **Automatic backup** — files are copied to a vault before destructive ops
- ↩️ **Undo** — restore deleted or overwritten files via `cmdguard undo`
- 📋 **Audit log** — every operation is recorded permanently; searchable & filterable
- 🤖 **Agent-aware** — explicit handling for AI agents and automation (see below)
- ⚙️ **TOML config** — glob path patterns, per-command overrides
---
## Design
### Protecting humans
Add aliases to `~/.zshrc` or `~/.bashrc`:
```bash
alias rm='cmdguard rm'
alias mv='cmdguard mv'
alias chmod='cmdguard chmod'
```
### Protecting AI agents
Put cmdguard's wrapper directory at the front of `PATH` so the agent's
`rm`/`mv`/`chmod` lookups hit cmdguard's wrappers:
```bash
export PATH="$(cmdguard config --bin-dir):$PATH"
export CMDGUARD_NONINTERACTIVE=1 # skip the 5s/10s confirm wait
```
`cmdguard config --bin-dir` prints the wrapper directory as a bare
path (default `~/.cmdguard/bin`, or wherever your install puts it),
so the export above stays correct even if you customise the layout.
Both methods can coexist.
### What cmdguard does NOT do
cmdguard cannot intercept calls that bypass `PATH` lookup, e.g. `/bin/rm /etc/passwd`.
The model is "protect + audit + recoverable", not "absolute lockdown".
---
## AI agent usage
When an AI agent invokes a guarded command and the target is a protected
path, cmdguard refuses to hang on an interactive prompt. The agent must:
1. **Set the env var** to declare itself non-interactive:
```bash
export CMDGUARD_NONINTERACTIVE=1
```
This skips the 5s/10s wait at confirm prompts. It does **NOT** grant
permission — the operation is still rejected.
2. **If the operation is genuinely safe**, retry with a `--bypass` identifier:
```bash
rm /path/to/file --bypass=///
```
The identifier must have exactly 4 segments:
| segment | meaning | example |
|----------|-------------------------------|------------------------|
| host | machine hostname / alias | `mac-studio` |
| platform | agent platform | `qwenpaw`, `cursor` |
| agent | agent id | `ai_research` |
| task | brief task slug | `cleanup-tmp-dirs` |
Allowed characters: `[a-zA-Z0-9._-]`. Empty segments, angle brackets,
and template placeholder words (`host`, `agent`, `task`, `xxx`, `foo`,
`todo`, ...) are rejected.
Every bypass is recorded in the audit log with the full identifier, so
the audit trail attributes every protected-path operation back to a
specific agent / task.
See [docs/commands.md](docs/commands.md#--bypass) for full details.
---
## Quick start
### Install
```bash
# Option 1 — pre-built binary (recommended)
# Download from the Releases page for your platform.
# Option 2 — build from source
git clone https://github.com/hyper0x/cmdguard.git
cd cmdguard
make install # installs to $GOBIN with version info baked in
```
### Initialize
```bash
cmdguard init
```
Creates `~/.cmdguard/{config.toml, bin/, log/, vault/}` and prints the
integration guide. Idempotent — re-running is safe. Use `--force` to
overwrite (old files are zipped to `~/.cmdguard/backup/`).
---
## Commands at a glance
| Command | Description |
|:----|:----|
| `rm/mv/chmod ` | Run a command through cmdguard |
| `init [--force] [--dry-run]` | Initialize the environment |
| `list [options]` | List audit log entries |
| `undo [options]` | Restore an operation from vault |
| `vault list [--json]` | List all vault backups |
| `vault clean [--dry-run]` | Purge expired vault backups |
| `config [--default \| --raw \| --bin-dir]` | Print configuration views |
| `path` | Show the cmdguard directory layout |
| `help` / `version` | Self-explanatory |
Full reference: [docs/commands.md](docs/commands.md)
---
## Configuration
Location: `~/.cmdguard/config.toml` (overridable via `CMDGUARD_CONFIG_DIR`).
```toml
[protect]
reject = ["/etc/**", "/private/**", "~/.ssh/**"]
confirm_double = ["~/.config/**"]
confirm = ["~/Documents/**", "~/Desktop/**"]
warn = ["~/Downloads/**"]
[vault]
retention_days = 7
auto_purge = true
[guard]
confirm_timeout = 5 # seconds; 'confirm' prompt
confirm_double_timeout = 10 # seconds per step; 'confirm_double' prompt
```
> **The config file is read-only from cmdguard's perspective.** cmdguard
> never modifies it at runtime; only `cmdguard init --force` may overwrite
> it (with a backup zip).
Full reference: [docs/configuration.md](docs/configuration.md)
---
## Vault & undo
- Backup directory: `~/.cmdguard/vault/_/`
- Retention: 30 days by default, configurable
- Restore: `cmdguard undo [--id ] [--interactive] [--dry-run]`
- Logs: permanent, JSON, one file per day
Details: [docs/vault.md](docs/vault.md)
---
## Build from source
```bash
git clone https://github.com/hyper0x/cmdguard.git
cd cmdguard
make build # build into ./dist with version from git tag
make install # install to $GOBIN
```
---
## FAQ
**Can cmdguard be bypassed?**
Yes. `/bin/rm /etc/passwd` skips the PATH lookup entirely. cmdguard is
protection + audit + recovery, not an absolute lockdown.
**What's the difference between `confirm` and `confirm_double`?**
`confirm` requires a single `y`. `confirm_double` requires `y` first and
then typing the full word `yes` — designed to defeat fatigue errors on
high-risk paths.
**What if my config file gets overwritten?**
`cmdguard init` never overwrites without `--force`. With `--force`, the
old file is preserved at `~/.cmdguard/backup/init-.zip`.
**Where do agents put `CMDGUARD_NONINTERACTIVE`?**
In the agent's shell init or wherever the agent's environment is set up.
Once exported, every cmdguard call in that environment skips the wait
and goes straight to the bypass-or-reject path.
**How do I uninstall?**
```bash
# Remove the alias / PATH line from your shell rc, then:
trash ~/.cmdguard # or rm -rf if you don't have trash
rm $(which cmdguard)
```
---
## License
[MIT](LICENSE)