Random Strings on the Command Line

Browsing through some old files today I came across this note:

To Generate A Random String:tr -dc A-Za-z0-9_ < /dev/urandom | head -c 8 | xargs

Curios if it still worked I pasted it into my terminal and, unsurprisingly, was met with an error:

tr: Illegal byte sequence

The tr utility is one of the many old-school Unix programs with history reaching way back to System V. It stands for “translate characters”, and with the -dc flags on, it should have ignored all input except for alphabet characters A-Z, both upper and lower case, and the integers 0 through 9, and the underscore character. The “Illegal byte sequence” error means it was really not happy with the input it was getting from /dev/urandom.

On macOS, the pseudo-device /dev/urandom is, according to the man page, “a compatibility nod to Linux”. The device generates random bytes, so if we read it we’ll get back raw binary data that looks like:

00010101 01011001 10111101

The reason the command is not working like it used to is because most modern computing system expect text character encoding to be UTF-8. When tr gets the string of random bytes from /dev/urandom, it expects the bytes to be in a specific sequence that it can translate into printable characters on the screen. Since we are intentionally generating random bytes though, we might get a few characters that translate properly, but eventually we’ll encounter the “illegal byte sequence” error above.

To fix the problem, all we need to do is set LC_ALL=C before running tr:

LC_ALL=C tr -dc A-Za-z0-9_ < /dev/urandom | head -c 14 | xargs

Setting LC_ALL=C sets the language setting back to POSIX, or the original C ASCII encoding for text. That means when tr is fed a random string of bytes, it interprets each byte as a character, according to the ASCII table, which looks something like this:

Character ASCII Decimal ASCII Hexadecimal Binary Representation
A 65 41 01000001
B 66 42 01000010
C 67 43 01000011

Now each byte is interpreted as a character that matches the list passed as an argument to tr.

➜ LC_ALL=C tr -dc A-Za-z0-9_ < /dev/urandom | head -c 14 | xargsRhac_WGis7tHzS

So, to break down each command in the pipeline:

  • tr: filters out all characters except those in the sets A-Z, a-z, 0-9, and _
  • head -c 14: displays the first 14 characters of the input from tr
  • xargs: adds a nice newline character at the end of the string, so it’s easy to copy.

This command could be easily adopted to use base64 instead of tr without setting LC_ALL=C if you wanted more random characters in the string:

base64 < /dev/random | head -c 14 | xargs

Expanding head -c to 34 or so makes for a nice password generator.

In fact, I’ve aliased this to pgen in my .zshrc:

pgen(){    base64 < /dev/random | head -c 32 | xargs}

There’s almost certainly easier ways to generate a random string in the shell, but I like this, and it works for me.


Update: The good Dr. Drang suggested ending the pipeline and running echo instead of xargs for clairity, which makes a lot of sense to me. I updated the alias to base64 < /dev/random | head -c 32; echo.

Gross Apple Marketing

I’m not sure what’s going on over in Cupertino for them to think that any of the recent Apple Intelligence ads they’ve been running are a good idea. They’re cringy at best, and honestly just flat out insulting.

In one a schlub writes an email to his boss and uses AI to make it sound ‘more professional’, in another a young woman uses it to lie about remembering an acquaintance’s name. In another the same young woman again uses it to lie about reading an email from a college, to her face, while she’s sitting with her. In yet another, linked to recently by Scott McNulty, a woman uses AI to lie to her husband about getting him something for his birthday.

If this is what Apple thinks their AI is for, I honestly don’t know that I want any part of it.

Compare and contrast with the video I posted yesterday, and with this beautiful animation from Canonical.

An error occurred.

Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.

I’ve watched that little animation several times, and they tell a better story in a minute twenty-five than all of Apple’s AI commercials combined.

Loading and Indexing SQLite

What a difference a couple of lines of code can make.

I recognize that databases have always been a weak point for me, so I’ve been trying to correct that lately. I have a lot of experience with management of the database engines, failover, filesystems, and networking, but too little working with the internals of the databases themselves. Early this morning I decided I didn’t know enough about how database indexes worked. So I did some reading, got to the point where I had a good mental model for them, and decided I’d like to do some testing myself. I figured 40 million records was a nice round number, so I used fakedata to generate 40 million SQL inserts that looked something like this:

INSERT INTO contacts (name,email,country) VALUES ("Milo Morris","pmeissner@test.tienda","Italy");INSERT INTO contacts (name,email,country) VALUES ("Hosea Burgess","kolage@example.walmart","Dominica");INSERT INTO contacts (name,email,country) VALUES ("Adaline Frank","shaneIxD@example.talk","Slovenia");

I saved this as fakedata.sql and piped it into sqlite3 and figured I’d just let it run in the background. After about six hours I realized this was taking a ridiculously long time, and I estimated I’d only loaded about a quarter of the data. I believe that’s because SQLite was treating each INSERT as a separate transaction.

A transaction in SQLite is a unit of work. SQLite ensures that the write to the database is Atomic, Consistent, Isolated, and Durable, which means that for each of the 40 million lines I was piping into sqlite3, the engine was ensuring that every line was fully committed to the database before moving on to the next line. That’s a lot of work for a very, very small amount of data. So, I did some more reading and found one recommendation of explicitly wrapping the entire load into a single transaction, so my file now looked like:

BEGIN TRANSACTION;INSERT INTO contacts (name,email,country) VALUES ("Milo Morris","pmeissner@test.tienda","Italy");INSERT INTO contacts (name,email,country) VALUES ("Hosea Burgess","kolage@example.walmart","Dominica");INSERT INTO contacts (name,email,country) VALUES ("Adaline Frank","shaneIxD@example.talk","Slovenia");COMMIT;

I set a timer and ran the import again:

➜  var time cat fakedata.sql| sqlite3 test.dbcat fakedata.sql  0.07s user 0.90s system 1% cpu 1:13.66 totalsqlite3 test.db  70.81s user 2.19s system 98% cpu 1:13.79 total

So, that went from 6+ hours to about 71 seconds. And I imagine if I did some more optimization (possibly using the Write Ahead Log?) I might be able to get that import faster still. But a little over a minute is good enough for some local curiosity testing.

Indexes

So… back to indexes.

Indexing is a way of sorting a number of records on multiple fields. Creating an index on a field in a table creates another data structure that holds the field values and a pointer to the record it relates to. Once the index is created it is sorted. This allows binary searches to be performed on the new data structure.

One good analogy is the index of a physical book. Imagine that a book has ten chapters and each chapter has 100 pages. Now imagine you’d like to find all instances of the word “continuum” in the book. If the book doesn’t have an index, you’d have to read through every page in every chapter to find the word.

However, if the book is already indexed, you can find the word in the alphabetical list, which will then have a pointer to the page numbers where the word can be found.

The downside to the index is that it does take additional space. In the book analogy, while the book itself is 1000 pages, we’d need another ten or so for the index, bringing up the total size to 1010 pages. Same with a database, the additional index data structure requires more space to hold both the original data field being indexed, and a small (4-byte, for example) pointer to the record.

Oh, and the results of creating the index are below.

SELECT * from contacts WHERE name is 'Hank Perry';Run Time: real 2.124 user 1.771679 sys 0.322396CREATE INDEX IF NOT EXISTS name_index on contacts (name);Run Time: real 22.129 user 16.048308 sys 2.274184SELECT * from contacts WHERE name is 'Hank Perry';Run Time: real 0.003 user 0.001287 sys 0.001598

That’s a massive improvement. And now I know a little more than I did.

The Perfect ZSH Config

If you spend all day in the terminal like I do, you come to appreciate it’s speed and efficiency. I often find myself in Terminal for mundane tasks like navigating to a folder and opening a file; it’s just faster to type where I want to go than it is to click in the Finder, scan the folders for the one I want, double-click that one, scan again… small improvements to the speed of my work build up over time. The speed is increased exponentially with the correct configuration for your shell, in my case, zsh.

zsh is powerful and flexible, which means that it can also be intimidating to try to configure yourself. Doubly-so when there are multiple ‘frameworks’ available that will do the bulk of the configuration for you. I used Oh My Zsh for years, but I recently abandoned it in favor of maintaining my own configuration using only the settings that I need for the perfect configuration for my use.

I’ve split my configuration into five files:

  • apple.zsh-theme
  • zshenv
  • zshrc
  • zsh_alias
  • zsh_functions

I have all five files in a dotfiles git repository, pushed to a private Github repository.

The zshenv file is read first by zsh when starting a new shell. It contains a collection of environmental variables I’ve set, mainly for development. For example:

export PIP_REQUIRE_VIRTUALENV=trueexport PIP_DOWNLOAD_CACHE=$HOME/.pip/cacheexport VIRTUALENV_DISTRIBUTE=true

