Obviously, I haven’t popped the top on this PC for a while:

Some fuzz made it past the grille:

PSA: In the unlikely event you still use a desktop PC, it’s time to pop the top on yours, too.
The Smell of Molten Projects in the Morning
Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
Remembering which tweaks worked

Obviously, I haven’t popped the top on this PC for a while:

Some fuzz made it past the grille:

PSA: In the unlikely event you still use a desktop PC, it’s time to pop the top on yours, too.

An obvious spam email blew past the filters:

You can tell it’s spam, too. Right?
Those of you running Windows should have undone whatever setting removes file extensions from the usual views, because by default Windows won’t bother you with such trivia.
But, hey, maybe an SVG file can contain an audio recording. I mean, there’s an online file converter for that, so it must be a thing.
Spoiler: Audio-in-SVG really is a thing.
Having been around this block a couple of times, though, let’s peek inside the SVG file with a text editor:

Huh. Not an audio recording, but a Javascript one-liner with a URL/URI/IRI/whatever aiming Your Default Browser at a presumably compromised server.
I didn’t go further, but surely the payload would wrestle Your Default Browser into a position allowing insertion of a remote compromise.
Well played, spammer!
Just another entry in the “Why friends don’t let friends run Windows” category, despite knowing whenever security and convenience come into conflict, convenience always wins.

For reasons I do not profess to understand, GIMP 3.0 does not work with plugins written for GIMP 2.0, including the XSane plugin that handles scanning. This seems like an obvious oversight, but after three months it also seems to be one of those things that’s like that and that’s the way it is.
Protracted searching turned up gimp-xsanecli, a GIMP 3.0 plugin invoking XSane through its command-line interface to scan an image into a temporary file, then stuff the file into GIMP. Unfortunately, it didn’t work over the network with the Epson ET-3830 printer / scanner in the basement.
It turns out gimp-xsanecli tells XSane to output the filename it’s using, then expects to find the identifying XSANE_IMAGE_FILENAME string followed by the filename on the first line of whatever it gets back:
if result != 'XSANE_IMAGE_FILENAME: ' + png_out:
Gimp.message('Unexpected XSane result: ' + result)
return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.EXECUTION_ERROR)])
The font ligature that may or may not mash != into ≠ is not under my control.
Protracted poking showed the scanner fires a glob of HTML through proc/stdout into gimp-xsanecli before XSane produces its output, but after the scan completes:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN "
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
… snippage …
</head>
<body><noscript>Enable your browser's JavaScript setting.</noscript></body></HTML>XSANE_IMAGE_FILENAME: /tmp/out.png
Complicating the process:
gimp-xsanecli expectsSo …
Insert a while loop into the main loop to strip off the HTML glob line by line by line:
while True:
# Wait until XSane prints the name of the scanned file, indicating scanning is finished
# This blocks Python but that is ok because GIMP UI is not affected
# discard HTML header added by scanner to first scan
while True :
result = proc.stdout.readline().strip()
if r'</body>' in result :
result = result.partition(r'</HTML>')[-1]
# Gimp.message('Found end of HTML: ' + result)
break
elif 'XSANE_IMAGE_FILENAME:' in result :
# Gimp.message('Found filename: ' + result)
break
else :
# Gimp.message('Discarding: ' + result)
continue
if result == '':
# XSane was closed
break
if result != 'XSANE_IMAGE_FILENAME: ' + png_out:
Gimp.message('Unexpected XSane result: ' + result)
return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.EXECUTION_ERROR)])
# Open image
image = Gimp.file_load(Gimp.RunMode.NONINTERACTIVE, Gio.File.new_for_path(png_out))
Gimp.Display.new(image)
# Remove temporary files
os.unlink(png_out)
if not SCAN_MULTIPLE:
proc.terminate()
break
os.rmdir(tempdir)
return Gimp.ValueArray.new_from_values([GObject.Value(Gimp.PDBStatusType, Gimp.PDBStatusType.SUCCESS), GObject.Value(Gimp.Image.__gtype__, image)])
While it’s tempting to absorb the whole thing in one gulp with proc.stdout.read().strip(), that doesn’t work because nothing arrives until the XSane subprocess terminates, which is not what you want.
A scan to show It Just Works™ :

I expect it doesn’t work under a variety of common conditions, but … so far so good.

I volunteered to reform the hulking electrolytic capacitors in a long-unused S-100 Bus computer:

Yes, it’s built into a recycled modem case. No, they don’t make modems like they used to, either. Regrettably, the five status indicators on the left were not set up as Der Blinkenlichten.
The inside view:

The multi-winding transformer in the back feeds bridge rectifiers (out of sight behind the caps) producing bulk DC:

The gray cap is 52 mF = 52000 µF 15 V for the +5 V regulators supplying the TTL logic on each board.
Two of the three blue caps (each 9 mF = 9000 µF 50 V) are for the +12 V and -12 V supplies. I think the third cap is a separate supply for a different purpose, but I did not trace out the wiring.
The on-board regulators seem to use solid electrolyte caps that should still be in fine shape you should replace on principle, per ericlscott’s experience. You’d want to bring up each board separately while probing the voltages; the box of stuff accompanying the system has an extender card that should make probing easier.
I hoped to boot the thing after restoring the caps, but a casual inspection showed wire corrosion:

You’d want to pull the backplane out and replace those jumpers, as well as clean the bus contacts, before applying power.
The system has two 8 inch floppy drives in a separate case with its own power supply:

There was some corrosion in there, too:

So I confined myself to reforming the caps and must let someone with more powerful motivation restore the rest of the system before trying to connect everything and booting CP/M.
The general idea behind “reforming” an electrolytic capacitor is to regrow the oxide layer separating the anode and cathode electrodes, which involves passing a current of about 1 mA for as long as it takes to bring the terminal voltage up to the cap’s maximum rated voltage:

That setup consists of an absurd number of PowerPole adapters putting the meter in series with a fuseholder repurposed to hold resistors to limit the current, with leads eventually ending up on the capacitor:

The red dot is the overpressure vent, not a polarity marker.
Apparently the Greek mu symbol wasn’t in the font available for the labels, as all the capacitors use m in its place: that capacitor is 52 mF = 52000 µF.
The white plastic ejection handle belongs on the right end of the CPU board seen in the second picture, which was not plugged into its slot when I opened the case. I snapped the handle in place and plugged the board in just to keep it out of trouble. The case does not have board guide slots along the edges that would let the handle eject the board, but all that was definitely in the nature of fine tuning back then.
I started with +15 V through a 16.9 kΩ resistor and swapped in 3.3 kΩ, 1 kΩ, and 220 Ω resistors as the cap voltage crept upward over the course of two days and eventually settled to a steady state:

After discharging, the cap measured 59.5 mF with a 0.3 Ω ESR, which definitely seemed Good Enough.
I reformed the three 9 mF 50 V caps at the same time by applying 50 V to three resistors captured on their screw terminals, changing the resistors as the voltages rose:

Those three caps eventually measured (clockwise from upper right):
The ESRs suggest they’re somewhat dried out, but I’d be tempted to run them anyway, because the on-board regulators should knock down the ripple.
All of the reformed caps had leakage currents of a few hundred microamps. They’re not new capacitors and never will be, but they may be Good Enough.
Getting the caps out of the diskette drive power supply required easing the entire supply frame / heatsink out of the case before unscrewing the capacitor clamps:

That one eventually measured 22.1 mF with 0.14 Ω ESR. Its sibling, nominally 38 mF at 15 V, came in at 48.9 mF with 0.95 Ω ESR.
The power supply PCB carries a handful of smaller aluminum electrolytic caps that are impossible to remove without unsoldering all the TO-3 transistor leads coming through the aluminum heatsink / frame, then completely dismantling the power supply:

Although I reformed the big caps, I think a better plan would be to replace the whole thing with a contemporary switching supply. AFAICT it has 24 V and 5 V outputs; because we live in the future, dual-output switchers are cheap & readily available.
And then I closed the cases to get them ready for the next part of their adventure …

Inkscape is not a CAD program (neither is LightBurn), but for my simple needs it works well enough, with the compelling advantage that OpenSCAD can import named layers and extrude them into solid models.
LightBurn can import Inkscape SVG images to define the patterns for laser cutting / engraving and will automatically put the vectors into layers corresponding to their colors if and only if the SVG image uses colors from the LightBurn palette. Regrettably, picking those colors from the default Inkscape palette is essentially impossible, but you can have Inkscape use a palette file that displays only the LightBurn colors corresponding to its layers.
I conjured this GIMP / Inkscape palette file based on the table in a LightBurn forum post, plus tool layer colors from another post:
GIMP / Inkscape Palette
Name: LightBurn Layers
#
0 0 0 BLACK
255 255 255 WHITE
0 0 0 LBRN #0
0 0 255 LBRN #1
255 0 0 LBRN #2
0 224 0 LBRN #3
208 208 0 LBRN #4
255 128 0 LBRN #5
0 224 224 LBRN #6
255 0 255 LBRN #7
180 180 180 LBRN #8
0 0 160 LBRN #9
160 0 0 LBRN #10
0 160 0 LBRN #11
160 160 0 LBRN #12
192 128 0 LBRN #13
0 160 255 LBRN #14
160 0 160 LBRN #15
128 128 128 LBRN #16
125 135 185 LBRN #17
187 119 132 LBRN #18
74 111 227 LBRN #19
211 63 106 LBRN #20
140 215 140 LBRN #21
240 185 141 LBRN #22
246 196 225 LBRN #23
250 158 212 LBRN #24
80 10 120 LBRN #25
180 90 0 LBRN #26
0 71 84 LBRN #27
134 250 136 LBRN #28
255 219 102 LBRN #29
243 105 38 LBRN T1
12 150 217 LBRN T2
Plunk that file (which I named Lightburn.gpl) into /home/ed/.config/inkscape/palettes/, restart Inkscape, then select it (the Name line defines its mmm name):

Which lays a row of the LightBurn layer colors along the the Inkscape window:

The text after the RGB triplet in each file line appears as the tool tip for the color swatch:

