Showing posts with label design. Show all posts
Showing posts with label design. Show all posts

Sunday, June 21, 2026

Defaults Aren’t Always Good

I am responsible for a b2b system with a few dozen per-client settings, many of which are old and have defaults.  For instance, there is the grace period, which allows a limited self-service time window for clients to enter data that was missed during the normal course of business.  It seemed like a good idea at the time, but now we have a set of invisible values hidden in code holding those defaults.  And we can’t change them.

The main problem is, changing a default is an operational change to an unknown scope of clients.  On top of that, there could be clients that have the current default explicitly configured: would we want to change them, too?  It probably loops in the CEO and takes at least a week of meetings to answer that.

From a b2b business standpoint, it is actually better to require a configuration value (and let the support team view/modify it), even if it introduces the possibility of failure.  Our clients will use the normal support channel (well-oiled and always staffed) to raise the error message to us, if tech doesn’t proactively alert themselves about it.

For an ecommerce or public website where users would just leave, it’s probably more costly to “crash on error” than it is to let an errant process continue.  But if management prefers to halt and catch fire, the defaults are the opposite of that.

Sunday, May 24, 2026

Ubuntu Studio 26.04: We Can’t Have Nice Things

Problem 1: KDE Plasma on Wayland retains the sticky-keys bug, where the latch state (press a modifier twice) is tracked, but doesn’t actually work. Here’s to at least six more months of X11!

Problem 2: the Orchis theme remains deeply, completely broken on KDE.

For science, I created a new user on 26.04 and then used the Wayland session for an as-close-to-default experience as I could manage.  The default experience is the “Ubuntu Studio Dark” theme, with a wallpaper like distressed concrete, a theme choice that died ten years ago.  Now that I had to look at it, we’re both distressed.

I didn’t get to see it very long.  The first thing the system told me was that I needed to reboot to optimize my audio settings, presumably for this new user.  Why Ubuntu Studio can’t get this to work out of the box is unfathomable, but here we are.

The second login, I was able to select the global Orchis-dark theme and apply the desktop layout, which immediately blew everything up.  The panel and wallpaper disappeared, leaving a vast blackness and nothing to interact with.  Except for the Overview hot-corner, anyway.  Fortunately, as someone who has run a FOSS desktop for more than a dozen years, I knew a shortcut to get logged out and back in.

At that point, I had a top bar, with a single visible object: a color picker’s separator and history (black.)  It rather looked like those minimalist corporate logos that are all the rage.  Also, various other parts of the bar had no highlight but would react to clicks, because the problem was actually the lack of icons.

Further research would uncover that Orchis-dark requires in its theme file:

  • Telu-circle icon theme
  • Vimix-dark cursor theme

I was not able to find these in the Ubuntu repositories.

For further science, or to “keep driving with a blown tire” maybe, I tracked down and installed those themes from the Orchis’ author’s GitHub.  This made the icons show up immediately, and did not fix the cursors.  Why not?  Because the theme installs Vimix and Vimix-white themes!  There is no Vimix-dark.

I began to wonder if I’m the problem, but I checked apt search orchis and there’s no newer/different thing in the repository.  For all the fanfare about the “beautiful new” theme in 24.10 (!), not only did it not work there, but it still does not work in 26.04 LTS.  Embarrassing.

(On top of it all, SDDM still doesn’t remember the last session in a per-user way, so I had to switch it back to X11 to log in as my regular user.)

Sunday, May 17, 2026

The Heart of the Process (2012)

Editor's Note: I found this in my drafts, dated 2012-01-25.  I have opted to retain the content unchanged, merely updating links for ubiquitous HTTPS and replacing broken links as needed.  Please enjoy this work of fiction.

The President saw that things took time to implement in code, and involved programmers, testers, and a deployment.  As someone who liked to make snap decisions and have the results available immediately, this did not sit well in his heart.  Long he meditated, then announced his solution:

Henceforth, the system would be Database-Driven.

Sunday, January 25, 2026

Identity Requires Long Term Secrets

One cannot remove all long-term credentials.  The process of establishing a session is one of identifying a stable entity (such as a user account) and giving that entity temporary access to the resource servers.  In simplest form, this is providing the username (entity) and password (secret that authenticates the username), and receiving a session cookie (temporary credentials) for accessing the service.

Somewhere at the root of trust must be a long-term credential.  Otherwise, if all temporary credentials have expired, how is the user authenticated in order to generate a new one?  What would stop anyone else from going through the same process for the user?