The next file is zshrc, which contains the main bulk of the configurations. My file is 113 lines, so let’s take it a section at a time.

source /Users/jonathanbuys/Unix/etc/dotfiles/apple.zsh-themesource /Users/jonathanbuys/Unix/etc/dotfiles/zsh_aliassource /Users/jonathanbuys/Unix/etc/dotfiles/zsh_functions

The first thing I do is source the other three files. The first is my prompt, which is cribbed entirely from Oh My Zsh. It’s nothing fancy, but I consider it to be elegant and functional. I don’t like the massive multi-line prompts. I find them to be far too distracting for what they are supposed to do.

My prompt looks like this:

 ~/Unix/etc/dotfiles/ [master*] 

It gives me my current path, what git branch I’ve checked out, and if that branch has been modified since the last commit.

The next two files, as their names suggest, contain aliases and functions. I have three functions and 16 aliases. I won’t go into each of them here, as they are fairly mundane and only specific for my setup. The three functions are to print the current path of the open Finder window, to use Quicklook to preview a file, and to generate a uuid string.

The next few lines establish some basic settings.

autoload -U colors && colorsautoload -U zmvsetopt AUTO_CDsetopt NOCLOBBERsetopt SHARE_HISTORYsetopt HIST_IGNORE_DUPSsetopt HIST_IGNORE_SPACE

The autoload lines setup zsh to use pretty colors, and to enable the extremely useful zmv command for batch file renaming. The interesting parts of the setopt settings are the ones dealing with command history. These three commands allow the sharing of command line history between open windows or tabs. So if I have multiple Terminal windows open, I can browse the history of both from either window. I find myself thinking that the environment is broken if this is not present.

Next, I setup some bindings:

  # start typing + [Up-Arrow] - fuzzy find history forward  bindkey '^[[A' up-line-or-search  bindkey '^[[B' down-line-or-search    # Use option as meta  bindkey "^[f" forward-word  bindkey "^[b" backward-word    # Use option+backspace to delete words  x-bash-backward-kill-word(){      WORDCHARS='' zle backward-kill-word    }  zle -N x-bash-backward-kill-word  bindkey '^W' x-bash-backward-kill-word    x-backward-kill-word(){      WORDCHARS='*?_-[]~\!#$%^(){}<>|`@#$%^*()+:?' zle backward-kill-word  }  zle -N x-backward-kill-word  bindkey '\e^?' x-backward-kill-word

These settings let me use the arrow keys to browse history, and to use option + arrow keys to move one word at a time through the current command, or to use option + delete to delete one word at a time. Incredibly useful, use it all the time. Importantly, this also lets me do incremental searching through my command history with the arrow keys. So, if I type aws, then arrow up, I can browse all of my previous commands that start with aws. And when you have to remember commands that have 15 arguments, this is absolutely invaluable.

The next section has to do with autocompletion.

