<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pascal Kuhn</title>
    <description>The latest articles on DEV Community by Pascal Kuhn (@carl0oo).</description>
    <link>https://dev.to/carl0oo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4008225%2F8ced0eec-ba9d-45d4-af5e-aedeed185280.png</url>
      <title>DEV Community: Pascal Kuhn</title>
      <link>https://dev.to/carl0oo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/carl0oo"/>
    <language>en</language>
    <item>
      <title>Local-First vs. Cloud Password Managers: What SMBs Should Know</title>
      <dc:creator>Pascal Kuhn</dc:creator>
      <pubDate>Wed, 01 Jul 2026 12:17:08 +0000</pubDate>
      <link>https://dev.to/carl0oo/local-first-vs-cloud-password-managers-what-smbs-should-know-314c</link>
      <guid>https://dev.to/carl0oo/local-first-vs-cloud-password-managers-what-smbs-should-know-314c</guid>
      <description>&lt;p&gt;A practical comparison of local-first and cloud-based password managers for small and medium businesses, with a focus on data sovereignty and compliance&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Choosing a password manager for an SMB shapes where credentials live, who can access them, and how your organisation responds to audits and regulatory questions. Cloud-based tools dominate because they deploy quickly and sync across devices. Local-first alternatives trade some convenience for tighter control over data location and trust boundaries. This article compares both from an IT decision-maker’s perspective — without declaring a universal winner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud password managers: convenience with a third-party dependency
&lt;/h2&gt;

&lt;p&gt;Most commercial password managers encrypt secrets on the client, then synchronise ciphertext through the vendor’s cloud. The vendor holds account metadata, device registrations, sharing policies, and recovery mechanisms. Even when vault contents stay unreadable to the vendor, your organisation depends on that third party for availability, policy enforcement, and incident response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expanded attack surface
&lt;/h2&gt;

&lt;p&gt;A cloud relay adds layers beyond the endpoint: the vendor’s API and sync infrastructure become persistent targets; account recovery flows can weaken the model if poorly governed; and supply-chain risk applies to every update channel and backend service. Breaches at centralised credential stores affect many customers at once, and SMBs without dedicated security teams can only investigate what the vendor discloses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operational dependency
&lt;/h2&gt;

&lt;p&gt;Outages, pricing changes, or data-residency shifts affect your workflows immediately. Vendor lock-in and subprocessors in GDPR documentation become recurring compliance work. Cloud managers are not inherently insecure — the point is architectural: you accept a shared trust boundary with an external operator whose incentives and jurisdictional exposure may not align with yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What “local-first” means in practice
&lt;/h2&gt;

&lt;p&gt;“Local-first” is often used loosely in marketing. Architecturally, it should mean that secret material is generated, encrypted, and primarily stored on infrastructure you control, without a mandatory cloud relay for core operations.&lt;/p&gt;

&lt;p&gt;In a local-first design:&lt;/p&gt;

&lt;p&gt;The authoritative vault resides on a workstation, server, or repository within your environment.&lt;br&gt;
Encryption and decryption happen on the client before data is written to disk or synced.&lt;br&gt;
Browser or SSH integrations communicate with the local client directly — for example via Native Messaging — rather than routing credentials through a vendor cloud hop.&lt;br&gt;
Synchronisation, when needed, uses a channel you choose (local network, self-hosted Git, file share) instead of a proprietary vendor backend.&lt;br&gt;
OxidVault follows this model. The Desktop Safe stores encrypted vault data locally, uses AES-256-GCM for data encryption and Argon2id for key derivation, and connects the browser extension to the desktop client through Native Messaging. Audit events are recorded as structured metadata chains suitable for ISO 27001-style traceability. There is no requirement to send secrets to OxidVault-operated cloud infrastructure for normal use.&lt;/p&gt;

&lt;p&gt;Local-first does not mean “never connect to the network.” It means the network is optional and under your control, not a structural dependency for every read and write.&lt;/p&gt;

&lt;h2&gt;
  
  
  GDPR and data sovereignty for SMBs
&lt;/h2&gt;

&lt;p&gt;For European SMBs, GDPR requires lawful basis, data minimisation, subprocessors documentation, and demonstrable compliance. Cloud password managers process employee identifiers, access logs, and device metadata on vendor infrastructure. Even encrypted vault ciphertext may trigger DPIA obligations or Schrems II concerns when metadata sits on US-hosted services.&lt;/p&gt;

&lt;p&gt;Local-first deployments can align data residency with where you run the vault, shrink subprocessor lists, and keep audit trails in systems you administer. This does not remove compliance work — policies, access reviews, and incident response remain yours — but it shifts the accountability boundary inward when credentials are high-risk assets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where local-first is harder — and how teams address it
&lt;/h2&gt;

&lt;p&gt;Local-first designs face legitimate operational challenges. Two come up repeatedly in SMB deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-device and multi-site synchronisation
&lt;/h2&gt;

&lt;p&gt;Cloud managers solve sync by default: install the app, sign in, and ciphertext propagates through the vendor. Local-first tools must define an explicit sync strategy. Without one, users maintain separate vaults on each device — a common source of drift, stale credentials, and unsafe workarounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Team onboarding and key management
&lt;/h2&gt;

