Sunday, November 9, 2025

The System Is the Authority

This is the first post for Decoded Node that has been written with league/commonmark instead of erusev/parsedown.  This technically aligns the Markdown implementation between this blog and my personal website (where CommonMark was readily integrated with Twig.)  Which is fancy talk for:

  • Footnote-style link syntax works on both sites now
  • Smart quotes are automated, instead of using platform-dependent input methods, or UniCycle

UniCycle “works,” but it’s not maintained (AFAICS), and the implementation strikes me as a bit of a hack.  It works fine in insert mode, but r' won’t replace with smart quote.  It’s a shame, because it has such a great name.

(Also along the way, I decided that .md won, so this processor no longer accepts anything like .mkd or .mdown.  Makes things easier than asking, “does this file match /\.ma?r?k?do?w?n$/?”)

Didn’t the Title Mention… Authority?

What strikes me about this is that “the format which I author in” is only incompletely described as “Markdown."  This particular move was feasible because league/commonmark is effectively a superset of erusev/parsedown.  If I decided I wanted to go back, I would have to edit all the new features out of newer posts.  Actually, if I want to update an old post and I happened to miss a smart quote in it, the non-smart quotes would also be changed by using the new parser.

What actually defines the output is not only the input, but the exact tool used to create it.

(I’m thrashing around in the direction of “the purpose of a system is what it does” and Hyrum’s Law, but neither of those things fit.)

That wouldn’t be much of an observation if I were saying, “An XLST processor doesn’t turn my Markdown into HTML,” but I didn’t really expect the choice of Markdown tool to matter so much to processing Markdown input.

Maybe it could be phrased as, “Markdown processors are not bug-for-bug compatible.”

Version Note

I use this code to write posts, not to mess around with code, so it’s not always clear whether the Markdown parser is up-to-date.  However, it seems I was using Parsedown 1.7.4 which is, at the time of writing this post, the latest stable release.  1.8.0 is in beta, and 2.0.0 seems to be barely started.

Sunday, November 2, 2025

Observations of Liquid Glass

The effect is very nice.  I don’t use it on my personal phone.  Reduce Motion inhibits the glassiness, and having motion on a phone that I actually use a lot is annoying.  I loathe waiting for an animation to finish before I am Permitted to interact with anything.

Clear icons aren’t… bad.  Unless they’re dark.  Then they’re not clear anymore, which makes me wonder if people in Cupertino actually understand their own language.  I don’t use clear icons on my personal phone, because I like the auto-dark mode.  Ironically, between color filters to greyscale (to make the phone less attention-hogging) and Reduce Motion, normal icons are not that far off their clear counterparts.

On the impersonal phone, clear icons and Liquid Glass look pretty nice together, at least on the iOS 26 wallpapers.  I have them set to “always light” due to their lackluster dark variants, but they look fine that way when the rest of the phone is in dark mode.

Opening an app brings up a solid-background splash screen, and app.  There’s so much πŸ’–newπŸ’– look, until it is touched, and then the glass disappears.  A subconscious wrongness suffuses it.  Apple took it halfway.  They Microsoft’ed it.  I can use it, but it keeps me wondering… why?

Sunday, October 26, 2025

ScreenZen as an RSI Aid

It hurts my hands to hold a phone for a long time, but apps, games, and the internet in general can be pretty compelling stuff.  I tried using the built-in Screen Time features, but they are fairly bare-bones.  One of the issues was that I would open an app “for five minutes,” and then get the message out of nowhere that there were only five minutes left on the app for the entire day. It meant I had just lost an hour or two, and maybe wouldn’t be able to use the app later.

(For the sake of argument, assume that I never bypass the limit, knowing it only makes everything worse.)

That’s where ScreenZen came in.  I needed a way—ideally, free—to say, “I only want to do these things for so much time, and then I want to be pushed off of them for a while.” When I found ScreenZen, that’s how I configured it.  It took me a bit to sort out the options and settings, so I wanted to walk through what has worked for me.

  • The basic settings were pretty obvious: “open each app up to [six] times a day, for [twenty] minutes each.” That’s the number of times the app(s) can be unlocked, and how long they are usable for (in real time) once unlocked.
  • I set “Strict block – after daily open goal,” so there’s a real consequence to opening things too much.
  • I chose some nice offscreen activities, and set that as the intervention screen.  (Back when I started using ScreenZen, this transformed it from “tap and then wait” to “wait and then tap,” which was much better for mindfulness.)
  • The time between the app being locked and being able to unlock it again is under “Advanced – cooldown time.”

I’ve also set up the schedules so that, in the wind-down before bedtime, more things are locked.  ScreenZen has become my “automatic sleep timer” for podcasts.  A short cooldown time there also prevents me from hearing it pause, deciding ‘yeah… sure… i feel awake,’ resuming the podcast, and then immediately falling asleep.  Sometimes, when only Downtime starting would stop playback, I’d find myself seeking half an hour back in an episode to get something familiar, but now, it’s not usually more than ten minutes.

Overall, this has turned out to be much better than trying to control everything through Apple’s Screen Time. If I’m on the phone for a reason, it generally doesn’t take 20 minutes, but if I get into “rat pulling a lever” mode, ScreenZen will interrupt me much sooner than Apple would.  Furthermore, because the stress of holding the phone is nonlinear over time, switching a one-hour daily limit into five half-hour-or-less sessions becomes a reasonable option.

And yes.  I would rather get off the phone than put it in a stand, or add a grip to it.  When it hurts, it’s mainly because I’m unaware of how much time I’m wasting on it.

The main downside to all this is that, if I need a how-to video on something, I have to plan ahead.  I sit down at a non-pocketable computer, watch the video, and take notes if necessary.  The videos cut out the boring parts, making it definitely impossible to follow along in real time.

That, and for other people, well… ScreenZen works hard to be “mindful” and not actually that “controlling.” It is willing to offer a bypass in several places.  I have managed not to touch it for almost an entire year, but I know it would be rather tempting for someone who doesn’t have physical reasons to avoid it.

So to wrap up… that link again is ScreenZen and it’s available for iOS, Android, and macOS.  This is not an ad nor a paid review, that isn’t an affiliate link (unless Blogger has made it one for their benefit), and this post is 100% human-generated.

Sunday, October 19, 2025

Apple Spies on Your Contacts

I have a work-only phone, that is for Teams and Outlook, mainly.

While checking to make sure that fitness/motion/surveillance data was turned off, I pulled an App Privacy Report on a whim, and it said that Health had accessed my Contacts.  What?  It doesn’t sound like something I would do, especially on this phone.

But worse: there are no settings for this. Health is not listed as permitted to access Contacts under Privacy and Security, and conversely, Contacts isn’t an option under Health.

That’s bad enough, but it gets worse.  I looked more closely at the privacy report.  Basically all of Apple’s built-in apps are freely accessing Contacts.  Mail, Music, Podcasts, Photos, and an icon-less “ShortcutsActions” app are all doing it.  (So are Messages and Phone, but that at least is more of a core feature of those apps.)

Again, none of these apps are showing as granted permission to access Contacts through the Privacy and Security screen.  Only Teams is there (and it’s forbidden.)

Apple has apparently given themselves special treatment to break the protections whenever and however they please, then (mostly) lie about it to their users.

Sunday, October 12, 2025

How to Un-Hide Firefox’s Vertical Tabs/Sidebar

The square button with the one side heavier than the other is the Sidebar button. By default, it is placed to the left of the Back/Forward buttons. It always opens the Sidebar, even if it is hidden. The blue-on-hover cue does… nothing, actually.

This comes about because I clicked “Hide sidebar and tabs,” closed the configuration, and then realized I had no idea how to open the sidebar. There was a hover cue, but I couldn’t figure out how to interact with it! KDE just wanted to resize the window.

After experimentation with the Sidebar options—the options reachable through the gear icon on the Sidebar itself—I think I understand the logic behind it.

There are three visibilities of the Sidebar:

  • Hidden: it is not displayed at all.
  • Closed: it is a narrow, icon-wide column.  If Vertical Tabs are on, the tabs are icon-only, with no text.
  • Open: it is a tab-width column; the tabs have the page’s title text, just like horizontal tabs do. This visibility is only accessible when Vertical Tabs are on.

Then, in all cases, clicking the Sidebar button expands or shrinks it, relative to the current configuration.  That means:

  1. Horizontal Tabs: It switches between Hidden and Closed.  There are no tabs in it.  Open is not possible, and the options are not selectable.
  2. Vertical Tabs with no other options: It switches between Closed and Open.  Hidden is not possible.
  3. Vertical Tabs with “Expand sidebar on hover” option: nearly identical to the previous, except that hovering the Closed sidebar with the pointer will Open it for the duration. The Sidebar button can be interpreted as “Keep Open” vs. “Open on Hover” in this case.
  4. Vertical Tabs with “Hide sidebar and tabs” option: It switches between Hidden and Open. Closed is not possible.

Although the interface presents check boxes for both of the options, it is actually the case that “Expand sidebar on hover” and “Hide sidebar and tabs” are mutually exclusive. Choosing either of them deactivates the other. We used to have a standard interface for this sort of thing.

Just sayin’.

Sunday, September 28, 2025

Vorta’s “No Matter What, Keep All…” Setting

Since switching from Pop!_OS (Gnome) to Kubuntu (KDE) for work, I have also changed my backup GUI.  There is no question that Vorta is more powerful than Pika Backup, but the price of that is the loss of simplicity.

One place I got confused was in the backup-retention rules, referred to as “pruning” by the GUI.  I have learned: when Vorta offers to “keep all backups made within…”, that is internally a separate rule with high priority. Therefore, when I set up my hourly backups to keep “one week” of hourly, two weeks of daily, and so on, but “keep everything from the last six weeks,” I ended up with seven weeks of hourly backups, followed by the two weeks of daily.

I noticed the problem when my laptop fans spun up for a while, which turned out to be Vorta verifying 300+ archives.  The work laptop is on only for work, producing backups for 8–9 hours per weekday, for around 250 extra archives over those first six weeks.

Unrelated, but one nice thing about Vorta is that, like Pika, it is a front-end to Borg.  I gave it the same repository on disk.  Now I have continuous backup history across the two GUIs, and emergency CLI access if necessary.

Sunday, September 21, 2025

Reflections on Breaking Something

Last week, I deployed some code, and then impossible phenomena followed on the website.  Ultimately, it was all my fault, because I have changed my design sensibilities over time.

Distant past me figured it would make for shorter commands if we left the .service suffix off of names.  It would be added automatically at the boundary, when actually invoking a systemctl command.

Present me is less tolerant of magic, hates checking at several places whether or not to add .service, and worries about whether the code works with other types of systemd units.

Hence, when I recently updated our deployment code, it also began passing the full service name to the reload script.  That script is a wrapper that sits between the deployment user and systemctl.  The deployment user has sudo rights to the script, which can only run very specific systemctl commands, and which validates the unit name against an “allowed” list.

For simplicity—because it is run through sudo—this wrapper script had zero magic. It expected the caller to give it an abbreviated name, to which it would add .service itself.  The change to the deployment code then broke that process.  Tests didn’t catch it, not only because there are none, but because the wrapper script lives outside of the deployment repository.  It’s an externally-provided service.

Consequently…

The “impossible phenomena” happened because the new files were unpacked, including templates, while the reload left the old code running.  The old code didn’t set the newly-used variables for the template to process, so the parts relying on those variables malfunctioned.  I had a lot of difficulty duplicating this effect, because out of habit, I restart the daemon with sudo systemctl ... after making code changes in dev.  I don’t use the wrapper script.  (Maybe I should.)

The first thing to do was fix the wrapper script to accept names with the .service suffix.

But after that, the biggest thing is that the deployer needs to cancel the operation and issue a rollback if the final reload fails.  This will restore consistency between the old code and the original files on disk.

I might also be able to improve robustness overall by using a relative path for the template root dir.  If we stay in a working directory below the symlink that is updated during deployment, instead of traversing that symlink on an absolute path, we’ll always get the templates that correspond to the running code. However, that’s more subtle and tricky than issuing a rollback, and hence, more likely to get broken in the future.

I like the sudo local-reload website.service approach.  The script can be tracked in version control easily, and the sudoers file remains as short and simple as possible.  Meanwhile, the deployment user isn’t given broad access to the entire set of subcommands that systemctl has to offer.