Metadata-Version: 2.4
Name: vw-keystroke
Version: 0.3.0
Summary: Capture, sync, rotate & revoke secrets across a local 600 env file and your vault (Vaultwarden/Bitwarden, KeePassXC, 1Password, or HashiCorp Vault).
Author: PolyDevHouse LLC
License: MIT
Project-URL: Homepage, https://github.com/PolyDevHouse/vw-keystroke
Project-URL: Issues, https://github.com/PolyDevHouse/vw-keystroke/issues
Keywords: vaultwarden,bitwarden,secrets,api-keys,dotenv,keyring
Classifier: Environment :: X11 Applications
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Security
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: keyring>=24
Provides-Extra: keepass
Requires-Dist: pykeepass>=4.1; extra == "keepass"
Provides-Extra: vault
Requires-Dist: hvac>=2.0; extra == "vault"
Provides-Extra: all
Requires-Dist: pykeepass>=4.1; extra == "all"
Requires-Dist: hvac>=2.0; extra == "all"
Dynamic: license-file

<p align="center">
  <img src="assets/rendered/vw-keystroke-256.png" width="120" alt="VW Keystroke">
</p>

<h1 align="center">VW Keystroke</h1>

<p align="center">
  <img src="https://img.shields.io/pypi/v/vw-keystroke" alt="PyPI">
  <img src="https://img.shields.io/github/license/PolyDevHouse/vw-keystroke" alt="License: MIT">
  <img src="https://img.shields.io/badge/python-3.9%2B-blue" alt="Python 3.9+">
  <img src="https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey" alt="Platforms">
  <img src="https://img.shields.io/badge/vault-Vaultwarden%20%7C%20Bitwarden-175DDC" alt="Vaultwarden / Bitwarden">
</p>