&lt;p&gt;Sharing a vault across colleagues requires more than a shared password. Each user needs authenticated access, ideally with individual credentials and second factors, without exposing a single master key to everyone.&lt;/p&gt;

&lt;p&gt;OxidVault addresses team use through a multi-user vault model where each user holds their own Key Encryption Key (KEK), derived from their individual password and 2FA. Vault data is encrypted with a Data Encryption Key (DEK); that DEK is wrapped separately for each authorised user (KEK-wrapped DEK). Adding or revoking a user means re-wrapping keys for the affected parties — a standard pattern that limits blast radius if one user’s credentials are compromised.&lt;/p&gt;

&lt;p&gt;For synchronisation, OxidVault offers Git-based sync: encrypted vault artefacts can be versioned in a repository your organisation controls. This provides an auditable history, supports offline-first workflows, and lets you choose hosting — internal GitLab, GitHub Enterprise, or air-gapped remotes — rather than a proprietary sync cloud. The trade-off is that someone must operate the Git remote and onboarding process; there is no invisible vendor backend handling it automatically.&lt;/p&gt;

&lt;p&gt;SSH Quickconnect, browser extension integration without a cloud intermediary, and compliance-oriented audit logging round out day-to-day use. The Community Edition supports up to five users per vault under AGPLv3; larger teams can evaluate Enterprise for LDAP and SSO. OxidVault currently ships a full Windows client; Linux and macOS are on the roadmap. Teams needing instant mobile access across unmanaged devices may find local-first workflows less convenient and should plan accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Cloud managers suit teams prioritising fast rollout and frictionless multi-device access. Local-first fits SMBs treating credentials as regulated assets or seeking fewer third-party dependencies — at the cost of more operational responsibility. The right choice depends on threat model, regulatory context, and how much infrastructure your team will run.&lt;/p&gt;

&lt;p&gt;Next steps:&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="//oxidvault.com"&gt;oxidvault.com&lt;/a&gt; for product overview, feature details, and contact options.&lt;br&gt;
Explore the open-source Desktop Safe and releases on &lt;a href="https://github.com/caRl0oo/oxidvault" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;br&gt;
Both resources are maintained for evaluation and deployment planning — not as a substitute for your own security assessment.&lt;/p&gt;

</description>
      <category>security</category>
      <category>rust</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Built a B2B Password Manager in Rust</title>
      <dc:creator>Pascal Kuhn</dc:creator>
      <pubDate>Mon, 29 Jun 2026 14:16:16 +0000</pubDate>
      <link>https://dev.to/carl0oo/i-built-a-b2b-password-manager-in-rust-2oij</link>
      <guid>https://dev.to/carl0oo/i-built-a-b2b-password-manager-in-rust-2oij</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;TL;DR:&lt;/strong&gt;  I built a file-based B2B password manager in Rust with multi-user encryption (DEK wrapping, no server required).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A practical look at building secure multi-user encryption without a server.
&lt;/h2&gt;

&lt;p&gt;At our office, passwords lived in a shared Excel file.&lt;/p&gt;

&lt;p&gt;Yeah..., I know.&lt;/p&gt;

&lt;p&gt;But if we're being realistic, most people have seen worse.&lt;/p&gt;

&lt;p&gt;I started looking at alternatives.&lt;/p&gt;

&lt;p&gt;Bitwarden is great, but it wants a server.&lt;br&gt;
KeePass works well, but multi-user support never really felt like a first-class use case.&lt;br&gt;
LastPass had… its moment.&lt;br&gt;
And 1Password means subscriptions and trusting the cloud.&lt;/p&gt;

&lt;p&gt;What I wanted felt surprisingly simple:&lt;/p&gt;

&lt;p&gt;An encrypted file sitting on a file server.&lt;br&gt;
No Docker. No VPS. No subscription.&lt;/p&gt;

&lt;p&gt;Just a vault multiple people could access — each with their own password.&lt;/p&gt;

&lt;p&gt;So I built it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Rust Ended Up Being the Right Choice
&lt;/h2&gt;

&lt;p&gt;I could have built this faster in Python or Go.&lt;/p&gt;

&lt;p&gt;But a password manager that leaves secrets hanging around in memory isn't a password manager — it's a liability.&lt;/p&gt;

&lt;p&gt;That’s where Rust made sense.&lt;/p&gt;

&lt;p&gt;Its ownership model removes an entire class of memory-related mistakes from the equation.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;zeroize&lt;/code&gt; crate quickly became essential. Every sensitive buffer gets explicitly wiped when it leaves scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;master_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Zeroizing&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derive_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Automatically wiped when dropped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Multi-User Problem Is Harder Than It Looks
&lt;/h2&gt;

&lt;p&gt;Single-user encryption is relatively straightforward.&lt;/p&gt;

&lt;p&gt;Multi-user encryption without a server gets interesting fast.&lt;/p&gt;

&lt;p&gt;The obvious approach is one shared master password.&lt;/p&gt;

