Metadata-Version: 2.4
Name: credvault
Version: 0.1.0
Summary: Hand API credentials to a coding agent without pasting them into the chat transcript.
Project-URL: Homepage, https://github.com/bigtownxyz/credvault
Project-URL: Source, https://github.com/bigtownxyz/credvault
Project-URL: Issues, https://github.com/bigtownxyz/credvault/issues
Author: bigtownxyz
License: MIT License
        
        Copyright (c) 2026 bigtownxyz
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: agent,claude-code,credentials,dotenv,env,secrets
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: X11 Applications
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: python-dotenv>=1.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# CredVault

Hand API credentials to a coding agent without pasting them into the chat.

When Claude Code (or Cursor, or any agent) says *"paste your Stripe key and I'll
wire it up"*, the key you paste is now in the conversation transcript, and in
whatever that transcript gets written to. CredVault replaces that moment. The
agent asks for a key by *name*; a small desktop window opens; you type the value
there; it goes straight into your project's `.env`. The agent gets back a list of
key names and never sees a value.

```
agent:  credvault request --key STRIPE_SECRET_KEY --wait
                 │
                 ▼
        ┌─────────────────────────────┐
        │  CredVault                  │   ← you type the value here
        │  STRIPE_SECRET_KEY  [•••••] │
        │  Will write to: ./.env      │
        │            [ Inject ]       │
        └─────────────────────────────┘
                 │
                 ▼
agent:  {"status":"injected","keys":["STRIPE_SECRET_KEY"]}
```

It also solves a smaller problem that bites harder than it should: it guarantees
the file it wrote is ignored by the **nearest enclosing git repo**, walking up to
find it. If your home directory is itself a repo, a `.gitignore` dropped next to
the `.env` would have done nothing.

## Install

```bash
uv tool install credvault      # recommended
pipx install credvault
pip install credvault
```

Pure standard library, no dependencies. Needs Python 3.9+ with `tkinter`
(bundled on Windows and macOS; `apt install python3-tk` on Debian/Ubuntu).

## Use it with Claude Code

This repo is also a Claude Code plugin. Installing it teaches Claude the rule
*"never ask for a secret in chat, ask CredVault"*, and it will do the rest on its
own.

```
/plugin marketplace add bigtownxyz/credvault
/plugin install credvault
```

For other agents, point them at [`skills/credvault/SKILL.md`](skills/credvault/SKILL.md)
or paste its contents into your `AGENTS.md`.

## Use it by hand

```bash
credvault
```

Pick a project folder, choose `.env` / `.env.local` / a custom name, type your
key/value pairs, and hit Inject. Values are masked with a per-row show/hide.

## The CLI

```bash
# Ask for credentials and block until the human deals with it.
credvault request --folder . --file .env.local \
  --key X_API_KEY --key X_API_SECRET \
  --hint "X_API_KEY=developer.x.com > Keys and tokens" \
  --note "For the tweet poster" --wait

# Is anything waiting?
credvault status
```

`request` writes the request, opens the GUI if it is not already running, and
with `--wait` blocks until you inject or dismiss. It prints a keys-only ack and
exits `0` injected, `2` dismissed, `3` timed out.

## Threat model

Read this before you trust the tool with anything.

**What CredVault protects against**

- Your credential being written into an agent's chat transcript, and from there
  into logs, telemetry, context windows, and anything the transcript is later fed to.
- Committing the `.env` you just created, because it gitignores it against the
  correct repo.
- A newly created `.env` being world-readable: it is created `0600` from the
  moment it exists, not chmod-ed afterwards.

**What CredVault does not protect against, at all**

- **The agent reading the `.env` afterwards.** It can. `cat .env` works fine.
  CredVault keeps the secret out of the *transcript*, not out of the agent's
  reach. The bundled skill instructs Claude not to read the file, and that is an
  instruction, not an enforcement boundary. If you need the agent to be unable to
  read a credential, do not put that credential on the machine the agent runs on.
- A malicious or prompt-injected agent, which can read the file, exfiltrate it,
  or run anything else you could run.
