Webframp Ops culture and other hacker ramblings

Growing with the SparkleFormation registry

Growing into SparkleFormation

In my continued usage with SparkleFormation I’m really growing to appreciate the convenience of having ruby available when composing templates. Things that would be challenging in a simplistic serialization format or lead to unmanageable duplication become easily solvable thanks to the powerful combination of an actual programming language, which json and yml are not, and the simplicity of the SparkleFormation DSL.

At it’s simplest you can mirror the structure of any cloudformation json (and finally have comments inline!) but you quickly discover more advanced use cases.

SparkleFormation registries

Registries are described as:

“lightweight dynamics that are useful for storing items that may be used in multiple locations. “

A dynamic in SparkleFormation is just a reusable block of code that generates some section of the template and can be very flexible. This is in comparison to components which are meant as single use items to insert a static block of code.

Registries and dynamics are similar in that they are just building blocks for composing the template elements for any declarative orchestration API such as CloudFormation or Azure Resource Manager, well any arbitrary json data structure really. Registries should be simpler and more static than a full fledged dynamic, useful for things like a list of AWS instance sizes as in the documentation:

SfnRegistry.register(:instance_size_default){ 'm3.medium' }

or even something a little more involved like shared AWS::CloudFormation::Init data.

A more dynamic registry for AWS cfn-init

Recently the need arose to provide a shared registry for the necessary init commands to bootstrap a new ec2 instance with chef-client. Since we deal with both Windows and linux nodes in ec2 it would be a bit annoying to have to declare different registries like


After a brief discussion on freenode irc #sparkleformation, @luckymike pointed me in the direction of a technique he discovered that took advantage of the cfn-init configsets feature and the fact that registries can take args, just like any other SparkleFormation dynamic.

Essentially, cloudformation init will look for an array of config names and run them in order. This can be used in ruby to create an empty array called default and then conditionally append to the array based on parameters passed to the registry dynamic.

SfnRegistry.register(:chef_client) do | _config = {} |
  metadata('AWS::CloudFormation::Init') do
    # take advantage of fact that cfn-init runs the `default`
    # configsets http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html
    configSets do |sets|
      if _config.fetch(:platform) == 'windows'
        set.default += ["windows_config"]
        set.default += ["default_config"]

    # Windows init config
    windows_config do
        # windows specific elements here
    default_config do
        # generic elements here

Clearly some of the code has been left out. With a fully fleshed out dynamic using this approach it can then be used in a template by passing :platform as a configuration element:

registry!(:chef_client, :platform => 'windows')

The intent of this reads much clearer in the template code and everything needed for the :chef_client registry is contained in the same block of code.

Another good example of this can be seen in the Sensu evaluation stack codebase. The RabbitMQ registry needed a generated password that is passed in as an argument when calling the registry in the stack template.

A simple example perhaps but this is exactly the type of thing that leads to better code reuse and eases management of complex definitions over the lifespan of any infrastructure as code workflow.

SparkleFormation Bedtime Story

A very brief whirlwind tour of sparkles

or: How I learned to stop writing terrible serialization formats directly and love the dsl

An imaginary story of the beginner exploring some infrastructure tooling.

Getting started

Set env variables. Maybe create an init.sh while practicing.

export AWS_ACCESS_KEY_ID="your key"
export AWS_SECRET_ACCESS_KEY="your sekret"
export AWS_REGION="us-east-1" # because YOLO
export NESTED_BUCKET="s3://mah_bukkit"

load that stuff into my current shell

source ./init.sh

Create a config

.sfn file aready exists, thanks @luckymike. no need to create one.

Let’s make a VPC!

Initial creation results

~/s/sparkleformation-workshops ❯❯❯ be sfn create training-vpc --file sparkleformation/vpc.rb

… (a bunch of cloudformation stack output) … (like really a lot)

The important stuff:

[Sfn]: Stack create complete: SUCCESS
[Sfn]: Stack description of training-vpc:
[Sfn]: Outputs for stack: training-vpc
[Sfn]:    Vpc Id: vpc-62950a06
[Sfn]:    Vpc Cidr:
[Sfn]:    Public Route Table: rtb-f45b0f90
[Sfn]:    Private Route Table: rtb-e95b0f8d
[Sfn]:    Internet Gateway: igw-625db206
[Sfn]:    Public Us East1a Subnet: subnet-bb0be8e3
[Sfn]:    Public Us East1c Subnet: subnet-ce829fe5
[Sfn]:    Public Us East1d Subnet: subnet-07ba7271
[Sfn]:    Public Us East1e Subnet: subnet-27fc781a

