<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[_canham]]></title><description><![CDATA[Things that I find interesting and I think you might too]]></description><link>https://blog.canh.am/</link><image><url>https://blog.canh.am/favicon.png</url><title>_canham</title><link>https://blog.canh.am/</link></image><generator>Ghost 3.13</generator><lastBuildDate>Thu, 26 Feb 2026 17:38:03 GMT</lastBuildDate><atom:link href="https://blog.canh.am/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Geemo is shutting down]]></title><description><![CDATA[Geemo has been a major part of our lives for the past two years and it’s challenging to let go of something that so much energy and passion has gone into. Carl and I leave Geemo proud of what we were able to build and the communities that we were able to impact.]]></description><link>https://blog.canh.am/geemo-is-shutting-down/</link><guid isPermaLink="false">5e8e641bb6ba1d0db0c7221d</guid><category><![CDATA[Startup]]></category><category><![CDATA[Product Management]]></category><dc:creator><![CDATA[Matt Canham]]></dc:creator><pubDate>Tue, 21 Apr 2020 05:39:04 GMT</pubDate><content:encoded><![CDATA[<p>The following is a duplicate of <a href="https://medium.com/geemo/geemo-is-shutting-down-3819ee7f55bc">the "official" post</a> on the Geemo blog. I'm posting it here so that those who follow along with my journey (now or in the future) can learn from this decision.</p><hr><p><em><strong>Carl and I have made the tough decision to shut down Geemo. We will continue to keep Geemo operational until Wednesday May 27 2020, at which point we will turn off the service permanently and delete all customer data from our database. All community mangers will have the opportunity to export their community data from Geemo prior to shut down.</strong></em></p><p>For the past 18 months, Geemo has been bringing together communities from around the globe, enabling them to collaborate and inspire action amongst their members. Geemo has seen many successful use cases: connecting university students with startup mentors, fostering international business relationships, inspiring humanitarian efforts across continents, and more. While Geemo has played a small part in the inspiring actions of our users, we are confident that without Geemo these communities will continue to thrive.</p><p>At the core of the communities that Geemo works with are some incredibly talented community managers that Carl and I have had the privilege of getting to know well. These community managers work tirelessly to drive the development of meaningful connections within their organisations, and to make a positive impact in people’s lives. To all of you who we were able to work with over the past 18 months, thank you for the passion you bring to your work.</p><p>Geemo was started as a university project and quickly grew into something much more. We had thousands of users from over 35 different countries, we traveled to China to compete in the world’s largest competition for startups, and we worked on Geemo full time over the 2018/2019 summer break while paying ourselves wages. We are very proud of what we have accomplished.</p><p>Managing a university workload while also being a founder was challenging. While we had early market interest, our limited ability to invest time and energy into Geemo resulted in a slow iteration cycle which prevented us from finding product–market fit. As a result Geemo remains a slow growing company, not the rocket ship that both Carl and I set out to build.</p><p>Now that both Carl and I have finished university it is time to reassess what our priorities are going forward. Carl has plans to travel the world visiting family and friends while continuing his role as Dev Lead for a New Zealand-based software company: Out There Business Solutions. Personally, I’ve moved to Sydney, Australia to begin a new role as an <a href="https://www.atlassian.com/company/careers/graduates/apm">Associate Product Manager with Atlassian</a>.</p><p>Carl and I both agreed that we are no longer able to give Geemo the attention that it requires. Support tickets are going far too long without a response, we haven’t released new features or bug fixes in months, and we struggle to find the time to talk to our users and understand how we could be improving Geemo — something that has always been very important to us. In the long-run we believe that shutting down Geemo now is the right decision for us as founders and for our users.</p><p>Geemo has been a major part of our lives for the past two years and it’s challenging to let go of something that so much energy and passion has gone into. Carl and I leave Geemo proud of what we were able to build and the communities that we were able to impact. We learned an immense amount about how to (and how not to) build product. Most importantly though, we built relationships with some of the most incredible people from around the world. We couldn’t have wished for anything more from this journey.</p>]]></content:encoded></item><item><title><![CDATA[How I incorporate randomness into my blog's stylesheet]]></title><description><![CDATA[How I use random colors to enhance my Ghost blog's stylesheet. Why I did it, and how you can do the same.]]></description><link>https://blog.canh.am/how-i-incorporate-randomness-into-my-blogs-stylesheet/</link><guid isPermaLink="false">5d1d3e60b6ba1d0db0c71fe8</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Matt Canham]]></dc:creator><pubDate>Fri, 05 Jul 2019 22:00:00 GMT</pubDate><content:encoded><![CDATA[<p>When investigating the technology that would power my new blog (the one that you are reading) I had two key objectives in mind:</p><ol><li>I wanted the blog to be beautiful and in order to achieve this I needed technology that had easily customizable styles</li><li>I wanted the blog to be easy, meaning that I wanted to be able to write, distraction free, and not have to muck around with settings or styles after the initial setup</li></ol><p>I chose to go with Ghost—the blogging platform. Ghost’s theming system gave me the flexibility that I needed to customize this blog’s style, and Ghost’s API provided a sensible structure that allowed for a beautiful publishing experience.</p><p>When considering the look and feel of the blog I had an idea: to give every post its own unique color. In my mind, having a unique color for each post would keep the theme interesting and provide a visual connection with any given post. Rather than using cheesy stock photos, I’d use colors—simple. My initial approach to this problem was to manually specify colors for each post and then have the theme render them. There were two problems with this approach:</p><ol><li>Ghost doesn’t currently allow for custom attributes on posts, so I would have had to find another way to store and retrieve the color associated with a post—that would complicate things.</li><li>I’m terrible at finding colors that work together. Over time, the task of choosing a color for every post I write would be time-consuming and boring (at least for me). In other words, selecting colors manually would go against the second reason that I went with Ghost: it’s easy.</li></ol><p>I considered whether or not I should just drop the idea of seperate colors for different posts. Then my engineering side got the best of me and I decided to develop a solution. Here is what I set out to build:</p><ul><li>A stylesheet that can randomly generate a <a href="https://en.wikipedia.org/wiki/Pastel_(color)">pastel color</a> for each blog post</li><li>The colors that are generated are unique to each post and are consistent for everyone, meaning they won’t change between page loads</li><li>The colors will be used in the header of the blog post (scroll to the top of this page to see what I mean) and also <a href="https://blog.canh.am">on the homepage</a></li></ul><p>What follows is a description of how I built a random color generator into the stylesheet of the Ghost blog that you are currently reading.</p><h1 id="the-color-space"><strong>The Color Space</strong></h1><p>When playing around with colors in Sketch I found a pattern to the colours that I wanted to use.</p><figure class="kg-card kg-image-card"><img src="https://blog.canh.am/content/images/2019/07/Color-Changer.gif" class="kg-image"></figure><p>In the HSL color space I could keep saturation at 100%, luminance at 93%, and could adjust the hue (between 0 and 360) to generate a range of colors that worked well with the theme of my blog. The equation here is quite simple, fix saturation at 100%, fix luminance at 93%, and randomly select a value for hue and voilà, we have a candidate color.</p><h1 id="the-random-generator"><strong>The Random Generator</strong></h1><p>It's pretty clear at this point that all we need to do to create a color is to generate a random number between 0 and 360 and tell CSS to do its thing.</p><!--kg-card-begin: markdown--><pre><code>function generateColor() {
  var randomValue = Math.round(Math.random() * 360);
  var colorString = &quot;hsl(&quot; + randomValue + &quot;, 100%, 93%)&quot;;
  document.body.style.backgroundColor = colorString;
}
</code></pre>
<!--kg-card-end: markdown--><p>You can play around with this in the CodePen below.</p><figure class="kg-card kg-embed-card"><iframe id="cp_embed_RzJebM" src="https://codepen.io/_canham/embed/preview/RzJebM?height=300&amp;slug-hash=RzJebM&amp;default-tabs=js,result&amp;host=https://codepen.io" title="Random Pastel Background Color" scrolling="no" frameborder="0" height="300" allowtransparency="true" class="cp_embed_iframe" style="width: 100%; overflow: hidden;"></iframe></figure><p>The problem is that we don't want our colors to be randomly generated every time the user reloads the page, we want each post to have its own color that will always be the same. In order to do this we need to associate our "random" color selection with some sort of seed value. What we need is a <a href="https://en.wikipedia.org/wiki/Hash_function">hash function</a> that can map from a post, to an integer value for our hue.</p><p>A simple Google search revealed a <a href="https://stackoverflow.com/a/8831937/6702715">StackOverflow answer</a> that contained a simple and fast hashing function implemented in Javascript. I'll paste the code below, not because you need to understand it, but because you may be interested in how a hash function works under the hood.</p><!--kg-card-begin: markdown--><pre><code>String.prototype.hashCode = function() {
    var hash = 0;
    if (this.length == 0) {
        return hash;
    }
    for (var i = 0; i &lt; this.length; i++) {
        var char = this.charCodeAt(i);
        hash = ((hash&lt;&lt;5)-hash)+char;
        hash = hash &amp; hash; // Convert to 32bit integer
    }
    return hash;
}
</code></pre>
<!--kg-card-end: markdown--><p>What this handy piece of code does for us is add a method to all strings that will generate a hash code for that given string.</p><!--kg-card-begin: markdown--><pre><code>&quot;hello&quot;.hashCode() //99162322
&quot;hello!&quot;.hashCode() //-1220935281
&quot;hello&quot;.hashCode() //99162322 (notice that this didn't change)
</code></pre>
<!--kg-card-end: markdown--><p>We can then convert our outputs from our hash function to fall into the range between 0 and 360. To do this we use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs">Javascript's Math.abs function</a> to convert to a positive integer and then use the <a href="https://en.wikipedia.org/wiki/Modulo_operation">modulo operator</a> to limit the range of the integer to our required range.</p><!--kg-card-begin: markdown--><pre><code>var getHashedHue = function(value) {
    return Math.abs(value) % 361;
}