&lt;p&gt;Which sounds convenient — until someone leaves the company.&lt;/p&gt;

&lt;p&gt;Now you're rotating passwords, re-encrypting data, distributing credentials, and inevitably doing it at the worst possible time.&lt;/p&gt;

&lt;p&gt;So I ended up with DEK wrapping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vault structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vault payload
└── Encrypted using DEK (random, never changes)

Each user:
└── KEK derived from user password (Argon2id)

└── DEK wrapped with that KEK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each person has their own password.&lt;/p&gt;

&lt;p&gt;Removing a user means deleting their wrapped DEK entry.&lt;/p&gt;

&lt;p&gt;The vault itself never needs to be re-encrypted.&lt;/p&gt;

&lt;p&gt;Getting there took three format versions.&lt;/p&gt;

&lt;p&gt;Not ideal — but at least the migration code survived.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tauri Was the Right Choice — Mostly
&lt;/h2&gt;

&lt;p&gt;One thing I didn't expect:&lt;/p&gt;

&lt;p&gt;The security boundary became incredibly clean.&lt;/p&gt;

&lt;p&gt;The React frontend never touches key material directly.&lt;/p&gt;

&lt;p&gt;Secrets move through a single command with rate limiting in place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[tauri::command]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;reveal_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AppState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;rate_limit_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 5 requests per 60 seconds&lt;/span&gt;
    &lt;span class="nf"&gt;ensure_vault_unlocked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser extension via Native Messaging turned out to be harder.&lt;/p&gt;

&lt;p&gt;Keeping the connection alive without creating focus loops while the app was minimized to the system tray took three complete rewrites.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Unexpected PDF Detour
&lt;/h2&gt;

&lt;p&gt;I needed PDF exports for GDPR compliance reports.&lt;/p&gt;

&lt;p&gt;First attempt: &lt;code&gt;printpdf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two days later: a high-severity vulnerability in &lt;code&gt;lopdf&lt;/code&gt;, no upstream fix.&lt;/p&gt;

&lt;p&gt;Second attempt: jsPDF in the frontend.&lt;/p&gt;

&lt;p&gt;Client-side. Offline. UTF-8 behaved properly. Output looked professional.&lt;/p&gt;

&lt;p&gt;In hindsight, I should have started there.&lt;/p&gt;

&lt;p&gt;Lesson learned:&lt;/p&gt;

&lt;p&gt;Use the simplest solution first — especially for things that aren't your core product.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Got Wrong
&lt;/h2&gt;

&lt;h3&gt;
  
  
  I wrote everything in German first
&lt;/h3&gt;

&lt;p&gt;My target market was DACH, so it felt reasonable.&lt;/p&gt;

&lt;p&gt;The first comment on r/rust:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"readme.de.md — very few people here speak German."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So this week I translated everything.&lt;/p&gt;




&lt;h3&gt;
  
  
  I built too many features before talking to users
&lt;/h3&gt;

&lt;p&gt;SSH Quick Connect.&lt;br&gt;
Git sync.&lt;br&gt;
GPO policy management.&lt;/p&gt;

&lt;p&gt;All before having a single real customer.&lt;/p&gt;

&lt;p&gt;Some of it was genuinely fun to build.&lt;/p&gt;

&lt;p&gt;Whether anyone actually needs it is still an open question.&lt;/p&gt;




&lt;h3&gt;
  
  
  I underestimated the system tray
&lt;/h3&gt;

&lt;p&gt;It seemed trivial.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;The interaction between minimize-to-tray, auto-lock policies, browser extension behavior, and focus management created bugs I'm still untangling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where OxidVault Stands Today
&lt;/h2&gt;

&lt;p&gt;OxidVault v2.2.0 currently runs on Windows and includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-user vaults (per-user password + TOTP)&lt;/li&gt;
&lt;li&gt;ISO 27001 audit logs with SHA-256 hash chaining&lt;/li&gt;
&lt;li&gt;GDPR compliance PDF export&lt;/li&gt;
&lt;li&gt;SSH Quick Connect with host key verification&lt;/li&gt;
&lt;li&gt;Browser extension via Native Messaging&lt;/li&gt;
&lt;li&gt;Configurable auto-lock timer in the UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Community Edition is free for up to 5 users (AGPLv3).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/caRl0oo/oxidvault" rel="noopener noreferrer"&gt;https://github.com/caRl0oo/oxidvault&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website&lt;/strong&gt;&lt;br&gt;
👉 &lt;a href="https://oxidvault.com" rel="noopener noreferrer"&gt;https://oxidvault.com&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Windows toast notifications for expiring passwords — the underlying logic already exists.&lt;/p&gt;

&lt;p&gt;And more importantly:&lt;/p&gt;

&lt;p&gt;Actually talking to users.&lt;/p&gt;

&lt;p&gt;That’s something I should have done much earlier.&lt;/p&gt;

&lt;p&gt;If you have thoughts on the crypto architecture or the DEK-wrapping approach, I'd genuinely love to hear them.&lt;/p&gt;

</description>
      <category>security</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
