<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Kristof Dewilde → Blog</title><description>Blog posts by Kristof Dewilde about software development and technology.</description><link>https://kristofdewil.de</link><language>en</language><item><title>Sequential UUIDs are a thing now</title><link>https://kristofdewil.de/blog/sequential-uuids-are-a-thing-now</link><guid isPermaLink="true">https://kristofdewil.de/blog/sequential-uuids-are-a-thing-now</guid><description>UUIDv7 shares the same format as v4, but is time-ordered: the ideal native primary key in PostgreSQL 18.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently fell down a bit of a rabbit hole regarding database primary keys, and I was pleasantly surprised to discover that PostgreSQL 18 brought native support for &lt;a href=&quot;https://www.postgresql.org/docs/current/functions-uuid.html#FUNC_UUID_GEN_TABLE&quot; target=&quot;_blank&quot;&gt;UUIDv7&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’ve spent any time building distributed systems or debugging performance issues on large tables, you know the classic dilemma: you want the security and decentralized nature of a UUID, but you hate the index fragmentation and performance tax that comes with the randomness of UUIDv4. Reading through the UUIDv7 spec felt like a massive hit of déjà vu,immediately reminding me of a previous project where we used &lt;a href=&quot;https://github.com/ulid/spec&quot; target=&quot;_blank&quot;&gt;ULIDs&lt;/a&gt; for exactly the same reasons.&lt;/p&gt;
&lt;h2 id=&quot;time-ordered-identifiers&quot;&gt;“Time-ordered” identifiers&lt;/h2&gt;
&lt;p&gt;Both ULID and UUIDv7 operate on the same clever principle that they aren’t just a blob of random bits. Instead, they are “comb” identifiers: half timestamp, half randomness:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First half is &lt;strong&gt;a Unix timestamp&lt;/strong&gt;: this ensures that every new ID generated is technically “larger” than the last one.&lt;/li&gt;
&lt;li&gt;Second half is &lt;strong&gt;cryptographically secure randomness&lt;/strong&gt;: this ensures that even if two servers generate an ID at the exact same millisecond, they won’t collide.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my previous project, we loved ULIDs because they gave us “mechanical sympathy”. Because they are sequential, the database’s B-tree index doesn’t have to reshuffle or perform expensive page splits to insert a new row in the middle of a table. It just appends it to the right-most edge, much like a traditional auto-incrementing integer.&lt;/p&gt;
&lt;h2 id=&quot;uuidv7-is-a-drop-in-upgrade&quot;&gt;UUIDv7 is a drop-in upgrade&lt;/h2&gt;
&lt;p&gt;The best part about moving to UUIDv7, especially if you are already using the common UUIDv4, is that &lt;strong&gt;the format is identical&lt;/strong&gt;: they are both 128-bit values.&lt;/p&gt;
&lt;p&gt;This means you don’t have to migrate your database columns, change your API schemas, or rewrite your frontend logic. It is a literal drop-in replacement. You simply swap the generator logic in your backend (or your SQL default), and your system immediately starts benefiting from better index performance without a single breaking change. The switch to v7 is basically a “free” performance upgrade. You keep the 128-bit format, but you gain a significant boost in write throughput and a much cleaner index over time.&lt;/p&gt;
&lt;h2 id=&quot;comparison-with-ulid&quot;&gt;Comparison with ULID&lt;/h2&gt;
&lt;p&gt;While I still have a soft spot for the human-readability of ULIDs because of how they use &lt;a href=&quot;https://www.crockford.com/base32.html&quot; target=&quot;_blank&quot;&gt;Crockford’s Base32&lt;/a&gt; to avoid ambiguous characters like &lt;code&gt;1&lt;/code&gt;/&lt;code&gt;l&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;/&lt;code&gt;O&lt;/code&gt;, UUIDv7 feels like the more professional choice. The main friction point with ULIDs in a PostgreSQL environment is the lack of a native type. You either store them as strings, which is storage-heavy, or perform manual binary casts to squeeze them into a &lt;code&gt;UUID&lt;/code&gt; column. It works, but feels like is a hack.&lt;/p&gt;
&lt;p&gt;With UUIDv7 now becoming a native citizen in Postgres:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Native Storage&lt;/strong&gt;: it uses the existing 16-byte &lt;code&gt;UUID&lt;/code&gt; type, which is incredibly efficient.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No More Extensions&lt;/strong&gt;: you can just use &lt;code&gt;DEFAULT uuidv7()&lt;/code&gt; directly in your schema without hunting for third-party libraries or let the backend handle thes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standardization&lt;/strong&gt;: It’s an official IETF standard (RFC 9562), meaning better long-term support across languages and frameworks like Spring or Quarkus, or Hibernate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m still a fan of ULIDs for public-facing URLs where a human might actually need to read the ID, but for the internal “source of truth” in the database, native UUIDv7 is a no-brainer. It’s the efficiency of a sequence with the safety of a UUID.&lt;/p&gt;</content:encoded><category>postgres</category><category>database</category><category>programming</category></item><item><title>No thanks, I rather stay on Sequoia</title><link>https://kristofdewil.de/blog/no-thanks-i-rather-stay-on-sequoia</link><guid isPermaLink="true">https://kristofdewil.de/blog/no-thanks-i-rather-stay-on-sequoia</guid><description>The latest macOS update is not for me and how I prevent my Mac from accidentally updating.</description><pubDate>Sat, 28 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;No need for me to be the next person to give you a plethora of reasons why macOS Tahoe is not a good system update, for many many reasons across different fields. For European users, macOS Tahoe isn’t much of an upgrade anyway, as Apple has disabled most of its headline features in the EU due to regulatory uncertainty around the Digital Markets Act. Though I’ve heard Preview has dark mode now. That’s awesome! But aside from that I’m fine on Sequoia for now.&lt;/p&gt;
&lt;p&gt;What I’m &lt;em&gt;not&lt;/em&gt; fine with is macOS nagging nudging me toward an upgrade I didn’t ask for with periodic notifications and dark patterns in the System Settings layout that can sneak in a major upgrade when you think you’re just installing the latest Sequoia patch (things we’d make Windows memes about).&lt;/p&gt;
&lt;p&gt;I was surprised when I saw &lt;a href=&quot;https://daringfireball.net/linked/2026/02/27/how-to-block-the-upgrade-to-tahoe-alerts-and-system-settings-indicator&quot; target=&quot;_blank&quot;&gt;John Gruber linking&lt;/a&gt; a post and &lt;a href=&quot;https://github.com/travisvn/stop-tahoe-update&quot; target=&quot;_blank&quot;&gt;GitHub repo&lt;/a&gt; with a script that generates a device management profile to postpone updates. Though in my opinion: a script in a repo that you need to clone with git and run in the terminal to create and configure a management profile… that just sounds like a management profile with extra steps. It introduces many moving parts and hurdles for non-technical people. All you really need is the &lt;code&gt;.mobileconfig&lt;/code&gt; file itself. Here’s a slightly cleaned-up version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;
&amp;lt;plist version=&quot;1.0&quot;&amp;gt;
&amp;lt;dict&amp;gt;
  &amp;lt;key&amp;gt;PayloadContent&amp;lt;/key&amp;gt;
  &amp;lt;array&amp;gt;
    &amp;lt;dict&amp;gt;
      &amp;lt;key&amp;gt;PayloadType&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;com.apple.applicationaccess&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;PayloadVersion&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;1&amp;lt;/integer&amp;gt;
      &amp;lt;key&amp;gt;PayloadIdentifier&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;org.stayonsequoia.restrictions&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;PayloadUUID&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;934F442D-F089-4C98-BF75-4AD2F4B263CC&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;PayloadEnabled&amp;lt;/key&amp;gt;&amp;lt;true/&amp;gt;
      &amp;lt;key&amp;gt;PayloadDisplayName&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;Software Update Deferrals&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;forceDelayedMajorSoftwareUpdates&amp;lt;/key&amp;gt;&amp;lt;true/&amp;gt;
      &amp;lt;key&amp;gt;enforcedSoftwareUpdateMajorOSDeferredInstallDelay&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;90&amp;lt;/integer&amp;gt;
      &amp;lt;key&amp;gt;forceDelayedSoftwareUpdates&amp;lt;/key&amp;gt;&amp;lt;false/&amp;gt;
    &amp;lt;/dict&amp;gt;
  &amp;lt;/array&amp;gt;
  &amp;lt;key&amp;gt;PayloadType&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;Configuration&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;PayloadVersion&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;1&amp;lt;/integer&amp;gt;
  &amp;lt;key&amp;gt;PayloadIdentifier&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;org.stayonsequoia.profile&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;PayloadUUID&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;806B5030-A5CA-4186-A43E-AAFD0FB4D70B&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;PayloadDisplayName&amp;lt;/key&amp;gt;&amp;lt;string&amp;gt;Stay on Sequoia: Update Deferrals&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This defers major OS updates by the maximum configurable 90 days, while leaving Sequoia’s own minor updates alone. If you want to suppress those too, set &lt;code&gt;forceDelayedSoftwareUpdates&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; and add these 2 lines beneath it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;key&amp;gt;enforcedSoftwareUpdateMinorOSDeferredInstallDelay&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;90&amp;lt;/integer&amp;gt;