- Any other process running as you. The request inbox is a plain directory.
- Secrets already in your shell history, your clipboard, or an existing `.env`.

**Trust boundary on the request channel**

Any process that can write to the request inbox can put text in the CredVault
banner and pre-select a target folder. Notes are treated as untrusted input:
truncated, stripped of control characters, and shown quoted and attributed to the
requester. **Always read the "Will write to:" path before hitting Inject.** That
line is the thing standing between you and a request that quietly aimed at
someone else's directory.

**Nothing secret is stored by CredVault.** There is no vault file, despite the
name. Credential values exist in the app only while the window is open, and are
written to exactly one place: the `.env` you chose. The state directory holds
pending requests (key names and notes), keys-only acks, and a list of recently
used folders.

## How values are written

There is no single `.env` specification and the dialects disagree, so CredVault
picks the encoding each value is safest in:

| Value | Written as | Why |
|---|---|---|
| `sk-abc123` | `KEY=sk-abc123` | Safe characters only, no quoting needed |
| `hunter two` | `KEY='hunter two'` | Single quotes are literal everywhere |
| `abc#def` | `KEY='abc#def'` | Bare `#` starts a comment in most parsers |
| `cost$5` | `KEY='cost$5'` | Bare `$` gets interpolated |
| `-----BEGIN…\nline2` | `KEY="-----BEGIN…\nline2"` | Newlines escaped; double quotes are the only form that survives |
| `it's` | `KEY="it's"` | Contains a single quote |

Verified by round-tripping through `python-dotenv`. A multi-line PEM key written
naively as `KEY=<value>` parses as several bogus keys with the real one
truncated, which is exactly the bug this replaces.

Existing comments, key order and blank lines survive an upsert, and an `export `
prefix is preserved.

## Where state lives

| OS | Path |
|---|---|
| Windows | `%APPDATA%\CredVault\` |
| macOS | `~/Library/Application Support/CredVault/` |
| Linux | `$XDG_DATA_HOME/credvault/` or `~/.local/share/credvault/` |

Override with `CREDVAULT_HOME`. If that directory happens to sit inside a git
repo, CredVault gitignores it.

## The request format

The CLI is the supported interface. If you must write a request by hand, drop a
JSON file into `<state dir>/requests/` using tmp-then-rename so the poller never
reads a half-written file:

```json
{
  "note": "X API v2 access for the poster",
  "target_folder": "/home/u/proj",
  "target_file": ".env.local",
  "fields": [
    { "key": "X_API_KEY", "hint": "developer.x.com > Keys and tokens" },
    "X_API_SECRET"
  ]
}
```

`fields` is required; each item is a key name or `{key, hint}`. `target_file`
must be a bare filename. The ack lands in `<state dir>/processed/` as
`<ts>-<request>.result.json`.

## Development

```bash
pip install -e ".[dev]"
pytest
ruff check .
```

`core.py` is pure and holds the env parsing, quoting and gitignore logic.
`ipc.py` is the request channel. `gui.py` is the only module that imports
tkinter, so the CLI and the tests run headless.

### A standalone executable

Not needed if you installed from PyPI. If you want a double-clickable app with
no Python on the target machine:

```bash
pip install pyinstaller
pyinstaller --onefile --noconsole --name CredVault \
  --paths src --distpath dist --workpath build --specpath build \
  src/credvault/gui.py
```

Kill any running `CredVault` first: it holds a lock on the output file.

The executable attached to each GitHub Release is built this way by CI. It is
**not code-signed**, so Windows SmartScreen will warn on first run. If that
bothers you, and it reasonably might for a tool you are about to type secrets
into, install from PyPI instead and read the source.

### Cutting a release

Publishing uses [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/),
so no API token is stored in this repo. One-time setup on PyPI: add a pending
publisher for owner `bigtownxyz`, repo `credvault`, workflow `release.yml`,
environment `pypi`.

Then, per release: bump `version` in `pyproject.toml` and `__init__.py`, tag it,
and publish a GitHub Release. CI runs the tests on three OSes, builds the wheel,
publishes to PyPI, and attaches a Windows exe.

## Licence

MIT.
