🏝 If you like the content of this blog, I would really appreciate your vote in Tuple's Send an Open Source Developer on Vacation contest. Just search for "Freek Van der Herten" in the nominees list and cast your vote. 🙌

😀 This holiday won't only be a nice reward for me, but also for my girlfriend who always gives me a lot of time working on open source packages and blog posts.

Replacing Keytar with Electron's safeStorage in Ray

Original – by Adriaan Marain – 3 minute read

Ray is an app we built at Spatie to make debugging your applications easier and faster. Being web developers, we naturally decided to write this app in Electron, which enabled us to move from nothing to a working prototype to a released product on 3 separate platforms within a matter of weeks.

About 9 months ago, Alex added a much requested feature that allows you to connect to remote servers and receive their Ray outputs securely over SSH.

To save the credentials to a server, we needed to find a secure way to save the password or private key passphrase. We quickly settled on node-keytar, a native Node.js module that leverages your system's keychain (Keychain/libsecret/Credential Vault/…) to safely store passwords, hidden from other applications and users.

Using a native Node module brought one major disadvantage: we couldn't easily build for other platforms anymore without running the actual build on that platform. There are some solutions provided by electron-builder, Keytar, and other packages, but these all came with their own layer of overhead. We eventually decided to run the build on all platforms separately using the GitHub Actions CI.

Three weeks ago, on September 21, 2021, Electron 15 was released, and somewhat hidden in the release notes we found a mention to a newly added string encryption API: safeStorage (PR/docs). Similarly to Keytar, Electron's safeStorage also uses the system's keychain to securely encrypt strings, but without the need for an extra dependency.

We jumped at the idea of simplifying our build process and removing a dependency, and wrote this simple implementation using safeStorage and electron-store, with an external API inspired by Keytar:

import { safeStorage } from 'electron';
import Store from 'electron-store';

const store = new Store<Record<string, string>>({
  name: 'ray-encrypted',
  watch: true,
  encryptionKey: 'this_only_obfuscates',

export default {
  setPassword(key: string, password: string) {
    const buffer = safeStorage.encryptString(password);
    store.set(key, buffer.toString('latin1'));

  deletePassword(key: string) {

  getCredentials(): Array<{ account: string; password: string }> {
    return Object.entries(store.store).reduce((credentials, [account, buffer]) => {
      return [...credentials, { account, password: safeStorage.decryptString(Buffer.from(buffer, 'latin1')) }];
    }, [] as Array<{ account: string; password: string }>);

The only downside is that any previously saved passwords and passphrases saved using Keytar are now inaccessible for the app, but you can still find them by opening your system's keychain application and looking for any mentions of ray, ssh_password_, or private_key_. We also hope Ray wasn't the only spot you saved your server passwords.

Upon opening the servers overlay, existing users will receive a notification that their credentials need to be entered again. Ray will save which servers have not had their credentials updated yet, and will display this to the user. We used electron-store's migrations feature for this:

migrations: {
  '>=1.18.0': (store) => {
        store.get('servers').map((server) => ({ ...server, needsCredentialsUpdate: true }))

Stay up to date with all things Laravel, PHP, and JavaScript.

Follow me on Twitter. I regularly tweet out programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.


What are your thoughts on "Replacing Keytar with Electron's safeStorage in Ray"?

Want to join the conversation? Log in or create an account to post a comment.