password-key
Cryptographically secure passwords that are safe to paste anywhere.
Most password generators hand you k9$P@x/2' and let you discover — an hour
later, three layers deep in a stack trace — that @ split your database URL,
$ was expanded by your shell, and ' broke your SQL. password-key is
built around one idea:
URL-safe output is the default. Letters, digits, and
- _ . ~— the only punctuation RFC 3986 guarantees is safe in a URL — and characters that also carry no special meaning in a SQL literal or a shell. At 32 characters that is still ~193 bits of entropy, far beyond any brute-force attack. The restriction buys safety and costs nothing.
The password is copied to your clipboard, never printed (terminal
scrollback is a file on disk), drawn from the OS cryptographic RNG via
Python's secrets module,
and the whole package has zero runtime dependencies — the smallest
possible supply-chain surface for a tool that generates credentials.
Install
pipx install password-key # recommended for CLI use
# or
pip install password-key
Usage
$ password-key
Length : 32 characters
Charset : URL-safe (letters, digits, - _ . ~) — safe anywhere
Strength : ~193 bits of entropy (excellent)
Clipboard : COPIED
Paste it into your password manager now, then copy
something harmless to clear the clipboard.
The essentials:
password-key # 32 chars, URL-safe → clipboard
password-key -l 48 # longer
password-key --words 6 # diceware passphrase (see below)
password-key --clear 30 # auto-clear the clipboard after 30 s
password-key --no-ambiguous # drop 0 O 1 l I | (for reading aloud)
password-key --full # full punctuation (see warning below)
password-key --show # display it too (still copied)
password-key --print # bare password on stdout, for scripts
password-key -i # interactive menu
pwk # short alias for all of the above
Passphrases
$ password-key --words 6
Words : 6
Charset : 6 words, EFF Large Wordlist (URL-safe)
Strength : ~77 bits of entropy (strong)
Clipboard : COPIED
Diceware passphrases from the EFF Large Wordlist
(7,776 words, ~12.9 bits each) — for the secrets a human has to type or
remember. The default - separator keeps even passphrases URL-safe.
Six words is the EFF's recommendation; use --words 7 (~90 bits) for
anything facing offline attack.
Auto-clear
$ password-key --clear 30
...
Clearing clipboard in 30s (Ctrl+C to keep it)
After the countdown, the clipboard is wiped only if it still holds the password — if you copied something else in the meantime, it is left alone.
Scripting
--print writes the bare secret to stdout and everything else to stderr,
so it composes:
DB_PASSWORD=$(password-key --print)
password-key --print --count 5 # five candidates, one per line
Python API
from password_key import generate, generate_passphrase, entropy_bits, FULL
generate() # 32-char URL-safe password
generate(48) # longer
generate(20, charset=FULL) # full punctuation
generate(exclude_ambiguous=True) # no 0 O 1 l I |
generate_passphrase(6) # 'correct-horse-battery-staple-...'
entropy_bits(66, 32) # 193.42...
Everything is drawn from secrets — never random.
When you do need punctuation
Some systems mandate a symbol class. --full adds
! # $ % & ( ) * + , - . : ; < = > ? @ [ ] ^ { | } _ ~ and guarantees at
least one upper, lower, digit, and symbol:
$ password-key --full
Charset : full punctuation — NOT safe in a DSN without percent-encoding
WARNING : percent-encode this before putting it in a connection string
An unencoded @ or % inside
postgresql://user:PASSWORD@host/db splits the string and surfaces much
later as a confusing "could not translate host name". If you must embed a
--full password in a URL, percent-encode it first:
from urllib.parse import quote
quote(password, safe="")
Even --full deliberately excludes quotes, backslash, backtick, and space —
they add ~0.1 bits per character and are the characters that turn a working
password into an escaping bug.
Security design
| Decision | Why |
|---|---|
secrets (OS CSPRNG), never random |
random is seeded, deterministic pseudo-randomness — unfit for credentials. |
| Unbiased selection | secrets.choice uses rejection sampling internally; no character is ever more likely than another. Checked by a chi-squared test over ~64k draws — test_generator.py, and the same test against the PowerShell script. |
| Clipboard, not terminal | Terminal scrollback is written to disk. The secret is displayed only on explicit request or when no clipboard exists. |
| Zero dependencies | Nothing to typosquat, nothing to compromise. Clipboard access uses the Win32 API directly (ctypes) and pbcopy / wl-copy / xclip elsewhere. |
| Guarded auto-clear | --clear wipes the clipboard only while it still holds the generated password. |
| No state, no telemetry, no network | Passwords are never logged, cached, or written anywhere. |
Found a vulnerability? See SECURITY.md.
Verifying this, rather than trusting it
A password generator asks for more trust than most packages, and a README is the wrong basis for granting it. Everything above is meant to be checkable by a stranger.
Check that the wheel you installed came from this source. Releases are published with trusted publishing — there is no long-lived API token that could be stolen and used to push a package this repository never built — and each artifact carries a PEP 740 attestation binding it to the commit it was built from:
VERSION=$(python -c "import password_key; print(password_key.__version__)")
pipx run pypi-attestations verify pypi \
--repository https://github.com/nathanramoscfa/password-key \
"pypi:password_key-${VERSION}-py3-none-any.whl"
That checks the version you actually have, not the one this README was
written against. A pass prints OK: password_key-<version>-py3-none-any.whl;
point it at any other repository and it fails, which is the point. (On
Windows, pypi-attestations needs Developer Mode enabled — its trust-root
cache creates a symlink, and without that privilege it stops with
WinError 1314 before checking anything.)
Installing nothing, the same evidence is on the
PyPI page under Verified
details: publisher nathanramoscfa/password-key, workflow
publish.yml, the commit SHA, and a
Sigstore transparency-log entry that is
public and append-only.
Then read it. No signature can tell you the code is good, only
that it is the code that was published. This package is deliberately
small enough to audit in one sitting: the entire security-relevant
surface is generator.py (167 lines) and
passphrase.py (80 lines), with zero
runtime dependencies to follow. There is no cryptography of our own to
review — every random draw is secrets.choice.
What checks it besides the author, on every push:
| Check | What it covers |
|---|---|
| Tests | 378 tests on Linux, macOS, and Windows × Python 3.9–3.13, at 87% branch coverage with a floor enforced in CI. |
| Fuzzing | An Atheris harness drives generate and generate_passphrase with adversarial input on every push, asserting they either honor the contract exactly or raise ValueError — and that entropy is never over-stated, the direction that would call a weak secret strong. |
| Pester suite | The PowerShell script is a second credential generator, so it gets its own bias and charset tests — including a parity check that its alphabet still matches the Python one. |
mypy --strict |
Run for linux, darwin, and win32, so the Windows-only clipboard path is type-checked on every commit rather than only when someone runs it. |
| CodeQL | GitHub's security-extended query suite; results are in the repository's Security tab. |
| Locked CI tooling | Every action is pinned by commit SHA and every pip install runs with --require-hashes, transitive dependencies included, so a substituted wheel cannot enter a build. Dependabot refreshes the pins, because a stale pin is still a stale dependency. |
| OpenSSF Scorecard | A third party scoring this repo's supply-chain posture — pinned actions, token scopes, release provenance — so the claim is not ours to make. |
What this does not have. It is a young project with one maintainer and no independent security audit. The checks above are automated ones; none of them is a human expert reading the code adversarially. Judge it on the source, which is the point of keeping it this small.
Windows double-click launcher
Prefer not to open a terminal? New Password.bat
launches the interactive menu with a double-click. A standalone
PowerShell implementation (no Python required) lives in
contrib/new-password.ps1.
Contributing
Issues and PRs welcome — see CONTRIBUTING.md.
License
Built by ArcForge Labs.
MIT. The bundled EFF Large Wordlist is © the Electronic Frontier Foundation, CC BY 3.0 — see THIRD-PARTY-NOTICES.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file password_key-1.1.2.tar.gz.
File metadata
- Download URL: password_key-1.1.2.tar.gz
- Upload date:
- Size: 81.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7484c3d34244f344413a5ebafad9b09f21b1c817b540e3c611bcaa00c5999ce4
|
|
| MD5 |
79361ae1d229561543fb1b0c90d81b90
|
|
| BLAKE2b-256 |
8dd7dbf07dd7a8fd1e9803e86abf9631f69765778c88aa506d871772201a94cb
|
Provenance
The following attestation bundles were made for password_key-1.1.2.tar.gz:
Publisher:
publish.yml on nathanramoscfa/password-key
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
password_key-1.1.2.tar.gz -
Subject digest:
7484c3d34244f344413a5ebafad9b09f21b1c817b540e3c611bcaa00c5999ce4 - Sigstore transparency entry: 2437784238
- Sigstore integration time:
-
Permalink:
nathanramoscfa/password-key@73b6bf28cc03582903a40a7840477d705ce9a123 -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/nathanramoscfa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@73b6bf28cc03582903a40a7840477d705ce9a123 -
Trigger Event:
release
-
Statement type:
File details
Details for the file password_key-1.1.2-py3-none-any.whl.
File metadata
- Download URL: password_key-1.1.2-py3-none-any.whl
- Upload date:
- Size: 63.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1448cffcead2e788d8876155d6dd9510af329106b3a5efd323939f45f4ea6d96
|
|
| MD5 |
54f630846a30c1561696b98309ffaf57
|
|
| BLAKE2b-256 |
52544f220dd73fab3b379ae08b4aa53e37da459732d64dc5a03707ee137ce826
|
Provenance
The following attestation bundles were made for password_key-1.1.2-py3-none-any.whl:
Publisher:
publish.yml on nathanramoscfa/password-key
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
password_key-1.1.2-py3-none-any.whl -
Subject digest:
1448cffcead2e788d8876155d6dd9510af329106b3a5efd323939f45f4ea6d96 - Sigstore transparency entry: 2437784302
- Sigstore integration time:
-
Permalink:
nathanramoscfa/password-key@73b6bf28cc03582903a40a7840477d705ce9a123 -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/nathanramoscfa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@73b6bf28cc03582903a40a7840477d705ce9a123 -
Trigger Event:
release
-
Statement type: