Installing Redis on macOS

March 8th, 2025

Redis Database

I have always liked redis as a wonderful example of very targeted software done very well. It's a single-threaded C app that does one thing, very well, and in the years since I started using it, it's only gotten better. As I've been building a new Clojure web app, one of the things I wanted to take advantage of, was the stored session state, specifically so that when I have multiple boxes running the code, they can all share the one session state - and quickly.

I've been getting my WebAuthN authentication going, and that is using session state as the place to store the :identity of the logged in user. After I worked out the serialization of the Authenticator for WebAuthN, I then turned my attention to persisting the session state with Ring's SessionStore.

I've started using Ring's wrap-defaults middleware. It's easily added to the options for wrap-defaults with:

  ; pull in the wrapper and base defaults
  (:require [ring.middleware.defaults :refer [wrap-defaults site-defaults]])
 
  ; augment the site defaults with the SessionStore from Carmine
  (def my-site-defaults
    (assoc site-defaults
      :session {:flash true
                :store (carmine-store)}))
 
  ; put it in the stack of the app routes...
  (-> app-routes
      (wrap-access-rules {:rules rules :on-error unauthorized-handler})
      (wrap-authorization backend)
      (wrap-authentication backend)
      wrap-user
      wrap-json-with-padding
      (wrap-defaults my-site-defaults)
      wrap-logging
      wrap-gzip)))

I've been a huge fan of carmine for many years, as it's a pure Clojure library for redis, and it's exceptionaly fast.

But first, I needed to install redis locally so that I can do laptop development and not have to have a connection to a hosted service. Simply:

  $ brew install redis

and then to make sure it's started through launchd, simply:

  $ brew services start redis

just like with Postgres. It's really very simple.

At this point, restarting the web server will automatically store the session data in the locally running redis, and for production deployments, it's easy enough to use redis cloud, or redis at AWS, Google Cloud, etc. It's super simple, and it's exceptionally reliable.

The web server can now be restarted without impact to the customers as redis has their session state, and postgres has their Authenticator for WebAuthN.

Persisting Java Objs within Clojure

March 6th, 2025

Clojure.jpg

For the last day or so I've been wrestling with a problem using WebAuthN on a Clojure web app based on compojure and ring. There were a few helpful posts that got be in the right direction, and using Buddy helps, as it handles a lot of the route handling, but getting the actual WebAuthN handshake going was a bit of a pain.

The problem was that after the Registration step, you end up with a Java Object, an instance of com.webauthn4j.authenticator.AuthenticatorImpl and there is no simple way to serialize it out for storage in a database, so it was time to get creative.

I did a lot of digging, and I was able to find a nice way to deserialize the object, and return a JSON object, but there was no way to reconstitute it into an AuthenticatorImpl, so that had to be scrapped.

Then I found a reference to an Apache Commons lang object that supposedly was exactly what I wanted... it would serialize to a byte[], and then deserialize from that byte[] into the object. Sounds good... but I needed to save it in a Postgres database. Fair enough... let's Base64 encode it into a string, and then decode it on the way out.

The two key functions are very simple:

  (:import org.apache.commons.lang3.SerializationUtils
           java.util.Base64)
 
  (def not-nil? (complement nil?))
 
  (defn obj->b64s
    "This is a very useful function for odd Java Objects as it is an Apache tool
    to serialize the Object into a byte[], and then convert that into a Base64
    string. This is going to be very helpful with the persistence of objects to
    the database, as for some of the WebAuthN objects, it's important to save
    them, as opposed to re-creating them each time."
    [o]
    (if (not-nil? o)
      (.encodeToString (Base64/getEncoder) (SerializationUtils/serialize o))))
 
  (defn b64s->obj
    "This is a very useful function for odd Java Objects as it is an Apache tool
    to deserialize a byte[] into the original Object that was serialized with
    obj->b64s. This is going to be very helpful with the persistence of objects
    to the database, as for some of the WebAuthN objects, it's important to save
    them, as opposed to re-creating them each time."
    [s]
    (if (not-nil? s)
      (SerializationUtils/deserialize (.decode (Base64/getDecoder) s))))

These then fit into the saving and querying very simply, and it all works out just dandy. 🙂 I will admit, I was getting worried because I was about to regenerate the AuthenticatorImpl on each call, and that would have been a waste for sure.

The complete WebAuthN workflow is the point of another post, and a much longer one at that. But this really made all the difference.

Making Myself less Valuable to Meta

February 24th, 2025

Facebook

This week, John Oliver did a piece about Meta and how it's removal of it's moderation policies is going to make the platform much worse for folks. In the piece, he posted a list of things to do to remove yourself from advertising - at least as much as possible, for Facebook and Instagram.

So I decided to do just that, and interestingly enough, the Instagram steps can be done within the Instagram iOS app - so I did. This was simple enough, and now I don't enrich Meta quite as much when I look for things from the family. I feel better.

Updated to Textual 7 – Standalone

February 21st, 2025

Yesterday I noticed that Textual 7 was removed from the App Store, and they took it Standalone with a separate license from the authors. I have the App Store version, so I asked - in Textual 🙂 - what the process was for upgrading to the Standalone version.

While I waited for an answer, I kept looking on the company website, and sure enough, they had a set of instructions for how to get a license from an App Store version. So this morning, I followed those instructions, and now have the latest version of Textual 7 with a new Standalone license from the authors.

I get the reason they left the App Store... the license only costs $10, so it's not like they make a ton of money on the app... so it makes sense to try and keep what they get. And I run it all the time, so it's not like I wouldn't have paid $10 for a new license, if that's what was needed. But this was really nice of them to offer a path.

Vim Text File Specific Settings

February 20th, 2025

vim.jpg

