Stavros' Stuff Latest Posts Latest posts on Stavros' Stuff. en-us Stavros Korokithakis The UX people https://www.stavros.io/posts/the-ux-people/ https://www.stavros.io/posts/the-ux-people/ <div class="pull-quote">Who are they?</div><p>Today, at work, I was debating with other engineers on what the wording of the message should be, when a patient tries to register an already-registered blood test kit. The specifics (or the people) aren&#8217;t really important here, but what struck a chord was when someone said &#8220;this is up to the UX people&#8221;.</p> <p>We don&#8217;t <em>have</em> &#8220;UX people&#8221;. &#8220;UX people&#8221; <em>don&#8217;t exist</em>.</p> <h2>The UX people</h2> <p>The unspoken assumption here is that <!-- break --> there&#8217;s a group of people in the company who are responsible for the UX, and they&#8217;re the ones who should decide what the error message should be. The problem with this way of thinking is that &#8220;UX&#8221; is shorthand for &#8220;making a product that&#8217;s easy to use/feels nice/does what you want&#8221;. UX is <em>definitely not</em> about making things pretty, you can have the ugliest UI in the world, but stellar UX.</p> <p>When you think about that, it&#8217;s clear that there <em>are</em> no &#8220;UX people&#8221;. You&#8217;re not supposed to do &#8220;eh, whatever&#8221; and throw it over the wall to someone who will double-check all the decisions you&#8217;ve made, and come to you and say &#8220;this is hard for users to use&#8221;. It&#8217;s the responsibility of <em>every single person</em> in a company (from designers to engineers to PMs to HR) to think about how what they do affects the user&#8217;s experience, and to try to improve it. Obviously, the closer you are to the end result, the more it is your job to think about UX, and to try to ensure it&#8217;s good, but there&#8217;s no single person that runs around being the &#8220;Let&#8217;s Make the Product Great&#8221; person.</p> <h2>We have designers, why not UX people?</h2> <p>You may have &#8220;UX experts&#8221;, people who are more experienced in UX and can point out improvements, but they don&#8217;t absolve you of responsibility, because UX isn&#8217;t an isolated aspect of a product. You can&#8217;t point to something and say &#8220;here&#8217;s the backend, and here&#8217;s the UI, and here&#8217;s the UX&#8221;. UX is the sum total of the behaviour that your product exhibits as the user interacts with it.</p> <p>There&#8217;s nobody to whom you can offload the responsibility of having to think about your user. <em>Especially</em> as a developer, all the interactions your user will have with what you&#8217;re building are your responsibility <em>the most</em>, because you&#8217;re the ultimate decisionmaker for <em>how the service will behave</em>. Of course, as I said above, this is a shared responsibility, but the responsibility increases the closer you are to the implementation.</p> <h2>Whose job is it?</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="swiss-army-phone.jpg" data-lightbox="gallery"><img src="swiss-army-phone-small.jpg"></a></div><span class="caption">The UX person was on holiday when designing the iPhone 13.</span></div><p>It&#8217;s very important to understand the above, especially in situations where you&#8217;re dividing up responsibilities. If you think of UX as some concrete aspect of the product, you might fall in the above trap, where UX is somehow not your responsibility, like the payments service isn&#8217;t your responsibility. This leads to the mindset that you can just do any old thing, and then a &#8220;UX person&#8221; will come along and correct you if you do it wrong, which is disastrous, because it results in a bad default.</p> <p>If you think that UX isn&#8217;t your responsibility, then you&#8217;re going to be making frustrating products until someone comes along, thinks about the user&#8217;s experience, and fixes some of it. This leads to mostly-frustrating products.</p> <p>If you think that UX is <em>everyone&#8217;s</em> responsibility, and think about how the user will perceive of the product, interact with it, feel while using it, then you make good products by default. Someone might <em>still</em> come along and propose improvements, but you&#8217;re going to start from a much better place.</p> <h2>Meraki</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="bridge.jpg" data-lightbox="gallery"><img src="bridge-small.jpg"></a></div><span class="caption">Close enough.</span></div><p>In Greek, there&#8217;s a word for this approach to building, &#8220;meraki&#8221;. Unfortunately, it&#8217;s extremely hard to translate accurately, but it mostly means &#8220;taking pride in one&#8217;s work, putting in the time to polish it, doing it well&#8221;. It&#8217;s more or less the opposite of the Chinese <a href="https://www.chinaexpatsociety.com/culture/the-chabuduo-mindset">&#8220;cha bu duo&#8221;</a> (roughly &#8220;eh, close enough&#8221;).</p> <p>It&#8217;s up to you how you want to make the things you make, but, personally, I find that spending a few minutes to think about how the user might use the stuff you make doesn&#8217;t take much effort, and usually leads to much better results. It&#8217;s the difference between a menu item requiring one click instead of three, which will add up to hours saved many people have to click the same menu item, many times a day.</p> <h2>Epilogue</h2> <p>These were just some random thoughts I had on the matter, and I&#8217;m glad the golden age of blogs is over because it means I can jot stuff down without stressing about whether it&#8217;s good or not, cha bu duo.</p> <p>In retrospect, I&#8217;m happy that my coworker said what he said, because hearing it bothered me, and led to me spending a bit of time trying to think about what exactly it was that bothered me, which led to the realization that UX isn&#8217;t something that can be isolated. I hope this article changes how you approach UX as well.</p> <p>As always, <a href="https://twitter.com/intent/user?screen_name=Stavros">Tweet</a> or <a href="https://mastodon.social/@stavros">toot</a> at me, or email me directly.</p> Wed, 11 Oct 2023 16:09:36 +0000 Spam spammers back https://www.stavros.io/posts/spam-spammers-back/ https://www.stavros.io/posts/spam-spammers-back/ <div class="pull-quote">Now with added ChatGPT</div><p>I&#8217;m writing this post sleepless and with a headache, which I find is the best way to write posts, because it removes all the verbal guardrails, so, be forewarned.</p> <p>Back in 2016, a year before recorded history, I stole a simple idea: What if I wrote a bot to reply to spammers, pretending to be interested in their wares, and wasting their time? After <a href="/posts/spamnesty-waste-spammers-time/">some creating this</a>, it turned out that it was possible, and <a href="https://spa.mnesty.com/">Spamnesty</a> was born.</p> <p>Spamnesty was cleverly disguised as a company, <a href="https://www.mnesty.com/">Mnesty, LLC</a>, Asia&#8217;s premier maritime logistics company, strategically located in the land-locked Mongolia. Of course, this didn&#8217;t tip spammers off that the whole thing was fake, because why would they even care to look at the site? This resulted in untold amounts of entertainment, whiling the small hours of the morning away, reading about the likes of the hapless <a href="https://spa.mnesty.com/conversations/hehkkbwb/">Abdullah Ishaq</a>, whose attempt of selling LedTrading.com to my bot did not <!-- break -->go well.</p> <h2>The eventual end</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="robot.jpg" data-lightbox="gallery"><img src="robot-small.jpg"></a></div><span class="caption">How it's made: Automated sales campaigns.</span></div><p>As with all good things, however, Spamnesty, too, had to come to an end. It didn&#8217;t, though it did become gradually less and less entertaining, as spammers decided that replying to people wasn&#8217;t economical, and opted to switch to scamming people with automated methods instead. This resulted in the emails being automated, and instead of highly-entertaining colloquies between a party with much to gain from the dialogue and a spammer, we ended up with bots spamming bots.</p> <p>As it turns out, this makes for <a href="https://spa.mnesty.com/conversations/baehezxx/">much less compelling reading</a>. Since spam didn&#8217;t end (that&#8217;s what I assume, anyway, as I haven&#8217;t opened my email since 2018), the world needed a new solution.</p> <h2>A new beginning</h2> <p>A few days ago, I received a spam email. To a layperson, it might have looked like a regular spam email, which they would have sent to the trash without a second thought. My trained eye, however, spotted the tiniest of details immediately: This email was in my inbox.</p> <p>Immediately I wondered how this email could have bypassed the spam filter, and inspected its raw headers.</p> <div class="highlight"><pre><span></span><span class="cs">X-Mail-from:</span><span class="cm"> spammer@aol.com</span> <span class="nt">Received:</span><span class="w"> </span>from<span class="w"> </span>mx1<span class="w"> </span>([<span class="mi">10.202.2.200</span>]) <span class="w"> </span>by<span class="w"> </span><span class="nf">compute4.internal</span><span class="w"> </span>(LMTPProxy);<span class="w"> </span><span class="nd">Fri, 28 Jul 2023 08:01:40 -0400</span> <span class="nt">Received:</span><span class="w"> </span>from<span class="w"> </span><span class="nf">mx1.messagingengine.com</span><span class="w"> </span>(localhost<span class="w"> </span>[<span class="mi">127.0.0.1</span>]) <span class="w"> </span>by<span class="w"> </span><span class="nf">mailmx.nyi.internal</span><span class="w"> </span>(Postfix)<span class="w"> </span>with<span class="w"> </span><span class="k">ESMTP</span><span class="w"> </span>id<span class="w"> </span>85CC123C0072 <span class="w"> </span>for<span class="w"> </span><span class="nl">&lt;info@me.com&gt;</span>;<span class="w"> </span><span class="nd">Fri, 28 Jul 2023 08:01:39 -0400</span><span class="w"> </span>(EDT) <span class="nt">Received:</span><span class="w"> </span>from<span class="w"> </span><span class="nf">mailmx.nyi.internal</span><span class="w"> </span>(localhost<span class="w"> </span>[<span class="mi">127.0.0.1</span>]) <span class="w"> </span>by<span class="w"> </span><span class="nf">mx1.messagingengine.com</span><span class="w"> </span>(Authentication<span class="w"> </span>Milter)<span class="w"> </span>with<span class="w"> </span><span class="k">ESMTP</span> <span class="w"> </span>id<span class="w"> </span><span class="nf">A6760957722.23F8823C008C</span>; <span class="w"> </span><span class="nd">Fri, 28 Jul 2023 08:01:39 -0400</span> <span class="nt">ARC-Seal:</span><span class="w"> </span>i=1;<span class="w"> </span>a=rsa-sha256;<span class="w"> </span>cv=none;<span class="w"> </span>d=<span class="nf">messagingengine.com</span>;<span class="w"> </span>s=fm3;<span class="w"> </span>t= <span class="w"> </span>1690545699;<span class="w"> </span>b=gC4GEHuuchzN5TtgSnkL6VmZEJgt+w5iltASfKE/cOgTVQg330 <span class="w"> </span>O9BbJ8suAAvWBBDhvQo2OWDEKEy0IVqttJT84rmjqotrneVpWQMD6ASEnH4/z9fq <span class="w"> </span>UimpGIGT4bPv1gpgbxo28sbmT0BxT7qdOtBjO7CuduW9RmUYSpRTq7dlazgqAFOV <span class="w"> </span>qlBdqganQiUMCdMlkr6ZXA3qqTk3mdpDgBPUhskO2NpTRo/5zqPEbawNgfOGv4jG <span class="w"> </span>1xpwwD3okjmDswRq96ZjJu0l6W816vzIh5vErLbzOr6zZ1uVq27vy2ToEmeFs13S <span class="w"> </span>S2nlO2OuEZF2Wv5/kfnl8kHrjrLnli6qfqEQ== </pre></div> <p>They looked like gibberish, so I stopped inspecting, and looked at the body of the message, where I noticed something equally surprising:</p> <p>This email was <em>written by a person</em>.</p> <h2>The email that was written by a person</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="spammer.jpg" data-lightbox="gallery"><img src="spammer-small.jpg"></a></div><span class="caption">Peter was taken entirely aback when he realised the job posting was not for the canned meats industry.</span></div><p>This spammer was an <strong>actual person</strong>, with a name and a signature and, presumably, flesh and tooth enamel. Yes, the email was sent without my consent, without solicitation, and automatically, but there was an actual, human person at the other end, who would presumably reply if I indicated the slightest bit of sales interest.</p> <p>Due to my line of work, I am familiar with a little-known tool that is basically a person in a computer: ChatGPT. The wheels in my head immediately started turning: I would use this person-in-a-computer to reply to this person-next-to-a-computer, and potentially waste his time.</p> <p>Without any delay, and with exceeding haste, I began asking ChatGPT to write the code for me, creating what ChatGPT named <a href="https://github.com/skorokithakis/spamgpt"><strong>SpamGPT</strong></a>.</p> <h2>SpamGPT</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="robots.jpg" data-lightbox="gallery"><img src="robots-small.jpg"></a></div><span class="caption">Two spammer robots, spamming each other.</span></div><p>SpamGPT is a simple program: It runs at a random minute every hour, opens my email, looks in a folder I&#8217;ve named <code>SpamGPT</code>, and replies to any emails in there that it hasn&#8217;t already replied to.</p> <p>All I have to do is find spam messages that looks like they were written by a person (mostly sales emails), and move them to the <code>SpamGPT</code> email folder, and SpamGPT will eventually reply to them. Its instructions are that it should pretend to be interested in whatever the spammer is selling, and do whatever it can to waste their time. This includes trying to set up meetings, pretending to have issues with its computer, insist that payment details are wrong, or that it has sent the payment, and whatever else it can conceive of.</p> <p>The result is as entertaining as ever.</p> <h2>Why not add ChatGPT to Spamnesty?</h2> <p>Many people have suggested that I add ChatGPT support to Spamnesty, as a natural next step, but there were two problems with this:</p> <ol> <li>As I said above, most spam is bots, and ChatGPT would compose long, detailed, and thoughtful replies to spam, only to receive a canned message back. Computer program or not, I couldn&#8217;t do this to something that can think more coherently than me.</li> <li>The amount of spam that is sent is massive, and the costs for having ChatGPT to reply to all of it (usually ineffectively, as per point #1) would be too much for me to pay.</li> </ol> <p>If we&#8217;re going to waste spammers&#8217; time and entertain ourselves at the same time, there needs to be a manual curation step. There needs to be someone at least glancing at the emails, making sure that they&#8217;re worth responding to. This is what SpamGPT does much better than Spamnesty.</p> <p>The other added benefit is that every user runs it for their own mailbox, so I neither get to see your email, nor pay for your spam countermeasures, which suits me fine.</p> <h2>Wider reach</h2> <p>Of course, one thing that Spamnesty had but SpamGPT was missing was the ability for other people to read the conversations. I couldn&#8217;t keep such entertaining material for myself, not when the answer lay, as always, a few lines of code away. So, I rolled up my sleeves, and got to work, asking ChatGPT to write the code to make the above a reality.</p> <p>Thus, <a href="https://thespamchronicles.stavros.io/welcome/">the Spam Chronicles</a> were born.</p> <p>The Spam Chronicles are a collection of all the emails that SpamGPT has responded to. It contains such gems as the conversation of <a href="https://thespamchronicles.stavros.io/emails/2023-08-02-meeting-now-with-advids/">the guy who tried for a week to set up a meeting with a bot</a>, or <a href="https://thespamchronicles.stavros.io/emails/2023-07-26-re-addthis-users/">the guy who tried in vain to get the bot to pay an invoice, and got told to &#8220;find more meaningful ways to contribute to society&#8221;</a>.</p> <p>I will keep updating this list as I get more messages, and, if enough people use SpamGPT, I might add a way to submit your spam emails to the Spam Chronicles automatically, for everyone to read. Hopefully I won&#8217;t get people submitting sales emails to the Spam Chronicles, even though spamming the anti-spam site would be delightful irony.</p> <h2>Epilogue</h2> <p>If you want to run SpamGPT for yourself, but missed the link above, you can find it on GitHub:</p> <p><a href="https://github.com/skorokithakis/spamgpt">https://github.com/skorokithakis/spamgpt</a></p> <p>Email is a sufficiently complicated and brittle protocol that I don&#8217;t have much faith that SpamGPT will work for all email servers, but it works for me, and if it doesn&#8217;t work for you, we&#8217;ll figure something out. In the mean time, if you do run SpamGPT, please drop me a line, I&#8217;d be interested to know how it works for you, and if you&#8217;re getting good results.</p> <p>I hope you enjoy messing with spammers, and make spamming expensive enough that they won&#8217;t bother us ever again, even if it means lining OpenAI&#8217;s pockets.</p> <p>As always, you can <a href="https://twitter.com/intent/user?screen_name=Stavros">Tweet</a> or <a href="https://mastodon.social/@stavros">toot</a> at me, or email me directly.</p> Sat, 05 Aug 2023 18:37:08 +0000 Use your Wii Balance Board as a scale (again) https://www.stavros.io/posts/use-your-wii-balance-board-as-a-scale-again/ https://www.stavros.io/posts/use-your-wii-balance-board-as-a-scale-again/ <div class="pull-quote">We did it again, internet</div><p>If you&#8217;ll recall, <a href="/posts/your-weight-online/">once upon a time I managed to get my Wii Balance Board to connect to my computer and function as a scale</a>. At some point throughout the years, Linux changed, and that method broke. It would still measure your weight fine, but you could no longer activate the balance board by tapping its front button, you had to flip it over, remove the battery compartment lid and press the red button every time. This was too much of a hassle, so I stopped using the board, hoping that a solution to this problem would arrive one day to liberate me from the scourge of adipose tissue.</p> <p>This day is today (well, a few months ago, actually, but I couldn&#8217;t be arsed writing about it until today, so). I received an email from a reader called Jawaad Mahmood, who had read my balance board article and spent a bit of time figuring out how to get the balance board to work, and packaged his work into <a href="https://github.com/jmahmood/bbev">a library called bbev</a>.</p> <p>I was initially sceptical because many people contacted me throughout the years, but nobody managed to get rid of the red button requirement. Talking to Jawaad for a bit, though, he confirmed that he could painlessly get the board to pair with his computer with the front button. Apparently something had changed in the Linux Bluetooth stack, and made pairing possible again, and he managed to figure out how.</p> <p>This was great news! However, <!-- break --> I wanted to make a few small changes to suit my use case, like disconnecting from the board after the measurement was done, running a command with the weight afterwards, etc.</p> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="wiiboard.jpg" data-lightbox="gallery"><img src="wiiboard-small.jpg"></a></div><span class="caption">What MidJourney thinks a balance board looks like.</span></div><p>I spent a bit of time writing some code, using most of the measurement code from Jawaad&#8217;s library, added the features I needed, and released it for your enjoyment:</p> <p><a href="https://pypi.org/project/weii/">https://pypi.org/project/weii/</a></p> <p>It&#8217;s a command-line application, but it&#8217;s fairly simple to use. You run <code>weii</code>, turn the balance board on, step on it, and a few seconds later you have your measurement and the board turns off.</p> <p>Pretty convenient!</p> <p>There isn&#8217;t really much more to say here, I just wanted to let you all know that we can finally once again weigh ourselves using our balance boards.</p> <h2>Epilogue</h2> <p>I&#8217;d like to thank Jawaad for figuring out how to solve the dreaded pairing problem, and for open-sourcing his code. I&#8217;m really glad I can finally use my board to conveniently weigh myself and <a href="/misc/weight/">post my weight online</a>, with one simple command.</p> <p>I hope you find <code>weii</code> as useful as I do!</p> <p>If you have any feedback, please <a href="https://twitter.com/intent/user?screen_name=Stavros">Tweet</a> or <a href="https://mastodon.social/@stavros">toot</a> at me, or email me directly.</p> Sat, 10 Jun 2023 22:53:36 +0000 Clearing up some misconceptions about Passkeys https://www.stavros.io/posts/clearing-up-some-passkeys-misconceptions/ https://www.stavros.io/posts/clearing-up-some-passkeys-misconceptions/ <div class="pull-quote">I love passkeys so much</div><p>I am <em>unreasonably</em> excited about <a href="https://developer.apple.com/passkeys/">passkeys</a>, I&#8217;ve <a href="/posts/authentication/">long</a> been looking for a better/more convenient way than passwords to do authentication, and I think passkeys are finally it.</p> <p>However, whenever I see passkeys mentioned (for example on the recent <a href="https://tailscale.com/blog/passkeys/">Tailscale post</a> about them), there are always a lot of misconceptions that surface in the debate. I&#8217;d like to clear some of them here, and hopefully explain a bit better what passkeys are.</p> <h2>A bit of backstory</h2> <p>Passkeys are a user-friendly name for, and an implementation of <a href="https://en.wikipedia.org/wiki/WebAuthn">WebAuthn</a>, which in turn is part of the <a href="https://en.wikipedia.org/wiki/FIDO_Alliance#FIDO2">FIDO2 project</a>. All that is basically a way to say that passkeys are an open standard, developed by a consortium of companies that want to make authentication more secure and more usable. My personal opinion is that passkeys are a great solution to that problem, and that&#8217;s why I&#8217;m so excited about them.</p> <p>At their core, passkeys are <em>just a way for a website to ask your browser for authentication</em>. That&#8217;s it, they aren&#8217;t tied to a specific piece of hardware or a way for that hardware to work. I&#8217;ll expound more on this further on.</p> <p>I want to lay out some common misconceptions about passkeys that I&#8217;ve been seeing, and <!-- break -->why they&#8217;re not accurate. If I&#8217;ve made a mistake somewhere, or have an inaccuracy, please let me know.</p> <h3>They&#8217;re proprietary/owned by Google/Apple/Microsoft</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="passkeys1.jpg" data-lightbox="gallery"><img src="passkeys1-small.jpg"></a></div><span class="caption">Obligatory random borderline-relevant MidJourney image.</span></div><p>Passkeys are an open standard. They aren&#8217;t tied to a single company, and don&#8217;t rely on a single company&#8217;s implementation. I&#8217;ve implemented passkeys <a href="https://www.pastery.net">on my</a> <a href="https://www.deadmansswitch.net">sites</a> for a while now, and I didn&#8217;t need anyone&#8217;s permission to do so.</p> <p>Conversely, I have lots of passkeys-compatible physical devices (USB keys, phone, laptop, etc), and they didn&#8217;t have to get any company&#8217;s permission to implement that standard either.</p> <p>In fact, if you want, you can write your own passkeys-authenticating device, with whatever security parameters you want to use, and it will work on all passkeys-supporting sites.</p> <h3>They require connectivity</h3> <p>Passkeys are strictly offline. They don&#8217;t require an internet connection, a phone, SMS, or anything else. Obviously, you need an internet connection if you&#8217;re authenticating to a site (so you can talk to the site itself), but the <em>authentication itself</em> doesn&#8217;t need any kind of connectivity.</p> <h3>They&#8217;re hardware-backed</h3> <p>They <em>can</em> be hardware-backed, but they don&#8217;t have to be. Using a hardware key as your passkeys authenticator is the most secure option, as nobody can steal the key from the device, but it is less convenient than alternatives (e.g. you can&#8217;t back it up).</p> <p>The passkeys standard (WebAuthn) doesn&#8217;t mandate what you can use, it&#8217;s just a way to request some credentials. You can use your Apple device&#8217;s FaceID, or your phone&#8217;s secure chip, or just your password manager, to hold your passkeys, and it will work with every compatible site.</p> <h3>If someone steals my USB key, they get access to everything</h3> <p>Passkeys doesn&#8217;t mandate how the USB key secures the keys. Yubikeys use a PIN that wipes the key after ten wrong attempts, so someone stealing your USB key will still need to know the PIN, or have ten tries to guess it.</p> <p>That makes it very unlikely that someone can authenticate as you, and it&#8217;s certainly much harder to steal a physical USB key from someone than to just find out/steal their password.</p> <h3>They can&#8217;t be shared/backed up</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="keeper.jpg" data-lightbox="gallery"><img src="keeper-small.jpg"></a></div><span class="caption">Please stop staring at my hands, I'm just the gatekeeper.</span></div><p>That depends on what you choose to use as your authenticator. If you use a password manager, or some other software-based authenticator, there&#8217;s no reason why they can&#8217;t be shared/backed up. Keep in mind that making the keys readable reduces security (e.g. someone might steal your backup, and then they can log in everywhere), but that&#8217;s a choice you will need to make for yourself. Passkeys don&#8217;t enforce anything one way or the other.</p> <h3>Passkeys are less secure than X</h3> <p>Passkeys are at least equally as secure as anything else we&#8217;ve had, in the sense that the standard has taken care to make the protocol as secure as possible, against as many attacks as possible. Your <em>authenticator</em> can be as secure as you like it, it can be more secure than anything (in the case of hardware authenticators) or less secure than anything (in the case where you write your private key on a PostIt note on your screen).</p> <p>Passkeys don&#8217;t enforce anything one way or the other, it&#8217;s up to you to make your personal tradeoffs regarding your authenticator device.</p> <p>And yes, you can literally write your passkeys private key down on a PostIt note and stick it to your screen. It&#8217;ll still work, if your authenticator supports it and you don&#8217;t mind typing the long key in every time.</p> <h3>I can&#8217;t recover the keys if I lose the hardware</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="lock.jpg" data-lightbox="gallery"><img src="lock-small.jpg"></a></div><span class="caption">Who knows what this is? Looks cool, though.</span></div><p>That is a risk you&#8217;ll need to take if you&#8217;re using hardware authenticators. The fact that the key isn&#8217;t copiable means you only have one of it, so you should probably be enrolling multiple hardware authenticators on each account, or just switching to a software authenticator if you don&#8217;t care about the decreased security.</p> <p>Passkeys don&#8217;t care one way or the other, it&#8217;s up to you what security/usability tradeoffs you make. Passkeys are flexible enough to support either.</p> <h3>They require extra software, passwords don&#8217;t</h3> <p>Do you not use a password manager?</p> <h3>They&#8217;re more complicated than passwords</h3> <p>This one is actually true, passwords are very simple to use. However, the increased complexity comes with orders of magnitude increased security. Essentially, passkeys are two authentication factors in one.</p> <p>They can&#8217;t be phished, they can&#8217;t be lost in website breaches, they can&#8217;t be compromised by reading your network requests, an attacker can&#8217;t make a fake website and use it to steal your credentials. If you care about your security, and don&#8217;t just want something like &#8220;password123&#8221; that you can remember easily, passkeys are a no-brainer.</p> <h3>What about attestation?</h3> <p>Attestation is a way for a website to mandate that you can only authenticate to it with authenticators of a specific brand. It&#8217;s useful for, say, companies that want to restrict their employees to log in with a specific, company-vetted brand of device.</p> <p>There are some concerns that websites will restrict authentication to specific brands of authenticators, but I don&#8217;t think that will happen for the same reason that we don&#8217;t see sites that don&#8217;t let you visit them in a browser other than Chrome. It&#8217;s just not good business to be restricting your customers&#8217; options for no benefit to you.</p> <h2>Epilogue</h2> <p>I hope that&#8217;s cleared up some of the questions you had around passkeys, and shown you why they&#8217;re a much better idea for security than the traditional password/2FA authentication we&#8217;ve been using so far.</p> <p>If you&#8217;ve noticed anything wrong, or have any input on the above, please <a href="https://twitter.com/intent/user?screen_name=Stavros">Tweet</a> or <a href="https://mastodon.social/@stavros">toot</a> at me, or email me directly.</p> Thu, 08 Jun 2023 14:41:28 +0000 I made an e-ink display that shows my calendar https://www.stavros.io/posts/making-the-timeframe/ https://www.stavros.io/posts/making-the-timeframe/ <div class="pull-quote">Time to relax? Think again.</div><p>There&#8217;s an old saying I just made up, it goes &#8220;a man has a problem. Give him a solution, now he has two problems&#8221;, and that&#8217;s how I felt when I came across <a href="http://www.lilygo.cn/prod_view.aspx?TypeId=50061&amp;Id=1384&amp;FId=t3:50061:3">the LilyGo T5</a>, a beautiful e-ink display with an ESP32 microprocessor and an 18650 battery holder.</p> <p>I needed to find something to make with it.</p> <h2>The idea</h2> <p>I realized that one thing that&#8217;s missing from my life right now is more time pressure. I have a job, which got me most of the way there, but I&#8217;m bad at remembering the time of each of the twenty meetings I have every day. I really needed something that would allow me to see my daily calendar at a glance, and I realized that a 4.7&#8221; e-ink screen was the perfect thing for that use case, so I quickly started working on making this a reality.</p> <p>The result was&#8230;</p> <h2>The Timeframe</h2> <!-- break --> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="1.jpg" data-lightbox="gallery"><img src="1-small.jpg"></a></div><span class="caption">The Timeframe, in all its 3D-printed glory.</span></div><p>The Timeframe is a beautiful, battery-powered, high-resolution e-ink device that sits on my desk and reminds me of the inexorable grind that saps my creativity and drains me of the will to live. It comes in a sleek, minimal white exterior, reminds you that you&#8217;re selling a third of what precious little time you have on this Earth to the highest bidder, and lasts for months on a single charge.</p> <p>Unfortunately, my C++ skills are abysmal to nonexistent, so it was very doubtful that I could even make something like this. I&#8217;m just saying this to build up some suspense, because it&#8217;s obvious from the photos that I did build it in the end, and it is <em>glorious</em>.</p> <p>Let&#8217;s see whether I managed to actually make this, which I did, and how!</p> <h2>How I maybe made this</h2> <p>When the screen arrived, my first step was to figure out how to draw things on the screen. That meant I had to do some research.</p> <h3>Some research</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="2.jpg" data-lightbox="gallery"><img src="2-small.jpg"></a></div><span class="caption">A side view.</span></div><p>I looked into various alternatives:</p> <ul> <li>There is <a href="https://github.com/Xinyuan-LilyGO/lilygo-micropython">a custom MicroPython version</a> that looks official. This would have been a fantastic solution to my problem, as I&#8217;m much more familiar with Python than C++. Unfortunately, when I tried it, this would fail to draw anything around 80% of the time. I never managed to find out why, so this didn&#8217;t work.</li> <li>There&#8217;s an e-ink library called epdiy, and it includes a very helpful and full-featured <a href="https://github.com/vroland/epdiy/tree/master/examples/lilygo-t5-47-epd-platformio">example for the LilyGo T5 that uses PlatformIO</a>. Unfortunately, when I tried it, it had some vertical ghosting lines on the screen, which made the image look bad. The official demo that came with the display worked fine, so I knew it wasn&#8217;t a problem with the display. It&#8217;s a shame that this doesn&#8217;t work on my display, because this library is great. I opened an issue with the developer, who confirmed that there&#8217;s no issue with his T5, so maybe it&#8217;s something with my display? I never found out.</li> <li>The official repository <a href="https://github.com/Xinyuan-LilyGO/LilyGo-EPD47/">contains a working example that uses PlatformIO</a>. It uses a customized version of epdiy that is, unfortunately, fairly old, and doesn&#8217;t support all the nice things that epdiy does nowadays, but it works! It&#8217;s landscape-only, and in one orientation, whereas the latest epdiy has four, but I decided to try drawing some things and seeing what happens.</li> </ul> <p>Armed with a library that could, at least, change the color of some of the pixels in some ways, I then had to figure out how to actually show a calendar, or some sort of agenda there.</p> <h3>A man, a plan</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="3.jpg" data-lightbox="gallery"><img src="3-small.jpg"></a></div><span class="caption">The left side, with the USB socket.</span></div><p>The main alternatives I could see were two:</p> <p>One option was to fetch some details from Google Calendar in some way, either fetching an ICS file directly and parsing it on-device, or writing a small proxy that would run on my server and would pre-process the file in some way. Afterwards, I would have to come up with some sort of UI (with my dismal design skills), and draw it on the device, either by drawing lines and text manually, or by using some sort of pre-drawn images and showing those for parts of the elements.</p> <p>This alternative was very lightweight (it doesn&#8217;t require much of a server, as most of the processing and drawing happens on the device), but it would take a lot (A LOT) of C++ code that I wasn&#8217;t confident I&#8217;d be able to write, and design I wasn&#8217;t confident I&#8217;d be able to do. Also, it would require some things I wasn&#8217;t sure the <em>library</em> could do, e.g. showing things in landscape.</p> <p>The second option was to make the device download and display an image. This was very flexible, as I&#8217;d be able to show anything I wanted (e.g. photos, widgets, anything), and sounded much easier to program, but it would require an external program to somehow generate the images. It didn&#8217;t even require the display to be able to rotate orientations, because I could just send it an already-rotated image!</p> <p>This wasn&#8217;t a big problem, and it meant I could do other things with the device outside working hours, such as show photos of rolling hills and calming meadows, to distract me from the fact that I would eventually die never having lived. This was very appealing, so that&#8217;s what I decided to go with.</p> <h3>The display firmware</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="dithered.jpg" data-lightbox="gallery"><img src="dithered-small.jpg"></a></div><span class="caption">Dithered graphics actually look really good.</span></div><p>Armed with a basic knowledge of C++, a copy of CoPilot that had as much chance to generate working code as to write insulting comments, and a desire to retire early, I began writing the code. The example included a way to draw something called a framebuffer onto something called a screen, which seemed like it did what I wanted.</p> <p>A framebuffer is basically a big array of pixels, which you set to the colors you want to set on the screen. You then tell the screen to display this framebuffer, and voila! Your screen has exactly what you wanted to show on it.</p> <p>With the help of CoPilot, I stumbled my way onto a solution that allowed me to download a file over HTTP, and copy the bytes directly onto the framebuffer. When it worked, I was overjoyed, I finally managed to show some stuff on the screen! I was extremely excited when I saw the first photo appear on the screen, it was immensely gratifying.</p> <p>The device contacts an HTTP server, downloads the file, displays it on the screen, and then goes to sleep, where it uses basically zero battery. The duration it will sleep for is configurable, I configured it to wake up every half hour to check for a new image. I think this balances responsiveness and battery life pretty well.</p> <p>Now to make the images to show on the screen.</p> <h3>The server-side script</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="4.jpg" data-lightbox="gallery"><img src="4-small.jpg"></a></div><span class="caption">In its place on my desk.</span></div><p>The next thing I needed to do was write a small script to convert images into the raw bytes that the framebuffer could display. This was easy enough to do in Python, and I added some additional niceties like automatic resizing to the appropriate resolution, brightness adjustment, <span data-expounder="dithering">dithering</span>, etc.</p> <p><span data-expounded="dithering"> The display only supports four grayscale tones grayscale, but dithering is a good way to fake more tones. Dithering basically breaks up your image into dots, and spaces them out to look lighter or darker, depending on how dark your image is. Because this display is fairly high-resolution, the result looks really good. Also, because newspapers use dithering for graphics, the result looks very much like a newspaper. </span></p> <p>The script takes an image in various common formats (PNG, JPG, etc), and converts it to the raw bytes that the display can copy to the framebuffer. The resulting file can then be copied to an HTTP server, where the Timeframe can fetch it.</p> <p>Because I didn&#8217;t want the Timeframe to waste battery refreshing the screen every time (not to mention that redrawing the screen is distracting, because it flashes black and white a few times before it draws), I had the script output a second file next to the image. This second file contains the hash of the contents of the image, so I can read that small file and know whether the image has changed or not. I store the contents of the hash file on the Timeframe, and whenever I download the hash I compare it to the previous one. If they match, I know the file didn&#8217;t change, and can just go back to sleep.</p> <h3>The calendar view</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="calendar.png" data-lightbox="gallery"><img src="calendar-small.png"></a></div><span class="caption">A Google Calendar screenshot.</span></div><p>The calendar view is also fairly straightforward. A Python script uses <a href="https://www.selenium.dev">Selenium</a> to visit Google Calendar, switch to the current day, take a screenshot of the browser window, and upload it to the web server for the Timeframe to eventually download.</p> <p>The hardest part in this is having to log in to Google, because Google <em>really</em> doesn&#8217;t like to let you go for too long without reauthentication, which makes sense for security, but hates my use case. Still, though, having to log in every few weeks isn&#8217;t such a big problem, and the Timeframe can show me a fun image when Google logs me out, so I&#8217;ll never be left wondering why my Timeframe isn&#8217;t updating.</p> <p>I&#8217;m very pleased with how easy and repeatable it is to take screenshots this way, and I can programmatically update the Timeframe&#8217;s display with the exact part of Google Calendar&#8217;s view that I want.</p> <p>Sure, this whole thing is an unholy abomination of glued-together code and barely-functioning hardware, but damn if it isn&#8217;t tons of fun to make.</p> <h3>The case</h3> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="case.png" data-lightbox="gallery"><img src="case-small.png"></a></div><span class="caption">The case, in portrait and landscape.</span></div><p>Of course, no project would be complete without a nice-looking case, not to mention that the T5 on its own falls over pretty easily. To make the case, I just put in a lot of time and sweat making everything. There aren&#8217;t really many considerations here, except the fact that designing buttons is actually surprisingly tricky, as you need to have enough clearance that you will be able to push the buttons properly, but need to design standoffs so pressing the side of the case won&#8217;t press all your buttons.</p> <p>You also need to go through many iterations to get the fit right, as getting tight tolerances when 3D printing is always hit and miss. After a few hours of designing, printing, designing, printing, and waiting for retirement so I could be too tired and sick to enjoy anything, I had something that fit together reasonably well.</p> <p>One detail I liked was that I designed the base in such a way that it can be be placed both in a portrait and in a landscape orientation, at a 75° angle. The entire thing can also be 3D printed easily, with minimal supports!</p> <p>The battery compartment is press-fit, and can easily be removed to change the battery, if you ever need to. You probably won&#8217;t need to, because the T5 recharges the battery via the USB port, which is easily accessible on the left side of my case.</p> <h2>The downfall</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="broken.jpg" data-lightbox="gallery"><img src="broken-small.jpg"></a></div><span class="caption">I broke the screen :(</span></div><p>Unfortunately, when I was trying to install a spacer in the case so that the screen wouldn&#8217;t move up and down so easily, I must have pressed slightly too hard, and noticed that the screen had a massive blank spot on the left side. On further investigation, I realized that I had cracked the screen at the bottom, rendering it useless two days after I got it.</p> <p>I was devastated, not just because this project represented my hope to avoid getting fired, but also because it takes a month for the thing to arrive from China, and that&#8217;s the fastest way to get one!</p> <p>I guess now there&#8217;s nothing else to do but regularly turn my camera off during meetings, so I can shed a single silent tear over my grave mistake, and knowing it was me and only me who did this to myself. Verily, that is the burden I must now bear, and bear it I shall, for Amazon Prime is not available in my location.</p> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="repurposed.jpg" data-lightbox="gallery"><img src="repurposed-small.jpg"></a></div><span class="caption">Only <i>most</i> of it was broken.</span></div><p>At least, with some 3D-printing, coding, and careful hiding, I managed to repurpose this Timeframe into a weather station. I printed a cover for the broken bits of the screen, and spent some time in Inkscape mocking up a weather display design that didn&#8217;t include the broken parts of the screen.</p> <p>With some help from <a href="https://pillow.readthedocs.io">Pillow</a> and a lot of pixel-counting, I ended up with the display you see on the right. It shows the current weather and date, and a (somewhat confusing) list of the weather for the rest of the day.</p> <p>It might be broken, but it&#8217;s <em>my</em> broken, so I still love it.</p> <h2>Make your own</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="t5.jpg" data-lightbox="gallery"><img src="t5-small.jpg"></a></div><span class="caption">The first front plate I designed.</span></div><p>Let&#8217;s face it, up until this part there was only one thing on your mind: How can I get a sweet, sweet Timeframe that I can call my own, and put my soul at ease?</p> <p>Well that&#8217;s pretty easy, it&#8217;s on Amazon and AliExpress, and all the code and stuff I made is free and open for you to use, because why not, money has no meaning when you have too much of it.</p> <p>The code itself lives in a repository, as nature intended:</p> <p><a href="https://gitlab.com/stavros/the-timeframe">https://gitlab.com/stavros/the-timeframe</a></p> <p>The repository contains instructions, links to buy a T5, more instructions, and all the scripts and software, both for the server and the T5. Isn&#8217;t that amazing? Go me, wow.</p> <p><a href="https://www.printables.com/model/416409-the-timeframe">The case is also available</a> for you to print or remix. I designed it with OnShape, so you can access the source designs online and change them to your heart&#8217;s content.</p> <h2>Epilogue</h2> <div class="clearfix"></div><div class="alignright"><div class="photo-container"><a href="power.jpg" data-lightbox="gallery"><img src="power-small.jpg"></a></div><span class="caption">Then I made a power meter for my home.</span></div><p>I really loved making this, even though I didn&#8217;t get as much sleep as I wanted because of it. Seriously, though, how can I start coding at midnight, look at the clock ten minutes later, and it&#8217;s 8 am?! Flow is a very weird thing.</p> <p>I really like the aesthetic of the e-ink display, and love that it&#8217;s wireless and lasts forever. I&#8217;m very happy with the flexibility of being able to show whatever image I want, and I think I&#8217;ll have a few Timeframes around the house to show various things, from AI-generated art, to dashboards, to smart home statuses.</p> <p>If you make your own Timeframe, I would very very much like to see it, please <a href="https://twitter.com/intent/user?screen_name=Stavros">Tweet</a> or <a href="https://mastodon.social/@stavros">toot</a> at me, or email me directly. I would also love any comments you might have, or suggestions for improvement.</p> Sun, 05 Mar 2023 05:40:08 +0000