PWM
A small CLI password manager in C3 built around encrypted SQLCipher vaults, explicit memory handling, and a deliberately tiny attack surface.
PWM came from a pretty simple instinct: if I am going to trust a password manager, I want to understand almost every moving part.
So instead of building a convenience-heavy browser-integrated system, I built a small CLI password manager in C3 that stores credentials in encrypted SQLCipher vaults, supports multiple independent databases, and copies passwords to the clipboard just long enough to use them before clearing it. The project ended up being as much about systems taste as it was about security.
Password managers are one of those domains where abstraction can be a double-edged sword. A slick UX is nice, but the more layers you add, the harder it becomes to reason about where secrets live, how long they stay there, and what happens when something goes wrong. PWM was an attempt to go in the opposite direction: fewer moving pieces, more visible tradeoffs, tighter control.
Why C3
I wanted low-level control without quite as much raw friction as C and without making the whole project about appeasing a borrow checker. C3 hit a useful middle ground.
What made it a good fit here:
- Explicit memory management — useful when you care where secret material lives and when it gets wiped.
- Fault-based error handling +
defer— cleanup paths stay readable, which matters a lot when cleanup is part of the security model. - Compile-time platform branching — clipboard behavior for macOS, Linux, and Windows can live in one codebase without turning the build into a mess.
One of the fun side goals was seeing whether C3 was comfortable enough for a real security-sensitive CLI rather than just toy examples. I think the answer is yes, with the usual caveat that the language gives you power, not absolution.
Security posture
PWM is meant to reduce ordinary exposure, not magically solve endpoint security.
Each vault is encrypted independently with SQLCipher, and each one has its own password. That means there is no single shared master vault password sitting over everything. The registry that tracks vault names and paths is intentionally lightweight and unencrypted, because it stores metadata, not secrets.
Within the process itself, I tried to be fairly strict about memory discipline:
- passwords are handled in fixed buffers where practical,
- secret-bearing paths clean up after themselves,
- zeroization is explicit rather than wishful,
- and clipboard use is treated as a compromise to be managed, not ignored.
That last part is important. The clipboard auto-clears after 10 seconds using platform-native tooling, but clipboard history and other OS-level behavior still exist. There is no honest way to build something in this space without admitting where the model stops. PWM is designed to be small, legible, and harder to misuse casually, not to defeat a sufficiently determined attacker with machine-level access.
Architecture
The codebase is intentionally compact.
src/
├── main.c3 — CLI orchestration and argument parsing
├── vault.c3 — core vault operations and registry logic
├── db.c3 — SQLite / SQLCipher FFI boundary
├── secure.c3 — zeroization and clipboard handling
├── termios.c3 — echo-disabled terminal password input
└── argparse.c3 — argument parsing support
The split reflects the main design goal: keep the high-risk edges obvious. db.c3 isolates the C/SQLCipher boundary, secure.c3 owns the secret-sensitive utilities, and vault.c3 stays focused on the actual behavior of the program instead of getting tangled with platform details.
Multiple vaults, because one vault is never enough
Real usage gets messy quickly. You end up wanting a personal vault, maybe a work vault, maybe a throwaway one at a custom path, and suddenly name collisions matter.
So PWM keeps a lightweight registry under ~/Documents/pwm/ that tracks known vault names and paths, prunes stale entries, and supports fuzzy matching when you mistype a vault name. It is a small feature, but it makes the tool feel much less brittle in practice.
I especially cared about path-aware resolution here. Two files can absolutely share the same name while meaning very different things. If the tool cannot represent that cleanly, it is not really a multi-vault system.
Clipboard handling
Retrieved passwords are copied using native platform tools: pbcopy on macOS, wl-copy or xclip on Linux, and PowerShell / clip on Windows. A detached background process clears the clipboard after 10 seconds so the main command can return immediately.
Again, this is best understood as damage control, not perfection. The point is to narrow the exposure window without pretending the clipboard is a secure enclave.
What I learned
Security-sensitive software has a way of making boring edge cases suddenly feel very non-boring.
You start by thinking you are writing CRUD around an encrypted database, and then very quickly you are asking better questions: what if a secret gets copied to the heap on an error path, what if the compiler gets clever about your wipe routine, what if two vaults share a name, what if the clipboard outlives the process, what if your “simple” abstraction is where the leak actually lives?
That is really what PWM was for me: a project about reducing hand-waviness. It made me think much harder about where guarantees are real, where they are best-effort, and how much cleaner a system becomes when you are willing to say no to features that expand the attack surface without adding much value.
Tech stack
- C3
- SQLCipher / SQLite
- Platform-native clipboard tooling