DEV Community

Cover image for pmcli: local cli for managing those forgetful passwords
Mohit Kumar Kushwaha
Mohit Kumar Kushwaha

Posted on

pmcli: local cli for managing those forgetful passwords

a Local CLI Password Manager in Python

I have been working on a small local password manager called PMCLI.

The goal is simple: store credentials locally, encrypt the saved passwords, and retrieve them from the terminal without printing secrets directly to the screen.

GitHub repo:

https://github.com/KimtVak8143/pmcli
Enter fullscreen mode Exit fullscreen mode

This is not meant to replace a production password manager like 1Password or Bitwarden. It is a learning project for building a secure-ish CLI tool with clean Python structure.

Tech Stack

  • Python
  • Typer for the CLI
  • cryptography for encryption
  • Fernet for symmetric encryption
  • PBKDF2 for deriving an encryption key from a phrase
  • pyperclip for copying passwords to the clipboard
  • JSON file storage

The vault is stored locally at:

~/.pmcli/vault.json
Enter fullscreen mode Exit fullscreen mode

Basic Usage

After setup, the tool can be used like this:

pmcli add github.com
pmcli list
pmcli get github.com
pmcli reveal github.com
Enter fullscreen mode Exit fullscreen mode

The get command shows only the username.

The reveal command does not print the password. It copies the password to the clipboard.

Project Structure

I split the app into small modules:

pmcli/
├── main.py
├── crypto.py
├── storage.py
├── commands/
│   ├── add.py
│   ├── get.py
│   ├── reveal.py
│   ├── list_cmd.py
│   └── config.py
└── README.md
Enter fullscreen mode Exit fullscreen mode

The separation is intentional:

  • main.py only registers commands
  • commands/ contains CLI behavior
  • crypto.py handles encryption and decryption
  • storage.py handles reading and writing the vault

This made the code easier to reason about as the project grew.

Encryption Design

One important design change was separating the master password from the encryption phrase.

At first, the master password was used directly for encryption and decryption. That worked, but it had a problem:

if the master password changed, all existing passwords became unreadable.

So I changed the design:

PMCLI_MASTER_PASSWORD=used to unlock reveal
PMCLI_ENCRYPTION_PHRASE=used to encrypt and decrypt stored passwords
Enter fullscreen mode Exit fullscreen mode

The master password is now used only as an access check before revealing a password.

The encryption phrase is the stable secret used for encryption.

That means the master password can be changed without breaking the vault, as long as the encryption phrase stays the same.

Adding a Credential

The add command:

  • asks for a username
  • asks for the password
  • validates empty input
  • prevents accidental overwrite
  • encrypts the password
  • saves it in the vault

The saved JSON looks roughly like this:

{
  "github.com": {
    "username": "user@example.com",
    "password": "gAAAAAB..."
  }
}
Enter fullscreen mode Exit fullscreen mode

The password value is encrypted before it is written to disk.

Revealing a Password

The reveal command:

  • checks if the site exists
  • asks for the master password
  • validates it against the configured master password
  • decrypts using the encryption phrase
  • copies the password to the clipboard

It intentionally does not print the password.

That small behavior matters. Terminal history, screen sharing, and logs are all easy ways to accidentally leak secrets.

Configuration

The local config lives in .env:

PMCLI_MASTER_PASSWORD=your-reveal-password
PMCLI_ENCRYPTION_PHRASE=your-stable-encryption-phrase
Enter fullscreen mode Exit fullscreen mode

The real .env file is ignored by git.

Only .env.example is committed.

Things I Learned

Some useful lessons from this project:

  • Keep CLI routing separate from business logic
  • Do not print secrets if copying to clipboard is enough
  • Never commit local secret config
  • Think carefully before tying encryption to a changeable password
  • Small command files are easier to test and modify

Final Thoughts

This was a fun project because it sits at the intersection of CLI design, encryption, local storage, and security tradeoffs.

Even a small password manager forces you to think carefully about defaults:

  • What should be printed?
  • What should be stored?
  • What should be ignored by git?
  • What happens when a user changes a secret?

The code is small, but the design decisions are real.

That is what made this project worth building.

You can find the code here:

https://github.com/KimtVak8143/pmcli
Enter fullscreen mode Exit fullscreen mode

Top comments (0)