All projects
10OPEN SOURCE2026
Lockr mark — a white bracket glyph on a rounded dark navy square

Lockr

A local-first CLI that keeps API keys in one AES-256-GCM vault on your own machine — no accounts, no sync, no servers.

Visit lockr.gtchakama.commacOS · Linux · Windows
lockr — zsh
$

Overview

What is Lockr

Lockr is a local-first CLI for the API keys and credentials you need on your own machine. Everything lives in one AES-256-GCM vault file, sealed with an Argon2id-derived key, and the master password is typed once and cached in the OS keychain. There is no account, no sync and no server anywhere in the tool.

Role
Creator & maintainer — designed the vault format, the crypto layer, the CLI and the release pipeline.
Platform
macOS · Linux · Windows
Stack
Go · Cobra · Argon2id · AES-256-GCM
Storage
one local vault file, mode 0600
Release
v1.1.0 · GoReleaser · Homebrew tap
Web
lockr.gtchakama.com

Contribution

What I built

  1. 01Designed the vault: one encrypted file at ~/.lockr/vault.enc (mode 0600) — no server, account, sync or network dependency anywhere in the tool.
  2. 02Built the crypto layer: Argon2id key derivation (one pass, 64 MiB, four threads, 32-byte key) over a fresh 16-byte salt, and AES-256-GCM sealing the whole vault under a new nonce on every write.
  3. 03Made it fast to use: the master password is cached in the native keychain on macOS, Linux and Windows, typed once and flushed with `lockr lock` (or wiped by `lockr destroy`).
  4. 04Shaped the CLI around real work: group namespacing (work/stripe_key), `list` that masks values and flags secrets older than 90 days, `get --copy` straight to the clipboard, and `export` that prints shell assignments.
  5. 05Shipped it properly: a `v*` tag drives GoReleaser on GitHub Actions to build CGO-free static binaries for five OS/arch targets and update the Homebrew tap in the same run.

How it works

How the data moves

The core paths through the system — a dot travels each hop, and the caption says what happens there.

Storing a secret

Type a value once; the whole vault is re-derived and re-sealed around it.

NOYESStart
lockr set work/stripe_key
Master password in the keychain?
Argon2id → 32-byte key (fresh 16-byte salt)
data[group][key] = {value, updated_at}
Prompt once, cache it in the keychain
AES-256-GCM seal under a new nonce
Write {salt, nonce, ciphertext} to vault.enc (0600)
End

Swipe sideways to see the whole chart.

01 / 09set

You store a secret under a group: work/stripe_key.

From the code

A few lines that matter

Key derivationinternal/crypto/crypto.go
// DeriveKey derives a 32-byte encryption key from a
// master password and salt. It uses Argon2id, which is
// resistant to both GPU and side-channel attacks.
func DeriveKey(password string, salt []byte) []byte {
	// time=1, memory=64MB, threads=4, keyLen=32 bytes
	return argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
}

One pass over 64 MiB with four lanes — slow enough to bother an attacker, instant for you.

Sealing the vaultinternal/crypto/crypto.go
func Encrypt(key, plaintext []byte) ([]byte, []byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, nil, err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, nil, err
	}
	// Generate a random nonce
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return nil, nil, err
	}
	// Seal encrypts and authenticates the plaintext
	ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
	return ciphertext, nonce, nil
}

AES-256-GCM authenticates as well as encrypts; the nonce is fresh on every write.

Decisions

Calls that shaped it

A single encrypted file instead of a service

A vault you can back up, diff and delete beats another account — and there is nothing to breach on a server.

Re-derive and re-seal the whole vault on every write

A fresh salt, key and nonce per write keeps GCM safe and the code simple; the vault is small enough that it costs nothing.

Keychain cache cleared explicitly, not by timer

Retrieval stays one command; `lockr lock` is the deliberate way to forget the password.

Print environment assignments rather than wrap the process

`eval $(lockr export work)` fits any shell and tool; the trade-off — values live in the shell environment — is explicit.

Stack

  • Go
  • Cobra
  • AES-256-GCM
  • Argon2id
  • OS Keychain
  • GoReleaser
  • GitHub Actions
  • Homebrew