OK, lets go with that, at least I’ve got something. Note to self: Save that, you’re going to need it later (or better yet use --apply-stack)!

Let’s update it to add an ec2 instance (Update plans are amaze!)

Update: 2015-12-14 sfn update is the wrong command here, better to use sfn create --apply-stack

~/s/sparkleformation-workshops ❯❯❯ be sfn update training-vpc --file sparkleformation/example.rb
[Sfn]: SparkleFormation: update
[Sfn]:   -> Name: training-vpc Path: /Users/sme/src/sparkleformation-workshops/sparkleformation/example.rb
[Sfn]: Stack runtime parameters:
[Sfn]: Stack Creator [sme]:
[Sfn]: Github User:
[ERROR]: Please provide a valid value
[Sfn]: Github User: webframp
[Sfn]: Hello World: How do you do!
[Sfn]: Vpc Id: vpc-62950a06
[Sfn]: Public Us East1a Subnet: subnet-bb0be8e3
[Sfn]: Pre-update resource planning report:

Update plan for: training-vpc
Resources to be removed:
[AWS::EC2::DHCPOptions]                    DhcpOptions
[AWS::EC2::InternetGateway]                InternetGateway
[AWS::EC2::VPCGatewayAttachment]           InternetGatewayAttachment
[AWS::EC2::RouteTable]                     PrivateRouteTable
[AWS::EC2::RouteTable]                     PublicRouteTable
[AWS::EC2::Route]                          PublicSubnetInternetRoute
[AWS::EC2::Subnet]                         PublicUsEast1aSubnet
[AWS::EC2::SubnetRouteTableAssociation]    PublicUsEast1aSubnetRouteTableAssociation
[AWS::EC2::Subnet]                         PublicUsEast1cSubnet
[AWS::EC2::SubnetRouteTableAssociation]    PublicUsEast1cSubnetRouteTableAssociation
[AWS::EC2::Subnet]                         PublicUsEast1dSubnet
[AWS::EC2::SubnetRouteTableAssociation]    PublicUsEast1dSubnetRouteTableAssociation
[AWS::EC2::Subnet]                         PublicUsEast1eSubnet
[AWS::EC2::SubnetRouteTableAssociation]    PublicUsEast1eSubnetRouteTableAssociation
[AWS::EC2::VPC]                            Vpc
[AWS::EC2::VPCDHCPOptionsAssociation]      VpcDhcpOptionsAssociation

Resources to be added:
[AWS::IAM::AccessKey]               CfnKeys
[AWS::IAM::User]                    CfnUser
[AWS::EC2::SecurityGroupEgress]     ExampleAllSecurityGroupEgress
[AWS::EC2::Instance]                ExampleEc2Instance
[AWS::EC2::SecurityGroupIngress]    ExampleHttpSecurityGroupIngress
[AWS::EC2::SecurityGroup]           ExampleSecurityGroup
[AWS::EC2::SecurityGroupIngress]    ExampleSshSecurityGroupIngress

[Sfn]: No resources life cycle changes detected in this update!
[Sfn]: Apply this stack update? (Y/N): Y

sfn update output:

Time                      Resource Logical Id                         Resource Status      Resource Status Reason
2015-12-12 02:35:36 UTC   training-vpc                                CREATE_IN_PROGRESS   User Initiated
2015-12-12 02:35:46 UTC   DhcpOptions                                 CREATE_IN_PROGRESS
2015-12-12 02:35:46 UTC   InternetGateway                             CREATE_IN_PROGRESS
2015-12-12 02:35:46 UTC   Vpc                                         CREATE_IN_PROGRESS
2015-12-12 02:35:47 UTC   DhcpOptions                                 CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:35:47 UTC   InternetGateway                             CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:35:48 UTC   Vpc                                         CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:05 UTC   InternetGateway                             CREATE_COMPLETE
2015-12-12 02:36:05 UTC   Vpc                                         CREATE_COMPLETE
2015-12-12 02:36:06 UTC   DhcpOptions                                 CREATE_COMPLETE
2015-12-12 02:36:06 UTC   PublicUsEast1aSubnet                        CREATE_IN_PROGRESS
2015-12-12 02:36:06 UTC   PrivateRouteTable                           CREATE_IN_PROGRESS
2015-12-12 02:36:07 UTC   PublicUsEast1dSubnet                        CREATE_IN_PROGRESS
2015-12-12 02:36:07 UTC   PublicUsEast1dSubnet                        CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:07 UTC   PrivateRouteTable                           CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:08 UTC   PublicUsEast1aSubnet                        CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:08 UTC   InternetGatewayAttachment                   CREATE_IN_PROGRESS
2015-12-12 02:36:08 UTC   PublicUsEast1eSubnet                        CREATE_IN_PROGRESS
2015-12-12 02:36:08 UTC   PublicUsEast1cSubnet                        CREATE_IN_PROGRESS
2015-12-12 02:36:08 UTC   PrivateRouteTable                           CREATE_COMPLETE
2015-12-12 02:36:08 UTC   PublicRouteTable                            CREATE_IN_PROGRESS
2015-12-12 02:36:08 UTC   InternetGatewayAttachment                   CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:09 UTC   VpcDhcpOptionsAssociation                   CREATE_IN_PROGRESS
2015-12-12 02:36:09 UTC   VpcDhcpOptionsAssociation                   CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:09 UTC   PublicRouteTable                            CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:10 UTC   PublicUsEast1cSubnet                        CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:10 UTC   PublicUsEast1eSubnet                        CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:10 UTC   VpcDhcpOptionsAssociation                   CREATE_COMPLETE
2015-12-12 02:36:11 UTC   PublicRouteTable                            CREATE_COMPLETE
2015-12-12 02:36:14 UTC   PublicSubnetInternetRoute                   CREATE_IN_PROGRESS
2015-12-12 02:36:15 UTC   PublicSubnetInternetRoute                   CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:24 UTC   PublicUsEast1aSubnet                        CREATE_COMPLETE
2015-12-12 02:36:24 UTC   InternetGatewayAttachment                   CREATE_COMPLETE
2015-12-12 02:36:25 UTC   PublicUsEast1dSubnet                        CREATE_COMPLETE
2015-12-12 02:36:26 UTC   PublicUsEast1aSubnetRouteTableAssociation   CREATE_IN_PROGRESS
2015-12-12 02:36:27 UTC   PublicUsEast1aSubnetRouteTableAssociation   CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:28 UTC   PublicUsEast1dSubnetRouteTableAssociation   CREATE_IN_PROGRESS
2015-12-12 02:36:28 UTC   PublicUsEast1eSubnet                        CREATE_COMPLETE
2015-12-12 02:36:29 UTC   PublicUsEast1cSubnet                        CREATE_COMPLETE
2015-12-12 02:36:29 UTC   PublicUsEast1dSubnetRouteTableAssociation   CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:31 UTC   PublicUsEast1eSubnetRouteTableAssociation   CREATE_IN_PROGRESS
2015-12-12 02:36:31 UTC   PublicSubnetInternetRoute                   CREATE_COMPLETE
2015-12-12 02:36:31 UTC   PublicUsEast1cSubnetRouteTableAssociation   CREATE_IN_PROGRESS
2015-12-12 02:36:32 UTC   PublicUsEast1cSubnetRouteTableAssociation   CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:32 UTC   PublicUsEast1eSubnetRouteTableAssociation   CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 02:36:43 UTC   PublicUsEast1aSubnetRouteTableAssociation   CREATE_COMPLETE
2015-12-12 02:36:45 UTC   PublicUsEast1dSubnetRouteTableAssociation   CREATE_COMPLETE
2015-12-12 02:36:48 UTC   PublicUsEast1eSubnetRouteTableAssociation   CREATE_COMPLETE
2015-12-12 02:36:48 UTC   PublicUsEast1cSubnetRouteTableAssociation   CREATE_COMPLETE
2015-12-12 02:36:50 UTC   training-vpc                                CREATE_COMPLETE
2015-12-12 03:01:43 UTC   training-vpc                                UPDATE_IN_PROGRESS   User Initiated
2015-12-12 03:01:59 UTC   CfnUser                                     CREATE_IN_PROGRESS
2015-12-12 03:01:59 UTC   ExampleSecurityGroup                        CREATE_IN_PROGRESS
2015-12-12 03:02:00 UTC   CfnUser                                     CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 03:02:01 UTC   CfnUser                                     CREATE_COMPLETE
2015-12-12 03:02:04 UTC   CfnKeys                                     CREATE_IN_PROGRESS
2015-12-12 03:02:05 UTC   CfnKeys                                     CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 03:02:06 UTC   CfnKeys                                     CREATE_COMPLETE
2015-12-12 03:02:16 UTC   ExampleSecurityGroup                        CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 03:02:16 UTC   ExampleSecurityGroup                        CREATE_COMPLETE
2015-12-12 03:02:18 UTC   ExampleAllSecurityGroupEgress               CREATE_IN_PROGRESS
2015-12-12 03:02:18 UTC   ExampleSshSecurityGroupIngress              CREATE_IN_PROGRESS
2015-12-12 03:02:18 UTC   ExampleEc2Instance                          CREATE_IN_PROGRESS
2015-12-12 03:02:19 UTC   ExampleHttpSecurityGroupIngress             CREATE_IN_PROGRESS
2015-12-12 03:02:19 UTC   ExampleSshSecurityGroupIngress              CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 03:02:19 UTC   ExampleHttpSecurityGroupIngress             CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 03:02:19 UTC   ExampleAllSecurityGroupEgress               CREATE_IN_PROGRESS   Resource creation Initiated
2015-12-12 03:02:19 UTC   ExampleAllSecurityGroupEgress               CREATE_IN_PROGRESS            Resource creation Initiated
2015-12-12 03:02:19 UTC   ExampleHttpSecurityGroupIngress             CREATE_COMPLETE
2015-12-12 03:02:20 UTC   ExampleEc2Instance                          CREATE_FAILED                 Invalid availability zone: [us-west-2a]
2015-12-12 03:02:20 UTC   ExampleAllSecurityGroupEgress               CREATE_COMPLETE
2015-12-12 03:02:23 UTC   training-vpc                                UPDATE_ROLLBACK_IN_PROGRESS   The following resource(s) failed to create: [ExampleEc2Instance].
2015-12-12 03:02:33 UTC   training-vpc                                UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS
2015-12-12 03:02:35 UTC   ExampleHttpSecurityGroupIngress             DELETE_IN_PROGRESS
2015-12-12 03:02:36 UTC   ExampleEc2Instance                          DELETE_COMPLETE
2015-12-12 03:02:36 UTC   ExampleSshSecurityGroupIngress              DELETE_IN_PROGRESS
2015-12-12 03:02:36 UTC   ExampleAllSecurityGroupEgress               DELETE_IN_PROGRESS
2015-12-12 03:02:37 UTC   ExampleSshSecurityGroupIngress              DELETE_COMPLETE
2015-12-12 03:02:37 UTC   ExampleHttpSecurityGroupIngress             DELETE_COMPLETE
2015-12-12 03:02:39 UTC   ExampleAllSecurityGroupEgress               DELETE_COMPLETE
2015-12-12 03:02:39 UTC   CfnKeys                                     DELETE_IN_PROGRESS
2015-12-12 03:02:40 UTC   CfnKeys                                     DELETE_COMPLETE
2015-12-12 03:02:41 UTC   ExampleSecurityGroup                        DELETE_IN_PROGRESS
2015-12-12 03:02:42 UTC   CfnUser                                     DELETE_IN_PROGRESS
2015-12-12 03:02:42 UTC   ExampleSecurityGroup                        DELETE_COMPLETE
2015-12-12 03:02:43 UTC   CfnUser                                     DELETE_COMPLETE
2015-12-12 03:02:45 UTC   training-vpc                                UPDATE_ROLLBACK_COMPLETE
[FATAL]: Update of stack training-vpc: FAILED
ERROR: RuntimeError:

FAIL! Why is it trying to us-west-2a for ec2 instance when I clearly set us-east-1 ?

oh, because example.rb is using hardcoded us-west-2a…

let’s change that, emacs sparkleformation/example.rb

diff --git a/sparkleformation/example.rb b/sparkleformation/example.rb
index f0a8155..fd89915 100644
--- a/sparkleformation/example.rb
+++ b/sparkleformation/example.rb
@@ -1,7 +1,7 @@
     example_ec2_instance do
       type 'AWS::EC2::Instance'
       properties do
-        availability_zone 'us-west-2a'
+        availability_zone zone

That should work. Now update again: be sfn update training-vpc --file sparkleformation/example.rb

now what?!

2015-12-12 03:15:40 UTC   ExampleEc2Instance                          CREATE_FAILED                                  The image id '[ami-e5b8b4d5]' does not exist

oh. This is not the ami you are looking for. Every. Time.

Why is this part of ec2 still such a PITA?

(fiercly googling “e5b8b4d5”) ….

Well hello, Ubuntu 14.04.2 LTS (Trusty Tahr) 64bit ebs

Next stop, Amazon EC2 AMI Locator, searching us-east-1. Gotcha! you sneaky ami-7388cd19

Sure, in a perfect world I could just fire up pry and do this kind of junk:

~/s/sparkle_formation ❯❯❯ pry
[1] pry(main)> require 'aws-sdk-core'
=> true
[2] pry(main)> ec2 = ::Aws::EC2::Client.new
=> #<Aws::EC2::Client>
[3] pry(main)> ec2.what? # I have no idea.

But we live in the real world of hostile amis that don’t love you. You’re on your own. Find it yourself and edit that file. (but at least we have the comfort of .tuesday? and .saturday? methods in ::Aws::EC2::Client, right?)

Now, let’s try that update again.

~/s/sparkleformation-workshops ❯❯❯ be sfn update training-vpc --file sparkleformation/example.rb

(lots of cloudformation output)

But this looks good, right?

2015-12-12 03:56:35 UTC   ExampleEc2Instance                          CREATE_IN_PROGRESS                             Received SUCCESS signal with UniqueId i-1dc9f7ad
2015-12-12 03:56:38 UTC   ExampleEc2Instance                          CREATE_COMPLETE

yea! maybe there’s an instance somewhere in mah clouds now? ok, I guess I’ll let this finish…

2015-12-12 04:00:06 UTC   InternetGatewayAttachment                   DELETE_FAILED                                  Network vpc-62950a06 has some mapped public address(es). Please unmap
those public address(es) before detaching the gateway.
2015-12-12 04:00:13 UTC   InternetGateway                             DELETE_IN_PROGRESS
2015-12-12 04:01:03 UTC   PublicUsEast1aSubnet                        DELETE_FAILED                                  The subnet 'subnet-bb0be8e3' has dependencies and cannot be deleted.
2015-12-12 04:01:06 UTC   Vpc                                         DELETE_IN_PROGRESS

huh?! wait, what!!? why am I deleting this vpc and testing instances? I didn’t ask for that but guess I’ll wait and see what happens.

2015-12-12 04:04:24 UTC   Vpc                                         DELETE_FAILED                                  The vpc 'vpc-62950a06' has dependencies and cannot be delete

Is that good or bad? I didn’t ask you to delete, but maybe you are trying to save me from myself. I give up, it’s time for bed.

bundle exec destroy training-vpc

Footnote: In all seriousness, none of this is ever easy and all software is terrible. The alternatives are far, far worse and you should just be using SparkleFormation

Update: 2015-12-14

The actual source of my issue here was not SparkleFormation. I was trying to create a new stack but the update command is intended to update an existing stack (Surprise!).

What worked perfectly as advertised was to actually use:

bundle exec sfn create example-stack --apply-stack training-vpc --file sparkleformation/example.rb

This automatically takes the stack outputs from the previously created vpc, training-vpc and uses it as inputs for the new example-stack created from the template example.rb

OS Install using VirtualBox raw disk access

Recently I had a need to create a bootable sd card containing OpenBSD. Since my day to day machine is a MacBook Air with no cdrom drive this initially seemed difficult. It turned out to be much easier than I expected thanks to VirtualBox raw disk access.

Raw hard disk access can be dangerous, mainly because there’s a risk you use the wrong device and blow away things you actually need, so be careful and make sure you know you’re using the right physical device.

The disk has to be connected but not mounted and the Mac tries to repeatedly automount the sdcard, if you encounter an error that may be the case. The command to unmount is simply:

diskutil unmountDisk /dev/disk6

A raw disk vmdk needs to be created which can then be attached to the VM to give it direct access to the disk. With Mavericks the disk is automatically owned by root each time it’s mounted but in order to create the raw disk vmdk your user needs to have full access to the disk. In my case the device was /dev/disk6 so it just required adjusting permissions:

sudo chown sme /dev/disk6

It’s not enough to just chown the resulting file, since it’s basically just a pointer to the raw disk the user VirtualBox runs as needs full device access.