getHashedHue(&quot;hello&quot;.hashCode()) //315
getHashedHue(&quot;hello!&quot;.hashCode()) //69
getHashedHue(&quot;hello&quot;.hashCode()) //315 (notice that this didn't change)
</code></pre>
<!--kg-card-end: markdown--><p>We are now at a point where we can use a string to generate a random color, with the same string always generating the same color. Try it out in the pen below. Also, try copying and pasting the title of this post below and check that you get the same color that appears in the header of this page.</p><figure class="kg-card kg-embed-card"><iframe id="cp_embed_LKrgMB" src="https://codepen.io/_canham/embed/preview/LKrgMB?height=300&amp;slug-hash=LKrgMB&amp;default-tabs=js,result&amp;host=https://codepen.io" title="String Seeded Pastel Background Color" scrolling="no" frameborder="0" height="300" allowtransparency="true" class="cp_embed_iframe" style="width: 100%; overflow: hidden;"></iframe></figure><p>The final thing to do is to tie this back to the Ghost theme so that parts of the user interface change depending on the seed that is provided. I decided to use the title of a blog post to be the seed that will define the color that is generated. I implemented this using a custom data attribute called data-post-color-background-seed (yeah, perhaps that could have been shorter). Here's how it works. First, you can add the data attribute to any html element. In the example below, I've applied it to a h2 element on my homepage and have provided the post title as a seed.</p><!--kg-card-begin: markdown--><pre><code>&lt;h2
    class=&quot;post-title&quot;
    data-post-color-background-seed=&quot;{{title}}&quot;&gt;
    {{{title}}}
