Blog

  • I finally built Whispermarks, the bookmark problem I never quite solved

    I finally built Whispermarks, the bookmark problem I never quite solved

    Whispermarks is a new project, but the idea behind it is old. Around 15 years ago, I built InBrowser, a privacy-first browser for Android that, for its time, became one of the biggest private browsers on mobile. That’s where this problem first showed up for me.

    The point of InBrowser was that it forgot. Close the app and your session was gone – history, cookies, everything. That was the product, way before incognito even existed on mobile. And there was just one problem. People wanted bookmarks.

    The bookmark problem

    Bookmarks are slightly awkward in a browser designed to forget everything. A normal bookmark is persistent by definition. You save a URL because you want it to be there tomorrow. That makes perfect sense in most browsers, but it never really fitted the privacy model I wanted for InBrowser.

    Bookmarks were also one of the most requested features, and I wanted them too. The easy answer would have been to bolt on a normal bookmark database, but that always felt wrong. If I was going to save something permanently in a privacy-focused product, the privacy part had to come first.

    That’s more or less where the idea for Whispermarks came from – a private place for bookmarks that wasn’t tied to the browser itself. I always thought I’d build it eventually. I just didn’t.

    Same itch, better timing

    I didn’t build it back then because I didn’t think I could build it well enough. Saving a URL is trivial. Building something where the service itself can’t read what you’ve saved is a very different problem. Keys, syncing and recovery all become part of the product.

    If I was going to build it, privacy had to be the foundation – not an option somewhere in settings.

    A lot has changed since then. Whatever you think about crypto, it has made concepts like recovery phrases, private keys and self-custody much more familiar. Fifteen years ago, asking a normal user to safely keep a sequence of recovery words would have been quite an interesting product decision. Today, the pattern isn’t nearly as strange.

    The tooling around cryptography is also much better now. The idea hadn’t really changed, but building the version I actually wanted finally felt realistic as a solo developer.

    Starting with the privacy model

    When I came back to the idea, the first question wasn’t what the bookmark interface should look like. It was what the server should be able to see. The answer was – as little as possible.

    Whispermarks encrypts the vault data locally before it leaves the device. The cloud stores the encrypted data, not the readable contents. The server gets encrypted blobs instead of your actual bookmarks.

    The cryptography is based around AES-256-GCM, with each vault having its own encryption key. Access to those keys comes from a recovery phrase held by the user. If you’ve used crypto wallets, the shape of that will feel familiar – just applied to bookmarks instead of money.

    That also means a vault is more than a folder. It’s its own cryptographic boundary. I can have one vault for personal links and another for a client, and decide where those vaults are available without putting everything into one big bucket.

    Deciding where to start

    The next decision was where to start. I landed on browser extensions, mostly because that’s where bookmarks already live.

    Whispermarks works on Chrome, Firefox and Edge. You can import your existing bookmarks, and more importantly, that vault isn’t stuck in one browser. The same vault can move with you between Chrome, Firefox and Edge. In that sense, portability comes for free. All wrapped in a first-class client experience (imho).

    I spent a lot of time on onboarding. Even if concepts like recovery phrases and keys feel more familiar now, they still need to be introduced properly. Creating an account, saving your recovery phrase, creating your first vault and installing the extension should feel like one flow, not a list of technical chores.

    That’s also why the browser was a good place to start. The site can guide you while the extension is right there. I don’t want someone to feel like they need to understand cryptography just to save a bookmark.

    There will be mobile clients later, but I started with browsers because that’s where I personally save most of my links, and getting that right felt more useful than trying to launch everywhere at once.


    A lot of the products I’ve enjoyed building started the same way. Something annoyed me enough that I wanted my own solution. Whispermarks is one of those, just stretched over a much longer timeline.

    The problem showed up years ago. I left it alone because I didn’t like the version I could build then. This is me finally fixing that old itch, and somewhere along the way, it turned into a product I think is pretty cool on its own.

    Whispermarks is available at whispermarks.com.

  • How I finally fit AI into the way I build software

    How I finally fit AI into the way I build software

    For a long time, I used AI the way I think many developers started using it. Chatting with ChatGPT, asking questions, poking at what it could do. Later, I brought it into my editor, Zed and VS Code, as autocomplete. Then the slow move over to terminal based AI assistance. Useful, sometimes even impressive. But it didn’t feel natural.

    I think part of the problem was how I was thinking about it. AI as the “Lovable model”: one prompt in, an okay-ish app out. Interesting, yes, but I could never wrap my head around that as a serious workflow. A rough app from a single prompt is still a rough app.

    The shift came when I stopped thinking of AI as autocomplete or a one-prompt app generator, and started treating it more like a engineer on my team. Not someone who owns the product, the architecture, or the decisions, but someone who can work extremely well inside a clear frame.

    That’s when it started fitting with how I already work.

    (more…)
  • How AI made leaving Evernote finally worth it

    How AI made leaving Evernote finally worth it

    For about 15 years, Evernote was one of the few apps I genuinely loved. I started with notes, then shifted to using it heavily for tasks and reminders as well.

    (more…)
  • Is the SaaSpocalypse real, or is it just a squeeze?

    Is the SaaSpocalypse real, or is it just a squeeze?

    There is a lot of noise right now about the SaaSpocalypse. Depending on who you listen to, AI is either about to destroy the software industry as we know it, or this is just another market panic dressed up as a technology story.

    I’ve spent close to 30 years building software, and much of that time has been spent building and managing products used by millions of people. So I’m not particularly interested in looking at this as a stock market story, or making dramatic predictions about every large SaaS company suddenly disappearing. I’m more interested in looking at it from the builder’s side.

    (more…)
  • What if the next programming language isn’t for us?

    What if the next programming language isn’t for us?

    We’ve spent decades optimizing programming languages for humans. Nicer syntax, higher-level abstractions, better type systems and better tooling, all in service of human authors. But something is shifting. Increasingly, we describe what we want and AI writes the code.

    If that continues, there’s a question I can’t shake:

    If AI becomes the primary author of software, why would the next programming language still be designed for us humans?

    (more…)
  • Four things wrong with the Evernote Android widget

    Four things wrong with the Evernote Android widget

    I’ve been a paying Evernote user since May 2010. For most of that time I’ve been happy with it. But almost all of my day-to-day use goes through the Android widget rather than the app itself, and the widget hasn’t kept up.

    Four things bother me. None of them are dramatic on their own. Together they add up.

    (more…)
  • How to trigger and run Firebase scheduled functions from localhost

    How to trigger and run Firebase scheduled functions from localhost

    Every wonder how to trigger Firebase scheduled (pubsub) functions directly from localhost during development? It’s actually quite easy, but for some reason not very well documented.

    Start by installing Firebase CLI:

    npm install -g firebase-tools

    Setup local Firebase emulators:

    firebase init emulators

    Make sure that your firebase.json has a pubsub port defined.

    "emulators": {
      ...
      "pubsub": {
        "port": "8085"
      }
    

    Start your local emulators:

    firebase emulators:start

    Shell into the running Firebase emulator instance

    firebase functions:shell

    You can now trigger any functions.pubsub.schedule directly from this prompt. E.g:

    scheduleNotifier();

    You’re now able to significantly improve your firebase function development, since you can trigger the scheduled functions interactively.

    firebase > scheduleNotifier();
    'Successfully invoked function.'
    firebase > >  No matching documents.
    >  Notifier done: 0
    
  • How to check a DNS TXT record using OSX Terminal

    A lot of services today authenticate DNS via TXT records and i find myself looking for web-services that checks those values for me. However, doing this will only give me the DNS record(s) for the DNS where the service is running, not actually my local DNS response.

    Doing this via the Mac/OSX Terminal (command line) is trivially easy:

    dig -t txt domain.com

    Which will give you the TXT record for the specified domain. You can change the “txt” part to any other DNS parameters.

  • Updating PHP to 7.2 on OSX using Homebrew

    Updating PHP to 7.2 on OSX using Homebrew

    PHP 7.2 provides several critical security updates and it’s important for any PHP developer to make sure that they leave the 5.x branch and jump on the 7.x branch as soon as possible.

    Updating to PHP 7.2 is a breeze If you’re on OSX and using Homebrew.

    Start by making sure that brew is up to date:

    brew update && brew upgrade

    Unlink any previous PHP installation (this may vary, so you might need to replace the php71 accordingly):

    brew unlink php71

    Install PHP 7.2:

    brew install php72

    Once done, you can verify that PHP 7.2 was installed by using php -v

    Happy coding!

  • How to fix “Unprotected Private Key File!”-errors when trying to connect to a EC2 machine

    Most Amazon EC2-users run in the to pesky “Permissions XXXX for ‘xxx.pem’ are too open. It is required that your private key files are NOT accessible by others.”-problem from time to time. And the solution is surprisingly simple.

    Since you are trying to access a machine using a private key, SSH requires that this key is only readable by the current user. This is to ensure that no-one else on the system can access this file and use it.

    Simply change the permission of the .pem file to 400, which means that it’s only accessible by your user and the problem will be fixed:

    chmod 400 xxx.pem