Art Chaidarun https://chaidarun.com en-us Sun, 19 Apr 2026 03:03:12 GMT Sun, 19 Apr 2026 03:03:12 GMT How to Officiate a Wedding in New York City https://chaidarun.com/officiate-wedding-nyc Tue, 23 Sep 2025 00:00:00 GMT https://chaidarun.com/officiate-wedding-nyc Last week I emceed my friends’ wedding as a one-day marriage officiant. Here’s a condensed, plain-English outline of exactly what needs to happen for that.

  1. The couple gets a marriage license. They go here and choose “Marriage License”. Either in-person or virtual will work. The virtual flow still requires an in-person appointment anyway. The license will be valid for only 60 days, so the appointment should be scheduled to occur <60 days before the wedding. The next available appointment slot may be a month or more from today, so plan accordingly.
  2. The officiant gets an officiant license. They print and complete the officiant application, which requires the marriage license number from the previous step1. The officiant goes here and chooses “Marriage Officiant” to schedule an appointment. They just need to be 18 years old—no exams or fake churches.
  3. At the wedding, everyone signs the officiant license. That’s two spouses, the witness(es), and the officiant. The license has space for two witnesses, but only one is legally required. Any adult guest can be a witness.
  4. The officiant exchanges both licenses for a marriage certificate. They go here and schedule a “Record Room” appointment2 at the same physical office as in step 1. This must take place within 5 days of the wedding.

These instructions assume that you don’t mind talking to city employees face-to-face. Alternatively you can fill out the paper forms and money orders by yourself, send them back and forth via snail mail, and cross your fingers.

  1. If the couple just wants a city hall marriage without any kind of wedding ceremony, this is the wrong guide. Stop reading and try the “Marriage Ceremony” option here. Good luck! 

  2. Shout out to Reddit. This same-day method works but is totally undocumented by the city. 

]]>
Simplest Fractions Per Percentage https://chaidarun.com/simplest-fractions-per-percentage Sun, 05 Jan 2025 00:00:00 GMT https://chaidarun.com/simplest-fractions-per-percentage When you see a statistic such as “75% of reviewers liked this movie”, you can’t help but wonder whether there were only four reviewers.

What about other, less obvious percentages? Here are the smallest possible denominators that correspond to each integer percentage value:

Percentage Simplest fraction
0% 0 out of 1
1% 1 out of 51
2% 1 out of 34
3% 1 out of 26
4% 1 out of 21
5% 1 out of 17
6% 1 out of 15
7% 1 out of 13
8% 1 out of 12
9% 1 out of 11
10% 1 out of 10
11% 1 out of 9
12% 1 out of 8
13% 1 out of 8
14% 1 out of 7
15% 2 out of 13
16% 1 out of 6
17% 1 out of 6
18% 2 out of 11
19% 3 out of 16
20% 1 out of 5
21% 3 out of 14
22% 2 out of 9
23% 3 out of 13
24% 4 out of 17
25% 1 out of 4
26% 4 out of 15
27% 3 out of 11
28% 2 out of 7
29% 2 out of 7
30% 3 out of 10
31% 4 out of 13
32% 6 out of 19
33% 1 out of 3
34% 8 out of 23
35% 5 out of 14
36% 4 out of 11
37% 3 out of 8
38% 3 out of 8
39% 7 out of 18
40% 2 out of 5
41% 5 out of 12
42% 3 out of 7
43% 3 out of 7
44% 4 out of 9
45% 5 out of 11
46% 6 out of 13
47% 7 out of 15
48% 10 out of 21
49% 17 out of 35
50% 1 out of 2
Percentage Simplest fraction
51% 14 out of 27
52% 9 out of 17
53% 7 out of 13
54% 6 out of 11
55% 5 out of 9
56% 5 out of 9
57% 4 out of 7
58% 7 out of 12
59% 10 out of 17
60% 3 out of 5
61% 8 out of 13
62% 5 out of 8
63% 5 out of 8
64% 7 out of 11
65% 11 out of 17
66% 2 out of 3
67% 2 out of 3
68% 11 out of 16
69% 9 out of 13
70% 7 out of 10
71% 5 out of 7
72% 8 out of 11
73% 8 out of 11
74% 14 out of 19
75% 3 out of 4
76% 10 out of 13
77% 7 out of 9
78% 7 out of 9
79% 11 out of 14
80% 4 out of 5
81% 9 out of 11
82% 9 out of 11
83% 5 out of 6
84% 11 out of 13
85% 6 out of 7
86% 6 out of 7
87% 7 out of 8
88% 7 out of 8
89% 8 out of 9
90% 9 out of 10
91% 10 out of 11
92% 11 out of 12
93% 13 out of 14
94% 15 out of 16
95% 18 out of 19
96% 22 out of 23
97% 28 out of 29
98% 39 out of 40
99% 66 out of 67
100% 1 out of 1

Calculator for arbitrary decimal inputs

What if you have a decimal like 0.3105 instead of an integer percentage? How do you determine that its denominator must be at least 161? Try this calculator!

It even works for inputs greater than 1, for example 3.14 → 22 / 7.

Methodology

I’m defining simplicity as having a small denominator. A percentage value of 50% might be describing “37 out of 74”, but a simpler explanation is “1 out of 2”.

Percentages are usually written as integer values, but in practice, you the reader have no idea whether the author rounded the percentage (66.66% → 67%) or simply truncated it (66.66% → 66%). I’m therefore taking both approaches into account and saying that the simplest possible ratio describable as 66% is “2 out of 3” rather than “19 out of 29”.

I created this page because I failed to find these answers via Google search. So for SEO purposes: you might also call these values the simplest possible fractions per percentage, the simplest ratios for each percentage, the smallest possible denominators for each percentage value, the smallest possible sample sizes for each percentage, or the smallest possible population sizes for each percentage.

Here’s the quick and dirty JavaScript one-liner that I used to generate the data:

for(p=0;p<101;++p){l:for(d=1;true;++d){for(n=0;n<=d;++n){f=100*n/d;if(Math.round(f)==p||Math.floor(f)==p){console.log(p,n,d);break l}}}}
]]>
Where Should We Eat https://chaidarun.com/eat Sun, 01 Dec 2024 00:00:00 GMT https://chaidarun.com/eat I’m not picky and I promise that I’m always equally down for any of the options below. Please just pick your favorite, choose randomly, or suggest another restaurant!

NYC

I haven’t eaten out much in NYC yet. Hit me up and help me build this list!

Pittsburgh

  • 98K Hamburger
  • Bird on the Run
  • Blue Sky
  • Butterjoint
  • Cafe 33
  • Casbah
  • Chipotle
  • Choolaah
  • Cilantro & Ajo
  • City Kitchen
  • Condado
  • Duo's Taqueria
  • El Sabor
  • Five Guys
  • Fujiya
  • Golden Gai
  • Industry Public House
  • Istanbloom
  • Kahuna
  • Kiin
  • La Feria
  • Mad Mex
  • Masala House
  • McDonald's
  • Mercurio's
  • Mola
  • Noodlehead
  • Park Bruges
  • Pho and Roll
  • Piada
  • Point Brugge
  • Roots
  • Senyai
  • Shake Shack
  • Silk Elephant
  • Smiling Banana Leaf
  • Square Cafe
  • Teppanyaki Kyoto
  • Thai Table & Kitchen
  • Took Took 98
  • Urban Tap
  • Wendy's
  • Yoshino

Bangkok

  • KFC
  • Kub Kao Kub Pla
  • Maisen
  • MK
  • Mo-Mo-Paradise
  • MOS Burger
  • Pan Pan
  • Sizzler
  • Swensen's
  • Thong Smith
  • Literally any restaurant in any mall near BTS

Not picky

It takes around 24 hours of (accidental) fasting for me to start feeling hungry, I’ve historically been prone to eating the same meal every day for months on end, I once consumed nothing but Soylent for several weeks… you get the idea. I eat to live.

However I do enjoy eating out quite regularly for social purposes and I want to contribute my fair share toward the sometimes difficult decision of where to eat1. This page’s “menu of menus” already accounts for practical considerations apart from the food itself, such as price and convenience, so please believe me when I say that I’m more than happy to leave the final choice up to you. Or even random chance!

  1. Yes, Beli is a thing and I do have an account that’s collecting dust. People have reasonably assumed that I’d be into it given how much personal tracking I do already, but I find that Beli is way too much paperwork and granularity for a subject that I care relatively little about. 

]]>
How to Disable Contactless Payment on a Credit Card https://chaidarun.com/disable-contactless-payment Sat, 19 Oct 2024 00:00:00 GMT https://chaidarun.com/disable-contactless-payment An exercise in hyperoptimizing my daily life.

What’s wrong with tap to pay?

Nothing, in general. It’s convenient and secure. But here’s my situation:

  • I want to pay for most things by tapping my phone, not my credit card. Apple Pay / Google Pay removes the need to carry all my physical credit cards everywhere.
  • I want to carry exactly one physical credit card as a backup for vendors whom I can’t easily pay by tapping my phone, e.g. most sit-down restaurants in the US.
  • I want to minimize my everyday carry by storing that backup card inside my phone case’s sleeve. I haven’t bothered using a standalone wallet in years.

The problem with this setup is card clash. If I hold my phone up to a scanner, how does the scanner know whether to charge my phone’s Google Pay vs. the physical credit card?

Another major annoyance is that Android beeps whenever it detects an NFC device in close proximity, and there’s no way to turn off that “feature” without turning off NFC itself. Housing my NFC-enabled credit card inside my phone case causes Android to beep every single time I unlock my phone.

The solution

Shining my phone’s flashlight through my plastic credit card revealed a long metal wire, the NFC antenna, running along the card’s perimeter. Like Alexander cutting the Gordian knot, I pulled out some scissors and made a tasteful half-centimeter snip just below the magnetic stripe. Problem solved!

Now I can keep my backup credit card inside my phone case without having it interfere with Google Pay or cause undue beeping. Although the physical card’s contactless payment feature is toast, I’ve verified that its chip and magnetic stripe both still work.

I don’t feel too bad about severing the antenna wire since I’ve already added the backup card itself to my digital Google Pay wallet, meaning that I can still use my phone to make contactless payments on that card’s behalf if I really need to for whatever reason.

Appendix: various non-solutions

Honestly, the main purpose of this whole post is to remind myself why I resorted to vasectomizing my own credit card. Here’s everything else I tried:

  • Removing the card from my phone case every time I pay for something. This obvious solution to the card clash problem wastes a few seconds, puts extra wear and tear on both the card and my phone case, introduces some risk of dropping the card and/or my phone, doesn’t get rid of Android’s unlock beeps, and is just plain annoying to do multiple times a day.

  • Requesting a contactless-less card. I called my credit cards’ issuers, carefully explained my use case, and asked whether they could send me a replacement card that totally lacks the contactless payment feature (as opposed to disabling it remotely on their end). None of the issuers said they could do so.

  • Erasing data. Apps like NFC Tools allow you to modify data on certain NFC devices, but unsurprisingly my credit cards are read-only.

  • Lots of aluminum foil. I hoped that an insulating layer of aluminum foil sandwiched between my phone and the card would allow me to choose a payment method by simply holding my phone in different orientations: scanning the back side of my phone would use the physical card’s contactless payment feature, while scanning the front side of my phone would use Google Pay. Unfortunately, my phone’s NFC reader is located in the middle of its back side and doesn’t work from the front.

  • Less aluminum foil. Since Google Pay works only on my phone’s back side, covering the entire back with foil isn’t an option. But what if I were to tape some foil directly onto the credit card instead, using only enough foil to block the card’s contactless receiver? Maybe my phone could still get enough signal passing through the unshielded portions of the card for Google Pay to work, kind of like how QR codes still work even if you stick a random logo in the middle of them. Alas, this approach fails because the credit card’s antenna takes up so much space, practically covering the entire card. Foil foiled again.

  • Dishonorable mention: microwaving the credit card. I didn’t actually try this method. Someone on Reddit suggested it as a way to disable the chip, which is overkill for my use case anyway.

]]>
The Camino de Santiago in a Million Easy Steps https://chaidarun.com/camino-de-santiago Wed, 25 Sep 2024 00:00:00 GMT https://chaidarun.com/camino-de-santiago I just spent 34 days walking the Camino de Santiago, a medieval pilgrimage route that starts in France and runs 800 km across Spain to the tomb of St. James the Apostle in Santiago de Compostela. This UNESCO-recognized tradition began 1200 years ago and is now undertaken, at least in part, by around half a million people every year.

Jump to: Background · Tips · Language · Observations · Memories · Trivia · Gallery

Background

Fitness: I’m in decent shape for a 30-year-old, although I normally avoid cardio whenever possible. I can count on one hand the number of hikes I had done in life before the Camino, each of which lasted well under an hour.

Language: I studied Spanish for a couple years at an American high school and completed the Duolingo course in 2013 but never had much occasion to use Spanish in real life. A little knowledge went a long way while traveling through rural Spain, although I also met some pilgrims who seemed to get along fine speaking only English.

Route: I took the French Way (Camino francés), the most popular and historically important one of the many established Camino routes. After giving serious thought to the more scenic Northern Way (Camino del Norte), I chose the French Way for its cultural significance—the world offers plenty of options beyond the Camino if you’re mainly interested in natural beauty.

Religion: I’m quite familiar with Catholicism but not religious myself. According to the Pilgrim’s Reception Office, over half of all pilgrims make the trip for non-religious reasons.

Motivation: I had some free time and wanted to really disconnect from my regular life—especially work, tech, and American society1. A week before my sabbatical was scheduled to begin, I happened across this fateful Reddit comment that informed me of the Camino’s existence. My month in Spain ended up being the longest time I’ve spent away from a computer keyboard since I was 2 years old!

Tips

The standard instructions for how to do the Camino are to just follow the painted yellow arrows and collect stamps, but there’s a lot more worth knowing before you begin.

Plan travel to your start point. The French border town of Saint-Jean-Pied-de-Port serves as the classic starting point since historically most foreign pilgrims entered Spain through it. I flew into Biarritz Airport, took a bus to Bayonne, and finally took a train to St. Jean. It’s all clearly laid out in Google Maps.

Prepare your money. Use a credit card with no international fees wherever possible, and select “pay in EUR” (as opposed to USD) for better exchange rates. Waiters will always bring you a handheld card reader instead of taking your card—that’s an American thing. For cash, I used a Schwab debit card to avoid all ATM fees2. Tipping isn’t expected, but it’s common to round up to the nearest euro when paying in cash. Waiters will generally leave you alone, so you’ll need to flag them down once you’re done eating.

Beat the heat. I’m a person who runs hot and hates feeling sweaty, so August was an unfortunate choice of starting month. I made up for it by going all out on sun protection: I wore hiking pants and long-sleeved sun hoodies every day, I chose a lightweight backpack with good ventilation, I attached a UV hiking umbrella to it, and I timed each day’s walk to end before the outdoor temperature reached 20 °C—even if that meant strapping on a headlamp and starting at 5am3, hours before sunrise.

Know mass etiquette. Non-Catholics and even non-Christians are still welcome to attend church services. The pilgrim mass in Santiago is especially popular. Just bring €10 or so for donation, don’t go in shirtless or barefooted, follow what everyone else is doing, and most importantly: do not accept communion (bread and wine) from the priest. You can either stay seated or cross your arms in an X over your chest to receive a blessing from the priest instead4.

Mobile apps I used:

  • Buen Camino for navigation. This was the Camino app that I most often saw other pilgrims using too. It shows your current GPS location on a satellite map of the route and also provides extremely helpful advice regarding alternate paths and challenging sections.
  • Google Maps for looking up local businesses’ locations, prices, and reviews. Hours are pretty hit or miss, especially in rural villages.
  • Google Translate for quickly translating restaurant menus and communicating with pilgrims who spoke neither English nor Spanish.
  • WhatsApp and Booking.com for reserving hostel beds. I’d typically write something simple like “Hola, quiero hacer una reserva para el 12 de agosto”.
  • Gronze for hostel prices and reviews.
  • Camino Pilgrim for stage planning. Since I had already bought a return flight that afforded me 34 days of walking, I took Gronze’s 33 stages and added a new one to the mountainous stretch between Astorga and Sarria. Camino Pilgrim contains the locations and amenities of over 200 towns on the Camino, which allowed me to easily plan stages with convenient stopping points and roughly equal lengths.
  • Airalo for mobile data. I paid $18 for a 10 GB eSIM that lasted 30 days. Hostels usually have free WiFi.
  • Chronofile, an app that I created myself, for tracking my daily activities. I slept for 7.93 hours and walked for 5.67 hours each day on average.
  • Google Keep for journaling. I found it helpful to write down the names of every pilgrim I met since I would so often run into them again sooner or later.

Language

Spain is less formal than Latin America. If you studied Spanish in the US, you’re probably aware that Spain has informal vosotros conjugations for second-person plural (i.e. “y’all” forms) while countries like Mexico always use formal ustedes. What surprised me more was that Spaniards rarely use formal second-person singular (usted) either, instead opting for informal even when addressing strangers and customers.

The lisp is real. Most Spaniards pronounce the letters c (before e or i) and z as th. For example, cerveza (beer) becomes “thervetha”. Despite my years of studying Latin American Spanish, I’m now stuck saying grathias for better or worse.

Common words and phrases on the Camino:

  • vale = “OK”. I must have heard this at least once per sentence on average.
  • Buen camino = “Have a good Camino”, similar to bon voyage.
  • albergue = “hostel”.
  • hospitalero / hospitalera = “hostel caretaker”, often volunteers.
  • meseta = the flat, dry plateau that covers central Spain from Burgos to Astorga.
  • aseo = “restroom”. This relatively formal word appears mostly on signage, while in spoken conversation people tend to say baño (bathroom).
  • menú del día = “menu of the day”. Most restaurants offer lunch as a set menu that includes bread, water or wine, an appetizer, a main dish, and dessert. That’s what menú normally refers to, so you’ll need to ask for carta if you want to see what Americans would typically call the menu.
  • Buen provecho = “Enjoy your meal”, similar to bon appétit.
  • Dime = “What can I get you?” Literally “tell me”.
  • vaso de agua = “glass of (tap) water”. All tap water in Spain is safe to drink and free at restaurants by law, but they’ll never give it to you unless you ask for it specifically.
  • ¿Coreano? = “Are you Korean?” Most of the East Asians I met on the Camino were Korean, thanks to a famous Korean author’s 2006 memoir. Once I even saw a Korean-run hostel that served ramyeon for pilgrim dinner.