An individual service can outsource user authentication: they can email a code, use SMS or a voice call, or integrate with a third party service like Okta or any OAuth provider.  In those cases, the long-term exists, but the actual location of the key store is externalized.  Then the service is at the mercy of the security of that key store.  Email is probably low risk for normal people, who have an account with multi-factor authentication at a large provider who’s going to notice ‘unusual’ logins, but my quirky personal email isn’t like that.

The other problem with outsourcing is that if the provider changes their mind about account requirements, users can get locked out of both their email and their service account at the same time.  (Ask someone how hard it is to maintain a secondary Google account.)

Everything else is a long-term credential stored by the service.  Passwords need their hash to be checked against.  Passkeys, authenticator app codes, and client certificates are also linked to a user account, so must be stored with it.  The service cannot accept any of these things for the wrong user.

Sunday, November 30, 2025

Container Friction

I find it inconvenient that a number of container settings are immutable.  Forget a volume?  Forget a port mapping?  Want to bring up a container, make a change, and then mark the root filesystem as read-only?  Start with a read-only root, and then realize it should have been read-write after all?

Too bad.  Go set up all of the other settings again, along with the desired change, and don’t miss anything or get any settings wrong this time.  If it’s “change something before going read-only,” it’s time to create a Containerfile and build a new image, too.  No matter what, don’t forget to delete the original container first, to free its name for reuse.

There’s no way to create a container based on an existing container’s settings.  There’s no direct option for it.  There’s not even an indirect option to export the settings of a container, and then use them as a template during container creation later.

It’s worse in Podman Desktop, which has no memory.  It doesn’t even have separate “last used directory” memory for building from a Containerfile versus choosing a volume to mount.  Why not make everyone go back and forth through the filesystem every time?

At least in the CLI, the previous commands could be in the shell history.  “Could be,” because I might have been distracted for two months, and the commands got pushed out of the history in the meantime.  (Speaking of, “the container” has its Containerfile baked in, but doesn’t remember where on the filesystem that Containerfile came from.  Not even locally.  Maybe it’s just me, but it’s always a treasure hunt to resume a project.)

I can see the logic of “not allowing settings drift” in production container environments, but it seems like Podman Desktop should be optimized for the developer experience and experimentation instead.

Sunday, November 23, 2025

Firefox’s new Profile Manager: a lightning review

Back in Firefox 138, the new profile UI made it to the stable channel.  If “Profiles” isn’t already showing near the top of the main menu, interested users can go in through about:config and flip the browser.profiles.enabled option to true.

I created a new Shopping profile to check it out.  (Formerly, my shopping has happened in a mix of dedicated containers for commonly-shopped stores, and temporary containers for less-common stores, in my core profile.  However, that profile frequently breaks checkout with its high level of privacy settings and extensions.)

First off, the good: this is far more convenient to access than about:profiles, and much prettier.  What’s more, it adds the profile badge to the Firefox icon in the Dock and Cmd+Tab list (macOS)!  There’s no more guessing about which identical Firefox corresponds to which profile.

Passkeys, since they are stored in the system’s keyring, are available across profiles.  Signing in at the new profile didn’t require any password management.  Finally, as a particularly geeky note, these are just like old Profiles, with independent extensions, themes, settings, bookmarks, and history.  The meaning of the “profile” name hasn’t been changed by this.

That leaves the one thing that could be improved.  This UI is completely separate from the traditional about:profiles.  Existing profiles do not import into the new UI.  Profiles created under the new UI aren’t visible at the old UI.  If my existing profiles had seamlessly imported, that would have been amazing.

Incidentally, if anyone needs to know, the new profile UI is at the url about:profilemanager.

On the whole, the new system is a no-brainer.  It’s love at first sight.  I will probably retire Containers from my core profile, only retaining them in Shopping or AWS profiles to keep sites/accounts separate within those domains.  I do know that AWS has multi-session support now, but I’m used to the containers for that.

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?

Update: [2025-11-15] I experimentally turned off Reduce Motion on my personal phone, and sometimes, the motion gets really confused.  This makes everything jittery with the phone held as still as humanly possible, like it’s had three cups of coffee.  It looks like the effect that I saw sometimes with Reduce Motion on, where the line above the dock would flicker, but with everything at once.

I also found out that icon customization is somehow tied to the screen layout or focus.  I made icons clear in Sleep mode, and they remained non-clear outside of Sleep.  I like this result, but the UI doesn’t make it clear where the setting applies.

