A CLI tool that protects API secrets from AI assistants during VibeCoding
Simply wrap your normal command with vg run -- npm run dev to create a development environment where AI assistants cannot physically access your API keys.
日本語ドキュメント: README_ja.md
| Feature | Description |
|---|---|
| Inject Mode | Loads secrets from ~/.vibeguard/secrets.json and injects them only into child process memory — never written to disk or .env. |
| Proxy Mode | Runs a local reverse proxy (default :8080). Requests to localhost:8080/proxy/stripe are forwarded to the real API with the Authorization header injected invisibly. |
| Log Mask Mode | Hooks child process stdout/stderr in real time, replacing any secret value with ***[MASKED]*** before display. |
flowchart TB
AI["🤖 AI Assistant\n(Cursor / Copilot)"]
subgraph project["Project Directory (Git-safe)"]
TOML["vibeguard.toml\n(secret:// refs only)"]
end
subgraph store["Global Secret Store (outside project)"]
JSON["~/.vibeguard/secrets.json\n(actual API keys)"]
end
subgraph vg["vibeguardian (vg run)"]
INJ["① Inject Mode\nResolves secret:// → env vars\nStored in child memory only"]
PROXY["② Proxy Mode\nlocalhost:8080\nAdds Auth headers invisibly"]
MASK["③ Log Mask Mode\nAho-Corasick scanner\nReplaces secrets → ***[MASKED]***"]
end
APP["Your App\n(e.g. npm run dev)"]
EXT["External APIs\n(Stripe, OpenAI …)"]
TERM["Terminal Output\n(secrets never shown)"]
AI -- "reads safely" --> TOML
AI -. "cannot reach" .-> JSON
TOML --> vg
JSON --> INJ
INJ -- "env vars in RAM\n(never written to disk)" --> APP
APP -- "HTTP /proxy/*" --> PROXY
PROXY -- "real request\n+ Authorization header" --> EXT
APP -- "stdout / stderr" --> MASK
MASK --> TERM
AI -. "sees only ***MASKED***" .-> TERM
brew tap jjjkkkjjj/vibeguardian
brew install vibeguardiancurl -LO https://github.com/jjjkkkjjj/vibeguardian/releases/latest/download/vs-x86_64-linux.deb
sudo dpkg -i vs-x86_64-linux.debDownload the latest binary from GitHub Releases and place it in your $PATH.
# macOS (Apple Silicon)
curl -L https://github.com/jjjkkkjjj/vibeguardian/releases/latest/download/vs-aarch64-apple-darwin.tar.gz | tar xz
sudo mv vs /usr/local/bin/
# macOS (Intel)
curl -L https://github.com/jjjkkkjjj/vibeguardian/releases/latest/download/vs-x86_64-apple-darwin.tar.gz | tar xz
sudo mv vs /usr/local/bin/
# Linux (x86_64)
curl -L https://github.com/jjjkkkjjj/vibeguardian/releases/latest/download/vs-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv vs /usr/local/bin/cargo install --git https://github.com/jjjkkkjjj/vibeguardian vs# 1. Generate vibeguard.toml in your project
vg init
# 2. Store secrets globally (never in the project)
vg set stripe/secret_key # hidden prompt
vg set openai/api_key sk_... # arg mode (warns about shell history)
# 3. Run your app in a protected environment
vg run -- npm run dev
vg run --profile prod -- node server.jsExample terminal output:
[Vibeguard] Proxy started at http://localhost:8080
[Vibeguard] Injected 2 env var(s) (profile: dev)
[Vibeguard] Log masking enabled
> next dev
...
Place at your project root. Contains no actual keys — safe to commit to Git.
[project]
name = "my-app"
default_profile = "dev"
# ── Inject Mode ──────────────────────────────────────────────────────────────
[env.dev]
# secret://global/... → resolved from ~/.vibeguard/secrets.json (shared across projects)
# secret://project/... → resolved from ~/.vibeguard/projects/my-app/secrets.json (this project only)
DATABASE_URL = "secret://global/supabase/dev_db_url"
STRIPE_KEY = "secret://project/stripe/secret_key" # project-scoped
NEXT_PUBLIC_API_URL = "http://localhost:8080/proxy/api" # plain text is fine for proxy URLs
[env.prod]
DATABASE_URL = "secret://global/supabase/prod_db_url"
STRIPE_KEY = "secret://project/stripe/secret_key"
# ── Proxy Mode ───────────────────────────────────────────────────────────────
[proxy]
port = 8080 # default
[[proxy.routes]]
path = "/proxy/stripe"
target = "https://api.stripe.com"
inject_headers = { Authorization = "Bearer ${secret://project/stripe/secret_key}" }
[[proxy.routes]]
path = "/proxy/openai"
target = "https://api.openai.com/v1"
inject_headers = { Authorization = "Bearer ${secret://global/openai/api_key}" }The AI reads vibeguard.toml, understands that requests go to localhost:8080/proxy/stripe, and writes correct code — but it can never see the actual key behind secret://.
| Flag | Description |
|---|---|
-p, --profile <PROFILE> |
Environment profile to use (default: dev) |
--no-mask |
Disable log masking (not recommended) |
--no-proxy |
Do not start the local proxy |
-- <CMD> |
Command to run (e.g. npm run dev) |
Generates a vibeguard.toml template in the current directory. Fails if one already exists.
Stores a secret in the global store (~/.vibeguard/secrets.json) by default.
Pass --project to store in the project-scoped store (~/.vibeguard/projects/<name>/secrets.json),
where <name> is read from vibeguard.toml in the current directory.
| Flag | Description |
|---|---|
--project |
Write to the project-scoped store instead of the global store |
- Omit
VALUEfor a hidden interactive prompt - Passing
VALUEas an argument warns about shell history exposure
vg set stripe/secret_key # → ~/.vibeguard/secrets.json
vg set --project stripe/secret_key sk_... # → ~/.vibeguard/projects/my-app/secrets.jsonReads vibeguard.toml and displays the list of env var names and proxy routes — values are always masked.
- All secret files are written with
0o600permissions (owner read/write only) vibeguard.tomlnever contains real secret values- Log masking uses Aho-Corasick for O(n) linear-time replacement — no performance impact on high-volume logs
| Scope | URI prefix | Store location | Use case |
|---|---|---|---|
| global | secret://global/... |
~/.vibeguard/secrets.json |
Shared keys (e.g. personal OpenAI key) |
| project | secret://project/... |
~/.vibeguard/projects/<name>/secrets.json |
Per-project keys (e.g. Stripe test key for this app only) |
# Store globally (shared across all projects)
vg set global/openai/api_key
# Store for this project only (reads project name from vibeguard.toml)
vg set --project stripe/secret_key# Build inside Docker
docker compose run --rm dev cargo build
# Run tests
docker compose run --rm dev cargo test
# Lint
docker compose run --rm dev cargo clippy -- -D warnings
# Shorthand from host
./cargo-docker build
./cargo-docker test
./cargo-docker clippy -- -D warningssrc/
├── main.rs Entry point
├── lib.rs Library root (used by tests/)
├── cli.rs Clap command definitions
├── inject/ Inject Mode — env var resolution helpers
├── mask/ Log Mask Mode — Aho-Corasick masker
├── proxy/ Proxy Mode — axum reverse proxy
├── config/ Config & secrets store parser
└── commands/ Subcommand implementations
tests/
├── mask.rs LogMasker integration tests
└── config/
├── resolver.rs secret:// resolution logic tests
└── secrets.rs Secrets store lookup tests
MIT