&lt;/h2&gt;</code></pre>
<!--kg-card-end: markdown--><p>On every page render a Javascript function runs that looks for elements with the custom data attribute and sets their background-color depending on the seed that is passed in. Note that below I am using JQuery, but this can easily be done in vanilla Javascript too.</p><!--kg-card-begin: markdown--><pre><code>function stylePage() {
    var elements = document.querySelectorAll(&quot;[data-post-color-background-seed]&quot;);

    $.each(elements, function(index, element) {
        var seed = element.dataset.postColorBackgroundSeed;
        var hash = getHashedHue(seed.hashCode());
        var hslString = &quot;hsl(&quot; + hash + &quot;, 100%, 93%)&quot;;
        
        $(element).css(&quot;background-color&quot;, hslString);
    })
};

stylePage();
</code></pre>
<!--kg-card-end: markdown--><p>I'm not convinced that this is the best way that I could be tying the color-generating logic into the hashing logic. Currently, Ghost's theming system does not allow for custom helpers (JS code) to be used (at least not easily), so this is something that is best handled on the frontend. In the future, when Ghost provides more customisation with themes, I'd like to move this logic to the backend. For now though, this rendering method seems to work.</p><h2 id="summary">Summary</h2><p>This solution is not perfect. An obvious issue is that the probability of any two posts having the same colour (a hash collision) is relatively high. In this case that's a tradeoff that I am willing to make as having two posts with the same color will not impact user experience. Also, when playing around with the CodePen above (the second one), I noticed that similar words have similar colors. For example, try the words "cat", "can", "cap" and "cab", they are all very similar colors. The same pattern is true for longer strings where all characters, apart from the final one, are identical. Again, I'm not too worried about this; I don't anticipate having too many blog post titles that are close to being identical. </p><p>As previously mentioned I'm not sold on the rendering logic required on the frontend. This is especially problematic when it comes to <a href="https://amp.dev/">AMP</a> versions of my posts, where custom JS code can not be executed. This is part of the reason that I've disabled AMP for the time being. Once Ghost has better support for adding custom logic to theme rendering I'll be moving this logic there.</p><p>Reflecting on my goals that I had when starting out: <em>"I wanted the blog to be beautiful"</em> and <em>"I wanted the blog to be easy"</em>, I believe that this approach to randomized styling has lead to an outcome that both looks great and is incredibly easy for me to maintain. I simply write a post and the title of the post is used to generate a color that I can be confident will look good. I challenge you to think about how your stylesheet could be enhanced with a touch of randomness.</p>]]></content:encoded></item><item><title><![CDATA[Why you should forget your mother's maiden name]]></title><description><![CDATA[Why are we still using knowledge-based authentication (KBA) questions to protect our online accounts?]]></description><link>https://blog.canh.am/why-you-should-forget-your-mothers-maiden-name/</link><guid isPermaLink="false">5d1af734b6ba1d0db0c71ec7</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Matt Canham]]></dc:creator><pubDate>Tue, 02 Jul 2019 20:22:21 GMT</pubDate><content:encoded><![CDATA[<p>It should come as no surprise that your mother's maiden name is no secret, nor is the name of your primary school, or the model of your first car. So why are we still using knowledge-based authentication (KBA) questions to protect our online accounts?</p><p>Knowledge-based authentication is something that the development community is slowly but surely killing off. I personally don't know any developers who have recently worked on a product where they implemented KBA questions. However, the actions of our past have left us with online services, typically legacy systems from larger organizations, that are still secured by questions which can be easily answered with a Google search or some form of social engineering.</p><blockquote>Social engineering (noun): the use of deception to manipulate individuals into divulging confidential or personal information that may be used for fraudulent purposes</blockquote><p>If you are interested enough to be reading this blog post you probably already have a hunch that KBA questions are a bad idea, and academic research agrees with you. <a href="https://dl.acm.org/citation.cfm?id=1408667">One study</a> found that 16% of security questions can be answered via publicly accessible information on social media. Some questions are more vulnerable than others, for example, "what is your mother's maiden name?" can often be answered by searching through the publicly available birth and marriage records.</p><p>When you add social engineering to the mix things are even more worrying. Using an email phishing campaign, researchers were able to demonstrate the ability to capture 92% of security question answers.</p><p>So you may be thinking to yourself: <em>sure, they're not all that secure, but security questions are a good way to recover an account when all else is lost, right?</em> In a <a href="http://static.googleusercontent.com/media/research.google.com/en/us/pubs/archive/43783.pdf">2015 paper</a>, Google found that only 60.8% of attempts to recover a password using security questions were successful. That's bad news for everyone; not only does using KBA make your accounts significantly less secure but there's also a 40% chance that it won't actually help you to recover your account when you have no options left. In that same paper, Google observed the significant improvements in account recovery rates that can be obtained from using email and SMS based recovery mechanisms.</p><p>The problems with KBA just keep going. For example, how many possible answers to the question "what is your favorite superhero?" do you think there are? Regardless of your superhero knowledge, we can agree that the answer space is trivially small, especially for the average user. How about the question "What is your favorite food?" Unsurprisingly, Google found that almost 20% of all English-speaking respondents to this question answered "pizza".</p><p>The key problem with knowledge-based authentication is that answers can either be secure or memorable; very rarely are they both. There is, however, a solution for those services that just refuse to do it any other way.</p><h2 id="so-how-can-we-use-security-questions-securely">So how can we use security questions securely?</h2><p>As we've seen, our answers to security questions can be either memorable or secure, but rarely both. In order to achieve both memorability and security, we will use a password manager.</p><p>If you are unsure of what a password manager is, this is where you should start. I won't go into <a href="https://www.troyhunt.com/only-secure-password-is-one-you-cant/">the details</a> but you really should be using a password manager. Assuming you have one, let's continue.</p><p>Essentially what we are going to do is treat answers to security questions like passwords. When we are asked "What is your mother's maiden name?" rather than answering with your mother's actual maiden name, you will answer with a randomly generated secure password. You'll then store this answer in your password manager. This way your answer is both hard to guess, and easy to remember (because if you use a password manager you won’t have to remember it at all).</p><p>Let's walk through this step by step. You are creating a new account with an online service and the following page shows up.</p><figure class="kg-card kg-image-card"><img src="https://blog.canh.am/content/images/2019/07/Security-Questions-Blank-Answers.png" class="kg-image"></figure><p>You know that if you answered these questions truthfully your account will be very insecure. You also know that if you don't answer truthfully it's going to be really difficult to recall the answers to these questions. So, the next step is to fire up your password manager of choice. I use <a href="https://1password.com/">1Password</a>, but any comparable manager should do the trick (<a href="https://www.lastpass.com/">Lastpass</a> has a free tier if you are just getting started).</p><!--kg-card-begin: markdown--><p><strong>1. Create a new section in your password manager's entry for the website you are currently creating an account with, and call this section &quot;Security Questions&quot;</strong></p>
<p><strong>2. For each of the security questions, record the question itself and then generate a unique password for each</strong></p>
<p><img src="https://blog.canh.am/content/images/2019/07/Security-Questions-Unmasked-1.png" alt="Security-Questions-Unmasked-1"><br>
<strong>3. Now, copy the randomly generated passwords from your password manager into the text fields on the website you are signing up to</strong></p>
<p><img src="https://blog.canh.am/content/images/2019/07/Security-Questions-1.png" alt="Security-Questions-1"></p>
<!--kg-card-end: markdown--><p>And that's it. You are done. In an ideal world, we would no longer have knowledge-based authentication in the form of security questions. The reality is though, that there are plenty of services out there that will require you to answer security questions in order to sign up. The good news is you now have a method of securing your online accounts that still insist on using knowledge-based authentication.</p>]]></content:encoded></item></channel></rss>