Nothing Daily

cdproxy

I've wasted a lot of time with connectivity issues between Python and Chromium.

Turns out playwright-python's WebSocket implementation is broken.

Released cdproxy to work around the issue. A Go binary that presents itself as a browser running with --remote-debugging-pipe proxying to a ws:// or wss:// URL chosen through the URL environment variable.

Install with:

GOBIN=/usr/local/bin go install github.com/pcarrier/cdproxy@latest

then replace:

from playwright.async_api import async_playwright

async with async_playwright() as p:
  browser = await p.chromium.connect_over_cdp(url)

with:

async with async_playwright() as p:
  browser = await p.chromium.launch(
    executable_path="/usr/local/bin/cdproxy",
    env={"URL": url}
  )

It's a hack, but it gets the job done.

Break

Taking a break from blogging daily.

I can't think of anything to write about other than the exciting but currently confidential things I'm working on.

jQuery out

Dropped jQuery from my Planck page. As far as I recall I don't use it anywhere anymore.

The transition was a breeze. Modern JS APIs are so nice. No more $ everywhere!

Time keeping

Not much to talk about publicly today.

Clocks jumped back 1 hour last night. Insert rant about societal inertia.

On the subject of time zones, if you're the kind of person who likes to read encyclopediae for fun, tzdata can be a fascinating read.

I hope not to offend with those quotes. Time keeping does evolve through colonialism, conflicts, and politics.

China

[…]
People's Republic of China. Yes, they really have only one time zone.
[…]
It seems that Uyghurs in Ürümqi has been using Xinjiang since at least the 1960's. I know of one Han, now over 50, who grew up in the surrounding countryside and used Xinjiang time as a child.

Japan

[…]
From Paul Eggert (2020-01-19):
Starting in the 7th century, Japan generally followed an ancient Chinese timekeeping system that divided night and day into six hours each, with hour length depending on season. In 1873 the government started requiring the use of a Western style 24-hour clock.

Mongolia

Shanks & Pottenger say that Mongolia has three time zones, but The USNO (1995-12-21) and the CIA map Standard Time Zones of the World (2005-03) both say that it has just one.
[…]
From Heitor David Pinto (2024-06-23):
Sources about time zones in Mongolia seem to list one of two conflicting configurations. The first configuration, mentioned in a comment to the TZ database in 1999, citing a Mongolian government website, lists the provinces of Bayan-Ölgii, Khovd and Uvs in UTC+7, and the rest of the country in UTC+8. The second configuration, mentioned in a comment to the database in 2001, lists Bayan-Ölgii, Khovd, Uvs, Govi-Altai and Zavkhan in UTC+7, Dornod and Sükhbaatar in UTC+9, and the rest of the country in UTC+8.

Palestine

[…]
To summarize, the table should probably look something like that:

Area \ when1918-19471948-19671967-19951996-
IsraelZionZionZionZion
West bankZionJordanZionJordan
GazaZionEgyptZionJordan

Wow, mpv

I've updated my mpv post to include an option I just found out about:

icc-profile-auto=yes

Makes a world of difference on my TV. Colors were washed out without it.

SlimBlade Pro as desired

The SlimBlade Pro I adopted a month ago can behave almost as described in that article using Mac Mouse Fix. Brilliantly simple:

Mac Mouse Fix buttons mapping

Mac Mouse Fix Options… view

I'm still missing a way to simulate a middle click with left and right buttons. Thought I had it with BetterTouchTool but a left click still triggers as well:

BetterTouchTool middle click through left and right buttons

If anybody knows a solution, please contact me! Though to be fair, I don't really need a middle button. I guess a delay would have to be introduced on normal left and right clicks, and I'd rather not.

Siri Remote

I'm mildly excited to be using a remote soon.

Siri Remote

Nintendo doesn't produce Wiimotes anymore. They're unnecessarily bulky for my needs anyway.

Logitech Spotlight is over 110€ here. For a BLE clicker with an accelerometer and gyroscope!?

Ordered Apple's 69€ answer to the problem of controlling my computer from my couch.

Apple's ecosystem appears to provide software for all my needs, and hopefully that won't break with macOS releases (looking at an Elgato capture device and a Brother scanner now stuck in my closet).

BetterTouchTool seems a bit messy. Remote Buddy 2 looks great. I'll report back here after a bit.

A bad controller

I love controllers. It's hard not to take one of each out for a bigger showcase.

Overview of a few game controllers

They all make some sort of sense to me, with one exception. Can you spot the outlier?

Steam controller

Maybe I would need to spend more time with it, but after a few hours I don't understand how this left the lab.

I can't picture how 2 touch surfaces are even supposed to be used, neither with existing nor potential games.

I don't think they justify missing a D-pad, and the extremely common left joystick + buttons combination is cramped, with the buttons angled wrong for their low position.

Handheld growth

Many handhelds (DS Lite, New 3DS XL, Switch Lite, Steam Deck)

Even as I've grown into a full-fledged adult, I find modern handhelds rather big.

Switch Lite, in light gray, barely fits case in my pocket. Steam Deck, in dark gray, requires a fair bit of bag space.

Switch Lite vs Steam Deck, top view

Switch Lite vs Steam Deck, front view

As much as I appreciate the option of accessing many of my Steam games on the go, I carry the Switch Lite more than the Steam Deck.

I hope the trend reverts back. New 3DS XL, in red, already feels rather big in a pocket.

Retro gamers have plenty of options, some clearly too small for my hands. The Analogue Pocket, on the right below, feels a bit cramped after a long session of Tetris.

Switch Lite vs Analogue Pocket

Macro: bill, pen, knife

Laowa 25mm f/2.8 Ultra Macro 2.5-5×.

50€ bill detail

Felt pen tip

Knife

47 keys

It's fun to think about keyboards. Sometimes you stumble upon a new idea. Sometimes it turns into commercial products.

First and latest Planck, frontFirst and latest Planck, back

Sometimes, you encounter people who actually use them day-to-day. I'm still surprised every time.

10 years after the first prototype, you can buy 47-key keyboards for cheap.

I've stuck to the HHKB “for now”. Strong inertia. But the pull is strong too.

Croix de Chamrousse with 3 lenses

Here's a view from my balcony with a 14mm lens:

14mm

Same day, 70-200mm lens at 200mm:

200mm

Another day, 200-600mm lens at 600mm:

600mm

A crop of the 600mm shot:

600mm cropped

Long ago…

I couldn't recover much from my collection of platters, with 7 disks resisting my best efforts so far. Here's my desk in March 2007 though!

Photo of a desk

mpv discovery

mpv, fork of mplayer2, fork of MPlayer, is a fantastic media player.

I just discovered by accident that it binds ga and gs to switch audio track and subtitles interactively. What a game changer for me! Good opportunity to point to the default key bindings.

I cannot resist sharing my config, which requires brew install molten-vk on Mac, and is ready for SmoothVideo.

mpv.conf

vo=gpu-next
hwdec=auto-copy
hwdec-codecs=all
profile=gpu-hq
save-position-on-quit=yes
alang=eng,en,enUS,en-US,fr,frFR,fr-FR
slang=eng,en,enUS,en-US,fr,frFR,fr-FR
cache-secs=60
embeddedfonts=yes
hr-seek-framedrop=no
icc-profile-auto=yes
target-colorspace-hint=yes
input-ipc-server=/tmp/mpvsocket
screenshot-format=webp
screenshot-webp-lossless=yes
screenshot-tag-colorspace=yes
screenshot-template="%f-%P-%n"
sub-auto=fuzzy
sub-blur=1
sub-color=1.0/1.0/0.0
sub-shadow-color=0.0/0.0/0.0
sub-font-size=16
sub-font=PragmataPro
osd-font-size=16
osd-font=PragmataPro

input.conf

[ add speed -0.05
] add speed 0.05
{ sub-step -1
} sub-step +1
v cycle sub-visibility
V cycle secondary-sub-visibility

X-ray vision

When web agents wonder what to do on a page, the action space typically includes clicking on page elements listening for such events (links, buttons, etc.).