Sometimes, the Apple apps in my sleep focus screen turn into empty-app indicators.  No reminders, notes, or music for the night!  But third-party apps are fine.  I cannot fathom how the first-party systems could exhibit such poor results.

I can use it, but there are so many rough edges, I keep 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 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 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.

Sunday, June 15, 2025

Nostalgia for the Amiga

When I was young, my father owned a series of Commodore machines, the last one being an Amiga 500.  BASIC was for chumps, and our only other alternative was an assembler and a single misprint-riddled book for it.  We didn’t have the internet, or any local user group/mentoring.  We made the asm stuff work, by which I mean, mostly I stole the results of my older brother’s reverse engineering.

Years later, just before the Java phenomenon reached our little community college, I started learning C++ (officially) and C (from the internet), and some mysteries started falling into place.

  • The large blocks of declare statements in assembly were a C struct.
  • The weird parts of that struct like next_window: dc.l 0 were spaces for the OS to put a pointer to the next window, creating a singly-linked list of windows on screen.
  • The difference between label and #label snapped into sharp relief when I reached Pointer Enlightenment. They were *p and p, respectively. (Somewhat; C doesn’t distinguish between absolute and PC-relative addressing.)
  • Loading the label at the start of a block of declare statements into a register before making a library call was passing a pointer to the OS.
  • The 68000’s data/address register split wasn’t arbitrary; only address registers could be used with an offset, so the pointer-to-struct went in an address register, for reading fields within the struct.
  • That weird WaitForEvent function in the OS library was the core of cooperative multitasking.  The Amiga also had preemptive multitasking, but the time slice was large enough to make the system visibly laggy if a process was uncooperative. Like all of my asm programs.

That experience of simultaneous, two-way enlightenment (asm to C and back) with pointers was incredible, but has also been a once-in-a-lifetime high.  It also gave me a lot of “if-only” feelings.  Had I understood all this stuff back then, I could have done so much more with the Amiga.

(Incidentally, that next-window pointer being in the window structure makes it invasive.  I guess this means my first encounter with invasive lists could have been before Linux was ever released.)

I also felt a sort of awe or magic about the hardware design, that was missing from the PC space.  The Amiga was half computer, half game console internally, with a bunch of custom chips to do really neat graphics. Although it had a limited number of colors in the palette, the co-processor could switch out the palette between scan lines, allowing quite a few colors per screen.  HAM mode [hold-and-modify] could also produce “thousands of colors” out of a 16-color palette, if one could accept some color-fringing artifacts.

We didn’t have a VCR or camcorder (the money was spent on the computer) but apparently, the Amiga was great at video, too.

But it was soon proven to be a dead end.  When RAM gets fast enough, a boring linear RGB framebuffer is the best option, and bitplanes (or HAM mode) turn from “cool hacks” into mere workarounds for the era’s limitations.  The 68000 family wasn’t able to keep up with Intel’s immense fab budget to fling clockspeed with the best of them, and Commodore died before a PowerPC transition could have happened.  And who even knows if they could have been as successful as Apple with it?

The Amiga’s ability to “just plug it into a TV” became a limitation over time, as the business world put all its money into PCs with dedicated monitors.  They were more expensive, but also better.  Our first PC would run in 800⨉600, although it could also produce a flickery 1024⨉768.  By comparison, Workbench on the Amiga ran in “medium” resolution, 640⨉200.  High resolution only doubled the line count, and flickered intensively on our monitor.  Ultimately, monitors came down in price and offered much better than TV quality; I went off to university (post-community-college) with a monitor capable of 1280⨉1024.

This entry was somewhat prompted by “Classical "Single user computers" were a flawed or at least limited ideas”.

Sunday, May 11, 2025

Thoughts from Trying Generators in PHP

I am late to the party, but I have been playing with Generators in PHP more, and running into the limitations of module boundaries.

Some module might produce a Generator so that iteration can be performed in chunks, reducing peak RAM.  For example, producing results one store at a time, instead of loading up all stores into a giant array.  Code that processes an entire database table, but wants to lower lock contention and memory use can also benefit; it can use a Generator to isolate the fetch-in-pages logic from processing the individual records.  The consumer sees one stream of results, while the Generator fetches more as needed.

In short, there are plenty of use cases.