Observations

Apart from obvious markers like language and currency, many subtler aspects of Spanish culture made it clear that I wasn’t in Pennsylvania anymore:

  • Restaurant beef is almost always veal, even in burgers. The most familiar beef that I ever encountered was during a post-Camino layover in Barcelona, where I had to try a Spanish McDonald’s exclusive: the McExtreme with gouda and pulled pork.
  • After wheat, sunflowers are the most common crop along the Camino. Sunflowers with the misfortune of having grown near the trail would often have individual seeds plucked out by pilgrims to form smiley faces (which I found rather tasteless).
  • Most hostels and hotels give you a single long, thin pillow that spans the width of the entire bed. It’s more or less impossible to stuff one of these anacondas into the flimsy disposable pillowcase that hostels provide; you need to grab one end of the tubular pillowcase and pull the pillow through it instead.
  • Fanta contains real fruit juice and tastes so much better than in the US—and this is coming from someone who really doesn’t understand all the fuss over Mexican Coke. Fanta Naranja tastes like actual orange juice, not liquified candy. Fanta Limón opened my eyes to how good soda could be.
  • The so-called siesta is real, although the more neutral term for small businesses’ midday break is descanso (rest). Many Spanish restaurants will open for only two hours at lunch and again at dinner. Show up ASAP to ensure that you get a table.
  • The tap water in the meseta tastes like ass. No wonder restaurants won’t give it to you. Galicia and the Basque Country have much softer water.

Memories

I met the mildly famous painter Joan Bueno by chance in a tiny village of 115 people. As soon as I sat down for lunch at the table next to his, he silently started sketching me unprompted. Once he finished I wondered whether he’d try to sell me the drawing, but no—he simply told me to take a photo and then flipped through the notebook, revealing dozens more sketches of people and places from his daily life.

I asked him if he was a pilgrim. He said that he was just visiting town for his art exhibition tomorrow but that he had walked 26 times before! After two low-tech weeks of speaking mostly Spanish, it felt surreal to find this 91-year-old Barcelonan in the middle of nowhere who vigorously spoke perfect English and recited his social media URLs from memory.

I met a pair of Mexican-American pilgrims at dinner that evening. We commiserated over our depraved methods for eating supermarket yogurt without having packed any spoons. A few days later, in an even smaller village of 66 people, I ran into them again and they invited me to try their hostel’s guitar. They warned me that it was out of tune, but of course that’s easy to fix—unlike the extremely untuned piano I had found earlier.

Unfortunately the problem was much more serious: two of the poor guitar’s six tuning knobs were completely missing! A nearby hospitalera noticed my plight and handed me some (also broken) pliers, saying that that’s how they’ve been managing to tune it.

Hours later as the other pilgrims filed off to bed, I thanked the hospitalera for waiting so long in the room with us. She replied that she wasn’t waiting for us and had just enjoyed hearing me play, saying that it reminded her of someone she knew many years ago.

When I asked her which song she had liked best, she answered that Recuerdos de la Alhambra gave her chills. Finally, I thought, I found a Spaniard in Spain who could be moved by the nostalgia of this Spanish guitar piece, arguably the most famous one in the classical repertoire, a piece I had practiced hundreds of times before… but it turned out that she had actually recognized it from the 2018 Korean drama of the same name.

My stay in the charming town of Los Arcos gradually devolved into a comedy of errors. At noon I checked into the municipal hostel, which at €8 was among the cheapest on the Camino. Upon seeing their one small room crammed with 28 bunks and at least as many flies, I decided to cut my losses and sleep elsewhere. (A week later, another pilgrim told me she wished she had done the same.)

The new place’s receptionist helpfully showed me to their rooftop laundry area. But when I came back later to collect my clothes, the rooftop door was locked?! I thought better of going into town wearing only swim trunks and also of trying to rake the door’s lock, so I texted the receptionist and ended up waiting several hours for him to return.

By the time I escaped the building, a huge festival was already in full swing. Apparently today was the feast day of the town’s patron saint. All supermarkets and restaurants had closed for the holiday, so I and some other pilgrims wandered the reveler-flooded streets looking for vending machines. I found one and quickly bought a pack of four cheese sandwiches, failing to notice that what I had really bought was… four massive hunks of cheese. I lived off 1600 calories of cheese that day.

Wanting to pack light, I brought rubber Teva sandals to Spain as my only pair of footwear. This caused much confusion among hospitaleros who tried to show me where I could store my nonexistent hiking boots. One group of Taiwanese pilgrims was particularly horrified to learn that I had been hiking through forests and over the Pyrenees mountains in sandals. When I passed them on the trail a couple days later, they were all wearing sandals. I never got a single blister.

On day 34 of my Camino, I started walking the final stage to Santiago at 5am in pitch black darkness. I had read about the Pilgrim’s Reception Office receiving more than a thousand pilgrims in a single day, so I wanted to arrive as soon as the office opened at 9am. Finishing my Camino so early in the morning did, however, leave me with several hours to kill before hostel check-in at 1:30pm. I headed over to the hostel anyway to loiter in its lobby, where I found that they had a guitalele!

This specimen was in amazingly good condition, much better than the guitar I had played weeks ago. I felt right at home on my main instrument and proceeded to spend the next few hours running through every song I knew, sometimes accompanied by other pilgrims’ singing. Not long after I finally put down the guitalele, an American pilgrim showed up, collected his belongings, and left with them… including the guitalele. I was shocked that the guitalele wasn’t the hostel’s, and everyone else in the room was shocked that it wasn’t mine!

Vibes along the Camino were overwhelmingly positive, but I did encounter two notable forms of antagonism.

One was Camino snobbery: the patently absurd phrase “Jesus didn’t start in Sarria” spray-painted on so many walls5, the 71-year-old Brit who crowed about walking double-length stages last year while calling Fisterra an idiotic scam and people who shuttled their luggage cheaters, and the Czech bro who argued at length with a patient hospitalera about the ridiculousness of seeing an Asian pilgrim walk in the sun with an umbrella and hardly any exposed skin. Clearly he didn’t realize that the pilgrim in question had a photosensitive skin condition and was now sitting right next to him!

The other occasional unpleasantness was anti-Camino graffiti, e.g. “FUCK MASS TOURISM FUCK THE CAMINO” painted in huge letters across the sidewalk on the final bridge into Santiago. I’m not above calling myself a tourist, I think it’s silly to obsess over what makes a pilgrim or tourist or “traveler”, and I’m annoyed by entitlement and inconsiderateness like anyone else. But surely these urban activists must realize that dozens of towns along the Camino, including Santiago itself, exist because of the Camino? It and its commercialization were there first by a long shot.

All that said, you can’t let a handful of people reflect badly on or ruin the experience of millions. I remember the young local boy who haltingly wished me “Good walk” (a very literal English translation of buen camino), the hospitalera who invited me to home-cooked dinner with her daughter and mother since I was her only guest that day, all the highway drivers who waved and cheered as they flew by, the middle-aged Irish pilgrim who told me how his children in Vietnam were his entire world and that I looked just like one of them, and the former hospitalera who seemed to think I was lost and turned around to walk with me until we reached the edge of town.

Trivia

I now give the people what they’re really here for.

  • During the American Revolutionary War, the Continental Congress sent future president John Adams to ask France for financial aid. When his ship started leaking, he and his sons (including future president John Quincy Adams) narrowly avoided death by getting off early at Fisterra, the westernmost point of Spain. They then traveled the entire Camino backwards all the way to Paris!

  • The Milky Way points in the direction of Santiago, and ancient pilgrims used it for navigation. In Spanish, the Milky Way is accordingly known as El Camino de Santiago.

  • Traditionally, a Camino pilgrim would walk from their own house to Santiago and then back home again. The Camino de Santiago is thus a network of many alternate routes: the classic French Way starting in France, the Portuguese Way starting in Lisbon, the English Way starting on Spain’s northern coast, and so on. But even Camino veterans may be surprised to learn of the Antarctic Way, which begins at a Spanish research base in Antarctica, crosses the Atlantic Ocean by boat, and finally joins the Portuguese Way at Pontevedra for a grand total of 14 megameters.

Further reading

  1. My plan A was to work a summer job in Antarctica and for the past few years I’ve been applying to dozens of positions as soon as they open, but it’s getting harder and harder to land a gig there nowadays. Do let me know if you have any USAP connections! 

  2. It takes some paying attention, but you can also get $101 in cash for free when signing up. 

  3. …which is effectively 4am. Spain lies directly south of the UK but has been in the wrong time zone since World War II, when Francisco Franco changed the country’s time zone to match Nazi Germany’s. So when you hear about Spaniards eating dinner at 10pm, remember that it’s really more like 9pm by the sun. 

  4. Source: I attended hundreds of masses across several countries as a Catholic school kid and never once took communion. 

  5. Pilgrims who walk at least 100 km may receive the compostela, a completion certificate awarded since medieval times. The vast majority of modern pilgrims therefore start their trip in Sarria, a town on the French Way located just over 100 km from Santiago. While it’s true that Jesus didn’t start in Sarria, he didn’t start in St. Jean or Jerusalem or anywhere else either… 

]]>
How to Play Guitar by Ear https://chaidarun.com/how-to-play-guitar-by-ear Thu, 28 Dec 2023 00:00:00 GMT https://chaidarun.com/how-to-play-guitar-by-ear While noodling around on guitar as the year wraps up, I thought of the song Auld Lang Syne and—to my pleasant surprise—banged out a fully formed fingerstyle arrangement in one go.

It wasn’t always so simple! I’ve never been interested in music theory for its own sake, I’m far from naturally gifted, and I completely relied on sheet music for many years… until eventually I got tired of hunting down and memorizing the tabs for every single song I wanted to play.

This post outlines some basic tips and tricks for producing attractive (if not exactly virtuosic) song arrangements by ear, as demonstrated in my tab of Journey’s Don’t Stop Believin’ and my cover of Final Fantasy X’s main theme:

Here we’ll apply these methods to a familiar pop tune, Leonard Cohen’s Hallelujah. No prior music theory knowledge required!

1. Assume C major

In piano terms, this essentially means ignoring the black keys, which typically correspond to sharps (♯) and flats (♭).

Most melodies can be played recognizably using only the white keys. This set of notes is also known as C major because a melody that uses only these notes will sound fully resolved to listeners if its final note is a C note1. Jingle Bells, Happy Birthday, Ode to Joy, you name it—all of these melodies end on C when pecked out on a piano’s white keys.

This means that we can set a song in C major by choosing C as its final note. Why does that matter? It’s less obvious than on piano, but C major is also arguably the most straightforward scale to play on guitar. The first three frets contain all the notes of C major and its commonly associated chords, minimizing hand movement.

Contrast this with the B major scale, which uses all of the piano’s black keys and four of the guitar’s frets.

Now that we know that C should be our song arrangement’s last note, how do we go about determining the rest of them?

2. Anchor the melody on C

Most songs alternate between a verse melody and a chorus melody before ultimately ending with an instance of the chorus. Hallelujah’s chorus (and the song itself) ends on the last syllable of the word “hallelujah” repeated four times. If we want this last “-jah” note to be a C, we have two comfortably reachable choices within the guitar’s first three frets: the A string’s third fret and the B string’s first fret2. We’ll choose the former (lower) option since Cohen sings the final “-jah” in a relatively low register, and we want to leave room for the rest of the song’s melody to go above it.

Play this low C a few times and listen to it carefully. Sing “-jah” at that pitch. Then continue singing straight into the song’s second verse, “Your faith was strong, but you needed proof”. Now we can hear how those notes should sound too, but where do we find them on the guitar?

3. Estimate interval sizes and directions

Since we’ve set our arrangement in C major, a scale that we know to be playable within the guitar’s first three frets, it’s not too hard to find that lyric’s notes through trial and error with careful listening. If we play the A string’s third fret as “-jah”, then “Your” is at the D string’s second fret, “faith” is on the open G string, and so on.

We can arrive there by guessing randomly, but that’s hardly efficient. The process becomes much faster if we hear that both “Your” and “faith” constitute small upward jumps in pitch from the preceding note. Hearing the direction and (roughly) the magnitude of such pitch changes is more important than identifying those interval sizes precisely. For example, The Killers’ Mr. Brightside repeats the same note over and over throughout its entire verse, while the climactic “or twoooooo” lyric in a-ha’s Take on Me makes a huge upward jump.

Notice that songs tend to change lyrics across verses while keeping the verses’ notes mostly the same. The opening notes of Hallelujah’s second verse are the same as those of its first verse, “I heard there was a secret chord”. Now that we’ve got the notes down for “I heard”, we can continue using each note to figure out the next one until we finally reach the song’s final “-jah”, the low C that we chose as our arrangement’s tonal center.

We’ve got a full melody line at this point, and if we were flatpicking on lead guitar then we might even call the job finished. But that’s boring—we also want to fill out our sound with chords and percussion!

4. Know the four chords

A chord is a set of notes that sound good when played simultaneously. We can’t just pluck two guitar strings at random frets and expect a decent result. Both notes should be part of the same chord.

The most commonly used chords in the C major scale are C major (yes, it’s named the same thing), G major, A minor, and F major3. Very unscientifically, we can play 80% of pop songs using only these four chords, and learning a few more (Em, Dm, D, E) will get us to 95%4.

So what do we do with these chords? When do we play them, and how do we know which one to use?

5. Keep the melody on top

Chord changes tend to happen on downbeats, evenly spaced points in time that usually coincide with emphasized syllables. Try singing Hallelujah’s first verse:

“I heard there was a secret chord that David played, and it pleased the Lord…”

The loudest syllables are “heard”, “se-“, “Da-“, and “pleased”. These points in time represent possible candidates for chord changes.

Once we have an idea of where a chord change might be happening, we can simply guess a chord to play along with the melody and see whether it sounds right! Since humans perceive high notes more strongly than low notes and we need to keep the melody audible, we should pluck only the strings of each chord that lie below the melody note’s string5.

How could we guess that Cohen chose C-Am-C-Am as the chord progression here? Trial and error works fine, but some rules of thumb may also help…

6. Understand each chord’s personality

Each of the four most common chords plays its own role in the song’s story:

  • Songs in C major typically end on a C chord, similar to how the melody typically ends on a C note. If you let a C chord ring out, expect the applause to begin.

  • The A minor chord does heavy lifting as the only sad-sounding chord of the four. Songs in C major rarely end on Am6. Songwriters often use Am in verse and prechorus sections to build tension that then gets released in the chorus.

  • G and F serve similar roles as happy-sounding chords that provide some flavor on the song’s way home to C. The G major chord sounds calm and expectant to me, often preceding a dramatic climax (“the baffled king com-POS-ing Hallelujah”) or the song’s final C chord (“Halle-luuuuuuuu-jah”). F sounds more casual and transient than G, as if it’s less worried about making it to C7.

Once you develop a feel for these chords’ moods, you’ll start noticing them everywhere. Hallelujah offers us a shortcut: its lyrics name the song’s own chords!

The C major scale’s fourth note counting from C is F, so “the fourth” refers to F major. Similarly, “the fifth” is G major. The “minor fall” and the “major lift” are Am and F again. (“Fall” and “lift” aren’t technical terms; they just evoke the sad and happy natures of minor and major chords.)8

7. Mix it up

Even after we put melody and chords together, our arrangement still sounds hollow. Strumming the new chord once at every chord change moves the song forward in a rather unsophisticated manner.

We can make our arrangement more interesting by arpeggiating the chords (playing their constituent notes one at a time), omitting some chords’ notes, or repeating those notes at different rhythms. We can improvise these right hand decisions on the fly instead of committing them to memory. As long as the left hand holds down the correct chord shape, anything the right hand does will sound fine.

Simply arpeggiate the C-Am chord progression in a certain way and you get Hallelujah’s distinctive intro:

8. Get slapping

Slapping on guitar is like cooking with MSG. It’s like white balancing your photos. It’s a hack that instantly improves most things.

The guitar’s most basic percussive technique is to slap the lowest string or two with the left side of your right thumb, usually notated as “X” in tabs. Doing this while simultaneously plucking a note takes some practice. Other possible percussive techniques include tapping the guitar’s body with your thumb, bumping it with your wrist, drumming it with your fingers, or even scratching it with your nails, but the humble string slap alone will carry you pretty far.

When to slap? Offbeats, the evenly spaced points at which people normally clap when clapping along to a song, are a good start. We could slap throughout the entire song, but for greater variety and dramatic effect, it’s often better to wait until either the first chorus or the second verse. Many songs have a quieter, more intimate bridge section that behooves us to take a break from all the slapping. As with our chord playing, slapping is improvisable and largely up to personal taste.

9. Throw in some ornamentation

As a final touch, we can sprinkle in some small flourishes such as grace notes, slides, and harmonics. Take care not to overuse these, and again feel free to improvise rather than memorize.

Why this approach?

I used to think that playing by ear meant pulling up a song on Spotify and reproducing it note for note. That might help train your ear, but otherwise it’s a waste of time. Next week when your adoring fans ask you to play Hallelujah, will you really remember all 1000+ notes of that Spotify recording? Even if you nail every single note, will your audience notice or care? Faithfulness clearly doesn’t count for everything, given that Jeff Buckley’s version of Hallelujah is at least as popular as Cohen’s original!