# Better autocomplete for file namesWORDCHARS=''unsetopt menu_complete   # do not autoselect the first completion entryunsetopt flowcontrolsetopt auto_menu         # show completion menu on successive tab presssetopt complete_in_wordsetopt always_to_endzstyle ':completion:*:*:*:*:*' menu select# case insensitive (all), partial-word and substring completionif [[ "$CASE_SENSITIVE" = true ]]; then  zstyle ':completion:*' matcher-list 'r:|=*' 'l:|=* r:|=*'else  if [[ "$HYPHEN_INSENSITIVE" = true ]]; then    zstyle ':completion:*' matcher-list 'm:{[:lower:][:upper:]-_}={[:upper:][:lower:]_-}' 'r:|=*' 'l:|=* r:|=*'  else    zstyle ':completion:*' matcher-list 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' 'r:|=*' 'l:|=* r:|=*'  fifiunset CASE_SENSITIVE HYPHEN_INSENSITIVE# Complete . and .. special directorieszstyle ':completion:*' special-dirs truezstyle ':completion:*' list-colors ''zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#) ([0-9a-z-]#)*=01;34=0=01'zstyle ':completion:*:*:*:*:processes' command "ps -u $USERNAME -o pid,user,comm -w -w"# disable named-directories autocompletionzstyle ':completion:*:cd:*' tag-order local-directories directory-stack path-directories# Use caching so that commands like apt and dpkg complete are useablezstyle ':completion:*' use-cache yeszstyle ':completion:*' cache-path $ZSH_CACHE_DIRzstyle ':completion:*:*:*:users' ignored-patterns \        adm amanda apache at avahi avahi-autoipd beaglidx bin cacti canna \        clamav daemon dbus distcache dnsmasq dovecot fax ftp games gdm \        gkrellmd gopher hacluster haldaemon halt hsqldb ident junkbust kdm \        ldap lp mail mailman mailnull man messagebus  mldonkey mysql nagios \        named netdump news nfsnobody nobody nscd ntp nut nx obsrun openvpn \        operator pcap polkitd postfix postgres privoxy pulse pvm quagga radvd \        rpc rpcuser rpm rtkit scard shutdown squid sshd statd svn sync tftp \        usbmux uucp vcsa wwwrun xfs '_*'if [[ ${COMPLETION_WAITING_DOTS:-false} != false ]]; then  expand-or-complete-with-dots() {    # use $COMPLETION_WAITING_DOTS either as toggle or as the sequence to show    [[ $COMPLETION_WAITING_DOTS = true ]] && COMPLETION_WAITING_DOTS="%F{red}…%f"    # turn off line wrapping and print prompt-expanded "dot" sequence    printf '\e[?7l%s\e[?7h' "${(%)COMPLETION_WAITING_DOTS}"    zle expand-or-complete    zle redisplay  }  zle -N expand-or-complete-with-dots  # Set the function as the default tab completion widget  bindkey -M emacs "^I" expand-or-complete-with-dots  bindkey -M viins "^I" expand-or-complete-with-dots  bindkey -M vicmd "^I" expand-or-complete-with-dotsfi# automatically load bash completion functionsautoload -U +X bashcompinit && bashcompinit

That’s a long section, but in a nutshell this lets me type one character, then hit tab, and be offered a menu of all the possible completions of that character. It is case-insensitive, so b would match both boring.txt and Baseball.txt. I can continue to hit tab to cycle through the options, and hit enter when I’ve found the one I want.

The last section sources a few other files:

[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh[ -f "/Users/jonathanbuys/.ghcup/env" ] && source "/Users/jonathanbuys/.ghcup/env" # ghcup-env[ -s "/Users/jonathanbuys/.bun/_bun" ] && source "/Users/jonathanbuys/.bun/_bun"source /Users/jonathanbuys/Unix/src/zsh-autosuggestions/zsh-autosuggestions.zshsource /Users/jonathanbuys/Unix/src/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

If I’m experimenting with Haskell, I’d like to load the ghcup-env variables. If I have bun installed (a way, way faster npm), than use that. The final two sources are for even more enhanced autosuggestions and command line syntax highlighting. So, typos or commands that don’t exist will be red, good commands where zsh can find the executable will be green. The autosuggestions take commands from my history and suggest them, I can type right-arrow to accept the suggestion, or keep typing to ignore it.

Taken together, I’ve been able to remove Oh My Zsh, but keep all of the functionality. My shell configuration is constantly evolving as I find ways to make things faster and more efficient. I don’t consider myself a command line zealot, but I do appreciate how this setup gets out of my way and helps me work as fast as I can think.


p.s. A lot of this configuration was taken from other sources shared around the internet, as well as the zsh documentation. I regret that I haven’t kept references to the origins of some of these configs. If I can find the links I’ll post them here.

Future Work and AI

I’ve been trying to wrap my small monkey brain around what ChatGPT will mean in the long run. I’m going to try to think this through here. In many ways the advances we’ve seen in AI this past year perpetuate the automation trend that’s existed since… well, since humans started creating technology. I’ve seen arguments that seem to be on two ends of a spectrum, that the AI is often wrong and unreliable, and we shouldn’t use it for anything important, to AI is so good that it’s going to put us all out of jobs. As with most truths, I think the reality is somewhere in between.

It’s my opinion that jobs that AI can replace, it probably will replace a lot of. But not all. Referring back to our discussion about the current state of Apple news sites, if the site is a content farm pumping out low-value articles for hit counts and views, I can see AI handling that. If your site is well thought out opinions and reviews about things around the Apple ecosystem, that I think will be safe. Because it’s the person’s opinion that gives the site value.

For more enterprise-y jobs, I could see fewer low and mid-level developers. Fewer managers, fewer secretaries, fewer creatives. Not all gone, but certainly less than before. If your job is to create stock photos and put together slide shows, you might want to expand your skill set a bit.

I think… the kind of jobs that will survive are the type that bring real value. The kind of value that can’t be replicated by a computer. Not just the generation of some text or code, but coming up with the why. What needs to be made, and why does it need to be made?

Maybe AI will help free us up to concentrate on solving really hard problems. Poverty, clean water, famine, climate change. Then again, maybe it’ll make things worse. I suppose in the end that’s up to us.

GPG Signing Git Commits

On my way towards completing another project I needed to setup gpg public key infrastructure. There are many tutorials and explanations about gpg on the web, so I won’t try to explain what it is here. My goal is to simply record how I went about setting it up for myself to securely sign my Git commits.

Most everything here I gathered from this tutorial on dev.to, but since I’m sure I’ll never be able to find it again after today, I’m going to document it here.

First, install gpg with Homebrew:

brew install gpg

Next, generate a new Ed25519 key:

gpg --full-generate-key --expert

We pick option (9) for the first prompt, Elliptic Curve Cryptography, and option (1) for the second, Curve 25519. Pick the defaults for the rest of the prompts, giving the key a descriptive name.

Once finished you should be able to see your key by running:

gpg --list-keys --keyid-format short

The tutorial recommends using a second subkey generated from the first key to actually do the signing. So, we edit the master key by running:

gpg --expert --edit-key XXXXXXX

Replacing XXXXX with the ID of your newly generated key. Once in the gpg command line, enter addkey, and again select ECC and Curve 25519 for the options. Finally, enter save to save the key and exit the command line.

Now when we run gpg --list-keys --keyid-format short we should be able to see a second key listed with the designation [S] after it. The ID will look similar to this:

sub   ed25519/599D272D 2021-01-02 [S]

We will need the part after ed25519/, in this case 599D272D. Add that to your global Git configuration file by running:

git config --global user.signingkey 599D272D

If you’d like git to sign every commit, you can add this to your config file:

git config --global commit.gpgsign true

Otherwise, pass the -S flag to your git command to sign individual commits. I’d never remember to do that, so I just sign all of them.

Make sure that gpg is unlocked and ready to use by running:

echo "test"  | gpg --clearsign

If that fails, run export GPG_TTY=$(tty) and try again. You should be prompted to unlock GPG with the passphrase set during creation of the key. Enter the export command in your ~/.zshrc to fix this issue.

Finally, Github has a simple way to add gpg keys, but first we’ll need to export the public key:

gpg --armor --export 599D272D

Copy the entire output of that command and enter it into the Github console under Settings, “SSH and GPG keys”, and click on “New GPG key”. Once that’s finished, you should start seeing nice green “Verified” icons next to your commits.

Why BBEdit?

I spend a lot of time in the terminal, but I spend an equal amount of time in my text editor. My main requirements for a text editor is that it be equally fast and powerful, but not try to do much more than just be a text editor. I have my terminal a quick command-tab away, so its very easy to jump back and forth, I don’t need a terminal built into my text editor. I also need my text editor to be completely accurate. What I see has to be what is written to the file, no “hidden syntax”. That’s why I prefer BBEdit. BBEdit has powerful text editing features, but it’s not overwhelming. It’s not trying to be your entire operating system.

In fact, BBEdit fits perfectly in the macOS environment. I used to be a dedicated vim user, but over time the context switching between the different macOS applications and vim became distracting. One of the great things about macOS is that once you learn the basic keyboard combos they apply almost everywhere, unless the application you are using is not a good Mac citizen. Vim has it’s own set of keyboard combos, endlessly configurable and expandable. I started forgetting my own shortcuts. BBEdit uses the macOS standard keyboard combos, many inherited from emacs, so if you learn how to navigate text in TextEdit you can apply those same shortcuts to BBEdit.

But, BBEdit is also powerful. You can include custom text snippets for autocompletion, run scripts to manipulate text, and use regular expressions for detailed search and replace operations. With the latest release you can also setup a language server for more powerful syntax checking and completion. BBEdit has been in active development for 30 years, and in that time the developer, Rich Siegel has continuously improved it and kept it up to date with the ever-changing architectures of macOS. BBEdit feels just as much at home on my M1 MacBook Pro as it did on the Macintosh of the 90’s.

BBEdit hits the right balance of power and simplicity for my workflow. It’s fast, reliable, and fits perfectly in the Mac environment. For as long as Rich is developing it, BBEdit will be in my Dock. I don’t know what will happen when he decides to retire, but I’m hoping that decision is many, many years away.

The Bug

Right now, in my garage, is a 1969 Volkswagen Beetle sedan. It’s a deep maroon in color (for now), has several spots of troublesome rust, a few holes in the floorboards or body, and, is absolutely beautiful. For now, I’m calling it Jerry Carcia.

When I was in high school in the early nineties I drove several cars, but none were as close to my heart as the Bug I drove back then. It was black, early sixties. I’d camp out in it, occasionally push start it, and once when I was driving on the highway the hood flew open and blocked my view. I had to roll down the window and stick my head out to pull over safely. But I loved it. When I joined the Navy, I left the Bug with my parents and they sold it for me. For over twenty years I’ve regretted getting rid of that Bug.

Over time I’d occasionally go to look at one. I’ve got a collection of advertisements for different Bugs that I’ve saved. At a dinner with friends a few months ago I mentioned that I wish I had kept my old Bug, and that I’d like to get another one eventually. A few days later my friend forwarded me a link to a Facebook Marketplace ad for the ‘69 in my garage now. It wasn’t far away, and the price wasn’t exorbitant, but in my mind I suspected I would pass this one over and continue my decades-long practice of wishing from the sidelines. But… since my friend sent it, and since it looked like it was in good condition, I talked it over with my wife. She looked at me and said: “You’ve been taking about doing this for as long as we’ve known each other, if you want to get it, I’m completely ok with that.”

I suspect that she also thinks I need something that’s going to occupy my time other than sitting in front of a computer. I further suspect that she’s right.

So, as soon as I could I arranged to go look at the Bug. I pulled a bunch of cash out of the ATM (always a hassle), and drove out to take a look. In person, the Bug looked like it was in even better condition than I thought. New carpeting inside, everything looked clean, the engine started, the tires and wheels were in great shape, no obvious rust issues on the body that I could see. I couldn’t drive it because the tires were flat, but I wasn’t expecting to drive this car, it’s a fixer-upper. I paid the man, got the title, and arranged with a friend to bring a trailer out to haul the Bug home.

After some finagling, we were able to drive the Bug onto the trailer, haul it home, drive it off the trailer, and into the garage.

I’ve had the Bug for a few months now. I’ve never been much of a mechanic, I’m learning a lot going through the different systems. My plan is to get the Bug to a drivable state, then clear out the third bay of my garage by building a shed in the back yard and moving all my equipment out there. Once that’s done, I’ll move the Bug over to the third bay and start on a full body-off restoration.

To get the Bug to drivable, my initial plan is to address these issues:

  1. Brakes. When I drove the Bug into the garage, only one wheel was braking, I’m going to need all four thanks.
  2. Electrical. One headlight doesn’t work, and there are wires all over the place that either don’t connect or I have no idea what they are doing. There’s an aftermarket stereo that’s going to go away, and the horn doesn’t work.
  3. Transmission. The Bug is an autostick, which I wasn’t even aware was a thing. It’s also not working at the moment, and I need to be able to disengage the clutch. More on this at a later date.
  4. Engine. The engine needs a tune up. It starts, but it takes a while, and there’s not a lot of power. Ok, well, there’s not a lot of power in a VW Bug to begin with, but… anyway, a tune up.

I figured once I’ve addressed each of these issues than I can at least drive it around town here for a bit. Run to the store for milk or what not. As of now I’ve addressed the brakes. All four wheels stop now. I’ll need to re-run the brake line going back to the rear wheels to get it run in the body right, then bleed the brakes again, after that they should be good till the full restoration. More on the brakes later.

It’s good to be a VW owner again. I’m looking forward to restoring the Bug. It’ll also give me something to write about and share here. The Bug has a long way to go before I’ll consider it “finished”. My intention is to restore as much as possible to factory original, with occasional upgrades for safety or modern convenience. When I sit in the bug, I’d like to feel like I’ve been transported back in time, back to the 90’s when I was first driving a VW through the woods of Montana.

My 2022 Mac Setup

Starting its fifth year on my desk, my 2017 iMac 5k is still going strong, but starting to show it’s age. There’s still nothing that I can’t do with it, but after looking at my wife’s M1 MacBook Air I can’t help but be just a little jealous of how fast that little machine is. I’ve yet to come across any computer that offers the same value as the iMac, especially when you factor in how good this 27” retina screen is. I’m tempted by both the current 24” M1 iMac, and the new 16” M1 Max MacBook Pro, but the rumor mill is talking about bigger, master iMacs and colorful M2 MacBook Airs, either of which might actually make the most sense for my desk. I want to see the entire lineup before committing. The good thing is that no matter which machine I choose, it’s going to be a major speed improvement from where I am now.

On the software side I’m in a bit of a state of flux. Old standards are now no longer given, but because I’ve been using them for so long I’m reluctant to step away. Apple’s Reminders has gotten good enough for most things, but after a brief experiment over the holidays I found that I was letting things slip through the cracks, and headed back to OmniFocus. OmniFocus, for all its complexity, feels like home.

1Password’s upcoming switch to Electron has me concerned, but after some consideration I wonder how much of my concern is practical and how much of it is philosophical. Ideally, I would have preferred if 1P 8 was an evolution of 1P 7, instead of an entirely different application, but at the end of the day I’m still going to use it for work, and I still have a family subscription that I get through my Eero subscription, so I’ll keep it around. And if I’m going to have it anyway… why not keep using it? The flip side of that coin is that when using Safari the built-in password manager is seamless, far simpler than 1Password, even for one-time codes. But, do I really want all my passwords only available in Safari? Sometimes I might want to use Firefox. This is all still up in the air.

For file management, I’ve still got DEVONthink, but it’s another one where I’m not sure I’ll keep it. Again, ideally, iCloud Drive would be encrypted end-to-end so I could safely trust that I could put sensitive work data on it and not be putting my career at risk, but that’s not the world we live in. Without DEVONthink I need to keep all my files on my local machine, which is normally fine, but every now and then I want to get to something while I’m out running around, and DEVONthink to Go is the only secure solution. I’ve heard rumors that encryption might be coming, but until it’s here I think I’m going to stick with DT.

Keyboard Maestro and Hazel are both on the chopping block. I still have both installed, but neither are running. For Keyboard Maestro, I honestly can’t keep extra keyboard shortcuts in my head, and I’ve never found that many uses for the application. For a long time the only thing I’d use it for is text expansion, and even then only for the date stamp, i.e. 2022-01-05_. If there comes a time when iCloud Drive is encrypted, I’ll probably turn Hazel back on for automatic file management. But given that everything now gets dumped into DEVONthink, all my files are organized by the apps AI and I have less to set up and maintain. And if I’m honest, I’ve always had problems with Hazel’s pattern matching, sometimes it would inexplicably not match a pattern in a file I could easily find while searching with Spotlight. Most of Keyboard Maestro’s automation features that I did use I’ve moved into Shortcuts. I’m looking forward to Hazel-like functionality being built into macOS.

Other than that, I’m happy with Day One, I’ve increased my use of the Apple Notes app quite a bit, I love NetNewsWire (although I’ve decreased the number of sites I’m subscribed to for less, but better, content), MindNode is a fantastic mind-mapping app, and I keep all my mail in Mail. The biggest new addition to my setup is Parallels for running Windows 10. I’ve found I need this for teaching the Microsoft Office course at the local community college. Of course, this is another area where I’m unsure what I’ll do when I finally do update to an M-series Mac, but I’ll cross that bridge when I come to it.

The Reminders Experiment

Over the past month I’ve been experimenting with cutting down on the number of applications I use on my Mac. One in particular I though was going to stick was moving my task management system over from OmniFocus to Reminders. I enjoyed the everywhere integration of Reminders, like how Siri would include any tasks I had scheduled for the day when I asked my HomePod “What’s my update?”. Unfortunately, when I sat down at the end of the holiday break to think about the projects I had going at work, and how to schedule them out for the upcoming week, I realized I needed to open OmniFocus, and once it was open, I knew the experiment was over.

I’ve been using the GTD system for so long now it’s a part of how I think. OmniFocus was built around that system. It’s the only to-do app that I’m aware of that does things like defer dates and built-in weekly reviews. It might be a deep and complex app, but I’ve got it customized to my liking. I know which parts of it I use (perspectives) and which parts I never touch (flagged tasks).

The long and the short of it is that I’ve got a complicated job as a senior devops engineer, I teach two courses at the local community college at night, I’ve got a family, and I’ve got a home and vehicles to take care of. To be able to focus, to be there for my family, for my work, I need a system that I can trust. For me, OmniFocus is the foundation of that system. I’m not excited about the new direction of their mobile app, but I can live with it. I imagine that OmniFocus and I are going to be together for a long time.