**Paste a secret once — it lands in your [Vaultwarden](https://github.com/dani-garcia/vaultwarden)/Bitwarden vault *and* a local `600` env file, in one move.**

Built for the "hot potato" moment: you just generated an API key and you want it
*off your clipboard and safely stored* without a five-step ritual — so it doesn't
end up lingering in a scratch file. Name it, paste it, done.

- 🗄️ Saves to your vault as a **Login** item (username = the env var, password = the value, with the env-file path recorded as a custom field).
- 📄 Writes `~/.config/<project>.env` (`chmod 600`, updated in place — never duplicated).
- 🔒 **Zero-knowledge**: your master password never leaves your machine; all vault crypto is done by the official `bw` CLI.
- ⚡ Caches the unlocked session in your **OS keyring** (Windows Credential Locker / macOS Keychain / Linux libsecret) so daily use needs no password.
- 🖥️ **GUI** and **CLI**, on **Windows / Linux / macOS**.
- 🔁 **Full lifecycle:** `drop` · `status` · `pull` · `rotate` · `revoke` — capture, reconcile, rebuild env files from the vault, rotate a value everywhere, or purge a secret from *everywhere* (env file, vault, shell history, clipboard).
- 🔌 **Pluggable backends:** Vaultwarden/Bitwarden (default), **KeePassXC** (offline `.kdbx`), **1Password**, **HashiCorp Vault/OpenBao** — one `BACKEND=` line switches the store.

> The secret value is never passed on the command line and never echoed — only
> the `600` env file and your vault ever see it.

---

## Requirements

1. **[Bitwarden CLI](https://bitwarden.com/help/cli/)** — `npm install -g @bitwarden/cli` (provides `bw`).
2. **Python 3.9+** (Tkinter ships with the standard python.org installers).

## Install

```bash
pipx install vw-keystroke        # recommended
# or
pip install --user vw-keystroke
# or straight from source:
pipx install git+https://github.com/PolyDevHouse/vw-keystroke
```

This gives you three commands: `vwks` (CLI), `keydrop` (alias), and `vwks-gui` (GUI).

### First-time setup

```bash
# point bw at your server (self-hosted Vaultwarden, or Bitwarden cloud)
bw config server https://vault.example.com
bw login                          # email + master password + 2FA (once)

# optional config
mkdir -p ~/.config
cp vw-keystroke.conf.example ~/.config/vw-keystroke.conf && chmod 600 ~/.config/vw-keystroke.conf
```

## Backends

Store secrets in the vault of your choice — pick one with `BACKEND=` in your config:

| Backend | `BACKEND=` | Install | Notes |
|---|---|---|---|
| **Vaultwarden / Bitwarden** *(default)* | `bitwarden` | needs the [`bw`](https://bitwarden.com/help/cli/) CLI | self-hosted or cloud |
| **KeePassXC** | `keepass` | `pipx install "vw-keystroke[keepass]"` | offline single `.kdbx` — ideal on a USB / VeraCrypt drive, no daemon |
| **1Password** | `1password` | needs the [`op`](https://developer.1password.com/docs/cli/) CLI | service-account auth |
| **HashiCorp Vault / OpenBao** | `vault` | `pipx install "vw-keystroke[vault]"` | self-hosted, KV-v2 |

Only items VW Keystroke created (marked with an `env_file` field) are ever listed, rotated, or revoked — your ordinary vault logins are never touched.

## Use

**GUI:** launch **VW Keystroke** from your app menu (Linux `.desktop` in
[`packaging/`](packaging/)), or run `vwks-gui`.

**CLI — the secret lifecycle:**
```bash
vwks OPENAI_API_KEY --project openai        # capture (drop): -> ~/.config/openai.env + vault
echo -n "$SECRET" | vwks --var FOO --project bar    # piped / non-interactive
vwks status                                 # reconcile vault vs env files (match / drift / …)
vwks pull --project openai                  # rebuild the env file FROM the vault  (or --all)
vwks rotate OPENAI_API_KEY --generate --exec "systemctl --user restart mybot"
vwks revoke OPENAI_API_KEY                  # purge from env + vault + shell history + clipboard
vwks --lock                                 # forget the cached session + lock now
```

## Configuration

`~/.config/vw-keystroke.conf` (all optional — see [`vw-keystroke.conf.example`](vw-keystroke.conf.example)):

| Key | Meaning | Default |
|-----|---------|---------|
| `BW_SERVER` | Vault server URL | current `bw` config |
| `DEFAULT_PROJECT` | env-file basename | `keys` |
| `VAULT_FOLDER` | folder for new items | `API Keys` |
| `KEYRING_TTL_DAYS` | cached-session lifetime (`0` = never cache) | `30` |
| `ENV_DIR` | where env files go | `~/.config` |
| `BW_CLIENTID` / `BW_CLIENTSECRET` | optional API-key login | — |
| `BACKEND` | vault backend: `bitwarden` / `keepass` / `1password` / `vault` | `bitwarden` |
| `HISTORY_FILES` | shell histories `revoke` scrubs | `~/.zsh_history,~/.bash_history` |
| `KEEPASS_DB` / `KEEPASS_KEYFILE` | KeePass `.kdbx` path (+ optional keyfile) | `~/.config/vwks.kdbx` |
| `OP_VAULT` / `OP_SERVICE_ACCOUNT_TOKEN` | 1Password target vault + service-account token | — |
| `VAULT_ADDR` / `VAULT_TOKEN` / `VAULT_MOUNT` | Vault URL, token, KV mount | `http://127.0.0.1:8200` / — / `secret` |

## How it works

`bw` handles login and all encryption. On a drop, VW Keystroke unlocks (or reuses a
cached session), then creates/updates the vault item and the env file. If the vault
is locked it asks for your master password once; the resulting session key is stored
in your OS keyring with a TTL and refreshed on use.

## Security notes

- Nothing sensitive is stored by this tool except the `600` env file you asked for and the keyring session (same protection as your desktop login).
- A cached session means anyone at your already-unlocked machine can drop/read keys — the same exposure as a logged-in browser vault extension. Set `KEYRING_TTL_DAYS=0` to disable caching.
- This project ships **no** credentials, servers, or keys of its own.

## License

MIT © PolyDevHouse LLC
