Metadata-Version: 2.4
Name: keepassxc-cli
Version: 3.0.0
Summary: CLI for KeePassXC using the browser extension protocol with biometric unlock
License-Expression: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: keepassxc-browser-api==1.5.0
Requires-Dist: pyperclip==1.8.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Dynamic: license-file

# KeePassXC CLI

A command-line interface for [KeePassXC](https://keepassxc.org/) that communicates via the browser extension protocol, supporting biometric (TouchID/fingerprint) unlock on supported platforms.

`kpxc-cli` talks to a running KeePassXC instance using the same native messaging protocol used by the KeePassXC Browser extension. This means:

- **Biometric unlock**: On macOS with TouchID (or similar) configured in KeePassXC, you can authenticate via fingerprint rather than typing your master password.
- **No master password in shell history**: Authentication happens through KeePassXC's GUI, not the terminal.
- **CRUD**: Add, edit, delete entries and groups.
- **TOTP**: Retrieve time-based one-time passwords.
- **Clipboard**: Copy credentials directly to the clipboard.

![Functionality demonstration](assets/demo.gif)

KeePassXC CLI based on [KeePassXC Browser API](https://github.com/mietzen/keepassxc-browser-api).

## Prerequisites

- **macOS** (uses Unix sockets and KeePassXC's browser extension socket)
- **Python >= 3.10**
- **KeePassXC** with:
  - Browser Integration enabled (Settings > Browser Integration > Enable browser integration)
    ![KeePassXC Browser Integration Settings screenshot](assets/settings-browser-integration.png)

## Install

### Homebrew (recommended)

See **[homebrew homepage](https://brew.sh/)** on how to setup homebrew.

```shell
brew install mietzen/tap/keepassxc-cli
```

### pipx

```bash
pipx install keepassxc-cli
```

## Setup

Before using `kpxc-cli`, associate it with your KeePassXC instance:

```bash
kpxc-cli setup
```

This performs a key exchange with KeePassXC (you will be prompted to allow the association in the KeePassXC GUI). The association is saved to `~/.keepassxc/browser-api.json`.

## Usage

### Global options

```
kpxc-cli [--config PATH] [--browser-api-config PATH] [-v] COMMAND [COMMAND OPTIONS]
```

| Option | Description |
|--------|-------------|
| `--config` | Path to CLI config file (default: `~/.keepassxc/cli.json`) |
| `--browser-api-config` | Path to browser API config file (default: `~/.keepassxc/browser-api.json`) |
| `-v, --verbose` | Enable verbose/debug logging |

Some commands support a `-j / --json` flag for JSON output — pass it anywhere after the subcommand name:

```bash
kpxc-cli show https://github.com -j
kpxc-cli status -j
```

### Commands

> **URL scheme**: All commands that accept a URL argument require a scheme (`https://` or `http://`). If you pass a bare hostname (e.g. `example.com`), the CLI will automatically prepend `https://` and emit a warning. KeePassXC derives the entry title from `QUrl(url).host()`, which returns an empty string for URLs without a scheme.

#### `setup` — Associate with KeePassXC

```bash
kpxc-cli setup
```

#### `status` — Connection and association status

```bash
kpxc-cli status
kpxc-cli status -j
```

#### `show` — Show entries for a URL

```bash
kpxc-cli show https://github.com
kpxc-cli show https://github.com -p     # reveal password and TOTP
kpxc-cli show https://github.com -j
```

Without `-p`, password and TOTP are omitted from the output entirely.

#### `totp` — Get TOTP code

```bash
kpxc-cli totp https://github.com
kpxc-cli totp https://github.com -j
```

#### `clip` — Copy a field to clipboard

```bash
kpxc-cli clip https://github.com password
kpxc-cli clip https://github.com username
kpxc-cli clip https://github.com totp
```
> **Note**: `clip` does not support `-j/--json` output — the field value is always copied silently to the clipboard.

#### `add` — Add a new entry

```bash
# Password is prompted securely if --password is not given
kpxc-cli add https://example.com user@example.com
kpxc-cli add https://example.com user --password mypass
# Place the entry in a specific group by UUID or by path
kpxc-cli add https://example.com user --group-uuid <group-uuid>
kpxc-cli add https://example.com user --group "Work/Projects"
```

> **Note**: The entry title is always derived from the URL hostname by KeePassXC. The protocol has no field to set a custom title.

#### `edit` — Edit an entry

```bash
# URL is positional; --uuid is optional when the URL matches exactly one entry
kpxc-cli edit https://github.com --username newuser
kpxc-cli edit https://github.com --password newpass
# Specify --uuid explicitly when the URL matches multiple entries
kpxc-cli edit https://github.com --uuid <uuid> --username newuser
```

#### `rm` — Delete an entry

```bash
kpxc-cli rm https://example.com         # prompts for confirmation
kpxc-cli rm https://example.com --yes   # skip confirmation
# Specify --uuid when URL matches multiple entries
kpxc-cli rm https://example.com --uuid <uuid> --yes
```

#### `lock` — Lock the database

```bash
kpxc-cli lock
kpxc-cli lock -j
```

#### `unlock` — Unlock the database

```bash
kpxc-cli unlock
kpxc-cli unlock -j
```

Triggers biometric (TouchID/fingerprint) unlock if configured. Raises an error if the unlock times out or KeePassXC is not running.

#### `mkdir` — Create a group

```bash
kpxc-cli mkdir "Work"
kpxc-cli mkdir "Work/Projects"   # create Projects inside Work
```

Use `/`-separated paths to create nested groups. KeePassXC creates any missing path segments automatically.

#### `group-uuid` — Look up a group's UUID by path

```bash
kpxc-cli group-uuid "Work"
kpxc-cli group-uuid "Work/Projects"
kpxc-cli group-uuid "Work/Projects" -j
```

Returns the UUID for the group at the given path (relative to the database root). Useful for scripting — pipe the UUID into `add --group-uuid`.

JSON output (`-j`):
```json
{
  "path": "Work/Projects",
  "name": "Projects",
  "uuid": "<uuid>"
}
```

#### `version` — Show the CLI version

```bash
kpxc-cli version
kpxc-cli version -j
```

Does not require a running KeePassXC instance.

## Configuration

### CLI config (`~/.keepassxc/cli.json`)

Only non-default values are stored. Available options:

| Key | Default | Description |
|-----|---------|-------------|
| `browser_api_config_path` | `~/.keepassxc/browser-api.json` | Path to the browser API config |
| `default_format` | `table` | Default output format (`table` or `json`) |

Example `~/.keepassxc/cli.json`:
```json
{
  "default_format": "json"
}
```

### Browser API config (`~/.keepassxc/browser-api.json`)

Shared with `keepassxc-browser-api`. Contains the association keys created during `kpxc-cli setup`. This file is automatically created and updated by the `setup` command.

Both config files are stored with `0o600` permissions (owner read/write only).

## Exit codes

| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | Generic error (unexpected KeePassXC error, OS error, config parse error) |
| `2` | KeePassXC is not running or the socket is not found (`ConnectionError`) |
| `3` | Database unlock timed out (`DatabaseLockedError`) |
| `4` | Access denied by user — either the access prompt was cancelled or "Allow access to all entries" was denied |

These codes are stable and suitable for scripting, e.g.:

```bash
kpxc-cli show https://example.com || case $? in
  2) echo "Start KeePassXC first" ;;
  3) echo "Unlock timed out" ;;
  4) echo "Access denied" ;;
esac
```

## Development

This package depends on [`keepassxc-browser-api`](https://github.com/mietzen/keepassxc-browser-api), which handles the KeePassXC browser extension protocol. The browser API credentials are stored in `~/.keepassxc/browser-api.json` and are shared with `keepassxc-ssh-agent` if installed.

```bash
git clone https://github.com/mietzen/kpxc-cli
git clone https://github.com/mietzen/keepassxc-browser-api
cd kpxc-cli

python3 -m venv .venv
source .venv/bin/activate

# Install local keepassxc-browser-api dependency first
pip install ../mietzen-keepassxc-browser-api/

# Install in editable mode with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest --tb=short -q

# Run tests with coverage
pytest --cov=keepassxc_cli --cov-report=term-missing

# Lint
ruff check --ignore=E501 --exclude=__init__.py ./keepassxc_cli
```

Verify with [manual tests](/tests/manual_test.md).

---

### Exit codes

| Scenario | Expected code |
|----------|---------------|
| Any successful command | `0` |
| Entry not found / generic error | `1` |
| KeePassXC not running | `2` |
| Unlock timed out | `3` |
| Access denied by user | `4` |

```bash
kpxc-cli show https://test.example.com; echo "exit: $?"    # 0
kpxc-cli show https://ghost.example.com; echo "exit: $?"   # 1
# Quit KeePassXC, then:
kpxc-cli status; echo "exit: $?"                           # 2
```

---

## Known Limitations

- Requires KeePassXC to be **running** and the database to be **open** (or biometric auto-unlock configured).
- The `clip` command requires `pyperclip` and a working clipboard (e.g., `xclip`/`xsel` on Linux, built-in on macOS/Windows).
- The browser integration protocol does not support moving entries between groups directly.
- Entry lookup is by URL/hostname only (same as the browser extension). Title-based search is not supported by the protocol.
- **String fields** (`string_fields` in JSON output) require the KeePassXC setting "Support KPH fields" to be enabled, and custom attributes must be prefixed with `KPH: ` in the KeePassXC entry's "Advanced" tab. This is a server-side KeePassXC requirement, not something the CLI can control.
