# kv

[![tests](https://github.com/SatishoBananamoto/kv-secrets/actions/workflows/test.yml/badge.svg)](https://github.com/SatishoBananamoto/kv-secrets/actions/workflows/test.yml)
[![PyPI](https://img.shields.io/pypi/v/kv-secrets)](https://pypi.org/project/kv-secrets/)
[![Python](https://img.shields.io/pypi/pyversions/kv-secrets)](https://pypi.org/project/kv-secrets/)
[![License](https://img.shields.io/github/license/SatishoBananamoto/kv-secrets)](LICENSE)

Encrypted secrets management for developers and AI coding agents.

**Stop pasting API keys into AI chat.** kv encrypts your secrets locally and lets your AI agent (Claude Code, Cursor, VS Code) make API calls without ever seeing the keys. The daemon holds secrets in memory and injects credentials at the HTTP transport layer — the key never enters the agent's process.

```
pip install kv-secrets
```

## How It Works

kv uses the same security model as `ssh-agent`: your keys are held in a daemon process and can only be **used**, never **exported**.

```
You: "call the OpenAI API with this prompt"

AI Agent                          kv daemon
   |                                  |
   |-- kv_api(provider="openai",  -->|
   |     path="/v1/chat/...",        |-- looks up OPENAI_API_KEY
   |     body={...})                 |-- adds Authorization header
   |                                 |-- makes the HTTP call
   |                                 |-- redacts any secrets from response
   |<-- response body (no key) -----|

The API key never enters the agent's memory.
```

## Get Started

```bash
cd your-project
kv init                        # Create encrypted vault (passphrase prompt)
kv set OPENAI_API_KEY          # Store a secret (hidden input)
kv unlock                      # Start the daemon
```

Now your AI agent can use `kv_api` to make authenticated API calls — the key stays in the daemon.

## Two Channels

### kv_api — Primary (key never leaves daemon)

For HTTP API calls. The daemon makes the request and injects credentials. The agent gets the response body only.

```python
# From your AI agent (via MCP):
kv_api(provider="openai", path="/v1/chat/completions", method="POST", body={
    "model": "gpt-4",
    "messages": [{"role": "user", "content": "Hello"}]
})

kv_api(provider="anthropic", path="/v1/messages", method="POST", body={...})
kv_api(provider="github", path="/user/repos", method="GET")
```

Built-in providers: `openai`, `anthropic`, `google`, `github`, `google-cloud`.

Add your own:
```bash
kv provider add stripe --url https://api.stripe.com --secret STRIPE_SECRET_KEY
kv provider add sendgrid --url https://api.sendgrid.com --secret SENDGRID_API_KEY
kv provider list
```

### kv_run — Fallback (for databases, SDKs, legacy apps)

For processes that genuinely need credentials in their environment — database connections, cloud SDKs, non-HTTP protocols. Secrets are injected as env vars with sandbox protections.

```bash
kv run -- python app.py         # Secrets as env vars, output redacted
kv run -- npm start             # Works with any language
```

Security layers on kv_run:
- Command validation (allowlist/blocklist)
- Network isolation (`unshare --user --net`)
- Output redaction (secret values replaced with `[REDACTED]`)
- Post-execution file scanning (detects encoded secrets written to disk)

## MCP Integration (AI Editors)

```bash
kv setup cursor
kv setup claude-code
kv setup vscode
```

| Tool | Profile | What it does |
|------|---------|-------------|
| `kv_status` | safe | Check vault status |
| `kv_envs` | safe | List environments |
| `kv_list` | safe | List secret names (no values) |
| `kv_run` | safe | Run commands with secrets injected |
| `kv_api` | safe | HTTP API call with credentials injected by daemon |
| `kv_set` | mutate | Store a secret (opt-in) |
| `kv_get` | reveal | Read a secret value (opt-in, use with caution) |

## Security Model

Three layers of defense:

1. **Passphrase barrier** — vault is encrypted with your passphrase. AI agents can't enter interactive prompts. Same model as ssh-agent. This is the foundation for client-agnostic security — works with any editor, any agent.

2. **Daemon isolation** — secrets live only in daemon memory. `kv_api` calls never expose the key. `kv_run` applies sandbox layers. Process memory protected with `PR_SET_DUMPABLE=0`.

3. **SSRF protection** — custom provider URLs are validated against private IP ranges and must use HTTPS. Prevents credential exfiltration to internal services.

Optional: add 2FA for passphrase-compromised scenarios:
```bash
kv setup-2fa    # TOTP with any authenticator app
```

## Audit Logging

Every daemon operation is logged to `~/.kv/audit.jsonl` with a tamper-evident hash chain. Key names only — never values.

```bash
kv audit                    # Recent events
kv audit --security         # Blocked commands and leak detections
kv audit --provider openai  # Filter by provider
kv audit --summary          # Usage stats and security event counts
kv audit --verify           # Verify log hasn't been tampered with
```

```
  kv -- audit (showing 5)

  2026-03-26 15:30  api_call          openai  /v1/chat/completions  → 200  1234ms
  2026-03-26 15:29  api_call          anthropic  /v1/messages  → 200  890ms
  2026-03-26 15:28  run_exec          python3  exit=0  450ms
  2026-03-26 15:27  run_blocked       curl  network tool blocked
  2026-03-26 15:20  agent_start
```

## Use in GitHub Actions

`.enc` files are safe to commit — they're encrypted noise without the key.

```yaml
steps:
  - uses: actions/checkout@v4
  - run: pip install kv-secrets
  - run: kv run -- pytest
    env:
      KV_MASTER_KEY: ${{ secrets.KV_MASTER_KEY }}
```

## Multi-Environment

```bash
kv env create staging
kv env create prod
kv set API_KEY sk-test --env dev
kv set API_KEY sk-live --env prod
kv run --env prod -- python deploy.py
```

## All Commands

```
# Vault
kv init                     Create encrypted vault
kv unlock [-b]              Open vault (start daemon)
kv lock                     Close vault (stop daemon)
kv upgrade-security         Add passphrase protection
kv setup-2fa                Enable TOTP 2FA

# Secrets
kv set KEY[=VALUE] [-e ENV] Store a secret
kv get KEY [-e ENV]         Decrypt and print
kv ls [-e ENV] [--reveal]   List secret names
kv rm KEY [-e ENV]          Remove a secret
kv run CMD [-e ENV]         Run with secrets (requires daemon)
kv export [-e ENV]          Export as .env
kv import FILE [-e ENV]     Import .env file

# Providers
kv provider add NAME        Add custom API provider
kv provider list            List all providers
kv provider rm NAME         Remove custom provider

# Security policy
kv allow [PATTERN]          Allow command for kv_run
kv deny PATTERN             Remove from allowlist
kv audit [--security]       View audit log
kv audit --verify           Check log integrity

# AI editor setup
kv setup EDITOR             Configure MCP (cursor, claude-code, vscode)

# Diagnostics
kv status                   Vault status
kv doctor                   Run health checks

# Remote (kvsecure.com — coming soon)
kv signup / login / logout  Account management
kv push / pull              Sync encrypted secrets
kv team                     Team management
kv token                    CI/CD API tokens
```

## Comparison

| Feature | kv | .env files | 1Password CLI | AgentSecrets |
|---------|:--:|:----------:|:-------------:|:------------:|
| Key never enters agent memory | **kv_api** | No | No (SDK returns plaintext) | Yes |
| Encrypted at rest | Yes | No | Yes | OS keychain |
| MCP server for AI agents | **Native** | No | No | Added later |
| Subprocess with sandbox | **kv_run** | No | No | No |
| Passphrase + 2FA barrier | Yes | No | Via app | No |
| Tamper-evident audit log | **Yes** | No | Enterprise only | JSONL only |
| SSRF protection | Yes | N/A | N/A | Yes |
| Custom providers | Config-based | N/A | N/A | 6 auth styles |
| Works offline | Yes | Yes | No | Yes |
| Dependencies | 1 (cryptography) | 0 | Many | Go stdlib |
| Free for individuals | Yes | Yes | No | Yes |

## Encryption

- **Cipher:** ChaCha20-Poly1305 (AEAD) — same as WireGuard, TLS 1.3
- **Key derivation:** BLAKE2b with environment name as context
- **Passphrase wrapping:** PBKDF2 (600K iterations) + ChaCha20-Poly1305
- **TOTP:** RFC 6238, encrypted with passphrase
- **Storage:** Binary `.enc` files — safe to commit to git

## Security

See [SECURITY.md](SECURITY.md) for vulnerability reporting.

| What | Where | Safe to share? |
|------|-------|---------------|
| `.enc` files | `.secrets/*.enc` | Yes (commit to git) |
| Master key | `.secrets/key` | No (share via secure channel) |
| Audit log | `~/.kv/audit.jsonl` | Contains key names, not values |
| Plaintext secrets | Never on disk | N/A |

## Requirements

- Python 3.10+
- `cryptography` (installed automatically)

## License

MIT — see [LICENSE](LICENSE)

## Links

- [kvsecure.com](https://kvsecure.com) — website
- [PyPI](https://pypi.org/project/kv-secrets/) — package
- [GitHub](https://github.com/SatishoBananamoto/kv-secrets) — source