This post’s methods optimize for memory efficiency, not fidelity. In computing terms, it’s lossy compression: JPEG vs RAW, MP3 vs WAV. Instead of memorizing unimportant details like the order in which the original recording arpeggiates a particular chord’s notes, we can improvise them at performance time. Sticking to the C major scale and reducing every song to its melody and chord progression lets us fit dozens—or even hundreds—of songs into our brain!

  1. On piano, a C note is any key immediately to the left of a cluster of two black keys. There are eight of them. On guitar, C notes are located at the third fret of the A string and at the first fret of the B string, among other places. 

  2. These two options’ placement on the fretboard is another reason why I prefer to set songs in C major rather than G major, the guitar’s other most common scale. The G major scale does avoid the awkwardness of barring an F major chord, and it does provide three options (on the G string and both E strings) for the song’s final note rather than two, but it also awkwardly crams two full octaves into the first three frets. Most melodies focus on a single octave, so I find it convenient that the C major scale provides a single choice of octave with ample breathing room above and below. In contrast, both of the G major scale’s two octaves are physically off-center on the fretboard. 

  3. Major chords like G major may be abbreviated as Gmaj or simply G, while minor chords like A minor are conventionally abbreviated as Amin or Am. Somewhat confusingly, the letter “G” without any further context may refer to the G note, the G major scale, the G major chord, or even the G string. 

  4. Taylor Swift is especially fond of and successful with the C-G-Am-F formula. She’s easily the biggest bang for your buck when playing songs by ear. 

  5. Keeping the melody note on top isn’t strictly necessary, especially when the melody is at a low enough point that barely any chord notes would fit underneath. Just make sure to always play the melody note the loudest so that listeners can identify it readily. 

  6. If you’re arranging a song that’s meant to sound sad the whole time, ignore everything I’m saying about C as the song’s final note and chord. The song might actually be using a sad minor scale (as opposed to a happy major scale), in which case we should aim to reach A as the final note and Am as the final chord. All other tips here remain mostly the same since the A minor scale happens to contain exactly the same notes as the C major scale. Examples of songs written in a minor scale include Linkin Park’s Numb, Simon and Garfunkel’s The Sound of Silence, Bill Withers’s Ain’t No Sunshine, and To Zanarkand as performed by myself at the top of this post. 

  7. I’m generally not synesthetic, but I do mentally associate these chords with certain colors: C is white, G is yellow, Am is red, and F is blue. G is an old man, F is a young woman. Go figure. 

  8. Hallelujah’s lyrics mention one more chord: the “secret chord” stated in the first verse. Can you guess what it is? It’s not actually F-G-Am-F, which is a progression of multiple chords. The secret chord is Gsus, of course :) More seriously, my money is on E7, the chord that dramatically accompanies the phrase “composing Hallelujah”. E7 marks the very first occurrence in the song’s lyrics of its namesake word “hallelujah”, it appears nowhere else in the song’s structure, it’s the song’s only chord other than the four most common ones, and it’s not named in the “it goes like this” lyrics—it’s literally a secret. 

]]>
How to Use a Custom Email Domain with Gmail https://chaidarun.com/custom-email-domain-with-gmail Mon, 16 Jan 2023 00:00:00 GMT https://chaidarun.com/custom-email-domain-with-gmail Here’s exactly what I did to set up my own [email protected] email address in Gmail, allowing me to seamlessly use that new address with my existing Gmail account:

This process takes an hour or two and costs around $10/year.

  1. Create a personal Gmail account if you don’t already have one.
    • Optional: go to gear icon > Settings > Images and disable automatic image loading. You know how some chat apps have read receipts? Well it turns out that nosy email senders (e.g. spammers) often use inline images in a similar way to determine whether the recipient has opened their message.

  2. Buy a domain name at Namecheap, e.g. chaidarun.com.
    • Try to get a .com for the credibility and familiarity. You could get a .org or .net if your preferred .com isn’t available, but then you run the risk of people forgetting and trying to email you at .com anyway1.
    • This is the only step that costs money!

  3. Go to your Namecheap account > Profile > Settings and enable 2FA.
    • By default, Namecheap sends a verification code to your @gmail.com address whenever you try to log in. Enabling 2FA via either TOTP or Yubikey will replace that step, removing the dependency on your old address—more on this later.
  4. Create a free Cloudflare account and “Add a Site” for your new domain
    • Like me, you might already be using their free CDN on your personal website. You should just reuse your existing site entry if your desired custom email address will be on the same domain.
  5. Go to your Cloudflare site’s dashboard > DNS and take note of your nameservers.

  6. Go back to your Namecheap account > Domain List > Manage > Nameservers > Custom DNS and enter in those nameserver values.
    • DNS is like the internet’s phonebook system. DNS records are pieces of metadata associated with a given domain name, similar to how a phonebook associates your phone number and home address with your full name. By pointing your domain to Cloudflare’s nameservers, you tell the entire internet to consult Cloudflare as the keeper of your domain’s metadata.

  7. Go back to your Cloudflare site dashboard > Email and enable email routing.
    • This is the point at which you actually choose your new custom address, e.g. [email protected]

    • Routing solves the easy half of the email equation: receiving email at your new address. The rest of this guide tackles sending email from your new address.
  8. Create a free SMTP2GO account.
    • This is SMTP as a service, similar to SendGrid and Mailgun and Mailjet and Amazon SES and many many more. Businesses normally use these services to blast out transactional (i.e. marketing) emails, but they also handle personal email perfectly fine. It makes little difference which SMTP service you choose—I went with SMTP2GO as recommended on Hacker News and Reddit.
    • You don’t really need to know anything about SMTP servers beyond the fact that you need one in order to send email.
  9. At SMTP2GO > Settings > Verified Senders, add your domain as a sender domain. Take note of its CNAME records.

  10. Go to your Cloudflare site dashboard > DNS and add those CNAME records.

  11. Go to SMTP2GO > Settings > SMTP Users and “Add SMTP user” with default settings. Take note of the connection details.

  12. Go to Gmail > gear icon > Settings > Accounts and Import > Send mail as > Add another email address, uncheck “Treat as an alias”, and enter the connection details from the previous step.
    • Some other guides online will tell you to use Gmail’s own smtp.gmail.com here, which mostly works and is slightly easier than setting up SMTP2GO. The problem there is that Gmail’s SMTP server won’t sign outgoing mail from your new address with DKIM, increasing the likelihood that your messages will get marked as spam. Services like SMTP2GO do handle DKIM, SPF, etc.

  13. Add your new email address to your Google account as an alternate email.
    • Profile pictures are a Gmail thing, not an email thing. Gmail users will find it sketchy if you send them a faceless email from your new address, so how do you tell Google what picture to show them? Setting your new address as an alternate email in your Google account will cause Gmail recipients to see your Google photo even when you email them from your non-Gmail address.

  14. Verify reliable deliverability with tools like Mail Tester and Learn DMARC.


Congrats, you did it! Now to explain some decisions above in more detail…

Why you need a custom email domain

Email is just one of many ways to communicate online, while your email address is your entire online identity. If you accidentally delete all your email messages, that’s a bummer but not too bad. If you ever lose control of your email address, which is tied to your accounts on dozens (if not hundreds) of other websites, that’s catastrophic.

In the worst case, someone takes over your Gmail and now has access to your other online accounts via password reset. In a hopefully more likely scenario, you might simply want to change email providers without needing to also update all your online accounts and human contacts. Gmail is popular today, but remember Hotmail and Yahoo Mail?

By decoupling your email address from your email provider, you retain ownership of your own identity.

Why you should still use Gmail

Once you fully own your email address, the choice of email provider becomes less important. Gmail is still perfectly fine for the actual handling of email—it filters spam with surgical precision, it tends to have better features than competitors, and it costs $0.

Privacy-conscious people might balk at giving their emails to the world’s largest advertising company, who also happens to be a world leader in artificial intelligence. That’s a reasonable take, but a few counterpoints worth considering:

  • Google already has access to all of your email communications with Gmail users
  • Google has likely become so good at ad targeting that they don’t even need to read your emails anymore
  • Gmail-to-Gmail messaging is arguably more secure than, say, Fastmail-to-Gmail since the data never leaves Google servers to travel across the open internet
  • Email is fundamentally insecure anyway

Why you shouldn’t worry about email privacy

Generally speaking, it’s a lost cause. Email was invented over half a century ago without any thought given to privacy or security—it’s the digital equivalent of postcards. Would you write your credit card info on a postcard, visible to your landlord and your nosy neighbors and who knows how many random postal workers? This is why your bank never emails you sensitive information. Instead, banks go to the trouble of building email-like communication channels on their own websites for customers.

If you really need email privacy because you’re a whistleblower or journalist then there’s PGP, but that’s hardly the normal use case for email. Extremely few people know how insecure email is, let alone care about it, let alone are willing to jump through user-unfriendly hoops like PGP or ProtonMail, let alone talk to people who also choose to jump those same hoops.

For the vast majority of ordinary people who just want their private messages with friends and family to actually be private, chat apps like Signal easily beat email. If Signal itself is too hard a sell (fair enough), WhatsApp and Google Messages are more popular alternatives that use the same end-to-end encryption technology created by Signal.


References:

  1. I have a friend who snagged fawm.org for his website, only to then spend several years battling a domain squatter for control of fawm.com. In the meantime, the latter domain housed a hardcore porn website. 

]]>
Apps I Use https://chaidarun.com/apps-i-use Sat, 31 Dec 2022 00:00:00 GMT https://chaidarun.com/apps-i-use Here’s a gratuitously detailed rundown of some of my software choices. I tend to prioritize efficiency and flexibility over aesthetics and ease of use out of the box. My dotfiles repo contains text-based config files for these apps.


Android

Mobile OS. Alternatives I’ve tried: iOS, Windows Phone, CyanogenMod

I prefer more control over my phone than iOS offers: I can freely customize Android’s home screen, sideload apps, pick and choose hardware specs, charge with any old USB-C cable instead of Apple’s not-even-included Lightning cable, easily develop and share my own apps, automate nearly anything with Tasker, and so on. I don’t mind appearing as a green texter in a chat app that I don’t use :)

Chronofile

Personal analytics, a.k.a. lifelogging, a.k.a. quantified self. Alternatives I’ve tried: spreadsheets

I’ve been continuously recording my daily activities by hand since I was 17, amounting to ~100,000 activities over the past 11 years. Most of it has taken place in an app that I built for myself. I described the process and data in a blog post that generated a lively discussion on Hacker News and got picked up by Taiwanese media (lol?)

Duolingo

Language learning. Alternatives I’ve tried: Anki, Memrise, LingoDeer, Drops, Tandem, HelloTalk, Cake, ELSA Speak, textbooks

Full disclosure: I work at Duolingo. But that’s because I was already a power user, not the other way around.

As a casual language learner, Duolingo’s gamification features had me hooked immediately. By now I’ve made significant progress through around a dozen courses, completing seven of them. Although language proficiency does fade quickly without ongoing practice, I was fairly decent at the time of finishing each course: not fully conversational and definitely not fluent, but equivalent after only a few dozen hours on Duolingo to having taken two or three years of high school instruction. The vocab that I do still remember comes in handy surprisingly often when I recognize the meanings of infrequent musical directives like pesante, brand names like Fjällräven, Spanish chatter at pickup volleyball, and even alien dialogue in science fiction.

To reach fluency, there’s no substitute for talking with humans. Tandem worked pretty well for me as a way to find someone with whom to practice conversational Thai offline, but within the app itself I’d say the best feature is its specialized UI for gracefully correcting any mistakes in your partner’s chat messages. Nowadays I mostly stick to Duolingo and use my phone keyboard’s voice typing feature to practice speaking. Google’s unforgivingly (and unintentionally?) strict speech recognizer serves as my pronunciation guide.

Firefox

Web browser. Alternatives I’ve tried: Chrome, Chromium, Edge, Opera

After falling behind Chrome in performance and features for many years, Firefox finally caught up. It continues to support privacy and ad blocking features in a way that no browser created by an online advertising company will ever match. I still use Google products that blow away the competition, but this is one place where it’s easy to avoid putting more eggs than necessary in the Google basket.

Browser extensions that I use include Dark Reader, Disconnect, I still don’t care about cookies, KeePassXC-Browser, Privacy Badger, Refined GitHub, Stylus, and uBlock Origin.

FitNotes

Workout tracker. Alternatives I’ve tried: Fitocracy, Strong, MyFitnessPal, spreadsheets

Way back in 2013 when PhoneGap reached peak hype, I went about learning it by developing my own workout tracking app… until I found FitNotes, which was the exact app I planned to build. No frills, streamlined UI, and full ownership of the data.

I find it comforting that FitNotes is also developed independently by just a guy like me, which is about the scope that feels appropriate for something as simple as a workout tracker. No worries that big fitness will aggressively try to monetize and engage me.

FreeTaxUSA

Tax preparer. Alternatives I’ve tried: TurboTax, TaxAct, Credit Karma, a human CPA

Despite the extremely scammy-sounding name (which they’ve apparently tried to address by offering exactly the same product under the alternate name TaxHawk), FreeTaxUSA is easily the most wholesome tax software that I’ve ever used—no surprise fees, shady upselling, or parasitic lobbying. You know they’re legit because the IRS website lists them as a trusted partner.

FreeTaxUSA does come with a small fee for filing state taxes, but I find it totally worth the lack of sleaze. It almost feels like a charitable donation in comparison to feeding the TurboTax monster.

Google Fi

Wireless carrier. Alternatives I’ve tried: T-Mobile, Verizon

My Fi family plan gets me unlimited calls, texts, and data for $24/mo after taxes and fees. I don’t travel enough internationally that it makes sense anymore, but previously I used their flexible plan for $20/mo plus $10/GB for mobile data that works practically anywhere in the world.

Although Fi was initially available by invitation only for a handful of phone models, they now support iPhones and many Android devices. If you decide to make the switch, you can save us both $20 by using this referral link or code: A2D70F

Google Keep

Notes. Alternatives I’ve tried: Evernote, OneNote, Notion, text files

As with FitNotes and Google Tasks, it’s a relief that Google Keep’s product decisions don’t suffer from the business pressure of being an entire company’s sole source of income. It’s just a dead simple note-taking app that syncs between my laptop and phone, which is all I want or need.

Google Tasks

Todo list. Alternatives I’ve tried: Remember the Milk, text files, physical planner

After languishing for years in a state of limbo and dubious recommendability, Google Tasks finally implemented the last feature on my wishlist: an Android widget to show upcoming tasks. Or maybe I should say second feature because the only other feature that I want out of a todo app is seamless integration with Google Calendar. The use cases for todo lists and calendars are so intertwined that I can’t imagine managing them nearly as effectively in two separate places.

Inoreader

RSS reader. Alternatives I’ve tried: Google Reader, Feedly, Tiny Tiny RSS, NewsBlur

Like many people, I migrated to Feedly upon Google Reader’s untimely demise. Feedly’s extravagant use of screen space and lack of customizability bothered me, so I then ran my own TTRSS server for several years until I got tired of having to regularly upgrade and troubleshoot it myself.

Inoreader feels closest to the spirit of Google Reader. After trying their free version with an ad blocker for a while, I happily upgraded to their $20/yr ad free plan.

KeePassXC

Password manager. Alternatives I’ve tried: LastPass, 1Password, KeePass, KeePassX

A security incident with some cloud-based password manager or another seems to make the news every other month. The most popular totally offline, totally FOSS option is KeePass and its forks. KeePassXC is a modern fork recommended by the EFF. All passwords live in a single file that I sync across my devices via Syncthing.

Linux

Desktop OS. Alternatives I’ve tried: Windows 95 - Windows 10, macOS Mavericks - Sequoia

Compared to Windows and/or Mac, Linux’s strengths include hassle-free package management, the ability to customize or switch out any part of the OS (e.g. to avoid shenanigans like start menu ads), being a smaller target for consumer malware, generally better performance and reliability on the same hardware, native support for industry standard programming tools like Docker, no data harvesting, and of course the $0 price tag. After years of troubleshooting my mom’s Windows machines, I set up Linux Mint for her and have gotten zero complaints in the decade since.

Linux’s weaknesses include the typical need to install it yourself and less support for proprietary software such as AAA games and professional creative tools. Depending on which Linux distro (variant) you choose, support for peripherals such as printers and cameras may also take some legwork.

Update I’m currently using macOS again at work since Apple requires it for iOS development. I’ve managed to recreate my Linux setup well enough on macOS that it’s no longer super jarring to switch between the two.

Meteogram

Weather app. Alternatives I’ve tried: Aix Weather Widget, Google Weather

In a vast ocean of Android weather apps, Aix Weather Widget was the perfect one for me until Android 11 finally broke it in 2020. Some time later, I asked my friend about the staggeringly detailed weather widget that I couldn’t help but notice taking up half their home screen. I was pleasantly surprised to discover that not only does Meteogram give you full control over every last aspect of its display, but it even comes with a minimalist “Aix lookalike” preset!

MobileSheets

Sheet music display. Alternatives I’ve tried: PDF viewers, paper

PDF viewers work okay for sheet music but come with a bunch of annoyances, chief among them the problem of page turning. I once hacked up my own solution using face recognition via my Linux laptop’s webcam, but that’s hardly a normal setting for musical performance.

Dedicated apps like MobileSheets provide helpful features like half-page turns, Bluetooth integration with page turning devices, custom setlists, and automatic whitespace cropping. MobileSheets is available for both Android and iOS, although forScore seems more popular on iOS.

Monarch

Personal finance tracking. Alternatives I’ve tried: Mint, Credit Karma, Empower / Personal Capital, Firefly III, spreadsheets

Every RPG out there has some kind of dashboard for checking your character’s stats. Mint provides a similar overview of your financial life. Its Android widgets let me easily monitor all of my accounts’ current balances and recent transactions. I’d prefer an equally convenient alternative that isn’t now owned by Intuit though, so maybe I’ll give Personal Capital a spin sometime.

Update Now that Intuit has shut down Mint, I’ve switched to Monarch. It comes from the original developers of Mint and feels much the same.

mpv

Media player. Alternatives I’ve tried: VLC, RealPlayer