Because LightBurn uses only the vector Stroke and ignores its Fill, you (well, I) must become accustomed to Shift-clicking palette colors.
You can fetch a similar palette file directly from the LightBurn doc, although minus the tool tips. GIMP and Inkscape have many palettes available, should you make artsy drawings where subtle color shading matters.
I generally use only a few cheerful primary colors, because I have trouble distinguishing (heck, in some cases even seeing) the more subtle colors against LightBurn’s light (or dark) workspace background. I assign the layer cut settings using the Material Library: reds for cutting, blues for marking, and grays for engraving.
When I need more than maybe half a dozen colors, I (eventually) realize I’m trying to be too clever and split the project into separate LightBurn files.

Rather than fiddle with the GUI program for my Yubikey, I use the ykman CLI program for TOTP authentication, because there’s always a command prompt / terminal open on the portrait monitor:
ykman oath accounts code -s ama
161413
Double-click to select the number in the terminal, then either copy-n-paste or middle-click into the target field of whatever needs convincing I am truly me, myself, and I.
I finally got a Round Tuit and piped the output into xclip to put the number into the clipboard:
ykman oath accounts code -s ama | xclip
Which lets me go directly to pasting or middle-clicking.
The command history is big enough that I now type only:
Ctrl-R ama
Which brings up the most recent version of the command, whereupon I whack Enter to execute it. Similar abbreviations extract the commands for dozen-odd companies / banks / institutions / whatever I deal with.
When I need a hint:
ykman oath accounts list
Should’a done that long ago.
For reference, a treatise on Yubikey config and usage.
Bonus! A cat:

Because SEO demands a picture.

I asked for the images from recent X-ray and MRI sessions, whereupon a CD arrived in the mail. Popping it into my desktop Linux box produced this directory listing:
ll /run/media/ed/Feb\ 21\ 2025/
total 146M
dr-xr-xr-x 2 ed ed 136 Feb 21 13:14 ./
drwxr-x---+ 3 root root 60 Mar 2 13:40 ../
-r--r--r-- 1 ed ed 146M Feb 21 13:14 -NISLEY-DMBG8yMQcf8qXcVj.iso
It seems whoever / whatever produced the CD copied the ISO image to the CD, rather than burning the ISO directly to the CD. As a result, the CD has one file.
Raise your hand if you’ve never done that.
Well, I was going to save the CD as an ISO file anyway, so I just copied it to the file server.
Attempting to mount it produces an odd result:
sudo mount -o loop "-NISLEY-DMBG8yMQcf8qXcVj.iso" /mnt/loop/
[sudo] password for ed: <make up your own>
mount: failed to set target namespace to ISLEY-DMBG8yMQcf8qXcVj.iso: No such file or directory
Oh, right, starting a filename with a leading dash is never a Good Idea™.
Rename it:
mv -NISLEY-DMBG8yMQcf8qXcVj.iso NISLEY-DMBG8yMQcf8qXcVj.iso
mv: invalid option -- 'N'
Try 'mv --help' for more information.
Which is why leading dashes are a Terrible Idea™.
Force the rename to happen:
mv ./-NISLEY-DMBG8yMQcf8qXcVj.iso NISLEY-DMBG8yMQcf8qXcVj.iso
The same syntax works in the mount command, but it’s easier to solve the problem once and be done with it.
Now mount the file:
sudo mount NISLEY-DMBG8yMQcf8qXcVj.iso /mnt/loop
mount: /mnt/loop: WARNING: source write-protected, mounted read-only.
That’s entirely expected, because the whole filesystem is intended for a non-writeable CD.
What’s inside?
ll /mnt/loop/
ls: cannot open directory '/mnt/loop/': Permission denied
Why would that be?
ll /mnt
total 58K
drwxr-xr-x 15 root root 4.0K May 21 2023 ./
drwxr-xr-x 17 root root 4.0K Mar 2 13:43 ../
… omitted …
drwxrwx--- 4 496 495 2.0K Feb 21 13:13 loop/
… omitted …
Maybe 496 and 495 are the UID and GID of whatever created the CD?
Force it to my UID:
sudo umount /mnt/loop
[ed@shiitake tmp]$ sudo mount -o uid=ed NISLEY-DMBG8yMQcf8qXcVj.iso /mnt/loop
mount: /mnt/loop: WARNING: source write-protected, mounted read-only.
[ed@shiitake tmp]$ ll /mnt/loop
total 16K
drwxrwx--- 4 ed 495 2.0K Feb 21 13:13 ./
drwxr-xr-x 15 root root 4.0K May 21 2023 ../
drwxrwx--- 4 ed 495 2.0K Feb 21 13:12 data/
drwxr-xr-x 5 ed 495 2.0K Feb 21 13:13 DICOM/
-rw-rw---- 1 ed 495 1.7K Feb 21 13:12 README.txt
-rw-rw---- 1 ed 495 3.2K Feb 21 13:12 view-studies.html
Now that’s more like it.
Finally, I can fire up Weasis to look at pretty DICOM images:

Apparently things looks suspicious around L4.