Luke Simmons

Finding duplicate logins in Bitwarden, without deleting anything

Author’s note: this post was drafted by Claude (Anthropic) from my project notes and source code, then reviewed and edited by me before publishing. The voice and judgments are mine; the typing isn’t.

The gap

Bitwarden has no built-in deduplicate. Years of saving logins leaves you with the same account stored three times — a http copy and a https copy, an old password and a new one, two saves of the same thing from different devices. It’s clutter, and clutter in a password manager is mildly dangerous: you can’t tell at a glance which copy is current.

So I wrote a small Python tool, bw_dedupe.py, that finds the duplicates and reports them. It’s about 200 lines and has no dependencies beyond the official Bitwarden CLI.

How it decides what’s a duplicate

It groups login items by name + username, case-insensitively. URIs are shown for context but don’t split groups, so a http:// and a https:// copy of the same account still land together. Within a group it then splits into two buckets:

That split is the whole idea. The machine is confident about exact duplicates and honest about its uncertainty everywhere else.

The actual design decision: it never deletes

The dedup logic is the easy part. The decision I actually thought about was what the tool is allowed to do, and the answer is: nothing.

bw_dedupe.py is read-only. It lists your items, prints a report, and stops. At most — if you ask for it with --write-deletes — it writes a file of bw delete item <id> commands for you to open, read, and run yourself. That file contains item names and ids, never passwords.

A few rules fall out of that stance:

I could have made it delete the extras automatically. It would have been fewer steps. But a tool that quietly modifies a password vault is exactly the kind of convenience I don’t want — the failure mode is “it removed the wrong copy and I didn’t notice.” Report-first, act-yourself is slower by one step and removes that entire class of mistake. For something holding every credential I have, that’s the trade I want every time.

The general principle

This is a tiny project, but it’s a clean example of a rule I keep coming back to: automation that touches something important should propose, not perform. Show me the diff, let me run it. The same instinct shows up in my file organiser (dry-run before anything moves) and in a Bitcoin-vault tool I keep meaning to write up. The dedup logic took an afternoon. The “never deletes” decision is the part worth keeping.

— Luke Simmons, Auckland

← All posts Projects →