I have been trying to set Vim settings, specific for text files, in my .vimrc for the longest of time, and I really wanted to get this figured out this morning. So here we go.

It's all about the autocmd, or au, command, and you can set it up pretty easily. What I had in the past was:

  autocmd FileType md
  \    set ai sw=2 sts=2 et
  autocmd FileType txt
  \    set ai sw=2 sts=2 et

where, in these two examples, I'm trying to set the autoindent to ON, the shiftwidth to 2, the softtabstop to 2 and expandtab to ON. And it wasn't working for files like goof.txt and for the life of me I could not figure this out.

And then it hit me - I had a FileType of javascript in the file, what if Vim needed to have text for the FileType? So let's try:

  autocmd FileType markdown
  \    set ai sw=2 sts=2 et
  autocmd FileType text
  \    set ai sw=2 sts=2 et

And all of a sudden, it worked like a charm. I also tried markdown using the same reasoning. I guess that goes to show me that Vim is interpreting the file extension, and not literally using it as the FileType. Smart. Appreciated.

Upgraded to Postgres 16.7

February 20th, 2025

PostgreSQL.jpg

I noticed that Postgres 16 was updated in Homebrew, so I took the time to upgrade the installation on my MacBook Pro, and it was remarkably easy. Because it was a simple "dot" upgrade, the installer did all the heavy lifting:

 $ brew upgrade postgresql@16

and then when it was done, simply restart the service with:

 $ brew services restart postgresql@16

And that was it. Everything is up and running just fine. What a treat. 🙂

This is the second "dot" upgrade I've done with Postgres 16 and Homebrew, and I just can't get over how clean and simple it is. I've checked all the databases, and they are good, and everything is fine.

Upgraded to Java 17.0.14 and 11.0.26

February 20th, 2025

java-logo-thumb.png

I have been looking at starting some projects in Clojure for work, and I thought it would be good for me to get the latest JDK 17 from Homebrew and Temurin. As it turns out, the latest for JDK 17 is now JDK 17.0.4, and since I had a slightly older version of that version, and the Homebrew name changed, I had to:

  $ brew tap homebrew/cask

and then to actually update it:

  $ brew install --cask temurin@17

When I checked:

  $ java -version
  openjdk version "17.0.14" 2025-01-21
  OpenJDK Runtime Environment Temurin-17.0.14+7 (build 17.0.14+7)
  OpenJDK 64-Bit Server VM Temurin-17.0.14+7 (build 17.0.14+7, mixed mode, sharing)

which is exactly what I was hoping for.

As an interesting note, the re-tapping of the cask updated the name of temurin11 to temurin@11, and I updated JDK 11 as well - why not? It's at 11.0.26, and I might use it... you never know.

Now I'm up-to-date with both versions, and I can easily switch from one to the other with the shell function I wrote. Excellent! 🙂

Fire off a Promise

April 19th, 2024

This morning I was thinking about the interrogation of the Promise in Node, and it was a little bothering to me that there was no way to easily see if it was complete. Then I thought - Maybe there is a different way? So I decided to give this a try.

The use-case is this: I want to be able to fire off a request to another Service, and maybe it'll get back to me in time, and maybe it won't. Either way, I don't want to wait. I have other work to do that must get done. So I want to fire this Promise off, and then, if it's done when I need its result - Great! If not, then Also Great!

But the normal:

  const foo = await callAsyncFunction()

isn't going to work, because that will wait until it's done. So how to work this out?

It turns out that it's not too hard.

  const { setTimeout } = require('timers/promises')
 
  const runTest = async () => {
    let done = false
    const foo = setTimeout(5000).then((val) => done = true)
    for (let i = 0; i < 10; i++) {
      console.log('FOO', foo, done)
      await setTimeout(1000)
    }
  }
 
  runTest()
    .then(() => console.log('All done running Timing test.'))
    .catch(console.error)
    .finally(() => process.exit())

and when I run this:

  $ node foo.js
  FOO Promise { <pending> } false
  FOO Promise { <pending> } false
  FOO Promise { <pending> } false
  FOO Promise { <pending> } false
  FOO Promise { <pending> } false
  FOO Promise { true } true
  FOO Promise { true } true
  FOO Promise { true } true
  FOO Promise { true } true
  FOO Promise { true } true
  All done running Timing test.

So all we need to do is to have a variable that indicates the state of the completeness, and then return that in the .then() call. Sure, it may make a lot more sense to have:

    const foo = setTimeout(5000).then((val) => {
      done = true
      return val
    })

so that we get the value back into foo, but that's easy... the point is to toggle the variable in that .then() and query that, as needed.

This way, I don't have to worry about any unsupported ways of finding out, it's simple. 🙂

Upgraded to Postgres 16.2

March 26th, 2024

PostgreSQL.jpg

I noticed that Postgres 16 was updated in Homebrew, so I took the time to upgrade the installation on my MacBook Pro, and it was remarkably easy. Because it was a simple "dot" upgrade, the installer did all the heavy lifting:

 $ brew upgrade postgresql@16

and then when it was done, simply restart the service with:

 $ brew services restart postgresql@16

And that was it. Everything is up and running just fine. What a treat. 🙂

Installing New TV

January 27th, 2024

TV.jpg

This morning it was time to see about installing the new TV I ordered and was delivered this week. It's a nice Sony, with good reviews, and it should last me another 10 yrs like the last one, but as with the last one, the installation of a 75" TV by myself is going to be a little bit of a challenge.

That's why I didn't try to install it when it arrived on Wednesday. Better to wait until I had loads of time, like today, to unbox it, get it all ready to go, and if I can't get it in myself, then my daughter is coming over later to help. We will see... keep a positive outlook, and don't push it... it's not small, that's for sure.