TL;DR: I added first-class WebDAV support to the open-source password manager pwsafe by introducing a transport plugin and lock daemon — without touching its core architecture. I built the feature collaboratively with Claude Code and then used my own OpenAI-powered CLI tool, ‘ask’, to perform structured security audits. The result: faster iteration, 35 issues found pre-ship (including 4 critical), and a clearer picture of how AI can safely augment legacy systems work.
pwsafe is a battle-tested, open-source C++ password manager originally designed by Bruce Schneier and now community-maintained. It has a long history, a layered architecture, and a strong emphasis on correctness and security.
The goal was simple to describe but non-trivial to implement: open a remote password database over WebDAV simply by typing a URL — no fragile OS-level filesystem mounts required.
For readers unfamiliar with it: WebDAV (Web Distributed Authoring and Versioning) is an extension of HTTP that allows remote file storage, retrieval, and locking. In theory, it makes remote files behave like local ones. In practice, integrating it cleanly into a mature C++ codebase is delicate.
Historically, if you wanted your database stored remotely, you mounted a WebDAV share at the OS level (for example, /z → https://webdav.example.com/).
If the mount wasn’t active, pwsafe failed. Locking was unreliable. The setup wasn’t portable. It felt like a workaround, not a feature.
What I wanted instead: File → Open URL…, type https://…, done.
The breakthrough came from a simple observation: the entire codebase ultimately opens files through just two functions — pws_os::FOpen and FClose.
That meant I didn’t need to refactor the world. I needed an interception point.
• Detect when the ‘filename’ is actually a URL• Route those operations through a transport abstraction• Keep the core and UI layers completely unaware
The result was a small, versioned plugin ABI and a dynamically loaded .so transport plugin. Local files behave exactly as before. URLs are handled by the plugin.
Alternatives considered:• Directly embedding WebDAV logic into the core• Treating remote files as a special case scattered through the codebase• Relying entirely on OS mounts
Those approaches would have:• Increased coupling• Made future protocol support harder• Risked destabilising mature code
The plugin abstraction isolates transport concerns. Tomorrow it could support S3 or another protocol without touching core logic. The lock daemon isolates lifecycle and signal-safety complexity away from UI code. Containment was the guiding principle.
WebDAV supports server-side locking — but unlocking may need to happen during shutdown, signal handling, or even a crash. libcurl is not async-signal-safe. That’s a problem.
The solution:• Fork a dedicated child process when acquiring the first lock• Let the child own the lock token• Communicate over a Unix domain socket• Release locks automatically if the parent crashes (socket EOF)
It’s subtle, and exactly the kind of code where small mistakes become security issues.
Every line of new C++ was written collaboratively with Claude Code.
Example prompt to Claude Code:
We need a Unix domain socket protocol between parent and lock daemon.
Constraints:
- Parent must only perform async-signal-safe operations.
- Child owns WebDAV lock token.
- On parent crash, locks must release automatically.
Propose a minimal protocol and explain failure modes.
The model proposed:• A line-oriented command protocol• Explicit ACK/NACK responses• EOF-based crash detection• A state diagram for lock ownership
Not all suggestions were correct — especially around fork semantics — but the iteration speed was extraordinary. It felt like architectural pair programming.
Earlier in the project, and again after implementation, I used a personal tool called ask — a Python CLI that wraps the OpenAI API with structured prompts tailored to tasks like code review, refactoring, and explanation.
Unlike Claude Code (interactive and conversational), ask is designed for targeted, one-shot analysis. It bundles selected files and applies a strict audit prompt.
You are a security auditor.
Analyse the attached C++ source files for:
- Memory safety issues
- TOCTOU races
- IPC injection risks
- Protocol parsing flaws
Provide severity ratings and concrete fixes.
Using this approach with OpenAI models (o3 and gpt-5.2), the audits surfaced 35 issues before deduplication — including four critical vulnerabilities.
Examples included:• Newline injection in the daemon’s IPC protocol• A TOCTOU race in the plugin loader• Cross-protocol redirects in libcurl that could write to local files
These were not hypothetical. They were real and fixable before release.
The models were systematic and fast. They flagged edge cases a human might skim.
But they missed a subtle file descriptor inheritance issue: without SOCK_CLOEXEC, a spawned GUI child could accidentally keep the daemon socket alive, preventing lock release. That required manual review.
AI is a powerful first-pass reviewer. It is not the final authority.
For developers considering AI-assisted work on legacy systems:
1. Constrain the surface area first. Architectural isolation (plugins, daemons) makes AI collaboration safer.
2. Treat AI output as a draft, not a decision. Especially around concurrency and Unix semantics.
3. Use structured prompts for audits. Specific threat models produce dramatically better results than vague requests.
4. Run multiple models. Different systems catch different classes of issues.
5. Keep human threat modelling in the loop. AI is excellent at scanning; humans are better at adversarial imagination.
Today, you can open a remote pwsafe database by typing a URL. No OS mount. Proper WebDAV locking. Crash-safe behaviour.
Under the hood: a transport plugin system, a hardened lock daemon, and code that has survived three independent audits.
The most interesting part wasn’t WebDAV. It was the workflow:AI as design partner → AI as code generator → AI as security auditor → Human as integrator and sceptic.
For experienced engineers, this combination expands what you can safely attempt. The machine doesn’t replace judgement — it amplifies it.
If you’re working on mature, security-sensitive systems, the question isn’t whether to use AI. It’s how to design your architecture so that using it makes the system safer, not riskier.