VLC is the best-known media player that can handle any audio or video file you throw at it. mpv is a similar (both are based on FFmpeg) CLI program that supposedly offers slightly better defaults, performance, and file support. If I hadn’t already been looking for a CLI media player, I’d probably just stick to VLC.

Mullvad

VPN

I’m not really the most privacy-conscious person in the world, and I don’t go around evangelizing VPNs. I just happen to spend more time than most people on public WiFi networks that mess with traffic, e.g. breaking my RSS reader for some reason. Mullvad doesn’t spend as much on marketing as NordVPN or ExpressVPN, but it’s the only commercial option that I’ve personally heard security engineers recommend. Mozilla’s own VPN offering is basically just rebranded Mullvad.

MuseScore

Music notation. Alternatives I’ve tried: Sibelius, Frescobaldi, LilyPond, TuxGuitar, Guitar Pro

I wrote a few scores in Sibelius as a teenager and it blew MuseScore out of the water back then. Now that the original Sibelius development team has moved to Steinberg, I’d guess that Dorico SE is an excellent free option for getting started today.

That said, MuseScore has improved a ton and I’ve seen some of my music teachers using it. It’s more than sufficient for my guitar tabbing and occasional piano scoring purposes. I’ve only ever tried my hand at a multi-instrument score once, but well… that’s one more time than trialware like Dorico SE would allow.

Overleaf

Typesetting. Alternatives I’ve tried: TeX Live, Microsoft Word

I learned LaTeX to make my high school resume prettier, wrote all of my college assignments in LaTeX for fun, and recently chose to use dockerized LaTeX for a project at my day job. Clearly I don’t hate LaTeX, but just getting it to run has always been a pain. Installations take up gigabytes of disk space, the tooling ecosystem is extremely fragmented, and good luck with basic tasks like changing the font. For the occasional resume tweak or formula render, an online editor like Overleaf will spit out a PDF with minimal hassle.

rclone

File backup. Alternatives I’ve tried: Carbonite, SugarSync, external HDD, crossed fingers

I use Syncthing to sync files across my devices and a public GitHub repo to house my program settings. By “backup”, I’m referring to the act of shipping my old photos and documents off to some remote datacenter where they’ll survive any disaster that may befall my laptop.

Rclone lets you easily sync your local files to pretty much any cloud storage provider. As easily as rsync, that is—both are CLIs after all. Rclone’s encryption at rest means that I don’t have to worry about the cloud provider snooping on my files or losing them in a data breach.

Reaper

Digital audio workstation (DAW)

Yes, this one is mostly about the money. Reaper is effectively donationware for which I happily shelled out $60 after a few mixing projects. Logic Pro and every other DAW worth mentioning all cost hundreds of dollars, which is more than I’d like to spend on a chore that’s only tangentially related to my actual hobby of playing music. Reaper’s blenderesque UI takes some getting used to but ends up feeling reasonably efficient once you’re comfortable with it.

Signal

Instant messaging. Alternatives I’ve tried: Messenger, WhatsApp, LINE, etc. etc. etc.

I’m not enough of an ideologue to forgo Messenger, WhatsApp, or whatever else my friends and family already use, but if we both happen to have Signal then that’s the chat app of choice. Signal’s security measures survive subpoenas while other apps get in PR trouble for unexpectedly sharing your data. As a bonus of being open source, Signal is also the only popular chat app that would ever let me dive into its code and fix a bug myself!

Syncthing

File sync across devices. Alternatives I’ve tried: Dropbox, SugarSync, OneDrive

Syncthing is self-hosted Dropbox. Five years after my blog post about why and how I made the switch, Syncthing still works well for me and gets recommended often in Hacker News comments.


Alacritty

Terminal emulator. Alternatives I’ve tried: xterm, xfce4-terminal, urxvt, Tilda, Terminal, iTerm2

Alacritty made a splashy entrance a few years ago with bold claims of trouncing every other terminal’s performance now and forever. I haven’t used any alternatives recently enough to remember how much slower their rendering felt, but I appreciate Alacritty’s philosophy at least as much as its results. I already use tmux for features like panels and scrolling, which leaves text rendering as the one thing for my terminal to do well.

Cursor

GUI code editor. Alternatives I’ve tried: Sublime Text, Visual Studio Code, Notepad++, various JetBrains IDEs, Eclipse, Visual Studio, etc. etc.

I use Android Studio for Android development because Google more or less forces you to. Whenever I have more of a choice, I avoid IDEs since they’re so slow and complex, crammed with tons of features that are unreliable and/or unnecessary for me. Even VS Code is too bloated out of the box for my taste despite my championing of GitHub Codespaces at my job.

I’ve spent at least 100 hours using each of the alternatives listed above, and Sublime Text remains my favorite for its speed and simplicity. The only action that causes any perceivable lag is opening a file that weighs several megabytes or even gigabytes, a task that other editors handle worse (if at all). Instead of trying to pare beefier IDEs down to just what I want, I install a few Sublime plugins that add helpful IDE features like symbol renaming and type inference hints.

Update I now primarily use Cursor, an AI-powered editor based on VS Code that I’ve customized to look and feel like Sublime Text as much as possible. It’s still annoying that basic actions like syntax highlighting and folder switching are noticeably laggy, but the AI assistance is a game changer that saves way too much time to pass up.

DejaVu Sans Mono

Programming font. Alternatives I’ve tried: Consolas, Inconsolata, Source Code Pro

I want my editor and terminal font to be monospaced, unambiguous when it comes to similar characters like 1 / l / I, legible at small sizes, available everywhere, and as plain or “normal” looking as possible—in that order. DejaVu Sans Mono does a pretty good job ticking those boxes. For many years, it was the default monospaced font on both MacOS and Fedora Linux.

Maybe I just haven’t spent enough time with them to reach enlightment, but I find ligature-heavy fonts like Fira Code impractically cute. Whenever I see something like in my editor, I’d rather not need to also remember that !~ is the “source code” for the source code itself.

DigitalOcean

Web host. Alternatives I’ve tried: AWS, Heroku, GitHub Pages, x10hosting, Raspberry Pi

I have no complaints about DigitalOcean and find their documentation in particular to be excellent, but my preference here is less about DigitalOcean specifically and more about VPS vs. cloud vs. PaaS vs. static HTML vs. shared hosting vs. home server. For me, the VPS route strikes the best balance between flexibility and maintainability. I can roll my own features like self-hosted blog comments and custom URL redirects without spending too much time on the ops side.

fzf

CLI autocomplete for common programs

fzf (“fuzzy finder”) is a general-purpose CLI tool that automatically converts long lists into searchable menus, similar to how a web browser suggests URLs as you type into its location bar. It takes over your shell’s Ctrl + R to present your command history in that browser-style UI, as opposed to readline’s functional but tedious behavior of paging chronologically through one entry at a time. Hitting Ctrl + T shows similar UI for recursively searching the current directory for any given file name.

My third main use case for fzf is typing kill **<TAB> to get a searchable menu of killable processes without needing to worry at all about PIDs, which handily beats the old ps aux + grep + kill ritual:

Isso

Blog comments. Alternatives I’ve tried: Disqus

I previously ran Disqus on this blog but eventually got tired of the ads and slowness. Isso is a self-hosted commenting system and admin panel that, like FitNotes, does exactly what I want in a simple way that closely matches what I’d otherwise just build from scratch for myself.

Jekyll

Blog CMS. Alternatives I’ve tried: Pelican, WordPress, Ghost, Drupal, Joomla, MODX

Jekyll popularized the idea of generating static HTML from Markdown files. I’m comfortable rsyncing the HTML to my VPS, so I don’t want or need anything more “engineered” than Jekyll. Despite murmurs of its deprecation, Jekyll seems unlikely to go away or even lose its status as the most popular SSG as long as it remains GitHub’s default method for publishing to GitHub Pages.

My Jekyll workflow: start the local development server, open a Firefox window next to a Cursor window, see the output refresh itself whenever I save in Cursor, and publish by rsyncing the entire build folder to DigitalOcean.

nano

CLI text editor. Alternatives I’ve tried: vi/vim/neovim, emacs

If you’re not all in on terminal editors like vim, nano comes preinstalled in most Linux distros and provides a much more user-friendly TUI for stuff like amending Git commits. Just set it as your $EDITOR and you’ll never need to worry again about forgetting rarely used keyboard shortcuts.

MacOS once came with nano but now preinstalls pico instead, which I can only assume was due to a licensing situation similar to Bash vs. Zsh.

ripgrep

Local code search. Alternatives I’ve tried: grep, ag

Ripgrep is a faster drop-in replacement for grep. Like other grep alternatives, it respects .gitignore by default. Microsoft now uses ripgrep to power VS Code’s search feature.

tmux

Terminal multiplexer (tab and pane manager). Alternatives I’ve tried: screen, GUI tabs

Most people who want to open multiple terminal windows use their terminal’s GUI tab feature, a familiar approach that matches web browsers’ UI. Some terminal apps get even fancier and let you split tabs horizontally or vertically into multiple panes. This all works fine until you land in an environment that doesn’t support your favored GUI terminal: a remote server you’re SSHed into, a headless local machine, or your IDE’s built-in terminal panel.

tmux uses Unicode art to implement those windowing features all inside a single terminal itself, removing the need for GUI tabs. Other than portability, tmux’s main draw is persistence across SSH sessions. I can log into a remote server, kick off some jobs inside tmux there, log out without killing those jobs, and come back later to view those jobs running inside the same tmux session exactly as I had left it.

Zsh

Interactive Unix shell. Alternatives I’ve tried: Bash

For all its hype, Zsh is actually almost identical to Bash (and just one year younger!). While Zsh does offer a few advantages like spellcheck and automatic cd, it owes much of its relatively recent popularity to a plugin manager called Oh My Zsh.

I don’t use Oh My Zsh anymore. Bundling the kitchen sink might be the fastest way to get a decent setup, but eventually I wanted better performance and control. Now I just declare my own Git aliases and install Zsh syntax highlighting directly.

To be clear, I only use Zsh as an interactive shell. Unless you’re specifically writing a Zsh plugin, Bash and Bourne shell remain the only sane choices for non-interactive scripting due to their ubiquity.

There’s also Fish, the only other Unix shell today with any voluntary adoption to speak of. While Zsh is essentially just Bash with a few extra features, the whole Fish shtick (sorry) is its thorough departure from POSIX standards. Some of Fish’s interactive features do sound tempting, but to me they don’t feel worth the mental burden of always having to remember what shell features are safe to use inside Bash scripts.


Arch Linux

Desktop Linux distro. Alternatives I’ve tried: Fedora, Ubuntu, Mint, ChromeOS

Everything I said above about Linux is even truer of the minimalist Arch Linux than of other common distros. I began with Fedora in college and stuck with it through my first few years of full-time work. Eventually I became comfortable enough with Linux to try out Arch, whose principles align more with what I want out of software: simplicity, modernity, pragmatism, user centrality, and versatility.

Simple doesn’t necessarily mean easy—it’s much easier to churn out a long and rambling blog post *cough* than a clear and concise one. In contrast to Arch, distros like Ubuntu are easy but complex. Here are my notes on how I’ve arrived at my current Arch setup.

clipman

Clipboard history.

Ctrl + V pastes the last thing you copied, while a clipboard manager can remember and provide easy access to hundreds of things you’ve recently copied. Life-changing.

Xfce’s clipman introduced me to the concept. On macOS, I use Maccy.

Easystroke

GUI navigation via mouse gestures

Easystroke translates mouse gestures into keyboard shortcuts, which programs then translate into commands like undo, refresh, next tab, etc. For example, I can draw an L-shape while holding down my mouse’s right button to send a Ctrl + W command that closes the active tab in programs like Firefox and Cursor. The only time I ever think about mouse gestures anymore is whenever I use someone else’s computer and realize how much I miss them!

i3

Window manager. Alternatives I’ve tried: GNOME, KDE Plasma, Xfce, Unity, Cinnamon

i3 is like tmux for desktop apps. If you hate wasting screen space and love keyboard shortcuts, i3 will let you manage your app windows as efficiently as possible.

Here’s a screenshot from my Arch laptop that shows Sublime Text and Alacritty equally sharing the third of six desktops, which I can jump to via CapsLock + 3. I can open a new app by hitting CapsLock + E and typing part of its name.

The half-screen window snapping features introduced in Windows Vista and MacOS Catalina don’t hold a candle to i3. AeroSpace for Mac is a solid i3 clone.

xbindkeys

GUI automation. Alternatives I’ve tried: AutoHotkey

On Linux I use xbindkeys to create system-wide keyboard shortcuts for common actions like typing out my email address.

That comes in handy several times a day but pales in comparison to the absurdly powerful AutoHotkey, a programming language whose name barely scratches the surface of what it makes trivially easy even for non-programmers: keyboard automation, mouse automation, GUI automation, GUI creation, audio playback, etc. Before getting into more mainstream programming languages like Python, I first learned TI-BASIC to develop calculator games and AutoHotkey to create my own productivity tools and RuneScape bots. Tragically, AutoHotkey is only available for Windows.

Xfce4 goodies

GUI-based OS utilities. Alternatives I’ve tried: Nautilus, Ranger, Flameshot, etc. etc.

Although I haven’t run Xfce itself in many years, I still use several of its components alongside i3 since they consistently follow Xfce’s philosophy of speed and simplicity: clipboard manager clipman, image viewer ristretto, file manager thunar with thunar-archive-plugin, system tray applets xfce4-battery-plugin and xfce4-clipman-plugin, and xfce4-screenshooter.

Yay

AUR helper. Alternatives I’ve tried: pacaur, yaourt, trizen

Yay is the AUR helper du jour and pacaur has been something of a zombie since its original author left in 2018, but the latter still works fine for me. I’m sure Yay is slightly nicer and it’s what I’d recommend to new Arch users—it just doesn’t seem worth switching over my existing machines and scripts.

Update Welp, pacaur finally died on me. Now I use Yay.


AeroSpace

Window tiling. Alternatives I’ve tried: yabai, Rectangle

At long last, sane window management. AeroSpace solves my biggest macOS gripe by faithfully copying the design decisions of Linux’s i3. Now my apps always make full use of all available screen space as soon as they open, and I can navigate between them instantaneously via keyboard shortcuts without having to wait for any annoying animations.

AutoRaise

Autofocus windows on hover.

AeroSpace quite reasonably forgoes reimplementation of one useful i3 feature that’s somewhat controversial on macOS: automatically focusing (raising) windows when you hover your mouse over them.

Why is that controversial? Unlike Windows and Linux apps that each contain their own menu bars, all macOS apps share a single menu bar at the top of the screen. If macOS were to have the same autofocus behavior that i3 provides, then you could accidentally end up switching apps while dragging your mouse up to click on the menu bar.

Personally, I don’t find that a problem at all. I rely a lot on keyboard shortcuts and don’t need to click on the menu bar too often anyway. Some apps like Alacritty don’t even have a menu bar. I use AutoRaise to get i3-style autofocusing on macOS.

Hammerspoon

GUI automation. Alternatives I’ve tried: AutoHotkey

Hammerspoon lets you programmatically automate GUI actions such as moving windows around and simulating keyboard events. It’s like AutoHotkey on Windows and Tasker on Android.

Its Lua-based APIs are powerful enough that I was able to build my own custom implementation of i3 in Hammerspoon (until I switched to AeroSpace for that). Nowadays I mainly use Hammerspoon to create global keyboard shortcuts for automatically typing out my various email addresses.

Maccy

Clipboard history.

This is a no-nonsense, open-source macOS clipboard manager similar to clipman.

]]>
Ten Years of Logging My Life https://chaidarun.com/ten-years-of-logging-my-life Sat, 25 Dec 2021 00:00:00 GMT https://chaidarun.com/ten-years-of-logging-my-life I’ve been continuously recording my daily activities by hand since I was 17, amounting to 85,000+ activities over the span of a decade.

Update See Hacker News for 100+ more comments on this post.

Why?!

Self-awareness. Am I getting enough sleep and exercise? Often I become engrossed in whatever I’m doing and forget about biological obligations like eating. My job is cool, but have I been making enough time for other people and hobbies I care about? Balance requires constant attention.

The data is also just endlessly fascinating to sift through. These pixels may not mean much to anyone else, but they tell an intensely personal story of my life’s ups and downs over the past four years:

How I track myself

My log’s first entry is from October 16, 2011, soon after I started college. The original incarnation of the log was a massive spreadsheet in which I recorded a short summary of each day’s happenings alongside a more detailed breakdown of the total hours spent per activity. Some columns’ entries were automatically highlighted to indicate whether I reached certain daily goals, e.g. 8 hours of sleep.

This setup worked fine for the next couple years of college, and with some minor tweaks it continued to carry me through my first four years of full-time work as well.

There was definitely room for improvement, though. I had to either enter activities immediately after they ended or simply try to remember them until I got back to the keyboard. By mentally rounding each activity’s duration to the nearest 15 minutes, I was able to enter data quickly but at the expense of precision. If I were to play piano for two hours, it would appear as a 2 in the spreadsheet but without any start or end time info. Tracking each activity’s geographical location was similarly infeasible.

The solution: developing my own Android app.

I named it Chronofile, which I later learned was also the name of a similar undertaking by Buckminster Fuller. Its code1 is open source and available on GitHub.

Update Hello Hacker News! As requested, here’s a release APK that you can try installing for yourself on Android 8+. Please open a GitHub issue if you run into any problems with the app.

Update Several years later, I’ve finally gotten around to uploading Chronofile to the Google Play store!

Internally the app saves each log entry as a TSV row containing the activity’s name, descriptive note (optional), latitude, longitude, and start time in seconds since 1970:

Work	40.461583	-79.926864		1639506621

Tapping on an old entry creates a new one for the same activity. Geolocation data comes from GPS. Chronofile’s streamlined interface has cut down the time that I spend tracking myself each day from around ten minutes to less than one minute.

Observations

(Chart of practice per musical instrument)