One way to expose those actions to models is to overlay labels on a screenshot of the page. Vimium offers this on the f key:

Yahoo JP with clickable elements overlaid

Overlays are not a great solution though, because they clutter the image, hiding parts of those elements and their neighborhood. It is possible to send screenshots with and without the overlays, but this drops information density, increasing computational costs.

We can also pass image coordinates in text. Models might evolve to understand them well, but as far as I understand, none does today.

The alternative approach I'm suggesting today would be to support extra channels besides the usual red, green, blue, sometimes alpha. This approach is already used on iOS to store depth maps. Here, we'd use them to introduce labels and other visual elements in the image purely additively. I propose calling those “X-ray channels”.

Unfortunately, no vision or multimodal model supports more than RGB images today. I hope RGB(A)X becomes a thing.

Barely Even Structured Text (BEST)

When implementing a toy programming runtime, I tried to implement my “language” as a stack of simple layers.

Today, I'd like to describe the bottom of the stack, Barely Even Structured Text or BEST.

Intended as a lightweight (but not strictly minimal) layer, its design can be summarized in a few choices:

Here is the full description:

Spaces are separators outside of double quotes and escape sequences.

Strings are prefixed by ' or delimited by ".

The rest is symbols. They can be prefixed by \', or delimited by \" and ".