Next to create the raw disk vmdk (the magic command):

VBoxManage internalcommands createrawvmdk -filename sdcard.vmdk -rawdisk /dev/disk6

This creates a tiny file, sdcard.vmdk, that can be attached to a VirtualBox VM to give it full access to /dev/disk6. Next I created a new OpenBSD VM, mounting the OpenBSD 5.4 iso and attached the raw disk vmdk as the only storage. This is done by selecting ‘Choose existing disk’ during the VM creation disk selection phase.

From here I was able to do a standard OpenBSD install and the VM saw the raw disk as the only available HD. After finishing the install I was able to ‘option’ boot to select the sdcard as the startup disk and run OpenBSD from the sdcard on my MacBook Air.

For more info check out this helpful SuperUser post

Email in emacs

I genuinely dislike email, yet it’s a necessary part of working and communicating these days. I suppose there is occasional value found in the community of certain mailing lists, but generally the way email is used today is simply more of a distraction or interruption.

A while back Steve Losh described how to setup Mutt the way he likes. It was an interesting read because he was detailed, technical and clearly prefers very customizable tools. I agree with him, and although I’ve long since abandonded Mutt I was motivated to describe my version of powerful, customizable, terminal based email management.

This isn’t intended to say that what I use is inherently better than the setup Steve described, it’s simply what works for me. Much of the setup is similar. I used Mutt for many years before giving Sup a try, and recently settling on the configuration I describe here. Sup is still actively developed and a great standalone mail client.

I do use Gmail and while the Gmail web interface has a great set of keybindings for most everything, they aren’t customizable so it’s a whole new set of bindings to learn. That’s not a terrible thing, but I spend most of my day in a text editor where the bindings (and subsequent muscle memory) I know are very different than those available in the Gmail web interface.

Several times in the past I’ve searched for a fast, configurable way to use emacs for accessing my email. For one reason or another the existing options were lacking in my opinion and didn’t fit my needs. However, a little over 2 years ago I stumbled across Notmuch, which is billed as a: {“fast, global search and tag based email reader to use within your text editor or in a terminal”}. This sounded like exactly what I was looking for.

It’s essentially a minimal interface to a Xapian index of your mail so powerful searching is easy, but not limited.

I also primarily use OS X so the basic design is very similar to what Steve mentions in his Overview, the difference is that I’ve removed Mutt from the picture and substitute the notmuch emacs integration.

Offlineimap and msmtp setup is essentially the same and I have two accounts configured, one personal and one for work both of which are google accounts.


If you use OS X, it’s easily available using homebrew:

brew install notmuch

And from emacs, assuming you have melpa setup, notmuch is only an M-x package-install away.

This gist has the settings I use in my emacs load-path to setup custom identities for my personal and work email accounts and configure how notmuch works.

There’s plenty of additional customization available depending on how deep the rabbit hole you choose to go. This setup has worked well for me up to this point and still allows me to use the gmail web interface in a pinch.

Fixing emacs bindings in iTerm2

I spend a decent amount of time in org-mode, using Emacs + iTerm2 on a Macbook Air. I fixed a recent annoyance I encountered when using Emacs org-mode to track time for a project.

Org-mode has a handy time tracking feature and as the manual points out both C-S-<up/down> S-M-<up/down> can be used to call various functions for adjusting recorded time. In particular I wanted to use org-timestamp-down to add some time for a CLOCK item I had forgotten to start before a Skype call.

Obviously though, I hadn’t used S-M-down previously because when I did instead of adjusting the time, I only got the raw escape code .

I tried adjusting the settings in iTerm2 to no avail, so after a bit of a meandering search down the emacs rabbit hole I found a solution that worked well for me.

(define-key input-decode-map "\e[1;10A" [M-S-up])
(define-key input-decode-map "\e[1;10B" [M-S-down])
(define-key input-decode-map "\e[1;10C" [M-S-right])
(define-key input-decode-map "\e[1;10D" [M-S-left])

(define-key input-decode-map "\e[1;3A" [M-up])
(define-key input-decode-map "\e[1;3B" [M-down])
(define-key input-decode-map "\e[1;3C" [M-right])
(define-key input-decode-map "\e[1;3D" [M-left])

One handy tip in discovering these keybindings was to use cat to display the raw keycodes:

$ cat

This can also be done use C-q in emacs if you prefer, both will give you the results you need.