Turns out I’ve been playing music for an average of 45 minutes a day over the past decade, which is honestly a little embarrassing given that I’m certainly better at it by now but still so far from where I’d like to be. Oh well, no need to be world-class at your hobbies I guess?

I did expect guitalele to top the list but was shocked to see that I’ve sunk 943 hours into this tiny guitar that I picked up for less than $100! Possibly my highest-ROI purchase ever.

(Chart of days between haircuts)

Yes I did the quarantine hair thing. Still kinda regret finally cutting it :S

The remaining charts cover only the past four years since it’s too cumbersome to extract this data from the pre-Chronofile spreadsheet era.

(Chart of transportation methods)

Lockdown sticks out like a sore thumb in this one too. It’s a small miracle that my legs still work—there were some months when I didn’t take a single step outside! Most of my pandemic travel has been on long road trips.

(Chart of daily media consumption)

Hidden in the chart above are a handful of games, 24 books, 39 shows, 102 movies, and thousands of news articles. I had never really been much into TV, but to stay somewhat social during lockdown I joined a viewing party that ended up covering all the corona classics: Tiger King, Love Is Blind, Avatar, kdrama after kdrama after kdrama…

You can see that “News” went flat for several months in 2021, corresponding to when my TTRSS server abruptly stopped fetching most of my RSS feeds due to some certificate error. Even though reading the news had been such a constant part of my daily life ever since the olden days of Slashdot and Google Reader, it took me five months to even notice this problem!

I have mixed feelings about that. I do think it’s important to know what’s going on in the world, but how much time is worth spending? Hacker News (via hnrss) is an incredibly valuable resource and I can’t imagine having become as good of an engineer without it, but it’s also a bit of an echo chamber. In any case, it’s a relief to have such clear proof that I’m not as addicted to the news as I once suspected.

Notably absent from the chart is social media, which I mostly gave up after high school.

(Chart of daily personal productivity)

In contrast to the previous chart of passive media consumption, this one maps out “personal productivity” loosely defined as activities that improve myself in some measurable way or make progress toward non-work (unprofessional?) goals.

Occasions when things weren’t all going my way were responsible for a few of the “Music” upticks above. I’ve found that playing music is the best medicine: it uses up vast amounts of physical, emotional, and mental energy that I might otherwise be tempted to spend more counterproductively.

Obviously exercise is good too, but I’ve been less sure about the particular kind. At no point during my first (and probably last) half-marathon in May 2019 or the training for it did I ever experience anything remotely resembling runner’s high, and the pump has never quite done it for me like it does for Arnold. I still lift a bit but most of my exercise nowadays comes from rock climbing and volleyball. As the chart suggests, those have been much more enjoyable to sustain at an average pace above one hour a day.

(Chart of sleep hours)

Monday always feels like the busiest day of my week, which the chart confirms by giving Monday the smallest bubbles of any night. Overall I’m pretty happy with how boring my sleep schedule has become in recent years, a far cry from the chaos of high school.

(Chart of work hours)

Some might find this chart more concerning. Is nothing sacred, other than Sunday 7am?! A less alarming interpretation: I have enough freedom in my work life and personal life that I can attend to both whenever it’s most convenient.

For a few months during lockdown, I settled into a pattern where I would handle the more interpersonal aspects of my job in the afternoon, take a long nap in the evening, and do “real work” (i.e. coding) after midnight while free from any distractions. That stands in contrast to most of 2014, when I would head to the gym at 1am, go to bed at 2:30am, wake up at 10:30am—usually right on the minute without an alarm—and sit down in my office chair at 11am.

Which of those two schedules is better? I’ve found that at least for me, biphasic sleep didn’t actually have much of a noticeable effect on my mood or productivity. Just getting 7-8 hours of sleep a day matters much more than how those hours are distributed.

(Chart of monthly activity correlation)

What surprises me most here is the extremely weak correlation between work and sleep, which I suppose is a good thing? Sleep and travel are negatively correlated because I categorize sleep while traveling as travel.

Apart from sleep, work is negatively correlated with everything! That makes sense—I do barely any business travel, for example. Burnout is definitely something to keep in mind here.

(Chart of number of log entries per day)

This chart may be the hardest of all to interpret. Chronofile’s interface has barely ever changed, so that doesn’t explain any variations here. Adding a new entry takes so little time anyway that I doubt it affects my behavior.

Does lower activity churn equate to better focus, longer attention span? Or should I instead aim for more variety each day? This chart’s shape over time doesn’t match any personal metric that comes to mind: happiness, stress level, body weight, credit score…

A few major life events such as relocations and changes in my *ahem* romantic situation do seem to show up in the chart, but they account for only a small fraction of its peaks and valleys. Is this chart mostly just noise? Maybe any underlying causes here will become more apparent as time goes on.

Conclusions

The quantity of time spent on a given activity doesn’t say much about the quality of that time, of course. And without a control condition, it’s hard to say whether any results of this experiment are causal or simply correlational. That said, I think it’s still worth noting some changes in my own thoughts and behavior that began around the time I started this project.

I get a dramatically more reasonable amount of sleep, a daily average of 7 hours and 38 minutes over the past decade.

I can make everyday plans with greater precision, knowing exactly how many minutes I’ll need to shower or drive or buy groceries or do laundry or water the plants.

I spend much less time on single-player games and idle internet stuff like Reddit. I fairly often still give them my full attention, but for the most part they’re restricted to downtime during other activities like traveling and eating. I don’t mean to suggest that these are bad habits in any practical or moral sense, just that I now have better control over them as opposed to the other way around.

Speaking more generally—although it may sound like Chronofile has taken over my life, it’s really no more stressful or controlling than my car’s speedometer. They’re both tools that do nothing more than show me the results of my own decisions, and it’s now equally hard to imagine flying blind without them.

  1. Chronofile uses RxJava to reimplement Redux (itself inspired by the Elm architecture) with type safety in 9 lines of code, some of the most satisfying that I’ve ever written! 

]]>
How to Ring Calls and Vibrate Notifications on Android https://chaidarun.com/ring-calls-vibrate-notifications-android Sun, 01 Nov 2020 00:00:00 GMT https://chaidarun.com/ring-calls-vibrate-notifications-android I want calls to ring audibly, and I want notifications to vibrate silently.

Is that too much to ask for? Recent Android versions make this exceedingly reasonable configuration nearly impossible to implement.

Update Android 14 finally fixed this problem by providing separate settings for ringtone volume and notification volume! Just set your notification volume to 0, set your ringtone volume to something above 0, and stop reading this blog post.

The solution

Android’s native Do Not Disturb (henceforth “DND”) feature is the perfect solution except that it doesn’t allow notifications to vibrate. So we’ll roll that feature ourselves:

  1. Enable DND and keep it turned on forever
  2. Allow calls to interrupt DND. On Android 11, this option is at Settings > Sound > Do Not Disturb > People > Calls
  3. Install Tasker
  4. Create a new “Do Disturb” profile that overrides the notification behavior of your permanent DND by triggering a 200ms vibration whenever a new notification arrives (optionally with your waking hours as a second constraint if you’re an extremely light sleeper like me):

I’ve been using this setup for six months now. The only downside is that I can no longer identify apps by their custom vibration patterns, but that’s a relatively small price to pay.


The non-solutions

My path to the solution above is littered with the bodies of many failed attempts, some documented below. Note that Android has two subtly annoying limitations to work around:

  • Vibrate mode achieves the desired notification behavior, but Android offers no way to ring calls audibly in vibrate mode
  • DND mutes notifications and allows calls to ring audibly, but Android offers no way to both vibrate and mute notifications during DND

Faking vibrate mode via inaudible notifications

Strategy: Remove notification audio directly.

  1. Permanently disable both DND and vibrate mode
  2. Roll your own vibrate mode by going into Sound settings and changing the Default notification sound to the None option (which might be located inside My Sounds)

Fatal flaw: Some apps use their own custom notification sounds, which still play.

Overriding system volume via PagerDuty

Strategy: Give up hope on ringing all calls audibly and settle for ringing only PagerDuty calls audibly.

  1. Permanently enable vibrate mode
  2. Go into the PagerDuty app’s settings and enable system volume override

Fatal flaw: That override option just flat out doesn’t work on my Pixel 2.

Temporarily unmuting the ringer via Tasker

Strategy: Stay in vibrate mode but use Tasker to temporarily disable vibrate mode during calls.

  1. Permanently enable vibrate mode
  2. Create a Tasker profile that disables vibrate mode whenever a call comes in and reenables vibrate mode when the call ends

Fatal flaw: Android doesn’t actually let you leave vibrate mode while ringing?!

Ringing on the media channel via Tasker

Strategy: Stay in vibrate mode and use Tasker to “ring” calls by simply playing an MP3 file that you specify.

  1. Permanently enable vibrate mode
  2. Follow these instructions to create a Tasker profile that plays an audio file when a call comes in

Fatal flaw: There’s an Android OS bug that causes the ringtone to continue looping forever?! The provided workaround doesn’t work on my Pixel 2.

]]>
TP-Link API Reference https://chaidarun.com/tp-link-api Wed, 02 Sep 2020 00:00:00 GMT https://chaidarun.com/tp-link-api My Kasa HS103 smart plugs arrived today, and I felt an uncontrollable urge to control them via API. This post is a distillation of these guides for my own reference. It should work for at least the HS1xx product line.

1. Get authentication token

Substitute your login details. You can use uuidgen to generate a UUID.

curl -s https://wap.tplinkcloud.com -H 'Content-Type: application/json' -d '{"method":"login","params":{"appType":"Kasa_Android","cloudUserName":"$ACCOUNT_EMAIL","cloudPassword":"$ACCOUNT_PASSWORD","terminalUUID":"$UUID"}}' | jq -r .result.token

2. List devices

Substitute the token obtained in the previous step.

curl -s "https://wap.tplinkcloud.com?token=$TOKEN" -H 'Content-Type: application/json' -d '{"method":"getDeviceList"}' | jq -S .result.deviceList

3. Change device state

Substitute your desired device’s info. State 1 means “on”, 0 means “off”.

curl -s "$APP_SERVER_URL?token=$TOKEN" -H 'Content-Type: application/json' -d '{"method":"passthrough","params":{"deviceId":"$DEVICE_ID","requestData":"{\"system\":{\"set_relay_state\":{\"state\":1}}}"}}'

That’s all for now! Let me know if TP-Link ever provides real API documentation…

]]>
How to Write Guitar Tabs in MuseScore https://chaidarun.com/musescore-tab Sun, 02 Jun 2019 00:00:00 GMT https://chaidarun.com/musescore-tab After brief forays into LilyPond and TuxGuitar (and a failed attempt at running Guitar Pro in Wine), I’ve settled on MuseScore for my guitar tabbing needs. Here are some notes on how I produce tab files like this one:

Journey - Don't Stop Believin'.pdf

For more advanced topics, check out MuseScore’s Tablature handbook.

Creating a new file

  1. In the new score wizard, enter Title and Composer
  2. Select the “Choose new instruments” template
  3. Add “Acoustic Guitar” or “Classical Guitar”
  4. Select the newly added staff, click “Add Linked Staff”, and change that second staff’s type to “Tab. 6-str. common”

Customizing the music font

In “Format” > “Style” > “Score”, set the music symbols font to Bravura. The default choice of LilyPond’s Emmentaler works fine, but I prefer heavier fonts for legibility.

Styling the tab staff

I find MuseScore’s default tab style pretty noisy and hard to read. Keeping note values and ties confined to the standard staff results in a cleaner tab staff.

  1. Right-click the tab staff and select “Staff/Part Properties”
  2. Click “Advanced Style Properties”
  3. Change the font to Musescore Tab Sans
  4. Decrease the font size to 8pt
  5. Uncheck “Show back-tied fret marks”
  6. In the “Note Values” tab, select “Shown as: None”

Note entry

A natural way to write your score might be to fully complete each measure before moving on to the next one, but unfortunately this is probably the slowest and most annoying way.

A much faster method is to write your score one layer at a time:

  1. Fill the first measure with eighth rests
  2. Create several dozen new measures and copy the first measure’s rests into them
  3. Starting back at the first measure, use your computer’s numpad and arrow keys to enter all of the song’s note pitches into the tab staff as eighth notes
  4. Repeat step 3 for voices (and then for note values, then for articulations, etc.)

Note that by the end of step 3, your tab staff is already 99% done! You can just consider your score finished at that point if you don’t care about the standard staff.

Performance techniques

Since MuseScore is intended as a general-purpose score editor for all instruments, expressing guitar-specific performance techniques sometimes takes a little creativity.

Hammer-ons and pull-offs

These are usually written as slurs. Since slurs are visually similar to ties, you’ll want to hide all ties in the tab staff first.

  1. Complete all aspects of your tab except for hammer-ons/pull-offs
  2. Right-click a tie (if any exist) in your tab staff and choose “Select” > “All Similar Elements in Same Staff”
  3. Hit v to toggle all selected ties’ visibility to invisible
  4. On the starting note of each hammer-on/pull-off, hit s to create a slur to the next note

Slides

I use a straight glissando for slower slides and an appoggiatura for faster slides. Both marks are available in the “Palettes” pane. Dragging a glissando to the tab staff (as opposed to the standard staff) will produce a clean line without the unnecessary “gliss.” text.

Slaps

  1. Enter each slap as an E2 note, i.e. fret 0 on the low E string
  2. Hold down Ctrl, select those 0’s, and hit Shift+X to display them as X’s
  3. In the standard staff, select the corresponding note heads and set “Head group: Cross” in the Inspector pane

Note that these X notes do make sound as E2 during audio playback, although they’re at least low enough to not be overly distracting. MuseScore doesn’t really provide a perfect solution to this problem, pointing to the fact that it’s primarily a score editor and not a score player.

Generating PDFs from the command line

mscore input.mscz -o output.pdf