The problem comes when a caller wants to pass “the data” produced by the Generator to another function or method that specifically takes an array.  Once that happens, either the destination needs to be reworked to accept the broader iterable type, or the efforts toward efficiency are erased by an iterator_to_array() call.

(Of course, back when generators were introduced to PHP, I didn’t use type declarations, so I could have gotten away with throwing a generator at something that assumed it would receive an array or PDOStatement. Dealing with larger teams and beginning to use an IDE were both great reasons to add the type information, and the array type forbids passing a Generator in its place.)

A separate issue is that anything consuming a Generator (thus, anything type-hinted iterable) needs to be aware of its once-only nature.  This only sometimes becomes a problem—for instance, if a template wants to output the data set and also some aggregate statistics over it for display before the main output.

Generators can also produce “return” values, which can be fetched by code that knows it is dealing with a Generator after the regular values are produced.  (I might change my mind later, with more experience, but it doesn’t pass the vibe check.  It feels a lot like requiring methods of a class to be called in a specific order, which is usually best to avoid.)  It implies that the entire system should lean into handling Generators in particular, and not allow them to mix with other iterable types.

These are (mostly) things I was vaguely aware of from reading about Python generators, but they weren’t on my mind while writing PHP.

Sunday, April 20, 2025

Simplicity can be Imaginary

There’s a comic about simplicity: how an Apple product has one place to touch, a Google product has one search field, and “your company’s app” has dozens of fields with interrelated requirements, obscure codes, strange highlighting, and “…” buttons.

The thing is, for internal or even b2b apps, the user probably knows what kind of thing they have, that they would like to search on.  If they are trying to look up a customer ID, then matching to a PO number is irrelevant; it will just take time and produce extraneous results.  If they can tell the computer directly, “Find customer #33448” then jump straight to the customer record, it saves them an extra round-trip through a search result page they didn’t need.

“Your company’s app” from the comic comes across as more of a data-entry page than the main point of interaction.  One might still organize the form along required/optional dimensions, and put auto-loaded fields in proximity with what will automatically update them.  However, to make the business happen, there’s a minimum amount of data that is genuinely required, that shouldn’t be crammed down to one textarea and parsed back out.

Sunday, February 23, 2025

Podman Desktop Isn’t Great

Since buying a new computer, my primary desktop is no longer Linux, and containers are no longer native.  I decided that the path of least resistance would be to try Podman Desktop, but it leaves a lot to be desired.

Sunday, February 9, 2025

The Ruthless Elimination of Differences

I am excited for image-based Linux.  Yes, I usually complain about people upending things just when they get stable, but I think there’s a real benefit here: the elimination of differences.

Why, exactly, does installing Ubuntu have to unpack a bunch of .deb files inside a system? Thousands or millions of machines will go consume CPU to run maintainer scripts, to hopefully produce identical output, when most of the desired result should have been possible to save as an image in the first place.  Upstream should know what’s in ubuntu-minimal!  Looking through a different lens, Gentoo distributes a stage2 image.

In theory, an installation CD could carry the minimal image, the installer overlay, and the flavor’s overlay.  The installer’s boot loader would bring up the kernel, use the minimal+installer pair as root file system, and the installer would unpack the minimal+flavor images into the new disk partition.

“Image-based Linux” more or less takes this one more step, running the entire system directly from the images (or a singular combined image.)  Everyone gets to use the same pre-made images, and bugs become less dependent on the history of package operations.

If any of this sounds like Puppy Linux, that’s not entirely accidental.

This is also the space where things like ABRoot are being introduced.  Image-based Linux lends itself well to having an integrated rollback/recovery pathway. Even on my non-image systems, having “a recovery partition” has been more valuable than I ever anticipated.  It let me test backups without having to work very hard about simulating a disaster. I also created my own recovery partition when I was still using a RealTek USB WiFi device, to avoid being stranded without internet.  (Word to the wise: use Mediatek instead, or an Intel PCIe card is a good non-USB option.)

Image-based Linux and the tools around it are poised to make real improvements to the repeatability and reliability of the systems.  I don’t know when I, personally, might benefit (as my daily driver is macOS now), but I am very excited about the progress being made here.

Sunday, January 26, 2025

Separate Components Allow “Least Privilege”

At work, our AWS image-building process exists as a set of scripts that are run manually, in-order, if everything looks okay to the human at the terminal.

The problem is, the image auto-installs security updates once it launches; if enough accumulate, instances launched from the image start failing to become ready for traffic before the timeout is up.  The system is guaranteed to decay if I don’t periodically build a fresh image, manually.