Quoting is preserved by parsers and printers, and meaningful in some circumstances (eg in STOR, [ starts a vector but \'[ does not).

\ can be used to escape:

  • ' single quote
  • " double quote
  •   space
  • \ backslash
  • n newline
  • r carriage return
  • t tab
  • f form feed
  • v vertical tab
  • b backspace
  • [0-9A-F][0-9A-F] arbitrary byte in hex
  • u[0-9A-F][0-9A-F][0-9A-F][0-9A-F] unicode code point in hex (represented in UTF-8)

For example, in:

A 'world \'of "dew"

A and of are symbols, the latter single-quoted. world and dew are strings, the latter double-quoted.

Here are 3 strings:

'A\ single\ string
"On each line"
"This one\nis two lines"

Single-quoted strings end before any unescaped space, whereas double-quoted strings only end before an unescaped double-quote, so this is a fine string too:

"A direct line
break works, so do
escaped quotes like \"!"

The naïve, probably bug-ridden implementation of parsing and printing fits in 200 lines of Nim code.

Looking forward to describing the layers above:

Exploring atob(null)

atob(null) is a valid JavaScript expression returning a valid JavaScript string, '\x9Eée'.

What's happening here is, I think, interesting. First things first, atob(null) is, thanks to type coersion, atob("null").

Given JS strings are UTF-16, those 4 codepoints really take 8 bytes, but decode to 3 bytes:

[...atob(null)].map((i)=>i.charCodeAt(0))
[158, 233, 101]

\x9E appears because U+009E is the non-printable Private Message.

Everything works out great for latin languages as Unicode codepoints 0x80-0xFF are the Latin-1 Supplement block.

What of encoding characters outside the first 2 blocks then, given btoa is the reciprocal of atob?

> btoa('π')
Uncaught InvalidCharacterError: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

I found this surprising. My intuition would have been that btoa works with arbitrary strings.

If and when armor64 gets a web implementation, I hope it offers UTF-8 encoding for JS strings, and only exposes the equivalent of btoa on Uint8Array, atob to Uint8Array.

Stick Fight: The Game

Ever have 2 to 4 players around, want to have a blast for a few tens of minutes?

I highly recommend Stick Fight: The Game.

Exactly what you'd expect from the trailer. Instant competition.

Platters

I've accumulated a bunch of hard drives in the days before cloud-everything.

Platters

Just ordered an adapter to connect 2.5” and 3.5” PATA drives and SATA drives to USB. I can't wait to dig up old stuff.

A few of them fried on me; I hope I can just swap PCBs from the same vendor and series.

Storage expansion

rabbit, the NAS+media server+… described in my desk setup post, has been running out of storage space. A 4TB NVMe SSD hosts the operating system and a big cache for the zfs pool, a raidz on 4×8TB of SATA SSD storage attached through the ThunderBay 4 mini.

I figure, given its growth rate keeps increasing, that I should invest in a solution that'll remain viable for many years.

So I'm adding a raidz of 8×18TB SATA HDDs to the pool, attached through the ThunderBay 8.

The new storage setup

The difference in size between 4×2.5” and 8×3.5” is a bit more intimidating than I expected. I relocated rabbit to the corner next to my desk, freeing some space on it.

That's 126TB of usable storage added to the 24TB I already have, for a nifty total of 150TB or 136TiB.

All this to announce: time to set up good offsite backup solutions for friends and family!

Long live Alfred

Alfred has had competition, but it remains my launcher of choice.

Out of its many features, one stands out as a huge boost to my productivity and comfort using computers: the clipboard history.

I've tried clipboard history apps on Windows and Linux, but Alfred's design is miles ahead.

It keeps up to months of history.
It automatically ignores entries from password management apps.
One global shortcut to search.
One keypress to insert.

While for me, tiling window managers are the “killer app” for Linux, Alfred is the “killer app” for Mac.

armor64.org is live

Every now and then I have to deal with base64. Most of the time, this leads to pain.

In 2016, I drafted a replacement, armor64. I once again got frustrated today, and “launched” the draft as a website at armor64.org.

Friends don't let friends use base64. Encode different. Encode armor64.

xmit.it

Started working on a mailing list+log service, xmit.it. If you have branding ideas, reach out!

The idea:

Design goals:

I might, at some point, try to generate revenue.
Eg bill for custom domains and/or managed dedicated instances and/or high subscriber counts.

Non-espresso coffee gear

Overview of the gear

Whilst I mostly brew espresso, including on the go, I also enjoy other coffee brewing methods. Here's my non-espresso coffee gear.

Hario V60 Immersion Dripper

My favourite way to prepare a big cup of tasty coffee. Big fan of the cloth filter.

Hario V60 Immersion Dripper

Aeropress

A classic I rarely use. Cloth filter again.

Aeropress

6-cup Bialetti Moka Express

Bialetti's iconic Moka Express in its 6-cup format, with its induction adapter. A good solution if I want to make a lot of strong coffee quickly.

Bialetti Moka Express

8-cup French Press

Simple and cheap, gets the job done. I follow James Hoffman's technique.

French press

DAB+ is lovely

After yesterday's post turned from design doc to a complete implementation including features initially planned for further iterations, I'm going to keep it short today.

Sony XDR-S61D on my nightstand

Proud owner of a Sony XDR-S61D, I've been enjoying DAB+ radio for a few months now.

It's a lovely experience:

Only complaints about the Sony XDR-S61D:

Form to mail

I suspect a lot of static sites want a single dynamic feature: submitting a form to an E-mail address.

I dove into building a service to make that easy, only to realize that it would be a lot easier and elegant to add it directly to my free static hosting platform, xmit.co.

Here is today's design; I welcome feedback and suggestions.

In my site's HTML, I can add a form like:

<form action="/contact" method="POST" enctype="multipart/form-data">
  <input type="text" name="name" placeholder="Name" required />
  <input type="email" placeholder="E-mail" name="email" required />
  <input type="subject" placeholder="Subject (optional)" name="subject" />
  <textarea name="message" placeholder="Message" required></textarea>
  <select name="domain">
    <option value="personal">personal</option>
    <option value="work">work</option>
    <option value="hobby">hobby</option>
  </select>
  <input type="file" name="files" multiple />
  <button type="submit">Send</button>
</form>

for this (now working) example:

To activate it, I add to my site's xmit.toml:

[[forms]]
from = "/contact"
to = "pc@rrier.fr"
then = "/posts/form2mail/"

I can then receive an E-mail like:

From: "John Doe" <john.doe.gmail.com@forms.xmit.co>
Reply-To: "John Doe" <john.doe@gmail.com>
Subject: [nothing.pcarrier.com] Hello

---
domain = 'personal'
---

How are you?

enctype is only required for file uploads, and all fields are optional. If I POST nothing (curl -d '' https://nothing.pcarrier.com/contact), I receive:

From: "nothing.pcarrier.com" <noreply@forms.xmit.co>
Reply-To: "nothing.pcarrier.com" <noreply@forms.xmit.co>
Subject: [nothing.pcarrier.com] Form submission

then is a URL to redirect to after the form is submitted. When absent, we serve whatever resource is at the requested URL.

I can't quite think of anything important missing in this design; can you? Are there other such small features you'd like to receive from your static hosting provider? I appreciate all feedback!

CAPTCHA… Why?

Been dealing with CAPTCHAs at work, and wow are they stupid.

Companies like CAPSOLVER are solving them faster than one can say “I'm not a robot” for dirt cheap.

So why don't we cut the middle person, the waste of time and energy on both sides of the equation, and offer micropayments as an alternative for the 99%? I'd gladly throw a fraction of a cent at websites to avoid the hassle. Heck, even 1¢ to save 10s of my time costs only $3.6/hour… What a bargain!

And once we have micropayments as alternatives to inferior user experiences, I'll gladly remove ads through bilateral financial agreements rather than undesired ad blockers.

I don't understand what stands in the way. Surely something does. The incentives are there for a microtransaction processing company to take its commission, for consumers to save time for what would be a nominal fee, and for the sites to fight bots through monetization… Win-win-win.

Why haven't Mozilla and Apple integrated a solution in their browsers yet? It truly seems like a no-brainer.

Wild Irish Whistles

I barely play any wind instruments. That is to say, I can play a couple of tunes poorly on the saxophone, and couple more on the tin whistle (Irish whistle).

A real beginner. In passing, I'd love a tool I can provide with appropriate sheet music to get it back annotated with tabs (fingerings and tonguings).

Yet despite barely knowing how to play anything, I couldn't resist a few purchases.

Wind collection

Pictured here:

Tin Whistles

It's a lot pricier than the other tin whistles, but it's immediately clear why, and remains much cheaper than an entry-level saxophone.

It looks and feels premium in every way. As a beginner, I love how much easier it is to play on the Wild. Its tone sounds a lot more clear yet consistent (one might say forgiving) to me.

Wild Tin Whistle, overviewWild Tin Whistle, detail

offrecord.ca: private chat

Started a new project last night: offrecord.ca, a private chat service.

Not much to say about it. Needed off-the-record communication one day, built the simplest solution offering privacy and confidentiality. Usage is trivial: pick a channel name (we offer random ones, with 64 bits of entropy), share it as you want (we offer a QR code and native sharing UI), chat away.

Messages and their authors' pseudonyms are encrypted with a TweetNaCl keypair derived from the channel name, whose public key is used as the channel identifier with the service. The service keeps the last 10 messages and their timestamps only in memory and unless the channel is wiped. A presence counter per channel could reveal somebody dropped in unexpected. No IP or effective identifier is ever logged. I track which user agents load the site's codebase, but not where they connect; user agents do not identify individuals.

And as often with my personal projects, it's open source (0BSD-licensed).

yas please

I started prototyping yas through its website, with the idea of making a small, fast, and cross-platform execution environment for scripts exposed online.

I'd like it to come bundled with a few libraries to make it useful:

Unfortunately an abundance of questions and tradeoffs demotivates me.

If anybody feels inclined to participate, that could give me a perspective on what's wanted and the push I need to move forward. I'm not necessarily looking for a team, but I'd love to see ideas bouncing around. I made a Discord channel.

Font fanatism: PragmataPro

I've been using PragmataPro since 2015-04-28. It's a tall monospaced font designed by Fabrizio Schiavi.

At €199 for desktop and €199 for web, I'm glad I purchased it for both.

It powers most pages on my main website and formic.id, my terminal, IDEs and editors, browsers, most apps on Linux, my business cards, my overgrown deck of cards. Really, I put it everywhere I think it fits. And I tend to think it fits everywhere, except lightweight websites as it takes a few MBs.

Check out the stunning character set in full!

Breaking Blink

A few years back, I had written a simple page that made most browsers sluggish, folks. It was intended as a benchmark, but I was still surprised at how hard some took it.

I can't exactly recall which performed how, but initial load and reflow on window resizing could take many seconds. Today, none of the 3 engines in widepread use (Safari's WebKit, Firefox's Gecko, Chrome's Blink) have any issue with it.

However, I once again find myself bringing Blink to a grinding halt. This time, visiting pages like Mathematics on Wikipedia, with custom CSS, in an 8K window. Sure, it's niche. Nice problem to have, I guess.

Safari, on the other hand, loads and reflows smoothly. Looks great, by the way (besides sans-serif not mapping to my favourite font, PragmataPro):

Mathematics on Safari

1-Bit Symphony

Tristan Perich pushed chiptune to the limits with his 2010 1-Bit Symphony. The album is stored in a standard crystal case. When a switch is on, a battery powers a microchip which outputs music through a digital pin to a headphone jack. One button to skip tracks, one potentiometer to control volume. I adore the minimalism.

1-Bit Symphony

Mine doesn't play “correctly” anymore. I hope its production is unique.

Comes with a leaflet with the complete sources:

Leaflet, recto

Leaflet, verso

SlimBlade Pro, a baller device

I used a SlimBlade trackball a few years ago. I had given up on it mostly because of its low DPI.

Ordered its successor, the SlimBlade Pro, at the beginning of the week.

It's wireless. DPI is configurable past what I want for my 8K display. Only thing I'm missing is software configurability. In short, I wish it let me:

Only the last point is missing from their solution, KensingtonWorks. Unfortunately, it's entirely broken in Sequoia, so I'm stuck with the default behaviour. Might try to build my own Mac software at some point. Update: Found what I wanted!

That being said, those are only wants, not needs, and the SlimBlade Pro has already replaced the Logitech MX Ergo as my input device of choice.

I go to Twin Labs' Paris office about 2 days every 2 weeks; there it was with me:

Desk at Twin Labs

Back at home today:

Desk at home

Great opportunity to add to the list of hardware from my desk post with items I take out to compose music:

Too many timelines

Early on, my computer exposed timelines through a calendar, 1:1 chat (ICQ, AIM), chatrooms (IRC), E-mail (including my beloved threaded mailing lists). I had desktop notifications for the first three, only on keywords for chatrooms. It already felt overwhelming at times, but manageable.

RSS was a nice addition, and captured most everything else for years.

Already then, I dreamt of unifying those disparate systems into the One App Of Prioritized Events. It was a vague dream, with a lot of complexities I barely understood, and a few wacky ideas I still wish I had pursued. Looking back, it might have a Golden Age of potential.

Today the number of timelines I follow exploded. I started listing them all here, only to quickly give up on the unpleasantness. They've caused a lot of disruptions, which often turn into doomscrolling.

Turning most notifications off, unsubscribing from most everything, avoiding reentry, closing the tab or even logging off when I faltered, blocking through /etc/hosts… Whatever I do, I'm never quite satisfied. I don't want to give up on the value, I just want more control over it, notably a better signal to noise ratio.

Today more than ever, I long for an application unifying all those systems. Lots of features immediately come to mind:

Unfortunately the interests of platforms are misaligned with many of those goals. They want as much of my attention as possible, to observe and control my experience. Largely to better sell ads or preferential treatment, directly and not.

I'm lucky enough that I could easily afford to pay for lost ad revenue, but the option often isn't there.

And companies have reasons to avoid interoperability besides the advertisement plague. They want to compete in the marketplace through innovation, evolving their product as often as they want, adding and changing features without the restraints of compatibility with third-party applications. I must also mention silly attempts at preventing redistribution (DRMs 🙄).

When data is unshackled from its presentation, when access is negotiated solely by producers and consumers without the meddling of intermediates, decentralization brings about the commoditization of communication and distribution channels, and enables the rise of more humane products and experiences for communicators, creators, and audiences.

I won't pretend I have a clear vision for what the best products would be in that world. I don't know that I'd be able to build much of what I'm hoping for, should that vision cristalize. But I'm convinced our collective intelligence could achieve greater results than the silos we all too often confine ourselves to.

Who pays for what how, I don't know either. But I'd happily give a lot of my resources, time included, to see meaningful progress in that direction. And should the opportunity arise, I could easily bet I'm far from alone.

baze utilities: shost

I covered how to install baze and introduced suc two days ago, then statistik yesterday. Today, we're looking at shost.

Looking to resolve DNS names? bind ships host and dig, ldns ships drill.

shost is quite terse in comparison:

> shost ident.me
2a01:4f8:c0c:bd0a::1
49.12.234.183
> host ident.me
ident.me has address 49.12.234.183
ident.me has IPv6 address 2a01:4f8:c0c:bd0a::1
> drill ident.me
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 15200
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; ident.me.    IN      A

;; ANSWER SECTION:
ident.me.       12      IN      A       49.12.234.183

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 4 msec
;; SERVER: 100.100.100.100
;; WHEN: Mon Sep 23 17:24:40 2024
;; MSG SIZE  rcvd: 42
> dig ident.me

; <<>> DiG 9.10.6 <<>> ident.me
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28298
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4095
;; QUESTION SECTION:
;ident.me.                      IN      A

;; ANSWER SECTION:
ident.me.               11      IN      A       49.12.234.183

;; Query time: 4 msec
;; SERVER: 100.100.100.100#53(100.100.100.100)
;; WHEN: Mon Sep 23 17:24:41 CEST 2024
;; MSG SIZE  rcvd: 53

>

It has the benefit of letting you reverse lookup in one go:

> shost -r ident.me
v6.ident.me
v4.ident.me

host, dig and drill all resolve names through their respective DNS implementation.

shost doesn't compete but completes, relying on the system's resolver instead.

So, while a lot less information is available about what happens in DNS, what you get back is what most software uses. For example, .local names are looked up through mDNS/zeroconf if your operating system is configured to do so:

pcarrier@cat ~> shost cat.local
::1
fe80::1%lo0
127.0.0.1
192.168.1.21
192.168.42.142
fe80::1cfa:8a61:53e2:5324%en0
fe80::1ce2:3342:8435:7cd7%en3
2a01:e0a:250:7160:482:db55:6b74:b323

This makes it a valuable addition to a troubleshooter's toolbox.

As usual in baze, very little code:

#!/usr/bin/env ruby

require 'socket'
require 'optparse'

reverse = false
soft = false

optparse = OptionParser.new do |opts|
  opts.banner = "Usage: shost [-r] host...\n" \
    "  Resolves names"

  opts.on '-r', '--reverse', 'Display reverse lookups' do
    reverse = true
  end
  opts.on '-s', '--softfail', 'Simply display failures instead of exiting' do
    soft = true
  end
end

optparse.parse!

raise 'host expected' if ARGV.empty?

ARGV.each do |host|
  begin
    infos = Socket.getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, 0, reverse)

    if reverse
      infos.each {|r| puts r[2]}
    else
      infos.each {|r| puts r[3]}
    end
  rescue SocketError => e
    if soft
      STDERR.puts "#{host} FAILED"
    else
      raise e
    end
  end
end

baze utilities: statistik

I covered how to install baze and introduced suc yesterday. Today, we're looking at statistik.

If you have a bunch of numbers and want a quick look at their distribution, statistik is the simple command for you.

For example, let's look at how many requests each IP made to ident.me. First, let's look at the first 5 requests of the last hour to confirm we're extracting the data correctly (I censored the results):

> doas head -n5 /var/log/nginx/access.log | jq -r .r
9.255.1.7
2600:40:4201:7a10::abc7
8.136.246.156
3.2.132.135
3.2.144.1

We wonder if some IPs make more requests than others, and what the distribution looks like. Let's find out by looking at the first 100,000 requests now, passing them through suc to get the number of requests per IP, then statistik for analysis:

> doas head -n100000 /var/log/nginx/access.log | jq -r .r | suc | statistik
size:  45157
sum:   100000.0
avg:   2.2144960914143987

min:   1.0
p10:   1.0
p20:   1.0
p25:   1.0
p30:   1.0
p40:   1.0
p50:   1.0
p60:   1.0
p70:   1.0
p75:   2.0
p80:   2.0
p90:   3.0
p99:   18.0
p999:  115.0
p9999: 277.0
max:   741.0

We see here that those 100,000 requests came from 45,157 unique IPs. A bit over 70% of IPs made only 1 request; at least 90% made 3 or fewer requests. The top 1% of IPs made 18 or more requests, with the top IP making 741.

I occasionally run public analytics on one hour of service. For comparison, here is the latest graph of number of IPs per request count. In my opinion, much harder to interpret:

Graph of IPs per request count

You might not be surprised to learn that statistik's implementation is trivial:

#!/usr/bin/env ruby

s = []
sum = 0

ARGF.each_line do |l|
  n = l.to_f
  s << n
  sum += n
end

s = s.sort

cnt = s.length

print "size:  #{cnt}\n"
print "sum:   #{sum}\n"
print "avg:   #{sum/cnt}\n\n"

print "min:   #{s.first}\n"
[10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 99].each do |p|
  print "p#{p}:   #{s[s.length*p/100]}\n"
end

print "p999:  #{s[s.length*999/1000]}\n"
print "p9999: #{s[s.length*9999/10000]}\n"
print "max:   #{s.last}\n"

baze utilities: suc

I've written a lot of shell one-liners over the years, and packed a few most useful “missing” tiny utilities under a Ruby gem, baze.

To install it system-wide, make sure Ruby is installed, then run:

$ sudo gem install baze --no-user-install

I'll be introducing my favourite utilities over the next few days. Today, we're looking at suc.

It's quite frequent that I want to list all line counts. If /var/log/xhttpd/access.log contains:

{"client": "1.2.3.4", "path": "/hello"}
{"client": "5.6.7.8", "path": "/favicon.ico"}
{"client": "8.7.6.5", "path": "/world"}
{"client": "4.3.2.1", "path": "/favicon.ico"}

I can produce a list of paths sorted by decreasing number of occurences with:

$ jq -r .path /var/log/xhttpd/access.log | sort | uniq -c | sort -nr
   2 /favicon.ico
   1 /world
   1 /hello

Unfortunately this is extremely inefficient at scale, taking for n lines O(n⋅ln(n)) time and O(n) space.

suc -r is a drop-in replacement leveraging a hash table, taking for n lines of k distinct values O(n) time and O(k) space.

$ jq -r .path /var/log/xhttpd/access.log | suc -r
      2 /favicon.ico
      1 /hello
      1 /world

You might have noticed suc differs in 2 other ways:

The -r flag plays the same role in both commands: start with most rather than least frequent lines.

Like the rest of baze, the source code is a short script depending only on Ruby:

#!/usr/bin/env ruby

require 'optparse'

reverse = false

optparse = OptionParser.new do |opts|
  opts.banner = "Usage: suc [-r] [file]\n" \
    "  Scale-friendlier equivalent of sort | uniq -c | sort -n"
  opts.on '-r', '--reverse', 'Sort from most to least frequent' do
    reverse = true
  end
end

optparse.parse!

counts = Hash.new 0

ARGF.each_line do |line|
  counts[line] += 1
end

if reverse
  sorted = counts.sort_by {|k,v| [-v, k]}
else
  sorted = counts.sort_by {|k,v| [v, k]}
end

sorted.each do |line, count|
  printf "%7i %s", count, line
end

Macro detour

There's tons to be said about macrophotography, and I don't intend to cover much of anything here.

The higher your focal length, the more magnification you get, but the less of the image is in focus.

The more you open up your aperture, the more light gets in, but the less of the image is in focus.

The smaller the object, the more magnification you need, and the more light you need to get in.

Already at half-macro or 1÷2 magnification (on a 36×24mm sensor, capturing a surface of 72×48mm), you'll find that opening at f/4, the depth of field is rather narrow:

Small flowers

Flow gynoecium

Things get wilder at 2.5× ultramacro (on a 36×24mm sensor, capturing a surface of 14.4×9.6mm) and f/4. For an object of relative depth (not very flat in the much narrower plane of focus), a couple of options are available:

We do the latter here using Helicon Focus. Here are 80 frames shown at 10 frames per second:

And the picture they combine to form:

Lighter

As magnification keeps increasing, photography gets harder: it requires extensive preparation including cleaning (very poorly done here), and avoiding vibrations through dozens and dozens, sometimes hundreds of frames.

Pentax K10D

What a trip down memory lane! My first digital camera in all its CCD, APS-C, 10MP, 2006 glory.

Pentax K10D

Here, equipped with a SMC Pentax-A 1:4 70-210 and compared with today's Sony α7R V equipped with the FE 4/70-200 Macro G OSS II.

Petit Casino

70mm f/4; Pentax 1/60s, Sony 1/200s. The difference between full frame and APS-C explains different zooms at same focal distance.

Pentax

Store front, Pentax

Sony

Store front, Sony

Mint in “macro”

70mm f/4, both 1/60s.

Pentax

Plant closeup, Pentax

Sony

Plant closeup, Sony

Made 1000 Teekos… Now what?

After my favourite video games, Tetris and N++, time to post about my favourite board game.

My first encounter with Teeko was in 2016, running into its Wikipedia page and devouring Blake Eskin's 2001 WaPo article.

After a journey of many seasons, I'm running an online version, and own over 900 physical copies of my own take (as a few were gifted or traded).

Teeko in its case, near a deck of cards and a chess clock

Teeko unpacked: a textile board, 2×4 pieces

It feels like merely the beginning though.

Online version: needs work!

The online version was built around my personal desire for remote play, and to turn my phone into a board at an instant's notice. It's functional for both. But…

Physical version: needs distribution?

The original trademark, registration #519087, expired in 2021. I have no problem distributing the game under its original name, and my design is distinct enough.

The stocks for boards, pieces, and bags are living in my bedroom closet. I have yet to print rules, and some assembly is required, but it's otherwise ready.

You can write to preorders@teeko.cc, but I have no concrete plans to distribute it beyond hand-delivering it around me at the moment.

Missed in Toronto

Of the many places I've visited in Toronto, a few instantly come to mind as dearly missed.

Granted, they're all from or near my old neighbourhood. But that only makes visiting them all easier.

Egg Club

BLT sandwich

Yes, it's a promotional photo. But that's the real deal.

If you're into the premise, every sandwich on their menu is fantastic.

Don't miss their hash browns. They come in the menu offer, unfortunately with brewed coffee that's only so-so.

Page One Cafe

Page One's front

Image from AccessTO

A cafe + bar. Despite living in San Francisco before, this is where I finally got into speciality coffee. Nice collection of typewriters.

Tsujiri

Matcha parfait

Image from Tripadvisor

I love matcha in all its forms. From o matcha to parfaits and shaved ice through lattes and cakes, I've had immense joy going through Tsujiri's menu.

Icha Tea

Icha Tea's counter

Image from blogTO

A bit further west from the rest, very much worth the walk. Great selection of premium teas served gongfu, delicious-looking desserts (never tried), a calm atmosphere, laptop-friendly.

N++: platforming perfection

Tetris got its post; time to cover my other favourite video game.

N++ is, in my opinion, the best platforming game ever made.

Launched under the Metanet Software umbrella, the third in a series of excellent games by Canadian duo Mare Sheppard and Raigan Burns, it features instant restarts upon failure, great physics, simple and elegant visuals, an addictive soundtrack, no scrolling, and pure determinism.

Once introduced to all the elements that can appear in a level, you can understand each new level through visual inspection before it starts. No surprises. Easy to learn but hard to master. Everybody will find their skills challenged here.

The level editor with online sharing complements thousands of levels of increasing difficulty, separated between solo, co-op and race categories, and only cleared by series of 5.

If you play one platformer in your life, make it N++.

Switch trailer

GDC talk

Decades of calculators

Given how slow my CASIO PB-700 was at plotting graphs, around age 8, my parents upgraded me to the Sharp EL-9400 (manual).

Sharp EL-9400

It was a big step up for math, but barely programmable. I wanted a fast BASIC playground.

After a lot of starring in stores and begging, I received the dream machine: the TI-92 Plus (manual). It was a TI-89 in the body of a TI-92. Fantastic machine to code in BASIC and, as I discovered after breaking an arm, take notes in class.

TI-92 Plus

Came with a powerful Symbolic Algebraic System and Cabri Géomètre (a brilliant app to construct geometry). Once I discovered how to transfer files from the Internet onto it, a lot of games followed.

It lasted quite a few years. When it finally gave up, I immediately replaced it with its successor, the TI Voyage 200 (manual), which I've kept to this day.

TI Voyage 200

Smaller form factor but AAA batteries instead of AA, double the flash, a really minor revision.

In my adult life, having lost interest in calculators for anything but quick calculations on the go, and having embraced RPN to the point of implementing a postfix language for fun, I've acquired a HP Prime (manual).

HP Prime

Sadly, I don't care to get past the home screen anymore.

PB-700: 1983 pocket computer

My first computer, or what I remember as such, is older than me. Passed down by my father, it was made by CASIO in Japan in 1983, the year System V launched.

200 × 88 × 23 mm, 4 KB of RAM, 4 lines of 20 characters or 32 lines of 160 pixels, with optional printers, tape, and RAM expansions I never saw.

Mine still works great today. And by great, I mean that plotting sin(x) and cos(x) across the screen in BASIC, highlights of my childhood, still takes mere minutes to complete.

Overview

CoverFrontBack, openedContrast controlPort

10 PRINT

10 PRINT "HELLO"20 GOTO 10Running

Booklet (in French)

Booklet coverBooklet 1Booklet 2Booklet 3Booklet 4Booklet 5Booklet 6Booklet 7Booklet 8Booklet 9Booklet 10Booklet 11

Tetromino galore

I've been mesmerised by Tetris for as long as I can remember.

Nowadays I play a lot of the open source Apotris on Analogue Pocket, Tetris Effect Connected on TV, and the occasional Puyo Puyo Tetris on Switch.

I'm fairly terrible at those, but who cares? It's fun with friends, it's fun when I have a few minutes on the go.

Apotris

Apotris in particular deserves a shoutout. You can play it for free on any potato, even from your browser.

Some videos copied straight from their homepage:

Soon, Tetris Forever

I can't wait for Tetris Forever to come out.

Grenoble, France

Grenoble seen from Bastille

Come to Grenoble, we have bubbles!

Grenoble-Alpes Métropole is an urban area of around 445,000 people surrounded by mountains.

Ranked #1 city for quality of life by Oxford Economics, it welcomes around 65,000 students every year.

Isère and Bastille

It has great museums, a vibrant cultural scene.

Musée de Grenoble

Bicycle lanes abound and public transportation is plentiful. History and nature are never far away.

Château de Vizille

Vizille

Big downside of the city: it's really hot in the summer.

Recommendations

La Rivière des Parfums

Front of La Rivière des Parfums

La Rivière des Parfums is a casual Vietnamese restaurant in Grenoble, France.

It's a small place with a few tables and a counter. The menu is simple, the food is delicious. The staff is friendly and the service is fast.

Super affordable. Their vegetarian bò bún, plenty enough for a meal, is 8€.

Bò bún, as servedBò bún, stirred

My fish shell

After many years on an ever-growing zsh configuration, I switched to fish a few years ago.

While perfectly satisfied with my setup, I wanted something I could recommend to a beginner. fish's simplicity and modern defaults seem like a much better starting point if eschewing POSIX compatibility.

My configuration hasn't grown much since, but a few quality of life improvements have accumulated, so I figured it is worth sharing.

Installs on Apple Silicon macOS

Let's assume you're starting from scratch and want to adopt all my suggestions. Open Terminal.app and run the following:

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
$ /opt/homebrew/bin/brew install aria2 bat delta direnv eza fish fzf keychain mise tig wezterm zoxide
$ chsh -s /opt/homebrew/bin/fish

~/.wezterm.lua

Most likely, you'll need to choose another font on line 3.

Unless, of course, you're interested in purchasing PragmataPro, a most excellent project I use nearly everywhere (eg my main site).

local wezterm = require 'wezterm'
local config = wezterm.config_builder()
config.font = wezterm.font 'PragmataPro Liga'
config.font_size = 14
config.hide_tab_bar_if_only_one_tab = true
config.native_macos_fullscreen_mode = true
config.pane_focus_follows_mouse = true
config.use_fancy_tab_bar = false
config.window_decorations = 'RESIZE'
config.window_padding = { left = 0, right = 0, top = 0, bottom = 0 }
return config

~/.config/fish/config.fish

I made it so it could be copied anywhere; none of the tools need to be installed, but they will be enabled if they are.

if type -q /opt/homebrew/bin/brew; /opt/homebrew/bin/brew shellenv       | source; end
if type -q keychain;               keychain --eval --quiet --inherit any | source; end
if type -q direnv;                 direnv hook fish                      | source; end
if type -q fzf;                    fzf --fish                            | source; end
if type -q zoxide;                 zoxide init fish                      | source; end
if type -q mise;                   mise activate fish                    | source; end
if type -q caniuse;                caniuse --completion-fish             | source; end
  1. The first line enables Homebrew on Apple Silicon macOS.
  2. keychain starts SSH and GPG agents as needed.
  3. direnv automatically sets up environments per project.
  4. fzf is a fuzzy finder that can be used to search for files and commands.
  5. zoxide speeds up file system navigation. I barely cd anymore.
  6. mise manages versioned toolchains per project.
  7. caniuse is caniuse.com for the command line.

We've reached the right step to switch from Terminal.app and zsh to WezTerm and fish. Close Terminal.app and start WezTerm before we move along.

Install node and caniuse through mise

> mise use -g node@latest
> npm install -g @bramus/caniuse-cli

~/.config/fish/functions/

All my functions worth sharing are saved aliases, so I provide commands to recreate them.

c.fish, g.fish

clear the shell, invoke git in one character.

> alias --save c clear
> alias --save g 'command git'

cat.fish

Replace cat with bat. Use /bin/cat when you need the real thing.

> alias --save cat bat

dl.fish

Download files with aria2.

> alias --save dl 'aria2c -x5 -j5'

ls.fish

Use eza instead of ls.

> alias --save ls 'eza --color=always --icons=always --long --git'

Leverage eza and bat in fzf

> set -U FZF_ALT_C_OPTS  "--preview 'eza --tree --color=always {}'"
> set -U FZF_CTRL_T_OPTS "--preview 'bat -n --color=always --line-range :500 {}'"

Get rid of the greeting

I don't need to be welcomed and invited to type help every time.

> set -U fish_greeting

~/.ssh/config extracts

I like my keys to be added to the agent automatically on first use, for host keys to be automatically accepted for new hosts, and to minimize bandwidth consumption.

AddKeysToAgent yes
Compression yes
StrictHostKeyChecking accept-new

~/.gitconfig extracts

With this configuration in place, git help config should open a page in your browser. I won't describe every detail, but a few essentials.

Only including 2 aliases, ss and sss, as everyone should know how status offers a compact output with -s and -b, and is sped up by -uno to avoid looking at the working tree in large repositories.

You should try tig for a quick view of your repository's history. lazygit does a lot more.

You can and should sign your commits with ssh (unless you prefer PGP).

[core]
	autocrlf = false
	whitespace = blank-at-eol,blank-at-eof
	pager = delta
[gc]
	auto = 0
[user]
	signingKey = ~/.ssh/id_ed25519
[fetch]
	prune = true
[push]
	default = current
[rerere]
	enabled = true
	autoUpdate = true
[color]
	ui = true
[branch]
	autosetuprebase = always
[log]
	date = iso
[rebase]
	autosquash = true
	autostash = true
[alias]
	cm = commit -v
	ss = status -sbuno
	sss = status -sb
[tag]
	sort = version:refname
[tig]
	show-changes = true
	commit-order = topo
	line-graphics = utf-8
	mouse = true
	blame-options = -C -C -C
[difftool]
	prompt = false
[mergetool]
	prompt = false
[commit]
	gpgsign = true
[pull]
	rebase = true
[init]
	defaultBranch = main
[help]
	format = web
[gpg]
	format = ssh
[submodule]
	recurse = true
[interactive]
	diffFilter = delta --color-only
[delta]
	navigate = true
	side-by-side = true
[merge]
	conflictstyle = diff3
[diff]
	colorMoved = default

NetEscape: challenging web agents

My employer, Twin Labs, builds ML-powered web automation.

We shipped my side project, NetEscape, an obstacle course for web agents.

Largely designed around our technology's current limitations, it proves a challenge for the agents we've put our hands on.

It is, however, easy for humans. Feel free to try it! Should you complete it, we look forward to your submission…

A little meta

This blog is powered by a static site generator and live previewer, 11ty.

direnv automates the installation of mise, which automates the installation of node and pnpm.

A blog command automates the installation of further dependencies and the invokation of package.json scripts.

It is deployed to xmit, my very own hosting platform, from a public GitHub repository.

GitHub Actions are available on the deploy branch, largely unused. Usually much faster to deploy locally.

Posts are written in Markdown, images are authored in high-res AVIF and made available in AVIF and JPEG in a variety of sizes.

An index lists all posts, its JSON Feed provides the last 10 posts to feed readers.

Update (2024-09-18): There is also a newspaper view for that old-school feel now.

Scavengers Reign (2023)

Scavengers Reign poster

Sci-fi animation inappropriate for children. Nothing short of my favourite piece of media for adults.

A visual masterpiece backing a captivating story, it will take your breath away.

I strongly recommend going in blind, then watching the Scavengers short included below.
That said, teaser & trailer follow in case you need convincing.

Scavengers (a 2016 short)

Teaser

Trailer

Hono Coffee House

Hono coffee shop front

Hono Coffee House is my favourite coffee shop in Grenoble, France.

Carrying their own roast and that of nearby Chulo, offering a variety of brewing methods and alternative drinks, they're a must-taste for any coffee lover visiting the city.

They also offer a great selection of vegan food, some gluten-free.

I visit almost daily, most often enjoy an oat milk flat white. I'm also partial to their dirty chaï, and would suggest espresso tonics if you're feeling adventurous.

Cup of dirty chaï

Neuf Lamen

My bowl at Neuf Lamen on Thursday night

Felt compelled to share that Neuf Lamen is a brilliant eatery in Grenoble, France.

I eat there often and always leave happy. The staff is super friendly and welcoming.

Their braised beef lamen is worth singling out for me, though I've never been disappointed.

Fediverse identity ownership falls short

I enjoy using my own domains — though I rent a few too many! One of them, rrier.fr was originally purchased solely so I could use pc@rrier.fr as my E-mail address.

Naturally, I wanted to also be known as @pc@rrier.fr on the Fediverse, as part of owning my online identity. Unfortunately, this turned out more challenging that I expected. Here are a few approaches I tried and what I learned along the way.

Forwarding WebFinger

My first approach, documented during my migration to xmit.co, was to set up a WebFinger redirect. https://rrier.fr/.well-known/webfinger redirected to https://mastodon.social/.well-known/webfinger?resource=acct%3Apcarrier%40mastodon.social, the WebFinger URL for @pcarrier@mastodon.social.

This let people search for @pc@rrier.fr and find an account. Unfortunately, that indirection would be resolved right there and then: they would then see and follow the mastodon.social account. I couldn't move providers smoothly, as the process to relocate an account disappears all previous content.

I wanted more control over where my content is hosted and stored authoritatively. Onto what seemed like the natural solution.

Running Mastodon

In my opinion, for a single customer, a server requires too much setup and maintenance work, and too many resources. I gave up a few steps into an installation when I realized the amount of memory my VPS would probably need, so I can't say much more.

I looked around, and found what I think is a better solution for my needs:

Running GoToSocial

Setup was a breeze. Everything is lightweight and fast. I have minor complaints, notably lack of edit capability (already on the roadmap), but it's a fantastic piece of software.

It does, however, suffer from what appears to be limitations inherent to the design of the Fediverse:

I'd love to learn that those issues can be addressed without protocol changes, or that such protocol changes are underway. In the meantime, I've learnt to live with those grievances. Edit: Not a great UX, but everything seems workable in the end.

Desk setup

Here's my desk, where I spend way too much time. Please pardon the total lack of cable management.

My desk, overview

Probably the most obvious characteristic is the big display.

At 8K and 65 inches, the Samsung Neo QLED 8K QN900D takes most of the space on my 200×50cm desk. While far from perfect, with a surface equivalent to 4×4K or 16×1080p, it's fantastic for interacting with and keeping an eye on tons of windows at once.

The sound system is a pair of Focal Alpha 80 Evo studio monitors I adore, connected to a Mackie Mix5 mixing table which has caused me a lot of troubles but does the job of mixing the audio from 2 computers at once.

Computers at my desk

There are, in fact, 3 computers in total.

Inputs on my desk

To control them, I wouldn't deviate from the HHKB line of products; lately a Happy Hacking Keyboard Hybrid Type-S Snow. I resisted Type-S for far too long, I love its feel. For the pointer, I alternate between a Logitech MX Master 3S and a Logitech MX Ergo.

Though my desk can be raised and lowered electrically, I rarely stand in front. My Steelcase Gesture chair is quite comfortable.

Also pictured are:

Update (2024-09-27): Added the SlimBlade Pro trackball to the setup, and documented my music composition gear there.

Espresso on the go

Mobile espresso gear, packed, with a banana for scale

Here's my mobile espresso setup, that I use when I'm out and about.

It has nothing on my main setup but size.

Add roasted beans, a scale & a kettle. Compact yet capable.

Mobile espresso gear, unpacked, with a banana for scale

Of course there are more practical non-espresso options.

A few tunes

Early study. It ain't good, but it's mine.

Played with (roughly) the constraints of a Game Boy first.

Dropped the constraints, kept a video game feel.

I'd like to find the time and motivation to make more.

Main espresso gear

I love lighter-roast espresso. Here's my main setup, that I use regularly at home.

Overview of my main espresso gear

From left to right then back to front:

Coming soon:

Dump off landing

My landing page had a “dump” section. Bringing back this blog is the perfect opportunity to move its content to a post.

Old stuff

Future stuff?

Should either become more concrete, it'll move to my creations.

Videos

What a time sink video can be.

Random shader I made

The puzzle of motivation — Dan Pink

Growing a Language — Guy L. Steele Jr.

Transcript.

Programming Obesity: A Code Health Epidemic — Aaron W Hsu

Chromebook for devs

This post was migrated from Medium.

Those notes assume the reader is very familiar with Unix environments. Get out whilst you can.

Put your Chromebook in developer mode. Read carefully about the procedure and its implications. Be ready to wipe your Chromebook again if things go South.

Use Crosh Window so keyboard shortcuts won’t get swallowed.

Install Ubuntu

Install Crouton, get a chroot up and running:

crosh> shell
chronos@localhost ~ $ sudo sh -e ~/Downloads/crouton -r trusty -t xiwi -e

Convenience launchers

/usr/local/bin/s

Useful to quickly open a shell or run a command in the chroot.

crosh> shell
chronos@localhost ~ $ sudo tee /usr/local/bin/s <<EOF
#!/bin/sh
exec sudo enter-chroot -l "\\$@"
EOF
chronos@localhost ~ $ sudo chmod a+x /usr/local/bin/s

/usr/local/bin/x

Starts your X environment.

crosh> shell
chronos@localhost ~ $ sudo tee /usr/local/bin/x <<EOF
#!/bin/sh
exec sudo enter-chroot xinit
EOF
chronos@localhost ~ $ sudo chmod a+x /usr/local/bin/x

Configure apt

Make those changes before installing anything.

/etc/apt/apt.conf.d/90noextras

Not much room on your average Chromebook, so let’s minimize disk utilization.

APT::Install-Recommends “false”;
APT::AutoRemove::RecommendsImportant “false”;
APT::AutoRemove::SuggestsImportant “false”;
Acquire::Languages “none”;

/etc/apt/preferences.d/backports

Backports are a no-brainer for zsh users, as our favourite shell won’t have manpages otherwise. We enable them for all packages here.

Package: *
Pin: release a=trusty-backports
Pin-Priority: 500

/etc/apt/sources.list

Pick and choose, I doubt you want them all…

deb http://us.archive.ubuntu.com/ubuntu/ trusty main restricted universe multiverse
deb http://us.archive.ubuntu.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://us.archive.ubuntu.com/ubuntu/ trusty-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu trusty-security main restricted universe multiverse
deb http://extras.ubuntu.com/ubuntu trusty main
deb http://archive.canonical.com/ubuntu/ trusty partner
deb http://repos.azulsystems.com/ubuntu stable main
deb http://dl.google.com/linux/chrome/deb/ stable main
deb http://dl.google.com/linux/chrome-remote-desktop/deb/ stable main
deb http://linux.dropbox.com/ubuntu trusty main
deb http://downloads.hipchat.com/linux/apt stable main
deb http://repository.spotify.com stable non-free
deb http://archive.getdeb.net/ubuntu trusty-getdeb apps games
deb https://get.docker.io/ubuntu docker main
deb http://debian.sur5r.net/i3/ trusty universe
deb http://winswitch.org/ trusty main
deb http://debrepos.franzoni.eu/atom squeeze main
deb http://ppa.launchpad.net/webupd8team/sublime-text-3/ubuntu trusty main
deb http://ppa.launchpad.net/git-core/ppa/ubuntu trusty main
deb http://ppa.launchpad.net/nowrep/qupzilla/ubuntu trusty main
deb http://ppa.launchpad.net/noobslab/indicators/ubuntu trusty main
deb http://ppa.launchpad.net/mc3man/trusty-media/ubuntu trusty main
deb http://ppa.launchpad.net/hugegreenbug/cmt2/ubuntu trusty main
deb http://ppa.launchpad.net/modriscoll/nzbget/ubuntu trusty main

First apt-get update

apt-get update will complain about missing keys. To import them, use something like:

% sudo apt-key adv --keyserver pgp.mit.edu --recv
1234567890ABCDEF FEDCBA0987654321

You should be verifying those PGP keys. [insert thought-through security blah blah everybody will ignore]

Configure the X session

As i3 maps quite a lot under its modifier, I use Mod4 (presented by i3 as “win” during the configuration assistant) but make it Right Alt instead of the Search key to avoid any conflicts. Sorry to users of non-US keyboards.

~/.xinitrc

#!/bin/sh -e
xrdb -merge ~/.Xresources
xmodmap ~/.Xmodmap
exec zsh -lc 'exec i3'

~/.Xmodmap

remove mod1 = Alt_R
add mod4 = Alt_R

~/.Xresources

Really a matter of taste…

URxvt*foreground: white
URxvt*background: black
URxvt*cursorColor: red

Running VPNs

Make sure the resolvconf package isn’t installed so /etc/hosts will be correctly written, both in the crouton chroot and on the host (as crouton ties them together).

TUN devices get automatically destroyed by the network manager, shill, which will break OpenConnect, vpnc and OpenVPN. To disable this behaviour until the next boot:

crosh> shell
chronos@localhost / $ sudo initctl stop shill && sudo shill --device-black-list=tun0,tun1

What’s next?

I kept most of my personal configuration out of this document, but I’d be happy to explore more in future posts. Let me know what you’d like to see covered on Twitter!

pcma: Page Cache My Assets

Many services rely on a dataset stored on disk.

If accesses are frequent and non-linear, performance remains reasonable as long as they are cached in memory. Then suddenly, some background job is triggered, a backup for example, and the data gets evicted from the page cache. Performance drops.

In many cases this is acceptable. The service throughput drops. In a request-reply model, requests get queued. But if latency is not critical, remains below the client timeout, and if the machine is dimensioned properly, data will be cached again, the service will catch up. The temporary slowdown was acceptable.

In other cases, this is catastrophic. Think high-frequency trading.

Here comes pcmad, the “Page Cache My Assets” daemon. It simply locks and unlocks files in memory as requested. When a file is locked, it won’t be evicted, whatever happens on the server. If the system runs out of memory, the oom-killer would be likely to kill this daemon first, so the upstart job and systemd unit indicate that killing pcmad should be avoided at all costs.

A simple and documented protocol, based on ØMQ and MessagePack, makes pcma easy to integrate with your existing services. For scripts, we also ship a client.

The project reached 0.2.0. It isn’t stabilized yet, so the protocol might not remain backward-compatible. However the code is simple and passed reviews, so feel free to give it a go!

Ubuntu Precise MOTD

--- /etc/pam.d/login.before
+++ /etc/pam.d/login
@@ -85 +85 @@
-session optional pam_motd.so
+# session optional pam_motd.so

A similar change is required for all PAM services showing the MOTD, often including sshd.

--- /etc/pam.d/login.before
+++ /etc/pam.d/login
@@ -85 +85 @@
-session optional pam_motd.so
+session optional pam_motd.so noupdate

Again, the same change is required in all services using pam_motd.so. You also need to disable the execution on boot with this change:

--- /etc/init/mounted-run.conf.before
+++ /etc/init/mounted-run.conf
@@ -22 +22 @@
- [ -d "/etc/update-motd.d" ] && run-parts --lsbsysinit /etc/update-motd.d > /run/motd &
+ # [ -d "/etc/update-motd.d" ] && run-parts --lsbsysinit /etc/update-motd.d > /run/motd &

Finding the Fedora 15 installer

On fedoraproject.org, the new prominent installation media with Fedora 15 is the live desktop, eg. Fedora-15-x86_64-Live-Desktop.iso.

Download it, boot it in VirtualBox. Gnome 3 will use the fallback mode. How do you start the installer? Well, turns out it is normally made prominent by a Gnome Shell extension (~/.local/share/gnome-shell/extensions/Installer@shell-extensions.fedoraproject.org/).

Did I mention that in fallback mode, which you can enjoy with most virtualization technologies, Gnome Shell is not running? Instead the installer ends up in the menu. Somehow it took me over 15 minutes to find it. Most people would be ashamed, I blog about it.

In case you wonder how to start the Fedora 15 installer in your virtual machine (SEO)… You can find it in Applications → System Tools → Install to Hard Drive. Alternatively, Alt+F2, liveinst. My bad for not partaking in the testing effort…

It makes me feel old and nerdy, but I love console-based installation processes.

7clock

I wanted a minimal, fullscreen clock I could run without X. In the longer term, I am thinking about a locking console “screensaver”; (more about that later). I ended up writing a small library (240 lines and counting) to render a 7-segment display in monospaced text at any “resolution”.

The end result adapts to the console size, instantaneously if resized. It lacks any options, if I'm bored again I might come back to it. It should be quite efficient, though more trivial optimizations are possible.

Older screenshots from various prototypes (the test program is available alongside the clock; as usual, I recommend sticking to the instructions in INSTALL at the root of the repo):

errnos

As a Technical Support Engineer at Red Hat, I got to read a lot of logs, error messages and code. One of my pain points was errnos.

They rely on magic numbers, which is perfectly understandable given their use cases (eg return values for system calls). But obviously, we're not going to be using magic numbers everywhere, hence they are defined as constants for the C preprocessor.

Numbers are not very user-friendly, so why not display them in a human-readable format when a human might want to read it? strerror comes to the rescue! Unfortunately, not every piece of code presenting errors to the user or administrator uses it. There are various reasons for that:

strerror strings are very readable; for example, it will turn ENOMEM into Cannot allocate memory under OSX. I will not lose too much of your time on the annoyances caused by weird usages of errnos, though I encountered quite a few already: they usually do not delay troubleshooting too much if you're not too trusting. For example ENOTDIR, represented by glibc's strerror as Not a directory, indicates when returned by keyctl_search(3) that one of the keyrings is a valid key that isn't a keyring.

The tricky part now? From a number or an strerror description in logs or an output, there is no trivial way to establish which constant to look for. What's more, C preprocessor constants are not available at runtime, you'd need the headers at hand, cpp, and your own list to go through as there isn't a standard one! For example, if you see 44, 0x2c or Channel number out of range, what should you git grep for in the affected software, libc and/or kernel? ECHRNG, of course!

I'd love to give you a simple table listing each errno constant, its representation in decimal and hexadecimal and its strerror description, but there are a few reasons why I can't:

However, this led to the creation of a simple command-line utility, errnos. Build and run it on the system you investigate, or a similar one (same operating system, same libc, same CPU architecture), and you will get something you can store and grep at will. It could also make an ironic wallpaper for your child's room, but don't blame me if they have nightmares of production systems going down.

For once, I used glib as I needed a hashtable of lists and couldn't be bothered implementing those for the millionth time in history (comp. sci. students who have to do so tonight, I share your frustration). There's a limited amount of magic involved in the build process, so please just clone the repository and stick to the build instructions unless you have time to lose.

The first column gives the number in its decimal representation, the second in hexadecimal. The third is either its strerror representation between double quotes or a preprocessor constant.

To close this post, here is the end of its output on my Mac:

$ errnos | tail
Stopped looking at 1128
99    0x63    "Not a STREAM"
99    0x63    ENOSTR
100   0x64    "Protocol error"
100   0x64    EPROTO
101   0x65    "STREAM ioctl timeout"
101   0x65    ETIME
102   0x66    "Operation not supported on socket"
102   0x66    EOPNOTSUPP
103   0x67    "Policy not found"
103   0x67    ENOPOLICY

nato, superglob

When you end up having to dictate Unix commands over the phone, you quickly learn from your colleagues that optimism doesn’t compensate your French accent. Turns out cancelling your lovefilm subscription triggers a similar problem.

The most common answer is to involve the NATO phonetic alphabet. Looking up a table pinned to your desk is tedious, getting fluent takes time. That’s where nato kicks in.X

Not much to say, really:

$ nato 'lspci|grep VGA'
lima, sierra, papa, charlie, india, pipe, golf, romeo, echo, papa, space, uppercase victor, uppercase golf, uppercase alpha,
$

After this very useful piece of software (at least to some), let’s have a look at something stupid and useless (I won’t even bother trying to explain the silly reason I needed it). If you ever have a good, real-life reason to run superglob, please E-mail me right away, I’ll owe you the drink of your choice. The idea is to build a glob matching all the arguments provided, but as little more as possible (and we’re far from being clever here).

$ superglob Hello World
[HW][eo][lr]l[do]
$ superglob Foo 'Bar Mitzvah'
[BF][ao][or]*
$ superglob /lib64/libpamc.so.0 \
  /lib64/libpamc.so.0.81.0 /lib64/libpam_misc.so.0 \
  /lib64/libpam_misc.so.0.81.2 /lib64/libpam.so.0 \
  /lib64/libpam.so.0.81.5
/lib64/libpam[._c][.ms][ios][.os][.0c]*

A good example of a really silly behaviour (here, *ahello would likely be the best choice):

$ ~/usr/bin/superglob ahello bahello cbahello
[abc][abh][aeh][ehl][el][lo]*

mkpasswd

You want passwords that are identical with AZERTY, QWERTY and QWERTZ keyboards? And easier to type on a mobile phone (no uppercase)? Alternatively, with the characters you list and just those?

You don’t want to choose a number of characters, how many digits and special characters should appear (or any nonsensical policy when you know what you are doing), but rather would rely solely on the number of bits of entropy you’ll get?

Generated from /dev/random, as it’s a good source of entropy (under a recent Linux kernel at least).

Check out mkpasswd.