To be extra fancy, you can set up inotifywait to run this command on save.

]]>
Bash Style Guide for Mac and Linux https://chaidarun.com/bash-style Sun, 30 Dec 2018 00:00:00 GMT https://chaidarun.com/bash-style Yesterday I had the deranged pleasure of writing 1000+ lines of cross-platform Bash, which got me thinking that I could probably save myself some unnecessary googling and/or decision-making in future projects by documenting my stylistic choices and the reasons behind them. I don’t expect that these guidelines will ever be completely finished, but for now:

  • Follow Google’s style guide and use ShellCheck. These resources have hundreds of good opinions that will save me a ton of typing here.

  • Stick to Bash 3. Apple preinstalls a decrepit version of Bash from 2007 because they don’t like GPL v3. I use Linux but often write scripts for Mac users, so that’s a portability issue I can’t ignore. Although Bash 4 globs and associative arrays must be avoided, older bashisms like [[ are totally fine by me.

  • If you must drop into another language, use version-independent Python. Some simple tasks like percent-encoding and JSON parsing require a more fully featured language than Bash. Macs and all major Linux distros ship with Python included (and Perl too, although Python is rather more hip these days).

    Heredoc syntax lets you declare foreign code snippets inline inside a Bash script. Your code should support both Python 2 and 3 since you’ll likely have end users on both versions. PyPI packages are out of the question—at that point you might as well give up on Bash and switch to either Docker or a statically compiled language.

    readonly response="$(curl -s 'https://example.com')"
    python - << EOF
    # -*- coding: utf-8 -*-
    import json
    for repo, result in sorted(json.loads(r'''${response}''')['Results'].items()):
      for f in result['Matches']:
        for l in f['Matches']:
          print('%s/%s:%s:%s' % (repo, f['Filename'], l['LineNumber'], l['Line']))
    EOF
    printf 'Hello from Bash land again\n'
    

    Note that in this example, the Bash variable response is interpolated into a raw, triple-quoted Python string as r'''${response}''' in order to avoid possible issues caused by unescaped characters.

  • Use shebang #!/usr/bin/env bash. The most common Bash shebangs are /bin/bash and (abusedly) /bin/sh, but I like to use env for consistency with other languages’ standard shebangs.

  • Always declare set -eu. There’s never a good reason to swallow errors and continue script execution as if nothing happened. There’s sometimes a good reason to allow undeclared variables, but I find it safer to use default values in those cases.

  • Always use printf instead of echo. This one’s a bit controversial. POSIX and Bash’s maintainer both recommend printf over echo for portability and robustness. Dogma aside, I prefer printf because I often don’t want echo’s trailing newline, e.g. when piping a string variable into grep -qE '^foobar$'.

  • Always use while read -r ... done < to loop through lines. The | while form of the loop runs its contents in a subshell, which is rarely desired since any variable assignments that happen in there will be lost.

    # Bad
    echo "${var}" | while read line; do
      printf '%s\n' "${line}"
    done
    
    # Good
    while read -r line; do
      printf '%s\n' "${line}"
    done <<< "${var}"
    
    # Good
    while read -r line; do
      printf '%s\n' "${line}"
    done < file.txt
    
    # Good
    while read -r line; do
      printf '%s\n' "${line}"
    done < <(find . -type f)
    
  • Always use [[ instead of [. The former is a more powerful drop-in replacement for the latter, and it’s simpler to always use [[ rather than remember all of the subtle differences.

  • Always use ${HOME} instead of ~. Only the former gets expanded inside double-quoted strings.

  • Prefer shift to $2. Calling shift immediately after reading each command-line argument lets you always refer to them as $1, which removes the need for a reader of your code to mentally keep track of argument indices and reduces the chance of making mistakes when adding or removing arguments.

  • Never read the same value from $1 twice. If you’re going to use a command-line argument multiple times, it’s a good defensive practice to immediately store it into a named variable—especially when your program accepts multiple arguments.

  • Print logging messages to stderr in color. This avoids tainting stdout and makes your logs easier to read and debug.

    # I'm calling these functions logX for consistency with Android's Log class
    
    logD() {
      if [[ -n "${DEBUG:-}" ]]; then
        printf '%s%s%s\n' "$(tput setaf 4)" "${1:-}" "$(tput sgr0)" >&2
      fi
    }
    
    logE() {
      printf '%s%s%s\n' "$(tput setaf 1)" "${1:-}" "$(tput sgr0)" >&2
    }
    
    logI() {
      printf '%s%s%s\n' "$(tput setaf 2)" "${1:-}" "$(tput sgr0)" >&2
    }
    
    logE 'I am a red error message'
    
  • Always use grep -E instead of grep -P. The Mac version of grep doesn’t support -P. POSIX ERE is usually capable enough; the only PCRE feature I regularly miss is multiline matching.

  • Mark all variables readonly by default. Code that doesn’t reassign variables all over the place is easier to read. readonly also alerts you to unintended reassignments. Inside a function, use local -r instead of readonly to additionally limit a variable’s scope to that function.

  • Always use = instead of == in tests. = is equivalent, shorter, and more POSIXy.

  • Prefer ${var:-default} to ${var-default}. It’s simpler to have only one kind of empty value rather than treating unset and null differently. This behavior is also consistent with string interpolation, where unset variables and null variables both get interpolated as an empty string.

  • Indent a level between pushd and popd. These two commands aren’t actually control flow statements, but indenting them as such improves readability and reduces the chance of mismatches.

    # Bad
    pushd foo > /dev/null
    printf 'bar\n'
    printf 'baz\n'
    popd > /dev/null
    
    # Good
    pushd foo > /dev/null
      printf 'bar\n'
      printf 'baz\n'
    popd > /dev/null
    
  • Prefer pushd/popd to cd. Combined with the previous tip, this makes code much easier to follow.

  • Always use command -v instead of which. The former is POSIX compliant and more consistent between GNU (i.e. Linux) and Mac.

  • Trim whitespace from wc output. The Mac version of wc prepends whitespace to its output. You can remove it with awk:

    find . -type f -name '*.pyc' | wc -l | awk '{print $1}'
    
  • Prefer single quotes to double quotes when interpolating into an eval. This is pretty niche, but basically it’s much simpler to programmatically escape characters inside single-quoted strings than inside double-quoted strings. Single-quoted strings only need to escape ', while double-quoted strings need to escape each of "$`\.

    readonly json="$(< 'request_body.json')"
    readonly curl_command="curl 'https://example.com' -d '${json//\'/\'\\\'\'}'"
    printf '%s\n' "${curl_command}"
    eval "${curl_command}"
    

    Note that printf %q also does the job, but it’s supposedly less reliable due to bugginess in old versions.

]]>
How to Connect to an L2TP/IPsec VPN from Linux https://chaidarun.com/l2tp-ipsec Wed, 26 Dec 2018 00:00:00 GMT https://chaidarun.com/l2tp-ipsec I needed to connect a Linux client to an L2TP/IPsec VPN using shared secret (a.k.a. pre-shared key) authentication, but every online guide I could find was inaccurate and/or incomplete. Here’s a fully working solution at the time of writing.

The initial 10-minute setup is pretty tedious, but once you’ve got it working you can throw it all into a script and never worry about it again.

Update A less-than-ideal but much easier fix is to build xl2tpd from source with one problematic line commented out and save : PSK "sh4r3ds3cr3t" to /etc/ipsec.secrets. This allows the use of Libreswan and NetworkManager, although on Arch Linux I also had to sudo mkdir -p /var/lib/ipsec/nss since that directory wasn’t created during Libreswan installation (?!)

Walkthrough

Let’s say you want to connect to a VPN server with public IP address 68.68.32.79 and shared secret sh4r3ds3cr3t. Your user account on the VPN server is johndoe with password j0hn5p455w0rd.

  1. Install Openswan for IPsec and xl2tpd for L2TP:

    # Arch Linux
    pacaur -S openswan xl2tpd
    
    # Ubuntu
    apt-get update && apt-get install openswan xl2tpd
    
  2. Run ip route show default to determine your local IP and default network interface. They should be the 3rd and 5th items in the single line of output. Let’s say they’re 192.168.1.1 and eth0.

  3. As root, create these four files, plugging in the six variables we’ve gotten so far:

    /etc/ipsec.conf

    version 2.0
    
    config setup
      virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12
      nat_traversal=yes
      protostack=netkey
      plutoopts="--interface=eth0"
    
    conn L2TP-PSK
      authby=secret
      pfs=no
      auto=add
      keyingtries=3
      dpddelay=30
      dpdtimeout=120
      dpdaction=clear
      rekey=yes
      ikelifetime=8h
      keylife=1h
      type=transport
      left=%defaultroute
      leftnexthop=%defaultroute
      leftprotoport=17/1701
      right=68.68.32.79
    

    /etc/ipsec.secrets

    0.0.0.0 68.68.32.79: PSK "sh4r3ds3cr3t"
    

    /etc/xl2tpd/xl2tpd.conf

    [lac vpn-connection]
    lns = 68.68.32.79
    refuse chap = yes
    refuse pap = yes
    require authentication = yes
    name = vpn-server
    ppp debug = yes
    pppoptfile = /etc/ppp/options.l2tpd.client
    length bit = yes
    

    /etc/ppp/options.l2tpd.client

    ipcp-accept-local
    ipcp-accept-remote
    refuse-eap
    require-mschap-v2
    noccp
    noauth
    idle 1800
    mtu 1410
    mru 1410
    defaultroute
    usepeerdns
    debug
    connect-delay 5000
    name johndoe
    password j0hn5p455w0rd
    
  4. Start Openswan and xl2tpd. These three commands, along with all other commands in the remainder of this walkthrough, should be run as root:

    ipsec setup --start
    xl2tpd -D &
    ipsec auto --up L2TP-PSK
    
  5. Connect to the VPN:

    echo 'c vpn-connection' > /var/run/xl2tpd/l2tp-control
    
  6. Determine the name of the PPP network interface by running ip address and looking for the entry containing ppp. Let’s say it’s called ppp0. Now look inside its entry for the word “peer” and the local IP that immediately follows it. Let’s say the peer IP is 10.1.14.252. This is the VPN server’s local IP.

  7. Add a rule to route subnet traffic through the VPN. 10.0.0.0/8 is CIDR notation for the range of local IPs assigned inside your remote network. It doesn’t need to be exact. For example, you can use 10.0.0.0/8 to specify that all IPs of the form 10.*.*.* belong to the VPN even if you suspect that your remote network only assigns IPs of the form 10.1.10.* and 10.1.14.*.

    ip route add 10.0.0.0/8 via 10.1.14.252 dev ppp0
    

    You can test the new rule by pinging or SSHing the local IP of another machine inside the VPN. If that’s all you need, you can stop here.

  8. Remember the variables we found in step 2? Use them here to route all internet traffic (not just subnet traffic) through the VPN:

    ip route add 68.68.32.79 via 192.168.1.1 dev eth0
    ip route delete default via 192.168.1.1 dev eth0
    ip route add default via 10.1.14.252 dev ppp0
    

Done! You can now open your web browser and access VPN-only resources. To disconnect:

  1. Undo the routing rules:

    ip route delete default via 10.1.14.252 dev ppp0
    ip route add default via 192.168.1.1 dev eth0
    ip route delete 68.68.32.79 via 192.168.1.1 dev eth0
    
  2. Kill the VPN connection, xl2tpd, and Openswan:

    echo 'd vpn-connection' > /var/run/xl2tpd/l2tp-control
    ipsec auto --down L2TP-PSK
    killall xl2tpd
    ipsec setup --stop
    

Caveats

I don’t recommend switching from Ethernet to Wi-Fi or vice versa while connected to the VPN since that will change the default network interface (eth0 in our example) and possibly leave your routing table in a weird state until reboot.

These instructions assume that you regularly use only one L2TP/IPsec VPN. It’s straightforward to simply add a new section to the .conf files for each additional VPN, but the script below doesn’t support that use case out of the box.

I’ve only tested this on Arch Linux. Let me know if your distro needs any tweaks!

Script

Once you’ve successfully executed the steps above by hand, you can automate them all using a script like the one below. I normally run it as sudo ./vpn toggle.

#!/usr/bin/env bash
#
# Enables or disables a client connection to an L2TP/IPsec VPN. Usage: save
# this file as `vpn`, `chmod +x` it, and run `./vpn [up/down/toggle]` as root.
# Requires openswan and xl2tpd packages. Tested on Arch Linux.
#
# Based on:
#   https://bbs.archlinux.org/viewtopic.php?pid=1773313#p1773313
#   https://bbs.archlinux.org/viewtopic.php?pid=1781882#p1781882
#
# Example `ip route` output before connecting to VPN:
#   default via 192.168.1.1 dev wlp4s0 proto dhcp src 192.168.1.7 metric 303
#   10.1.14.252 dev ppp0 proto kernel scope link src 10.1.16.107
#   192.168.1.0/24 dev wlp4s0 proto dhcp scope link src 192.168.1.7 metric 303
#
# Example `ip route` output after connecting to VPN:
#   default via 10.1.14.252 dev ppp0
#   10.0.0.0/8 via 10.1.14.252 dev ppp0
#   10.1.14.252 dev ppp0 proto kernel scope link src 10.1.16.107
#   71.245.184.58 via 192.168.1.1 dev wlp4s0
#   192.168.1.0/24 dev wlp4s0 proto dhcp scope link src 192.168.1.7 metric 303

set -eu

# VPN settings (edit these!)
vpn_server_public_ip='68.68.32.79'
vpn_subnet='10.0.0.0/8'
vpn_pingee_local_ip='10.1.10.22'
vpn_shared_secret='sh4r3ds3cr3t'
vpn_username='johndoe'
vpn_password='j0hn5p455w0rd'

# Ensure that we're running as root
if [[ "$(id -u)" != 0 ]]; then
  echo 'Must run as root!'
  exit 1
fi

# Ensure that required packages are installed
if ! command -v ipsec > /dev/null; then
  echo '`ipsec` command not found! Please install Openswan.'
  exit 1
fi
if ! command -v xl2tpd > /dev/null; then
  echo '`xl2tpd` command not found! Please install xl2tpd.'
  exit 1
fi

# Handle subcommands
subcommand="${1-}"
if [[ "${subcommand}" = 'toggle' ]]; then
  if ip address | grep -q ': ppp'; then
    subcommand='down'
  else
    subcommand='up'
  fi
fi
case "${subcommand}" in
  # Connect to VPN
  up)
    # Ensure that we're not already connected
    if ip address | grep -q ': ppp'; then
      echo 'Already connected to VPN!'
      exit 1
    fi

    # Write config files
    default_network_device="$(ip route show default | cut -d' ' -f5)"
    cat > /etc/ipsec.conf << EOF
version 2.0

config setup
  virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12
  nat_traversal=yes
  protostack=netkey
  plutoopts="--interface=${default_network_device}"

conn L2TP-PSK
  authby=secret
  pfs=no
  auto=add
  keyingtries=3
  dpddelay=30
  dpdtimeout=120
  dpdaction=clear
  rekey=yes
  ikelifetime=8h
  keylife=1h
  type=transport
  left=%defaultroute
  leftnexthop=%defaultroute
  leftprotoport=17/1701
  right=${vpn_server_public_ip}
EOF
    cat > /etc/ipsec.secrets << EOF
0.0.0.0 ${vpn_server_public_ip}: PSK "${vpn_shared_secret}"
EOF
    cat > /etc/xl2tpd/xl2tpd.conf << EOF
[lac vpn-connection]
lns = ${vpn_server_public_ip}
refuse chap = yes
refuse pap = yes
require authentication = yes
name = vpn-server
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.client
length bit = yes
EOF
    cat > /etc/ppp/options.l2tpd.client << EOF
ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
idle 1800
mtu 1410
mru 1410
defaultroute
usepeerdns
debug
connect-delay 5000
name ${vpn_username}
password ${vpn_password}
EOF

    # Start Openswan
    ipsec setup --start
    ipsec setup --status | grep -q 'IPsec running'

    # Start xl2tpd
    xl2tpd -D &
    sleep 1
    ipsec auto --up L2TP-PSK
    ipsec setup --status | grep -q '1 tunnels up'

    # Connect to VPN
    echo 'c vpn-connection' > /var/run/xl2tpd/l2tp-control
    while true; do
      sleep 1
      ppp_device="$(ip address | grep -oE '^[0-9]+: ppp\w+' | cut -d' ' -f2)"
      if [[ -n "${ppp_device}" ]]; then
        break
      fi
      echo 'Waiting for ppp device...'
    done
    peer_ip="$(ip address show "${ppp_device}" | grep -oE 'peer [0-9.]+' | cut -d' ' -f2)"
    ip route add "${vpn_subnet}" via "${peer_ip}" dev "${ppp_device}"
    [[ -z "${vpn_pingee_local_ip}" ]] || ping -c 1 "${vpn_pingee_local_ip}"

    # Route all internet traffic through VPN
    local_ip="$(ip route show default | cut -d' ' -f3)"
    ip route add "${vpn_server_public_ip}" via "${local_ip}" dev "${default_network_device}"
    ip route delete default via "${local_ip}" dev "${default_network_device}"
    ip route add default via "${peer_ip}" dev "${ppp_device}"
    ;;

  # Disconnect from VPN
  down)
    # Ensure that we're already connected
    if ! ip address | grep -q ': ppp'; then
      echo 'Not connected to VPN!'
      exit 1
    fi

    # Undo routing rules
    default_network_device="$(ip route show "${vpn_server_public_ip}" | cut -d' ' -f5)"
    ppp_device="$(ip address | grep -oE '^[0-9]+: ppp\w+' | cut -d' ' -f2)"
    peer_ip="$(ip address show "${ppp_device}" | grep -oE 'peer [0-9.]+' | cut -d' ' -f2)"
    local_ip="$(ip route show "${vpn_server_public_ip}" | cut -d' ' -f3)"
    ip route delete default via "${peer_ip}" dev "${ppp_device}"
    ip route add default via "${local_ip}" dev "${default_network_device}"
    ip route delete "${vpn_server_public_ip}" via "${local_ip}" dev "${default_network_device}"

    # Kill VPN connection
    echo 'd vpn-connection' > /var/run/xl2tpd/l2tp-control
    sleep 1

    # Kill xl2tpd
    ipsec auto --down L2TP-PSK
    killall xl2tpd
    sleep 1

    # Kill Openswan
    ipsec setup --stop
    ;;

  *)
    echo 'Invalid subcommand!'
    exit 1
    ;;
esac

echo 'Success!'
]]>
How to Replace Dropbox with Syncthing https://chaidarun.com/dropbox-syncthing Thu, 17 Aug 2017 00:00:00 GMT https://chaidarun.com/dropbox-syncthing Dropbox is convenient, but there are several reasons why you might want to drop it:

  • You feel uneasy about handing sensitive personal info to strangers
  • You find the Dropbox client too resource hungry or invasive
  • You don’t want to pay for storage beyond the free 2 GB

The most popular open source, self-hosted alternatives to Dropbox are ownCloud, Seafile, and Syncthing. I went with Syncthing mainly because it’s the smallest and simplest, following the Unix philosophy of doing one thing well. Much of its simplicity stems from the fact that Syncthing uses a peer-to-peer protocol as opposed to the other options’ client-server models.

Dropbox logo Syncthing logo

Here’s how I made the switch:

  1. Set up a home server

    In other words, obtain a computer that you can keep running 24/7. It doesn’t need to be especially powerful: I use a Raspberry Pi attached to a portable 2 TB HDD. Plugging your server into a UPS is a nice touch.

    It’s important to have at least one peer always online so that someone can provide a complete copy of your files at any given time. No syncing can ever actually take place if your devices never have the opportunity to sync with each other!

    But wait, doesn’t this scheme sound more like client-server than peer-to-peer? The P2P aspect becomes more important in the event that your home server goes offline for whatever reason, e.g. your house burning down. That would be a single point of failure if not for the fact that your remaining devices can continue syncing among themselves as peers. (The ideal setup is to have two “server” peers located in different cities in order to protect your data from catastrophes that could otherwise wipe out your entire network of synced devices.)

  2. Install Syncthing on your devices

    On Linux, I additionally use Syncthing-GTK for laptops and Syncthing-inotify for my headless home server.

    On Android, I use the unofficial Anyplace Sync Browser app. It’s bare-bones but much more practical than the official app because it downloads files on demand instead of copying all of the files in your sync folder onto your phone. (Yes, you can have multiple top-level sync folders, but that’s more complexity than I care for right now.)

    Update To use Anyplace Sync Browser on Nougat or above, you’ll need to go into the system settings and manually grant it the “Storage” permission. Now I just use the official Syncthing app, though, since my current phone has enough space to fit my entire sync folder.

  3. Make your Dropbox folder your Syncthing folder

    Before firing up Syncthing for the first time, stop Dropbox on all of your devices to prevent conflicts. Then start Syncthing on your home server. After it creates the default files, stop Syncthing. Locate the newly created sync folder, e.g. ~/Sync/default.

    Now that both Dropbox and Syncthing are off, it’s safe to symlink ~/Sync/default to ~/Dropbox or wherever your existing Dropbox folder is located. Alternatively, you can simply move/copy everything from your Dropbox folder into the new Syncthing folder.

    Start Syncthing again on your home server. On each of your other devices, you can now start Syncthing, add your home server as a remote device, and share the default sync folder. Adding the home server as an “introducer” has the benefit of automatically creating share connections among your non-home-server devices.

  4. Enable file versioning

    One creepy but lifesaving feature of Dropbox is that it stores every version of every synced file ever. In contrast, a real danger with Syncthing is that file deletion propagates to all devices and is irreversible by default (unless, of course, the deleting device moved the file into a trash can folder somewhere).

    Fortunately, you can tell Syncthing to use either its own trash can or Dropbox-style file versioning. Whenever you delete or modify a file in a versioned sync folder, Syncthing places a timestamped copy of the original file into a special .stversions subdirectory of the sync folder. I’ve enabled staggered file versioning with no maximum age on my home server.

    After enabling file versioning, you may notice this minor quirk. It really makes no practical difference, but if you’re OCD about that stuff then you may take interest in this script I wrote.

  5. Optional: Access your home server from the public internet

    Maybe you want to check out your home server’s Syncthing dashboard, or maybe you want to remotely restore some deleted files from your home server’s .stversions. How do you connect to your server when you’re not at home?

    Home server

    You’ll first need to go home and open ports on your router, whose admin panel is typically accessible at 192.168.0.1 or 192.168.1.1. Look for the port forwarding settings and map the router’s port 8384 to your home server’s port 8384. The Syncthing dashboard runs on port 8384; you can also open ports for other programs like SSH1. This wiring step is necessary because all devices on your home network share the same public IP address (e.g. 123.45.67.89), and so your router needs to know which one of them should handle incoming address-port combinations like 123.45.67.89:8384.

    You can now remotely view your home server’s Syncthing dashboard by going to https://123.45.67.89:8384/ in your web browser. Go into the dashboard settings and set a password so that nobody else will be able to view it.

    One last thing: that IP address is hard to remember and not even guaranteed to stay the same forever! Registering for a free subdomain with a service like FreeDNS will let you access your server via something more reasonable like https://johndoe.jumpingcrab.com:8384/. FreeDNS works by giving you a cron script for your home server that periodically sends your router’s current IP address to the FreeDNS DNS servers, which in turn map it to a human-readable domain name.