One reason it isn’t automated (say, rebuilding itself weekly using AWS infrastructure) is that the process takes more permissions than normal business operations.  It must be able to run and terminate instances, tag things, and reconfigure what image is launched by Auto Scaling.  Those would be scary permissions to give to any instances, which generally only have write access to specific data storage locations.

However, if the various steps (build, test, update configuration) each existed as separate objects visible to AWS, we could give each component unique permissions.  The configuration-update step would be the only one to have access to the image ID in SSM Parameter Store.  Likewise, that step would be forbidden from creating or terminating EC2 instances.

Due to implementation details, for us and our system, we could split it into the following components:

  1. Configuration and test script deployment (run by developers off-AWS; stores to S3)
  2. Ubuntu base image lookup (read-only)
  3. Image build sequence (create/wait/destroy EC2 instances, tag instance and image, read configuration scripts from S3, invoke the AWS-maintained SSM Automation to create the image from the instance)
  4. Image test sequence (create/destroy EC2 instances, read the test script from S3)
  5. Configuration update (describe the image, update a specific SSM Parameter Store item)
  6. Garbage collection (read running configuration to determine “unused” status, deregister images, delete their snapshots)

We only need to update the configuration scripts if we make changes to them; otherwise, doing a rebuild simply pulls in the available security updates.  Nothing inside AWS gets special permission to write these files.

After that, we can run step 2 to find an appropriate base AMI on our cron host.  That requires no write access, so all the cron host really needs is permission to start and monitor the latter tasks.  Tasks 3-5, in particular, are simple enough (once inputs are determined in step 2) to be run via AWS SSM Automation. I imagine there will be a “coordinating” automation that runs those three tasks in order, and the granular tasks exist mainly for ease of debugging each one.  Finally, garbage collection is somewhat compute-heavy but requires no waiting, so AWS Lambda might be the best option for it.

The end result will be a major improvement: each step will actually have minimal privileges, and the union of all privileges is much less than the admin privileges that I am technically granting to the process.  It “works” in the sense that we trust my entire laptop, but what if we didn’t need to?

Sunday, January 19, 2025

Reminiscing about fvwm

Inspired by Chris Siebenmann talking about his setup and reminiscing about MGR, let’s jump in the Epoch and set the dial for 2002.

I was in college, absolutely blown away by the customizability of X11.  You could have a “full desktop” like CDE on the Suns, or KDE/Gnome on my home computer (variously Linux and FreeBSD); or else you could choose to run with only a window manager.  Those ranged from quasi-desktops like WindowMaker and Fluxbox, down to minimalist options like Ratpoison (so named because it was intended to be used without the mouse), with what I can only describe as “normal” options in between.

In this milieu, I found fvwm2 and really dug into that.  The configuration makes it more of a “window manager construction kit” than a fully-defined window manager.  (In contrast, one could barely do anything but theme Metacity.)  I put my window title bars down the left side, because it let me have ten more pixels of vertical space!  And, let’s be honest, just because I could.

I had keyboard shortcuts for everything.  Nobody in Unix space used Macs in those days; we were busy calling the iMac a “lampshade.” Therefore, Linux applications all used ctrl/alt/shift for shortcuts, and left the Windows key (Super) free for my window management shortcuts.  Oh, and I think I drew my own icons, so I could have Amiga-style raise/lower buttons in the title bars.

I also found out I had limits.  I configured a 3x3 grid of virtual desktops each with 3x3 pages on them, but even “9 places to look for windows” was absurdly oversized.  In practice, I only ever used 3 pages on 1 desktop.  By the time I wrote Layer Juggling, I had forgotten about fvwm’s “pages” layer entirely.

I had a great run with fvwm and WindowMaker for 2–4 years there, but it soon became clear I was trading features (like having a volume control, or instantly applying themes) for an environment that nobody else could use.  Meanwhile, I was losing familiarity with Windows, which would slow me down if I were using anyone else’s computer.  I switched to KDE by 2004, and I would eventually, relucantly, capitulate and try Gnome again in 2008.  More or less just in time for Gnome and Canonical to blow everything up again!

Ironically, over time, there has been less need to use other peoples’ computers.  Besides which, I have kept using Dvorak for 20+ years now, despite that being a much bigger issue when switching systems.

Tuesday, December 17, 2024

What I Learned Trying to Install Kubuntu (alongside Pop!_OS)

