DEV Community

A0mineTV
A0mineTV

Posted on

Building a Deterministic Password CLI in Go with Argon2id

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
Enter fullscreen mode Exit fullscreen mode

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.go handles the CLI flags and builds the config
  • generator.go contains the derivation and password generation logic
  • go.mod defines 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:

  • github
  • GitHub
  • GITHUB

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
Enter fullscreen mode Exit fullscreen mode
go run . -master="MyUltraSecret" -label="bank" -length=16 -symbols=false
Enter fullscreen mode Exit fullscreen mode

Increase the Argon2 cost parameters:

go run . -master="MyUltraSecret" -label="github" -time=4 -memory=64
Enter fullscreen mode Exit fullscreen mode

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)