&amp;lt;key&amp;gt;enforcedSoftwareUpdateNonOSDeferredInstallDelay&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;90&amp;lt;/integer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I believe the UUIDs in the config profile need to be unique per device, and reinstalling the same profile after 90 days should work fine without changing them. So really, sharing a ready-made &lt;code&gt;.mobileconfig&lt;/code&gt; file has a wider reach than a script: no terminal required, but paste the XML into a new file, name it &lt;code&gt;anything.mobileconfig&lt;/code&gt;, double-click it, and confirm in System Settings. Done.&lt;/p&gt;
&lt;h2 id=&quot;alternative-the-sequoia-public-beta&quot;&gt;Alternative: the Sequoia public beta&lt;/h2&gt;
&lt;p&gt;There’s actually a simpler approach that requires no profile at all: enroll in Sequoia’s public beta program! Now that Tahoe has shipped, Sequoia will realistically only receive security updates, meaning beta and stable are likely to stay close. No profile to manage, no repo to clone: just System Settings → General → Software Update, and enable beta updates.&lt;/p&gt;
&lt;p&gt;Funnily enough, this takes me back to the iOS jailbreak days, when people would install a tvOS management profile to stop their iPhone from talking to Apple’s update servers. Some things don’t change.&lt;/p&gt;</content:encoded><category>macos</category></item><item><title>Enabling Touch ID for sudo</title><link>https://kristofdewil.de/blog/enabling-touch-id-for-sudo</link><guid isPermaLink="true">https://kristofdewil.de/blog/enabling-touch-id-for-sudo</guid><description>Quite a public secret nowadays, but you can enable Touch ID for sudo authentication in your terminal on macOS.</description><pubDate>Fri, 13 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2021 I was still on a trusty 2015 MacBook Pro when a new client project came with a perk: a company provided MacBook Pro for their developers. I received, I believe, a 2019 model that a few months in I swapped for an M1 Pro, which quicky convinced me to finally retire and upgrade my personal machine too.&lt;/p&gt;
&lt;p&gt;Touch ID was new to me, and how well it was woven into macOS left an impression. Using my fingerprint instead of typing a password (or unlocking with Apple Watch if I had too much time on my hands) felt natural almost immediately… which made it all the more jarring that the terminal still demanded a typed password for &lt;code&gt;sudo&lt;/code&gt;, even in Apple’s own Terminal app!&lt;/p&gt;
&lt;p&gt;MacBooks with Touch ID had been around since 2016, and people had figured out early on that you could enable it for &lt;code&gt;sudo&lt;/code&gt; by adding this line to &lt;code&gt;/etc/pam.d/sudo&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auth       sufficient     pam_tid.so
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However: macOS updates do revert that file, so for years I kept the alias below sourced in my &lt;code&gt;zsh&lt;/code&gt;. It checks whether the line is already there, and if not, prepends it. Ran it after every update, and moved on.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alias touchidterminal=&quot;if grep -q &apos;pam_tid\.so&apos; /etc/pam.d/sudo; then echo Touch ID already enabled!; else sudo sed -i &apos;.bak&apos; &apos;1s/^/auth       sufficient     pam_tid.so\&apos;$&apos;\n/g&apos; /etc/pam.d/sudo &amp;amp;&amp;amp; echo Touch ID enabled! fi&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;About seven years later, a &lt;a href=&quot;https://www.youtube.com/watch?v=KVCKc1-gHvI&quot; target=&quot;_blank&quot;&gt;short video&lt;/a&gt; I almost skipped taught me there’s a better way: a set-and-forget solution that &lt;em&gt;does&lt;/em&gt; survive system updates!&lt;/p&gt;
&lt;p&gt;Since macOS Sonoma, you can use &lt;code&gt;/etc/pam.d/sudo_local&lt;/code&gt; instead of &lt;code&gt;/etc/pam.d/sudo&lt;/code&gt;. Apple even ships a &lt;code&gt;/etc/pam.d/sudo_local.template&lt;/code&gt; with instructions, but here’s your copy-pasteable oneliner:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;auth       sufficient     pam_tid.so&quot; | sudo tee /etc/pam.d/sudo_local
# ... and type your password one last time
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some fun trivia about &lt;code&gt;sudo&lt;/code&gt; to wrap up: &lt;code&gt;sudo&lt;/code&gt; isn’t really “authenticating” in the same sense as “logging in as a privileged user” (that’s what &lt;code&gt;su -&lt;/code&gt; or &lt;code&gt;sudo -i&lt;/code&gt; are for). It’s closer to a proof of presence: confirming that the person at the keyboard is who the terminal thinks they are, in case you walked away without locking your screen.&lt;/p&gt;
&lt;p&gt;A succesful &lt;code&gt;sudo&lt;/code&gt; writes a timestamp to a temporary file so that within a grace period it won’t ask for password/fingerprint again. I believe on macOS that period is 5 minutes by default, but if you really want to, you can edit this with &lt;code&gt;sudo visudo&lt;/code&gt; (add &lt;code&gt;Defaults        timestamp_timeout=5&lt;/code&gt;). And if you want &lt;code&gt;sudo&lt;/code&gt; to forget you immediately: &lt;code&gt;sudo -k&lt;/code&gt; clears the timestamp so it’ll ask again on the next invocation!&lt;/p&gt;</content:encoded><category>macos</category></item><item><title>An unusual Bluetooth pairing issue</title><link>https://kristofdewil.de/blog/an-unusual-bluetooth-pairing-issue</link><guid isPermaLink="true">https://kristofdewil.de/blog/an-unusual-bluetooth-pairing-issue</guid><description>Bluetooth refusing to pair, mystery BSODs on login, and a Windows service quietly misconfigured.</description><pubDate>Fri, 27 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week, a friend hit me up for a gaming session. I powered on my Windows PC, untouched for over two months, and braced for a flood of updates. What I didn’t brace for was my CPU hitting over 100°C on cold boot, all fans screaming and the desktop running at what felt like 10 FPS. The system shut itself off after 3–4 minutes, right in the middle of a cumulative Windows Update. When it came back up, two things were broken.&lt;/p&gt;
&lt;h2 id=&quot;windows-update-stuck-at-16&quot;&gt;Windows Update stuck at 16%&lt;/h2&gt;
&lt;p&gt;The interrupted update left Windows Update in a bad state: stuck at 16% every time, eventually spitting out error code &lt;code&gt;0x80073712&lt;/code&gt; and a message about missing update files. It never self-healed, and manually downloading the update from Microsoft’s Update Catalog didn’t help either. What did work was running these two DISM commands from an elevated prompt, in order:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DISM /Online /Cleanup-Image /StartComponentCleanup
DISM /Online /Cleanup-Image /RestoreHealth
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;RestoreHealth&lt;/code&gt; appears to freeze at &lt;strong&gt;62.3%&lt;/strong&gt;, that’s a known Windows quirk: this is where it sits while downloading, and it can take a while depending on your connection.&lt;/p&gt;
&lt;h2 id=&quot;bluetooth-pairing-completely-broken&quot;&gt;Bluetooth pairing completely broken&lt;/h2&gt;
&lt;p&gt;This one was weirder. After the reboot, my MX Keys keyboard stopped working on the login screen — but my MX Anywhere mouse, also Bluetooth, was fine. I’d get in using the on-screen keyboard, and about a minute later: BSOD, instant restart. Second boot everything worked normally. This behavior repeated for four days straight.&lt;/p&gt;
&lt;p&gt;On day four I tried the obvious: unpair and re-pair the keyboard. The unpairing hung, which left me with no mouse since I’d disabled Bluetooth mid-process. 🧠 After sorting that out and finally getting the keyboard unpaired, I hit a new wall — pairing wouldn’t work at all, for any device. Always the same: “Try connecting the device again” and Swift Pair timing out after a minute.&lt;/p&gt;
&lt;p&gt;The driver was up to date and functional, so I started digging. First I cleared all known Bluetooth devices from the registry at &lt;code&gt;HKEY_LOCAL_MACHINE\&lt;wbr&gt;&lt;/wbr&gt;SYSTEM\&lt;wbr&gt;&lt;/wbr&gt;CurrentControlSet&lt;wbr&gt;&lt;/wbr&gt;\Services\&lt;wbr&gt;&lt;/wbr&gt;BTHPORT\&lt;wbr&gt;&lt;/wbr&gt;Parameters\&lt;wbr&gt;&lt;/wbr&gt;Devices&lt;/code&gt;. You can do this manually, or from an elevated command prompt (note: this is &lt;code&gt;cmd&lt;/code&gt; syntax, not PowerShell). Perhaps run this once without the &lt;code&gt;reg delete &quot;%i&quot; /f&lt;/code&gt; first.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for /f &quot;tokens=*&quot; %i in (&apos;reg query HKLM\SYSTEM\CurrentControlSet\Services\BTHPORT\Parameters\Devices&apos;) do (
  echo Deleting %i
  reg delete &quot;%i&quot; /f
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m not certain cleaning the registry was strictly necessary, but it doesn’t hurt. The actual fix was this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Services&lt;/strong&gt; (&lt;kbd&gt;⊞&lt;/kbd&gt;+&lt;kbd&gt;R&lt;/kbd&gt; and type &lt;code&gt;services.msc&lt;/code&gt; or search for “services”)&lt;/li&gt;
&lt;li&gt;Find &lt;strong&gt;Bluetooth Support Service&lt;/strong&gt;, right-click and choose &lt;strong&gt;Properties&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Log On&lt;/strong&gt; tab&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On my PC, the account was set to &lt;strong&gt;Local System Account&lt;/strong&gt;, but on my work laptop, it was &lt;strong&gt;Local Service&lt;/strong&gt; account instead. So:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the &lt;strong&gt;This account&lt;/strong&gt; radio button and click &lt;strong&gt;Browse&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;local service&lt;/code&gt; and click &lt;strong&gt;Check Names&lt;/strong&gt; (should resolve to &lt;code&gt;Local Service&lt;/code&gt; or &lt;code&gt;NT AUTHORITY\Local Service&lt;/code&gt;) or click &lt;strong&gt;Advanced&lt;/strong&gt; and search for that user.&lt;/li&gt;
&lt;li&gt;Restart the service (or reboot to be safe, especially if you also edited the registry)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On a clean Windows install, the Bluetooth Support Service runs as Local Service by default. Something had changed it (possibly the failed or a previous update), and that seems to have been what caused pairing to break. After switching it back, both devices paired instantly.&lt;/p&gt;
&lt;p&gt;Like most Windows problems, the web is full of half-baked fixes for this one. Hopefully sharing this saves someone a few hours, or at least provides useful training data for an LLM. 🤖&lt;/p&gt;</content:encoded><category>windows</category></item><item><title>Mac QWERTY layout on Windows</title><link>https://kristofdewil.de/blog/mac-qwerty-layout-on-windows</link><guid isPermaLink="true">https://kristofdewil.de/blog/mac-qwerty-layout-on-windows</guid><description>The tilde and backtick key belongs next to the left Shift key, not under the Escape key!</description><pubDate>Mon, 05 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I grew up in Belgium, where the default keyboard layout is AZERTY: a French layout with dedicated keys for accented characters, used pretty much exclusively in France and Belgium (perhaps French-speaking parts of Switzerland and Africa too). Notably, it’s also the default for Flemish speakers, even though Dutch has little use for those accent keys (the Netherlands just uses QWERTY).&lt;/p&gt;
&lt;p&gt;AZERTY is rough for programming: curly and square brackets are buried on the number row behind &lt;em&gt;AltGr&lt;/em&gt; (fun fact: stands for “Alternative Graphic”), which is not as accessible or used as e.g. &lt;em&gt;Shift&lt;/em&gt; as a modifier. On Apple’s AZERTY layout it’s even worse, as there’s no printed bracket key at all: curly brackets need &lt;em&gt;Option&lt;/em&gt; + a parenthesis key and square brackets need &lt;em&gt;Option&lt;/em&gt; + &lt;em&gt;Shift&lt;/em&gt; + parenthesis. I owned exactly one AZERTY MacBook — my first — before switching to QWERTY and never looking back.&lt;/p&gt;
&lt;p&gt;Fast-forward to today: I regularly work on client-provided Windows laptops, and this thing never stopped bothering me: on a Windows ISO QWERTY layout, the backtick and tilde live on the top row, right below Escape, whereas on &lt;a href=&quot;https://support.apple.com/en-us/102743&quot; target=&quot;_blank&quot;&gt;Apple’s ISO international English&lt;/a&gt; keyboard, they’re on the bottom row, left of the left &lt;em&gt;Shift&lt;/em&gt; key, where my muscle memory firmly expects them. Mistyping those characters practically daily pushed me to find a fix.&lt;/p&gt;
&lt;h2 id=&quot;msklc&quot;&gt;MSKLC&lt;/h2&gt;
&lt;p&gt;Microsoft has a tool for exactly this: &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=102134&quot; target=&quot;_blank&quot;&gt;Microsoft Keyboard Layout Creator (MSKLC)&lt;/a&gt;. It’s pretty dated but still lets you load an existing layout, tweak individual keys, and build a native driver that integrates at the system level. Once installed, it shows up in Settings like any other layout, available to all users &lt;em&gt;and&lt;/em&gt; on the lock screen.&lt;/p&gt;
&lt;p&gt;A good starting point is the US QWERTY layout as it has no &lt;a href=&quot;https://en.wikipedia.org/wiki/Dead_key&quot; target=&quot;_blank&quot;&gt;dead keys&lt;/a&gt;: click &lt;strong&gt;File&lt;/strong&gt; → &lt;strong&gt;Load Existing Keyboard&lt;/strong&gt;. From there, remap the key left of left &lt;em&gt;Shift&lt;/em&gt; to backtick and tilde, and optionally swap the only other different key under &lt;em&gt;Escape&lt;/em&gt; too. Then click &lt;strong&gt;Project&lt;/strong&gt; → &lt;strong&gt;Build DLL and Setup Package&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;admin-rights&quot;&gt;Admin rights&lt;/h2&gt;
&lt;p&gt;Installing a custom layout requires admin rights, and perhaps installing MSKLC does too (it’s been a while: once you’ve created the DLL and installer, you don’t need MSKLC anymore), which isn’t always available on a company laptop. While you can use any (virtual) machine to install MSKLC onto and create the layout, then share it with your work laptop, installing it might be a bit trickier.&lt;/p&gt;
&lt;p&gt;If you do have some elevated access, the installer just does two things: drops the DLL into System32 and adds an entry under &lt;code&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\&lt;/code&gt;. You can attempt to duplicate an existing entry but point it to your DLL (it must live in &lt;code&gt;%WINDIR%\System32&lt;/code&gt; for Windows to load it, though).&lt;/p&gt;
&lt;p&gt;If the registry route doesn’t stick, there’s a more blunt alternative: overwrite the existing US layout DLL at &lt;code&gt;C:\Windows\System32\kbdus.dll&lt;/code&gt;. I went this route on a laptop where a tool could grant any app temporary admin rights — provided you gave a reason, which no one ever checked. PowerShell with admin rights basically gives you full access to your own machine. Not professional advice. 🤓&lt;/p&gt;
&lt;p&gt;If none of that is an option, &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/powertoys/&quot; target=&quot;_blank&quot;&gt;PowerToys&lt;/a&gt; Keyboard Manager can remap individual keys. It’s user-level, so no admin needed, and needs to run in the background. Less elegant than a proper layout driver, but it gets the job done.&lt;/p&gt;</content:encoded><category>macos</category></item><item><title>Running OracleDB 12 on Apple Silicon</title><link>https://kristofdewil.de/blog/running-oracledb-12-on-apple-silicon</link><guid isPermaLink="true">https://kristofdewil.de/blog/running-oracledb-12-on-apple-silicon</guid><description>OracleDB 12 won&apos;t run on ARM, not even emulated. So I emulated the entire Docker environment instead.</description><pubDate>Fri, 22 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During a project for a previous client, I had to deal with some seriously old codebases. They’d all been containerized before the move to Kubernetes, which helped… until I met OracleDB 12. That one refuses to run on Apple Silicon, and unlike most x86_64 images, you can’t just emulate your way out of it.&lt;/p&gt;
&lt;p&gt;The root cause is subtle. Most container tools handle the amd64→arm64 mismatch fine: they pull the image, QEMU’s emulation steps in, and you pay a performance tax but it just works™. OracleDB 12 however breaks at a lower level, because it relies on certain kernel behaviors during initialization that QEMU doesn’t faithfully replicate. The container spins up, hangs silently, and that’s it. No logs, no crash, no useful signal. For comparison, OracleDB 19 emulated just fine under &lt;a href=&quot;https://orbstack.dev/&quot; target=&quot;_blank&quot;&gt;OrbStack&lt;/a&gt; on the same machine. The hanging is specific to 12.&lt;/p&gt;
&lt;h2 id=&quot;enter-colima&quot;&gt;Enter Colima&lt;/h2&gt;
&lt;p&gt;The fix isn’t to emulate the OracleDB container, but to emulate the entire Docker environment. &lt;a href=&quot;https://colima.run/&quot; target=&quot;_blank&quot;&gt;Colima&lt;/a&gt; lets you spin up a lightweight Linux VM with a specific architecture, meaning the Docker daemon itself runs as x86_64. OracleDB 12 never knows it’s on ARM hardware.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brew install colima
colima start --cpu 4 --memory 8 --arch x86_64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a QEMU-backed x86_64 VM. Everything inside it — the daemon, the containers, the kernel interface — is x86_64. That’s the difference that makes OracleDB 12 happy.&lt;/p&gt;
&lt;p&gt;A couple of optional flags worth knowing about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--vm-type=vz --vz-rosetta&lt;/code&gt;: switches from QEMU to Apple’s Virtualization Framework with Rosetta 2 for x86_64 translation. Faster in theory, but has Rosetta handle userspace binaries. If OracleDB 12’s problem is at the kernel interface level, this may not help. I have not tested this.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--mount-type=virtiofs&lt;/code&gt;: significantly faster volume mounts. But not needed if you’re not mounting host directories into the container.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;fitting-it-into-your-workflow&quot;&gt;Fitting it into your workflow&lt;/h2&gt;
&lt;p&gt;If you’re already using OrbStack (or Docker Desktop), Colima registers itself as a separate Docker context. You don’t need to touch your existing setup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker context use colima
docker compose up -d  # OracleDB 12 is alive in Colima
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you’re done, switch back:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker context ls # if you&apos;re unsure which contexts exist
docker context use orbstack
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Colima and OrbStack run happily in parallel and can both forward ports to the host. My day-to-day rhythm once the VM was created:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;colima start # remembers previous config, no need to repeat flags
docker context use colima
docker compose up -d oracle12
docker context use orbstack
docker compose up -d service1 service2 # or use service profiles or even a separate compose file for just OracleDB 12
# daily grind...
colima stop # optional, but frees up RAM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The emulated VM is noticeably slower than OrbStack’s native ARM environment, so I’d keep Colima reserved for containers that actually need it. If you’re using OracleDB 12 constantly and want Colima to start on boot:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brew services start colima
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bonus tip not to lose track of your context, as switching contexts manually is error-prone: &lt;a href=&quot;https://starship.rs/&quot; target=&quot;_blank&quot;&gt;Starship&lt;/a&gt; has a &lt;a href=&quot;https://starship.rs/config/#docker-context&quot; target=&quot;_blank&quot;&gt;Docker Context module&lt;/a&gt; that shows your active context in the prompt whenever it detects a &lt;code&gt;Dockerfile&lt;/code&gt; or &lt;code&gt;docker-compose.yml&lt;/code&gt; in the project. Saved me from pushing to the wrong daemon more than once.&lt;/p&gt;</content:encoded><category>docker</category><category>macos</category></item></channel></rss>