First and foremost, once again, this is clearly not a supported configuration that I tried to make.  I'm sure that if I wiped the drive and started afresh, things would have gone much better.  I just… wanted to push the envelope a bit.

Pop!_OS installs (with encryption) with the physical partition as a LUKS container, holding an LVM volume group, and the root filesystem is on a logical volume within.  The plan was hatched:

  • Create a logical volume for /home and move those files over to it
  • Create a logical volume for Kubuntu’s root filesystem
  • Install Kubuntu into the new volume, and share /home for easy switching (either direction)

Things immediately got weird.  The Kubuntu installer (calamares) knows how to install into a logical volume, but it doesn’t know how to open the LUKS container.  I quit the installer, unlocked the thing, and restarted the installer.  This let the installation proceed, up to the point where it failed to install grub.

Although that problem can be fixed, the whole installation ended up being irretrievably broken, all because booting Linux is clearly not important enough to get standardized. Oh well!

Sunday, September 15, 2024

The Wrong Terminal

Somewhere in my Pop!_OS 22.04 settings, I set Tilix as the preferred terminal emulator. When I use the Super+T* keyboard shortcut, I get a Tilix window.  However, when I use a launcher that Distrobox has created for a container from the Super+A (for Applications) UI, the command-line doesn’t come up in Tilix… it comes up in gnome-terminal instead. Why is that, and can I fix it?

AIUI, all the Application Launcher UI does is ask the system to open the .desktop file that Distrobox added.  That file has the “run in terminal” option, but lacks the ability to request some specific terminal. That gets chosen, eventually, by the GIO library.

GIO isn’t desktop-specific, so it doesn’t read the desktop settings.  It actually builds in a hard-coded list of terminals that it knows how to work with, like gnome-terminal, konsole, and eventually (I am assuming) ye olde xterm.  It walks down the list and runs the first one that exists on the system, which happens to be gnome-terminal.  AFAIK, there is no configuration for this, at any level.

It is also possible that one of the distributions in the chain (Debian, Ubuntu, or Pop!_OS) patched GIO to try x-terminal-emulator first.  If so, it would go through the alternatives system, which would send it directly to gnome-terminal, since between that and Tilix, gnome-terminal has priority.  We are deep into speculative territory, but if all of that were the case, I could “make it work” by making a system-level change resulting in all users now preferring Tilix… but only for cases where x-terminal-emulator is involved, specifically.

I want the admin user to have all the defaults, like gnome-terminal, because the less deviation made in that account, the less likely I am to configure a weird problem for myself.** (Especially for Gnome, which has a configuration system, but they don’t want anyone to use it.  For simplicity.) Changing the alternatives globally is in direct contradiction to that goal.

It seems that the “simplest” solution is to change the .desktop file to avoid launching in a terminal, and then update the command to include the desired terminal.  It would work in the short term, but fall “out of sync” if I ever changed away from Tilix as default in the desktop settings, or uninstalled Tilix.  It’s not robust.

It seems like there’s some sort of desktop-environment standard missing here.  If we don’t want to invoke threads or communication inside GIO, then there would need to be a way for the Gnome libraries to pass an “XDG Configuration” or something, to allow settings like “current terminal app” to be passed in.

If we relax the constraints, then a D-Bus call would be reasonable… but in that case, maybe GIO could be removed from the sequence entirely.  The Applications UI would make the D-Bus call to “launch the thing,” and a desktop-specific implementation would pick it up and do it properly.

It seems like there should be solutions, but from searching the web, it looks like the UX has “just done that” for years.

* Super is the |□| key, because it's a System76 laptop, a Darter Pro 8 in particular.

** As a side effect, this makes the admin account feel like “someone else’s computer,” which makes me take more care with it.  I may not want to break my own things, exactly, but I would feel even worse about breaking other people’s stuff.

Monday, September 9, 2024

Some Solutions to a Problem

We have an EC2 instance that has a quickly-produced shell script that runs on boot.  It sets a DNS name to the instance’s public IPv4 address.  Since time was of the essence, it hard-codes everything about this, particularly, the DNS name to use.

This means, if we want to bring up a copy of this instance, based on a snapshot of its root volume, the copied instance will overwrite the DNS record for the production service. We need to stop this.

As a side project, it would be nice to remove the hard-coding of the DNS name.  It would be trivial to “stop DNS name conflicts” if we did not have a DNS name stored on the instance’s disk image to begin with.

What are the options?