In the end, this is all a bit of legwork but definitely worth the peace of mind. I’ve found my holy grail of a sync/backup solution that’s free (in both senses), secure, fast, simple, and robust.

  1. If you do decide to expose SSH, either map your server’s port 22 to a different port number on your router or edit your server’s SSH config to use a port other than 22. This simple security-through-obscurity measure defeats the 99% of login bots that target the default SSH port. 

]]>
Get a Guitalele Already https://chaidarun.com/guitalele Wed, 24 May 2017 00:00:00 GMT https://chaidarun.com/guitalele The guitalele is a miniature guitar built like a ukulele, and it should be your next—or even first—musical instrument.

For comparison with any instruments you might already play, here are my flamebaity ratings of several instruments’ qualities:

  Fun Versatility Portability Affordability Learnability
Acoustic guitar ★★★★ ★★★★★ ★★★ ★★★ ★★★
Bass guitar ★★ ★★★ ★★★ ★★★★
Clarinet ★★ ★★ ★★★★ ★★ ★★★
Electric guitar ★★★★ ★★★ ★★★ ★★★ ★★★
Guitalele ★★★★★ ★★★★ ★★★★ ★★★★ ★★★
Harmonica ★★ ★★ ★★★★★ ★★★★★ ★★★★★
Keyboard ★★★ ★★★★★ ★★ ★★★ ★★★
Piano ★★★ ★★★★★ ★★ ★★★
Recorder ★★ ★★★★★ ★★★★★ ★★★★★
Saxophone ★★★★ ★★★ ★★★ ★★ ★★★★
Sousaphone ★★★★★ ★★ ★★★★
Tuba ★★★ ★★ ★★★★

The guitalele does earn the most stars here, but that’s a pretty crude metric. To elaborate, you should get a guitalele already because…

It’s fun

Some instruments like violin are culturally very serious and competitive. Others like sousaphone are utterly ridiculous and more about showmanship than musicianship. The guitalele inherits a casual, friendly attitude from its ukulele roots.

Its six strings are tuned ADGCEA, which is uncoincidentally equivalent to capoing a regular guitar at the 5th fret. If you can play guitar, you already know how to play guitalele! The familiar fingering and small size make it even more conducive to passing around the campfire than a guitar is.

For newcomers to stringed instruments, the guitalele has another easily overlooked benefit: its low-tension strings won’t hurt your fingertips.

It’s versatile

One big reason why guitar and piano are so popular is that they can produce multiple notes at the same time. Unlike wind instruments, they also allow you to sing along and—in the case of guitar—use percussive techniques like tapping and slapping. This all adds up to the ability to play virtually any song on a guitalele without the need for accompaniment.

It’s portable

As my go-to travel instrument, my guitalele has joined me on trips to Mexico, Hawaii, Costa Rica, and Thailand. Ukulele lookalikes are especially appropriate in tropical locations :)

A guitalele can fit easily in an overhead bin and, in a pinch, underneath the seat in front of you. Flight attendants usually don’t mind either way. Tenor ukulele cases happen to fit perfectly and are a good idea if you travel often.

My one small gripe about guitaleles is that they’re only slightly louder than ukuleles and can be hard to hear in noisy environments. That’s not an entirely bad thing, though! I can practice during layovers without bothering anyone, whereas I still have yet to find a place soundproofed enough for sax.

It’s cheap

$100 is way down on the low end as far as musical instruments go, making it a responsible investment for musical newcomers. Only toy instruments like recorders and diatonic harmonicas are much cheaper.

My guitalele has far and away the lowest cost per playing hour of any instrument I’ve owned, and yet it’s still in great condition after three years.

Update Eleven years now!

It’s easy

If you want to start playing music but don’t have 10,000 hours to spend practicing, you really only need to learn four chords in order to play most pop songs. Unless you have huge hands, you’ll also probably find the widest frets on the guitalele more comfortable to hold down than on a regular guitar.

Once you get tired of strumming the basic chords, you can start the long journey of learning more colorful guitar techniques like bends, harmonics, and tremolo picking.

Another advantage over wind instruments in particular is that you can just pick up a guitalele and put it down without the hassle of assembling, greasing, wiping, and draining.

I have no ties to Yamaha and wasn’t paid to write this! Having finally found an instrument that’s both extremely fun and extremely practical, I hope this review can help end that search for anyone else in the same situation.

]]>
Arch Linux Notes https://chaidarun.com/arch-notes Wed, 29 Mar 2017 00:00:00 GMT https://chaidarun.com/arch-notes This page is a living document intended to save time for my future self in case the same issues ever crop up again. It’s based on my experience with running Arch on a Raspberry Pi 2, a Lenovo Flex 4, a ThinkPad P50, a ThinkPad P51, and a GMKtec NucBox M2 Pro S.

Arch is surprisingly stable if you remember the single most important post-install step: subscribe to the official news feed for breaking changes!

Arch screenshot

OS

Network

Display

Miscellaneous

Installing Arch via archinstall wizard

One weird trick Arch devs don’t want you to know: Arch comes with an installation wizard. It’s barely documented (by Arch wiki standards) and never even mentioned in the official installation guide.

Fair enough, you should probably have some idea of what you’re doing before taking shortcuts. I’ve installed Arch enough times by now that I know what partitions are and exactly how I want them set up, but I’m not interested in figuring out such rarely used commands for the dozenth time.

Definitely don’t be too lazy by blindly accepting the defaults. There are a few important non-default things to set up during archinstall that are more painful to try doing later:

  • Create a root password and non-root user
  • Create partitions encrypted via LUKS
  • Copy your USB installer’s WiFi connection settings

Refreshing the mirror list

By default, your Arch installation will never update its own list of mirrors (software package download sources). This is bad because these volunteer-run mirrors come and go over the years, and the fastest one today will not remain so forever. Reflector helps you periodically generate a fresh list of mirrors sorted by speed:

sudo reflector --latest 10 --protocol https --save /etc/pacman.d/mirrorlist --sort rate

Disabling -debug packages

In early 2024, I noticed that installing foobar would also end up installing foobar-debug by default. You can disable that new behavior by editing /etc/makepkg.conf to specify !debug instead of debug.

Recovering from a bad upgrade

I once ran a pacman -Syu that spat out a bunch of errors, probably due to an unfortunate interaction between package versions available at the time. After lazily crossing my fingers and restarting the computer, I got a boot error (of course):

Failed to execute /init (error -13)
Starting init: /sbin/init exists but couldn't execute it (error -13)
Starting init: /bin/init exists but couldn't execute it (error -13)
Starting init: /bin/sh exists but couldn't execute it (error -13)
Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/init.txt for guidance.

Some weeks later I crossed my fingers and ran the upgrade command again, which fixed everything. How do you run commands on a system that you can’t even boot into? I followed this advice to use arch-chroot. Cue painfully detailed guide to arch-chroot:

  1. Before turning on the computer, plug in a USB stick with Arch on it. Also plug in an ethernet cable if your planned fix requires a connection.

  2. Turn on the computer and go into BIOS by hitting some manufacturer-specific key combination after the screen turns on (e.g. Fn+F2 on my Lenovo laptop). Find the boot priority list and reorder it so that the USB drive has the highest priority. Save, exit, and boot into USB when asked.

  3. Now you have a command prompt. This is where you’ll be using arch-chroot. Pleeease don’t blindly run this snippet as a script!

    # First, list all partitions and take note of the one that seems like your computer's root filesystem, e.g. /dev/sda8
    fdisk -l
    
    # Mount that drive
    mount /dev/sda8 /mnt
    
    # Enter your computer's root filesystem as root
    arch-chroot /mnt
    
    # Do whatever's necessary to fix the original problem (in my case, another upgrade attempt)
    pacman -Syu
    
    # Leave the chroot environment
    exit
    
    # Unmount the filesystem
    umount /mnt
    
    # Done!
    reboot
    
  4. As the computer reboots, go back into BIOS and move GRUB back to highest boot priority. Boot into your hopefully working system and remove the USB drive.

Installing from PKGBUILD

Problem: A libreswan update broke compatibility with networkmanager-l2tp. Downgrading libreswan (AUR) via downgrade wasn’t an option since I aggressively clear my pacman cache, and after a week I got tired of waiting for the networkmanager-l2tp fix to land in Arch’s official repo.

Solution: Clone the libreswan repo to /tmp, cd into it, git reset --hard to the last compatible version, and run makepkg -si to build and install.

Using an external hard drive

External HDDs not marketed specifically for Mac are typically formatted as NTFS, the file system used by Windows. It’s best to keep NTFS if you ever plan to access your HDD from Windows, in which case you’ll need to do a little extra setup for Linux:

# First, plug the HDD into your computer and verify that it can be detected
lsusb

# Check that the kernel logs contain fairly detailed info about the HDD. If all you see is a few generic lines about a new USB device being detected, try restarting the computer
dmesg | tail -n 20

# List all partitions and find the one that seems like your HDD, e.g. /dev/sdb1
# (Note that NTFS drives might show up as type "fuseblk")
sudo fdisk -l

# Install ntfs-3g, which adds NTFS support to `mount`
sudo pacman -S ntfs-3g

# Create the mount point at /mnt/whatever-name-you-want
sudo mkdir /mnt/seagate

# Mount the partition you found from `fdisk -l`
sudo mount -o rw,user,async -t ntfs-3g /dev/sdb1 /mnt/seagate

# Unmount when done (before you unplug the HDD)
sudo umount /mnt/seagate

Make sure to provide async as a mount option instead of sync! The latter absolutely kills write performance on NTFS; we’re talking less than 100 kB/s.

Encrypting an external hard drive

Or if you’re sure you’ll only ever want to use your HDD with Linux, you can get rid of NTFS and reformat the drive with full-disk encryption via LUKS:

# Determine your HDD's device name, e.g. sda1
lsblk

# Unmount HDD if mounted
sudo umount /dev/sda1

# Optional: zero out the HDD for peace of mind
sudo dd if=/dev/zero of=/dev/sda1 bs=4M status=progress

# Format HDD (choose a strong password and don't lose it)
sudo cryptsetup luksFormat /dev/sda1

# Open container and invent a mapper name for it, e.g. seagate
sudo cryptsetup luksOpen /dev/sda1 seagate

# Create filesystem
sudo mkfs.ext4 /dev/mapper/seagate

# Mount filesystem at empty directory of your choosing
sudo mkdir -p /mnt/seagate
sudo mount /dev/mapper/seagate /mnt/seagate

# Give yourself ownership of the filesystem (root by default)
sudo chown -R art:art /mnt/seagate

# To unmount, first unmount the mapper and then close the container
sudo umount /dev/mapper/seagate
sudo cryptsetup luksClose seagate

Freeing up disk space

When uninstalling packages, always use pacman -Rs to include would-be orphans.

Pacman doesn’t delete old package versions, so you’ll need to do so yourself after package upgrades:

$ paccache -r
==> finished: 523 packages removed (disk space saved: 1.75 GiB)

Pacgraph shows your largest installed packages, which you can then remove if no longer needed.

The systemd journal can grow up to 4 GiB. If you don’t need all those logs, you can cap it to a lower value.

I had Docker for a while and later decided to uninstall it. Deleting /var/lib/docker freed up 8.1 GB.

Increasing the file watch limit

Many applications that watch your filesystem for changes can hit the OS’s (usually rather small) default limit on the number of files that can be simultaneously watched. I’ve personally run into this issue with Android Studio, Webpack, Watchman, and Syncthing.

Consider this the missing step in the Arch setup guide: drastically bumping up the inotify watch limit.

Update Arch now seems to set a high limit by default, 524288 on a new install.

Booting UEFI with an existing EFI partition

GRUB was presenting me with its own slow-ass shell instead of an OS selection menu. The problem was that the live USB had installed some rather important files into the root filesystem’s /boot folder instead of the EFI partition.

I had mistakenly been under the impression that mounting the existing EFI partition (used by Windows) to the root filesystem’s /boot was unnecessary because both already existed and had stuff in them. Reinstalling with the partition mounted fixed things.

Ditching GRUB (only for UEFI systems)

GRUB is the kitchen sink bootloader. Systemd comes with its own UEFI bootloader that’s much simpler and works perfectly fine if all you need is an OS selection menu. As explained in the guide, just enable systemd-boot and create the two files arch.conf and loader.conf.

Suspending after inactivity

In xfce4-power-manager-settings I specified that my laptop should go to sleep after an hour of inactivity, but that setting never seemed to work. The fix is to just edit one line in one file, as described here.

Switching to Linux LTS

This is normally unnecessary but did fix a problem I had on my Thinkpad P51: after upgrading my system in September 2024 for the first time in several months, resume from suspend would reliably greet me with a screen full of errors like “Fixing recursive fault but reboot is needed” and “BUG: scheduling while atomic: irq/148-nvidia…”

I switched to the more stable LTS version of Linux by backing up /boot/initramfs-linux-fallback.img /boot/initramfs-linux.img /boot/vmlinuz-linux to somewhere in my home directory, deleting those files to make space in /boot, installing linux-lts and nvidia-lts via pacman, editing /boot/loader/entries/arch.conf (see above) to reference the newly created -lts files inside /boot, and restarting the computer.

Booting into USB on GMKtec NucBox

I flashed Arch onto a USB drive and set it to highest boot priority in BIOS, but that setting kept getting ignored. Finally I found a solution deep in the bowels of Reddit: flash Arch onto two USB drives and plug them both in. Weird AF indeed.

Manually connecting to a WPA network

TLDR of the Arch guide (run as root):

ip link set wlp4s0 up # WiFi interface name `wlp4s0` obtained from `ip a`
wpa_supplicant -D nl80211,wext -i wlp4s0 -c <(wpa_passphrase "My WiFi SSID" "p4ssw0rd")
# (Switch to another TTY at this point)
dhcpcd
ping 8.8.8.8

My home WiFi and/or my Raspberry Pi’s WiFi setup are pretty flaky, so I run cron as root to force a reconnect every day:

0 9 * * * killall dhcpcd wpa_supplicant; sleep 5; wpa_supplicant -D nl80211,wext -i wlan0 -c <(wpa_passphrase "My WiFi SSID" "p4ssw0rd") & sleep 20; dhcpcd &

ping works but curl doesn’t

Pinging 8.8.8.8 worked, but pinging or curling google.com didn’t. I opened my /etc/resolv.conf and saw this:

# Generated by resolvconf
search tail39f21.ts.net
nameserver 100.100.100.100

Looks like Tailscale overwrote this file. I found another helpfully named file /etc/resolv.pre-tailscale-backup.conf with these contents:

# Generated by NetworkManager
nameserver 1.1.1.1
nameserver 8.8.8.8
nameserver 4.2.2.2

Adding those entries back to /etc/resolv.conf fixed my DNS… temporarily. Within a few seconds, the file reverted to its previous state.

The permanent fix is to uninstall resolvconf (which I had apparently installed as a dependency of netctl), start and enable systemd-resolved (which was even installed already but disabled?), symlink /etc/resolv.conf, and restart everything. When this happened on my Raspberry Pi, I also had to restart systemd-networkd. Now DNS works and my /etc/resolv.conf looks like this:

nameserver 127.0.0.53
options edns0 trust-ad
search [REDACTED].ts.net

NTP active but not syncing

My UPS battery died and I finally had to shut down my Raspberry Pi after several years of continuous uptime. When I turned it back on, I noticed that the time as shown by date was several years off. I manually corrected it:

sudo timedatectl set-time '2024-10-23 04:41:10'

Why was the time wrong in the first place, though? timedatectl showed that NTP syncing was active but not affecting my system clock:

System clock synchronized: no
              NTP service: active

My /etc/systemd/network contained a file called eth0.network but no file for WiFi. Adding one fixed the problem.

Fixing iptables unknown options

The default version of iptables drops support for a command-line option that Docker uses:

docker: Error response from daemon: driver failed programming external connectivity on endpoint tender_volhard (954d664336eb5ea7b2c7f808889b3033977b45f53f99ba38bbc66bfcf14a61ef):  (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 80 -j DNAT --to-destination 172.17.0.2:80 ! -i docker0: iptables v1.8.2 (legacy): unknown option "--dport"
Try `iptables -h' or 'iptables --help' for more information.

Switching to iptables-nft restores the legacy --dport option. Note that whenever you update the Linux kernel, the option may become unavailable again until the next reboot.

Port 53 already in use

When I tried to set up Pi-hole in Docker, I got this error:

Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint pihole (851ce571120db7262a96ab24f4c019005a37094543b008cd1ec90baa35b392cf): failed to bind host port for 0.0.0.0:53:172.19.0.2:53/tcp: address already in use

The solution is to set DNSStubListener=no in /etc/systemd/resolved.conf and then sudo systemctl restart systemd-resolved.

Fixing slow pip downloads

One day my pip install suddenly became comically slow, seemingly on the order of 10 kB/s. A pip -vvv revealed that it was hanging on “Starting new HTTPS connection (1) pypi.org”, which led me to this post that correctly identified my issue: pip tried and failed to connect using multiple IPv6 addresses until it finally gave up and fell back to IPv4.

My workaround was to simply disable IPv6 in kernel params 🤷

Connecting to an L2TP/IPsec VPN

Just to be safe, uninstall all of the following packages: openswan, strongswan, networkmanager-openswan, networkmanager-strongswan, and networkmanager-libreswan. I couldn’t get any of these to work. Openswan seems capable but there’s no way I’m going to run through this gauntlet for a single VPN connection if I can avoid it.

Install networkmanager, libreswan, and networkmanager-l2tp. (It’s important to install networkmanager-l2tp last!) Then add the VPN entry as usual by right-clicking the NetworkManager applet or running nm-connection-editor.

When setting up the VPN entry, go into “IPsec Settings” and check “Enable IPsec tunnel to L2TP host”. You may also need to uncheck “Perfect Forward Secrecy”.

Update Due to a Linux kernel regression, you supposedly now must also install linux-lts and then run sudo grub-mkconfig. I still haven’t gotten it working, though.

Update I’ve given up on libreswan and networkmanager. Here’s a guide for openswan.

Getting Intel Wireless 8260 card to work

Trying to activate my WiFi card with ip link set wlp4s0 up failed with a vague RTNETLINK answers: Input/output error message. Turns out that version 22 of the Linux kernel’s firmware for my particular WiFi card was problematic. You can see what firmware version you’re on by running dmesg | grep 'iwlwifi.*loaded'.

The solution was to force Linux to use the previous version of the firmware by disabling the latest version. As root:

# Stop kernel module "iwlwifi", which will need to be restarted. (Not sure what iwlmvm is, but `rmmod iwlwifi` complains that it's in the way)
rmmod iwlmvm
rmmod iwlwifi

# Rename the firmware file so that Linux won't find it
cd /lib/firmware
mv iwlwifi-8000C-22.ucode iwlwifi-8000C-22.bak

# Re-enable modules
modprobe iwlwifi
modprobe iwlmvm

Since I was setting up Arch at my office without the luxury of ethernet, I actually ended up having to go through this process twice: once on the live USB and again on my laptop’s instance of Arch.

Fixing the default LaTeX font

Rendering a LaTeX file to PDF works fine after installing texlive-core, but what’s up with the fugly text? Zooming in reveals that the bitmap version of Computer Modern is being used. We need to specify an infinitely scalable vector font instead.

The first step of the fix is to enable Latin Modern, the widely accepted successor to Computer Modern:

updmap --enable Map=lm.map

Then add this to the preamble of your LaTeX document:

\usepackage{lmodern}

Generate the PDF again, and font rendering should be fixed.

Supporting true color in xfce4-terminal

Set TERM=xterm-256color in your .zshrc / .bashrc.

Multi-monitor on ThinkPad P51

Using the default nouveau graphics driver results in xrandr freezing with an external monitor plugged in. I still haven’t gotten the DisplayPort ports to work, but here’s my solution for VGA/DVI:

  1. Install nvidia package
  2. Uninstall xf86-video-nouveau if installed
  3. Reboot computer. In BIOS, switch from “Hybrid Graphics” to “Discrete Graphics”

However, fonts might look huge in some apps after switching to nvidia

Huge fonts with NVIDIA driver

The proprietary nvidia driver misdetects my laptop screen’s DPI for some reason. This can be fixed by adding the command below to ~/.xinitrc and then rebooting.

# Replace `DP-2` with whatever `xrandr` says your laptop screen is called
xrandr --output DP-2 --dpi 96

A possibly related issue is that X fails to identify the laptop screen as the primary screen (even when no external monitors are connected), causing i3status to hide its notification area. This can be fixed by appending --primary to the command above.

Ignoring GTK theme in Firefox

In xfce4-appearance-settings I’ve selected the Adwaita-dark GTK theme. It looks decent everywhere except in Firefox, which will render dark scrollbars and form controls on otherwise light pages.

To force Firefox to use the default (light) Adwaita theme, I’ve created an executable /usr/bin/local/firefox script with these contents:

#!/usr/bin/env bash
GTK_THEME=Adwaita /usr/bin/firefox

Make sure /usr/bin/local appears before /usr/bin in your PATH.

Adding Thai font support

Install fonts-tlwg from the AUR. It looks perfectly fine and just works. I first tried ttf-ms-win10 but gave up on getting all of my Windows fonts to the same versions expected by the PKGBUILD.

Pairing Bluetooth headphones

Steps distilled from the Arch wiki and this answer:

pacman -S bluez bluez-utils blueman pulseaudio-bluetooth
modprobe btusb
systemctl enable bluetooth.service
pulseaudio -k
blueman-applet

Fixing audio mute

First of all, make sure you have the alsa-utils package installed. It provides two useful programs to keep open while debugging sound issues: speaker-test -t wav -c 2 plays a looping audio sample, and alsamixer shows the current volume and mute status of all audio channels.

Most people online suggest muting via the command amixer set Master toggle. This works but has an unfortunate quirk on some systems: running it a second time doesn’t actually unmute!

Muting Master mutes all channels, but unmuting Master unmutes only the Master channel. That’s a problem if you have other audio channels that depend on Master, e.g. Speaker and Headphone. The pulseaudio package provides pactl, an alternative to amixer that doesn’t suffer from the same bug. Run pactl set-sink-mute 0 toggle to toggle the mute status of all channels in unison.

In my i3 config, I have that command bound to my keyboard’s mute button like so:

bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute 0 toggle

Pacman color option unrecognized

A pacman bug causes this error when installing AUR packages with pacaur:

/usr/bin/pacman: unrecognized option '--color never'

To fix, uncomment Color in /etc/pacman.conf.

Fixing Flatpak Steam

I installed Steam via Flatpak as documented in the Arch wiki, which initially worked fine. However, I got this error the second time I tried running Steam:

$ flatpak run com.valvesoftware.Steam
**
flatpak:ERROR:libglnx/glnx-shutil.c:155:mkdir_p_at_internal: assertion failed: (!did_recurse)
Bail out! flatpak:ERROR:libglnx/glnx-shutil.c:155:mkdir_p_at_internal: assertion failed: (!did_recurse)
zsh: abort (core dumped)  flatpak run com.valvesoftware.Steam

It looked like some symlink targets were missing…

$ ls -AGl ~/.var/app/com.valvesoftware.Steam
total 8
lrwxrwxrwx 1 art    6 Jul 28 02:38 cache -> .cache
lrwxrwxrwx 1 art    7 Jul 28 02:38 config -> .config
drwxr-xr-x 2 art 4096 Jul 28 02:38 config.old
lrwxrwxrwx 1 art   12 Jul 28 02:38 data -> .local/share
drwxr-xr-x 2 art 4096 Jul 28 02:38 .ld.so

Pointing them to their counterparts in my home directory solved the issue:

$ ln -sf ~/.cache ~/.var/app/com.valvesoftware.Steam/cache

$ ln -sf ~/.config ~/.var/app/com.valvesoftware.Steam/config

$ ln -sf ~/.local/share ~/.var/app/com.valvesoftware.Steam/data

$ ls -AGl ~/.var/app/com.valvesoftware.Steam
total 8
lrwxrwxrwx 1 art   16 Jul 29 02:02 cache -> /home/art/.cache
lrwxrwxrwx 1 art   17 Jul 29 02:02 config -> /home/art/.config
drwxr-xr-x 2 art 4096 Jul 28 02:38 config.old
lrwxrwxrwx 1 art   22 Jul 29 02:02 data -> /home/art/.local/share
drwxr-xr-x 2 art 4096 Jul 28 02:38 .ld.so

Fixing Android Studio

I was trying to set up React Native, which involves setting up Android Studio. Everything went well up until react-native run-android:

java.io.IOException: Cannot run program "/home/art/Android/Sdk/build-tools/23.0.1/aapt": error=2, No such file or directory

The solution is to enable the multilib repository and then install some packages:

pacman -S lib32-gcc-libs lib32-glibc lib32-libstdc++5 lib32-ncurses lib32-zlib

If your JAVA_HOME environment variable is unset, you may also need to set it to the grandparent directory of the file found by locate tools.jar. For example:

export JAVA_HOME=/opt/android-studio/jre

If you get errors like the one below when running Android Studio 3’s emulator, this and/or this should fix it.

Emulator: libGL error: unable to load driver: i965_dri.so

My setup

On all Arch installations:

pacman -S bc fd-rs fzf git htop ncdu ntfs-3g python-pre-commit rclone reflector ripgrep rsync sudo syncthing tmux words zsh zsh-syntax-highlighting

On graphical Arch installations:

pacman -S alacritty dmenu docker easystroke evince feh firefox gimp i3-wm i3blocks i3blocks-contrib-git i3lock i3status imagemagick jq keepassxc network-manager-applet numlockx pacgraph scrot shellcheck-bin sublime-text-dev thunar xfce4-goodies xfce4-power-manager

My .zshrc and other dotfiles

]]>
Elixir for Node.js Developers https://chaidarun.com/elixir-nodejs Mon, 26 Dec 2016 00:00:00 GMT https://chaidarun.com/elixir-nodejs Bilingual dictionaries are some of the most valuable tools for learning natural languages like French. The idea also applies perfectly well to programming languages, and so here’s the guide I wished I had before embarking on my trip to Elixir land. Corrections from those more fluent in Elixir are welcome!

The Elixir ecosystem

Let’s first take a look at the software that comes with Elixir. I find tooling the biggest obstacle to learning any new language; the language itself is usually more stable and straightforward.

BEAM

BEAM is also known as the Erlang Virtual Machine (EVM). It’s the platform that Elixir runs on, similar to how Chrome’s V8 engine is what powers Node.js. A better comparison would be to Java’s JVM, which now supports a variety of languages other than Java.

OTP

I initially thought OTP (“Open Telecom Platform”) was just an incredibly fancy name for Erlang’s standard library. Now I think of OTP as the subset of Erlang’s stdlib that facilitates the actor model of concurrency. People sometimes refer to Erlang as “Erlang/OTP” because the actor model is such a big deal—it’s what BEAM was built for.

Mix

Mix is Elixir’s build tool / task runner. It’s analogous to Jake, Gulp, and much of the npm client (which seems to think the “p” in its name means “project” instead of “package”).

Mix uses the dev environment by default but can be overridden with an environment variable: MIX_ENV=prod. This is similar to how React expects you to set NODE_ENV=production for release builds.

When setting up a new Node.js project, you typically run npm init and then add node_modules to your .gitignore. The Elixir equivalent is mix new project_name. We’ll soon see a few more of Mix’s many available commands.

Hex

Hex is Elixir’s package manager, analogous to the npm registry and the parts of the npm client that actually deal with package management. One big difference is that you don’t get a separate “hex” command when you install Elixir. You instead use Hex via Mix commands like mix deps.get (analogous to npm install).

Elixir’s equivalents of package.json and npm-shrinkwrap.json are mix.exs and mix.lock, respectively. Running mix deps.get will install all packages declared in those files. There currently aren’t any Mix commands that work like npm install --save, so to add or remove individual packages you’ll have to edit mix.exs manually and then run mix deps.get again. On the upside, Mix automatically updates mix.lock for you.

IEx

Okay, enough about npm already. The Elixir counterparts to Node.js’s node command are iex and elixir. Running iex will open an interactive Elixir prompt (or REPL), while running iex -S mix will open the prompt in the context of the current directory’s compiled Mix project. You can alternatively execute the compiled project via mix run if you don’t need the prompt.

Wait, you need to compile Elixir? Although you do need to compile Mix projects via mix compile, you can also run a standalone Elixir script with the command elixir foobar.exs (note the .exs extension, as opposed to .ex). Mix itself is implemented as an Elixir script!

ExUnit

Elixir has an official unit testing framework that integrates with Mix via mix test. Having one true framework is pretty great if for no other reason than avoiding the dependency hell of Mocha, Chai, Karma, Sinon, etc.

Dialyzer

Despite being a dynamically typed language, Erlang comes with a static analysis tool called dialyzer. It can automatically detect certain type errors even in the absence of type declarations, kind of like TypeScript and Flow. If you do prefer declaring types yourself, both Erlang and Elixir offer an annotation syntax that Dialyzer understands.

Dialyxir makes Dialyzer much easier to use with Elixir.

Phoenix

Phoenix has a lot in common with Express.js and Ruby on Rails: they’re the go-to web frameworks, they’ve made household names out of their authors, and they don’t actually ship with the language itself. I’m mentioning Phoenix only because it’s so popular and what you’ll probably be using if you’re coming from Node.js.

The Elixir language

The official guide does an excellent job on its own, so I’ll just add some JavaScript perspective to a few basic Elixir concepts that aren’t found in ES5.

Atoms

Atoms resemble ES6 symbols and serve much of the same purpose as opaque enums. They’re arbitrarily named, intrinsically meaningless identifiers whose most useful property is global uniqueness. In JavaScript you might have a function that returns true or false to indicate success; the Elixir equivalent might instead return :ok or :error.

As another example, consider Webpack’s output.libraryTarget config option. Its possible values are restricted to a small set of strings: "var", "amd", etc. An idiomatic Elixir API would use lightweight atoms like :var and :amd instead of full-fledged UTF8 strings.

Ampersand operator

The official guide introduces the capture operator & in chapter 8 with a link to details in the language reference. I think & deserves extra attention due to how unfamiliar yet common it is. Elixir newcomers may not realize that it has three distinct meanings depending on context:

  1. & obtains references to named functions, allowing them to be passed around as variables and called as anonymous functions (a.k.a. lambdas). In JavaScript, you can simply refer to a named function like Math.floor by its name:

    [1.62, 2.72].map(Math.floor); // [1.0, 2.0]
    var myLambda = Math.floor;
    myLambda(3.14); // 3.0
    

    In Elixir, you also need & and the function’s arity:

    Enum.map([1.62, 2.72], &Float.floor/1) # [1.0, 2.0]
    my_lambda = &Float.floor/1
    # Lambdas must be called with a "."
    my_lambda.(3.14) # 3.0
    

    Why the additional syntax? Elixir functions can be defined multiple times for different arities, so you need to specify which version you want. Since parentheses are optional in Elixir when empty, you also need & to prevent the compiler from interpreting Float.floor/1 as “the result of Float.floor() divided by 1”.

  2. & is shorthand notation for declaring simple lambdas that take at least one argument. These are all equivalent:

    // ES5
    var sum = function (a, b) {
      return a + b;
    };
    
    // ES6
    const sum = (a, b) => a + b;
    
    # Elixir
    sum = fn(a, b) -> a + b end
    sum = &(&1 + &2)
    

    The outer & behaves like the fn keyword. The two inner &’s bring us to #3…

  3. & denotes arguments to shorthand lambdas. &1 represents the first argument, &2 the second, and so on. Here are some direct translations of sum = &(&1 + &2) into JavaScript:

    // ES5
    var sum = function () {
      return arguments[0] + arguments[1];
    };
    
    // ES6
    const sum = (...args) => args[0] + args[1];
    

At this point you might be wondering how the compiler (or anyone else reading your code, for that matter) will manage to keep all of these &’s straight if you try nesting them. Easy answer: you’ll get a CompileError saying “nested captures via & are not allowed”.

Pipe operator

Underscore and Lodash have a _.chain function that lets you express data transformations in chronological order of application. It looks really nice but ends up being clunky to use in practice. The same functionality is built into Elixir as the pipe operator |>. A JavaScript example adapted from the Underscore docs:

const lyrics = [
  { line: 1, words: "I'm a lumberjack and I'm okay" },
  { line: 2, words: "I sleep all night and I work all day" },
  { line: 3, words: "He's a lumberjack and he's okay" },
  { line: 4, words: "He sleeps all night and he works all day" },
];

const histogram = _.chain(lyrics)
  .pluck("words")
  .map(words => words.split(" "))
  .flatten(true)
  .reduce((acc, word) => {
    acc[word] = (acc[word] || 0) + 1;
    return acc;
  }, {})
  .value();

// histogram is { lumberjack: 2, all: 4, night: 2 ... }

Translated into Elixir:

histogram =
  lyrics
  |> Stream.map(&(&1.words))
  |> Stream.flat_map(&String.split/1)
  |> Enum.reduce(%{}, fn(word, acc) -> Map.update(acc, word, 1, &(&1 + 1)) end)

# histogram is %{"lumberjack" => 2, "all" => 4, "night" => 2 ... }

Just like in Underscore and Lodash, Elixir functions specify the data that they work on as their first argument by convention. Chained function calls can then omit that first argument because it gets populated with the result of the previous step in the pipeline.

Blessed with native support via |>, chaining works with all functions that take at least one argument. But there’s another reason it’s ubiquitous in Elixir: the language’s lack of objects. Since instance methods like JavaScript’s String.prototype.toUpperCase don’t exist, all such features are implemented as “static” methods in Elixir:

# Prints "HELLO" and returns :ok
IO.puts(String.upcase(String.trim(" hello")))

# Same, but laid out more naturally
" hello" |> String.trim |> String.upcase |> IO.puts

Final thoughts

Although it’s hard to say whether Elixir lives up to its massive hype, I’ve generally liked what I’ve seen so far. The flavor of functional programming is more pragmatic than dogmatic, the concurrency model is as elegant as promised, and the included development tools are easily the best of any language I’ve used.

I’m still a bit skeptical about dynamic typing, but maybe “let it crash” just hasn’t sunk in yet. I’m also really not a huge fan of all the Rails-esque boilerplate that Phoenix generates. The current lack of mature framework options has more to do with how young the language is, though, and it’s not too hard to pare down Phoenix anyway.

Consider it a glowing endorsement of both Elixir and Phoenix that these minor details are my only complaints :)

]]>