Most password generators focus on randomness.
GitHub repo: VincentCapek/argon2-password-cli
This project takes a different route: it derives the same password every time from a master password and a label such as github, gmail, or bank.
That makes it a great little Go project for learning a few useful concepts at once:
- building a CLI with
flag - structuring a small Go codebase
- using Argon2id
- understanding deterministic derivation versus random generation
- shaping output with validation and character groups
The idea
Instead of generating a brand-new password on every run, the CLI derives a reproducible one from two inputs:
- a master password
- a label
So this:
go run . -master="MyUltraSecret" -label="github" -length=20
Will always return the same password as long as the inputs and options stay the same.
Change the label to gmail, and the output changes too.
That gives you a simple mental model:
- same master + same label = same password
- same master + different label = different password
Why this is interesting
When you first build a password generator, the obvious route is to use crypto/rand and produce something fully random.
That is still the right approach for many use cases.
But a deterministic password CLI teaches a different lesson: you are not generating randomness for storage or one-time usage, you are deriving a stable result from a secret and a context.
That is exactly why this kind of mini project is interesting from a learning perspective.
It pushes you to think about:
- what a salt is
- why context matters
- how to separate CLI parsing from generation logic
- how to build a reproducible byte stream
- how to shape the final password so it remains usable
Project structure
The repository is intentionally small:
-
main.gohandles the CLI flags and builds the config -
generator.gocontains the derivation and password generation logic -
go.moddefines the module and dependencies
That small size is actually one of the project’s strengths. You can read the whole codebase in one sitting and still touch several important ideas.
How the generator works
The generation pipeline is clean and easy to follow.
1. Parse CLI options
The CLI accepts a master password, a label, a desired length, and options for lowercase, uppercase, digits, and symbols.
That already makes the project practical while staying beginner-friendly.
2. Normalize the label
Before using the label, the code normalizes it by trimming spaces and converting it to lowercase.
That means values like:
githubGitHubGITHUB
all resolve to the same normalized label.
This is a small but important detail, because it avoids accidental password drift caused by casing or whitespace differences.
3. Turn the label into a salt
The normalized label is hashed with SHA-256, and the first 16 bytes are used as the salt.
This is a neat design choice for a deterministic CLI:
- the label remains human-friendly
- the salt is derived in a consistent way
- changing the label changes the salt, which changes the result
4. Derive a seed with Argon2id
The project then uses argon2.IDKey(...) to derive a 32-byte seed from:
- the master password
- the derived salt
- the cost parameters
This is the cryptographic core of the project.
Instead of directly turning the label into a password, the code first asks Argon2id to produce a strong derived key. That key becomes the seed for the next stage.
5. Build a deterministic byte stream
From there, the project uses a custom byteStream.
The idea is simple:
- combine the seed with a counter
- hash that combination with SHA-256
- use the resulting bytes as a deterministic stream
- increment the counter when more bytes are needed
This is a nice educational touch, because it makes the generation process easier to reason about than hiding everything behind a single opaque helper.
6. Build the password from character groups
The code defines separate character sets for:
- lowercase letters
- uppercase letters
- digits
- symbols
It first guarantees at least one character from each enabled group.
Then it fills the rest of the password from the merged character set.
Finally, it performs a deterministic shuffle so the password does not always start with the same kind of characters.
That last part matters a lot. Without the shuffle, the output shape would be predictable even if the actual characters were not.
A small project, but a good set of lessons
What I like about this repository is that it stays small without being trivial.
It introduces several ideas that show up again and again in real projects:
- validating input early
- separating config from logic
- composing small helper functions
- avoiding duplicated logic for character groups
- keeping the CLI thin and the generator focused
In other words, this is not just a “security toy.” It is also a solid exercise in writing clean Go.
Random generation vs deterministic derivation
This is probably the most important distinction in the whole project.
A traditional password generator says:
give me a fresh random password right now
This CLI says:
derive the same password again from a secret and a label
That difference changes the whole architecture.
If you want true randomness, crypto/rand is the right tool.
If you want reproducibility tied to a context, a derivation approach like this becomes much more interesting.
That distinction is one of the reasons this project works so well as a learning exercise.
Example commands
Generate a 20-character password for GitHub:
go run . -master="MyUltraSecret" -label="github" -length=20
go run . -master="MyUltraSecret" -label="bank" -length=16 -symbols=false
Increase the Argon2 cost parameters:
go run . -master="MyUltraSecret" -label="github" -time=4 -memory=64
What I would improve next
If I were extending this project, I would probably add:
- hidden master-password input instead of passing it directly on the command line
- unit tests for deterministic output and validation rules
- profiles for site-specific password policies
- clipboard support
- a
cmd/layout if the project grows
None of that is required for the project to be useful today, but each item would be a natural next step.
Final thoughts
I like projects like this because they stay focused.
You do not need a framework, a database, or a large architecture to learn something meaningful. Sometimes a compact CLI is enough to explore:
- Go flags
- data validation
- helper types
- deterministic generation
- Argon2id integration
If you want a small but instructive Go project, building a deterministic password CLI like this is a very good exercise.
It teaches both language fundamentals and design trade-offs in a way that stays concrete from start to finish.
Top comments (0)