
Federated Diffie-Hellman Key Exchange System
We recently paused feature development to audit our entire codebase. KeyPears shipped fast—cross-device sync, server authentication, encrypted secret storage—and we wanted to make sure we hadn't accumulated technical debt or, worse, security issues along the way.
What we found was instructive. The good news: our architecture is sound, our cryptography is correct, and our zero-knowledge design holds up. The concerning news: we found debug logging that would have exposed cryptographic keys in production. This is exactly why we audit.
Our audit covered six packages across the monorepo, examining each for:
We ran automated tools first (pnpm lint, pnpm typecheck, pnpm test, cargo clippy), then manually reviewed each package against our checklist.
The most significant discovery was in our Tauri app's vault creation and import flows. During development, we'd added extensive console.log statements to debug the cryptographic key derivation process:
// What we found and removed:
console.log("Password Key:", passwordKey.buf.toHex());
console.log("Login Key:", loginKey.buf.toHex());
console.log("Encryption Key:", encryptionKey.buf.toHex());
console.log("Decrypted Vault Key:", vaultKey.buf.toHex());
There were approximately 80 of these statements across two files:
import-vault.tsx and new-vault.3.tsx. Each one logged a sensitive
cryptographic key in hexadecimal format.
In development, this is harmless—helpful, even, for understanding the key derivation flow. In production, it's a disaster waiting to happen. Anyone with access to browser developer tools could see every key involved in vault encryption. The zero-knowledge architecture we carefully designed would be meaningless if the client itself was leaking keys to the console.
We removed all 80 statements. The code now proceeds silently, as it should.
Lesson learned: Debug logging during development is fine, but it must be
removed before shipping. Our audit checklist now includes searching for
console.log statements containing key-related terms (Key, password,
secret, token).
React Router v7 provides a href() function that type-checks route paths at
compile time. If you rename a route file, any href("/old-path") calls will
fail to compile—catching errors before they reach production.
We found several places where developers had used string literals instead:
// What we found:
<Link to="/">Home</Link>
<Link to="/new-vault/1">Create Vault</Link>
// What it should be:
<Link to={href("/")}>Home</Link>
<Link to={href("/new-vault/1")}>Create Vault</Link>
The difference seems minor, but it matters. With string literals, renaming
/new-vault/1 to /vault/new/step-1 would silently break links. With href(),
the compiler catches it immediately.
We updated all navigation in the Tauri app to use type-safe routes: navbar, footer, vault creation wizard, import flow. It's a small change that prevents a category of bugs entirely.
KeyPears uses the Catppuccin color palette with CSS variables for theming. Error
text should use text-destructive, which maps to the appropriate red in both
light and dark modes.
We found inconsistent usage:
// Inconsistent:
<p className="text-red-500">{error}</p>
// Consistent:
<p className="text-destructive">{error}</p>
The difference is subtle in light mode but significant in dark mode, where
text-red-500 might not have sufficient contrast against dark backgrounds.
Using theme variables ensures the design system works correctly across all
themes.
We standardized error colors in several components: the password generator, password memorizer, and vault name input.
In tauri.conf.json, we found:
{
"frontendDist": "../ts-tauri/dist"
}
The correct path is ../tauri-ts/dist. The folders are named tauri-ts (for
TypeScript) and tauri-rs (for Rust), not ts-tauri.
This typo hadn't caused problems yet because we typically run the dev server rather than building production bundles locally. But it would have failed the first time someone tried to build a release binary, causing confusion and wasted debugging time.
Lesson learned: Config files deserve the same scrutiny as code. Paths, URLs, and identifiers are easy to typo and hard to spot in review.
Not everything was problems. Much of the codebase held up well:
Cryptography: Our three-tier key derivation (password → passwordKey → encryptionKey + loginKey) is correctly implemented. The server never receives encryption keys, only login keys—and those are further hashed server-side. The zero-knowledge architecture is sound.
Memory management: All intervals, timers, and event listeners in the Tauri
app have proper cleanup in useEffect return functions. No memory leaks.
Sync performance: The background sync service uses exponential backoff on errors (5s → 10s → 20s), preventing thundering herd problems. Pagination is implemented for activity logs.
Accessibility: Interactive elements have proper aria-label attributes.
Keyboard navigation works throughout the app.
Rust code: Our Tauri backend is minimal (~43 lines) by design—all business
logic is in TypeScript. cargo clippy passes with no warnings.
For future reference, here's how we structured the audit:
Automated checks first: Run lint, typecheck, and tests for each package. Fix any failures before proceeding.
Dependency review: Run pnpm outdated for each package. Update
dependencies to latest patch versions.
Manual review by category: Work through the checklist systematically. Security issues get fixed immediately; style issues get noted for later.
Document findings: Update the audit guide with lessons learned. Future audits benefit from past discoveries.
We've published our full audit checklist in the repository at docs/audit.md. It covers everything from TypeScript best practices to zero-knowledge architecture verification to UI accessibility checks.