<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Nelson Chen's Blog</title><link>https://mindflakes.com/</link><description>Recent content on Nelson Chen's Blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>Copyright Nelson Chen</copyright><lastBuildDate>Sun, 13 Sep 2026 10:34:51 -0700</lastBuildDate><atom:link href="https://mindflakes.com/index.xml" rel="self" type="application/rss+xml"/><item><title>My wobbly VHS pipeline</title><link>https://mindflakes.com/posts/2026/09/13/my-wobbly-vhs-pipeline/</link><pubDate>Sun, 13 Sep 2026 10:34:51 -0700</pubDate><guid>https://mindflakes.com/posts/2026/09/13/my-wobbly-vhs-pipeline/</guid><description>&lt;p&gt;I wanted to recover some VHS tapes. Somehow this turned into tapping a signal inside a Sony VCR, putting capture cards in a computer from the junk pile at work, spreading the processing across three machines, and asking Codex to help redesign an amplifier PCB.&lt;/p&gt;
&lt;p&gt;There is a reason I went this way. The usual way to digitize VHS is to connect a VCR&amp;rsquo;s video and audio outputs to a capture device and record them on a computer. The VCR reads the tape and turns its signal into a picture before the computer ever sees it.&lt;/p&gt;
&lt;p&gt;For better results, you can look for a nicer VCR with a built-in time base corrector, or TBC, which helps correct playback timing errors that make the picture wobble. Unfortunately, the decks I was looking at were $$$. With ancient media having a moment again, it feels like everybody wants the same aging hardware. The expensive stuff is, well, expensive.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/oyvindln/vhs-decode"&gt;VHS-Decode&lt;/a&gt; offers a more hardcore route. You still need a working VCR, but you tap the RF signal inside it, near the tape-head amplifier, before the machine turns it into ordinary video. This is not the antenna connector on the back. Suitable capture hardware records that earlier signal, and the computer reconstructs the picture, including time base correction, in software.&lt;/p&gt;
&lt;p&gt;That sounded pretty appealing: use an ordinary VCR, spend less on a fancy deck, and accept more fiddling. Keeping the RF recording also means I can try different settings or a better decoder later without playing the physical tape again.&lt;/p&gt;
&lt;p&gt;I first heard about VHS-Decode about two years ago. I lacked the hardware, time, and initiative to really start. Codex helped me get past a lot of those hurdles. I drove the whole setup through Codex, right down to configuring Ubuntu on the capture machine. It was involved from the beginning, not something I added after getting everything working.&lt;/p&gt;
&lt;p&gt;To be clear about who wrote what: Codex wrote a lot of the custom code for this setup. I directed and oversaw the work, made the decisions, and handled the physical hardware. When I call these “my scripts” or “my pipeline,” I mean the setup I am building and responsible for, not that I personally typed all the code.&lt;/p&gt;
&lt;p&gt;The pipeline repo has an &lt;code&gt;AGENTS.md&lt;/code&gt; with instructions for Codex and a &lt;code&gt;journal.md&lt;/code&gt; where we take notes as we go. That journal now runs through months of experiments, failures, and procedures that apparently needed another procedure.&lt;/p&gt;
&lt;p&gt;It is still a constant WIP. But looking back through those notes, there is an actual journey from “can I get a signal?” to something that helps me understand and organize the tapes too.&lt;/p&gt;
&lt;h2 id="first-get-a-picture"&gt;First, get a picture&lt;/h2&gt;
&lt;p&gt;I already had the Sony SLV-688HF. The capture computer was a Dell OptiPlex 7010 rescued from the junk pile at work. An old business desktop with room for capture hardware seemed like a reasonable place to start.&lt;/p&gt;
&lt;p&gt;In late June and early July, the work was mostly getting that machine ready: Ubuntu installation and configuration, the CXADC driver, capture cards, cables, and figuring out whether the signal coming out of the VCR was useful. I was directing that work through Codex while handling the physical hardware myself.&lt;/p&gt;
&lt;p&gt;On July 3, the first input we tried was basically flat, even with the tape playing. Switching from the white BNC input to the red one gave us a much healthier signal. A small detail, but fairly important when the whole project depends on recording something other than nothing.&lt;/p&gt;
&lt;p&gt;That day&amp;rsquo;s one-minute test produced about 1.6 GiB of RF data. Decoding it on my MacBook took a little over five minutes in that early test, then produced roughly another 3.2 GiB of intermediate picture data. This was already a hint about where the project was going.&lt;/p&gt;
&lt;p&gt;First there was a viewable black-and-white preview, without sound. Then a short color preview.&lt;/p&gt;
&lt;p&gt;There was an actual picture. The weird route worked well enough to keep going.&lt;/p&gt;
&lt;h2 id="one-minute-was-manageable-two-hours-was-different"&gt;One minute was manageable. Two hours was different.&lt;/h2&gt;
&lt;p&gt;The next problem was doing this at tape length. I added a Samsung 980 1 TB NVMe SSD I already had on hand as scratch storage in the capture machine. By August 8, a two-hour-and-two-minute capture had produced about 196 GiB of RF data, plus separately recorded audio. The machine&amp;rsquo;s 250 GB boot SSD was not going to provide comfortable working room for that.&lt;/p&gt;
&lt;p&gt;Now storage and transfers were part of the job. The source needed to get onto the storage machine, and the copy needed checking before I treated it as safely transferred. Then came decoding, with another set of enormous files to put somewhere.&lt;/p&gt;
&lt;p&gt;I store the captured RF on the NAS in FLAC. Yes, FLAC, the format usually associated with lossless audio. Here it holds the sampled RF signal, not a finished video. The separately captured audio has its own FLAC file. This saves space without throwing away the signal samples I want to decode again later.&lt;/p&gt;
&lt;p&gt;I had Codex write custom scripts and capture tooling, under my direction, to FLAC the streams on the fly. That avoids making every capture a huge raw file followed by another whole compression job. The compressed captures still need to get safely onto the NAS, and the tooling has to keep track of whether samples were lost or an encoder failed. Compression is useful; silently losing part of the tape is not.&lt;/p&gt;
&lt;p&gt;I tried decoding on the storage machine too. One run was heading toward roughly two days of processing. Later the production decoding moved to a Windows machine. The setup grew into three main jobs on three machines: capture, preservation storage, and decoding/export.&lt;/p&gt;
&lt;p&gt;The Windows stage now turns the retained RF recording into intermediate luma and chroma picture files, then makes H.265/AAC copies I can watch. Those convenient video files do not replace the preservation source. Keeping the earlier signal is a large part of why I chose this approach in the first place.&lt;/p&gt;
&lt;p&gt;Audio added its own little complication. I capture it separately, and separate devices have separate clocks. Their crystal oscillators can run at slightly different rates, so audio that lines up at the start can drift away from the picture later.&lt;/p&gt;
&lt;p&gt;Synchronizing the hardware clocks is one approach. I was okay with resampling the audio to fit the video timeline instead. That means adjusting its timing over the recording, not just moving the whole track left or right and hoping. It also means checking synchronization beyond the beginning.&lt;/p&gt;
&lt;p&gt;At this point, “capture a tape” had acquired quite a few more steps than pressing record.&lt;/p&gt;
&lt;h2 id="then-i-had-to-find-out-what-was-on-it"&gt;Then I had to find out what was on it&lt;/h2&gt;
&lt;p&gt;An August entry in that journal has a useful lesson in why the end of a recording is not necessarily the end of a tape.&lt;/p&gt;
&lt;p&gt;A fixed-duration capture can keep recording after a short tape has stopped, wasting a ridiculous amount of space. But looking for static is not enough to decide where useful material ends either. One tape had a static gap followed by another recording. Stopping at the gap would have missed it.&lt;/p&gt;
&lt;p&gt;These tapes were recorded over, sometimes only partially. A cassette can contain television, home video, and then more television. Sampling a few places and finding TV does not establish that there is nothing personal elsewhere.&lt;/p&gt;
&lt;p&gt;So my rule is to capture the whole tape first and analyze it afterward. I do not stop because the interesting-looking recording ended, television started, or the picture went to static. There could be something else further along. The power meter helps detect the physical end of the tape; figuring out which parts to keep in the viewing copies is a separate, later job.&lt;/p&gt;
&lt;p&gt;This became one of the most useful parts of having Codex involved. It has been great at scene analysis, helping with transcription and translation, and writing up what is happening on the tapes. It can help sift the home-video sections out of recorded TV programming and leave a map of the recording alongside the watchable files.&lt;/p&gt;
&lt;p&gt;The review procedures grew around that work: frames throughout the recording, extra scene-change samples, and moving picture with audio around anything ambiguous. I want to know what was actually checked, not just get a confident summary based on three screenshots.&lt;/p&gt;
&lt;p&gt;Codex also helped read the wonky Chinese handwriting on the old labels. Some of it is barely readable scratches at this point. A plausible reading and translation can give me a starting point, and the footage can help make sense of it. Uncertain characters need to stay marked as guesses. The same goes for difficult speech in a transcript.&lt;/p&gt;
&lt;p&gt;I am keeping the tape contents and detailed records private, but this part matters to the story. Getting a video file is useful. Having help figure out what is in it, where the different recordings start, and how to find them again makes the whole project much more useful to me.&lt;/p&gt;
&lt;h2 id="of-course-it-grew-a-labeling-system"&gt;Of course it grew a labeling system&lt;/h2&gt;
&lt;p&gt;By mid-August, I had also connected a Brother PT-D600 label printer over USB. Apparently the tape project needed a labeling regime too.&lt;/p&gt;
&lt;p&gt;I stopped using printed status labels. Keeping those updated was more fuss than it was worth; off-the-shelf smiley-face stickers from Amazon were easier.&lt;/p&gt;
&lt;p&gt;The labels I still print are for identity and content only. Each tape gets a stable ID, and its box gets the same one. Content labels describe the recording, with a date when known. Processing status stays in the digital records, not on a succession of replacement labels.&lt;/p&gt;
&lt;p&gt;Yellow means “could be better.” Green is reserved for what I would call a perfect capture. I am not there yet: I still do not have the shared-clock hardware to synchronize the captures, so I am using the resampling approach described earlier. Green is a goal for now, not a claim about the captures I already have.&lt;/p&gt;
&lt;p&gt;That connects the digital notes back to the object in front of me. Codex can help inspect a tape, write up the findings, and print the labels. If the box and cassette get separated, I can still identify both.&lt;/p&gt;
&lt;p&gt;There is also an M5Stack Atom Lite, a little ESP32 device with a built-in infrared transmitter, for sending the VCR commands like play, stop, and rewind. It cost me $14. I 3D-printed a small stand for cents to hold it up against the VCR, aimed at its infrared receiver. So the automation can press the remote buttons too, although sending an IR command is not proof the VCR obeyed it.&lt;/p&gt;
&lt;p&gt;For that, I also monitor the VCR with a power meter. The deck draws different amounts of power when the tape transport is active and when it is stopped with a tape loaded. The workflow watches those measured states rather than assuming an IR command worked. To rewind, it sends Stop, checks that the deck has settled, sends Rewind, then watches for sustained activity followed by a stable return to the stopped state. Those checks are specific to this VCR and setup.&lt;/p&gt;
&lt;p&gt;At capture time, the software starts recording before sending Play, so it does not miss the beginning while getting ready. It then watches for the deck to become active. When the tape reaches its physical end and the transport stops, the power draw settles back into the stopped range. That sustained change is how the workflow detects end-of-tape, alongside the capture&amp;rsquo;s own integrity checks. It records through the TV programming, static, and silence instead of using the content to guess where the tape ends. An unexpected stop or ambiguous meter evidence still needs checking; a power reading alone cannot explain why the transport stopped.&lt;/p&gt;
&lt;p&gt;Then, later in August, I had it help redesign the RF amplifier PCB for a more ordinary, externally regulated 9V input. I wanted the power setup to be less exotic. This was a later hardware experiment, not something I had to finish before getting that first picture in July.&lt;/p&gt;
&lt;p&gt;The redesign passed KiCad electrical and layout checks, and the prototype fabrication order shipped. That is not a bench-tested result yet. Still, going from a project I had not managed to start to ordering a revised amplifier board is quite a progression.&lt;/p&gt;
&lt;h2 id="the-setup-kept-finding-new-ways-to-be-annoying"&gt;The setup kept finding new ways to be annoying&lt;/h2&gt;
&lt;p&gt;Moving the decoding to Windows did not end the infrastructure work. The intermediate files were still huge. Writing them through a mount to the storage machine saved local scratch space, until the mount became unreliable.&lt;/p&gt;
&lt;p&gt;The decoder itself became part of the work too. In my tape-decode fork, Codex investigated and wrote fixes for a bunch of issues where multithreaded decoding would jam up, with me overseeing the work. So this was not just asking an agent to run somebody else&amp;rsquo;s program. It was also digging into why the program got stuck and changing the code. That does not mean every threading problem is gone, but those fixes are another part of how this setup kept moving forward.&lt;/p&gt;
&lt;p&gt;A decoder can still be running while its output is no longer reaching the destination. A mount process can exist without being useful. Successfully launching a command tells me surprisingly little about whether the job is going to finish.&lt;/p&gt;
&lt;p&gt;So there are checks for actual file growth, child-process logs, transfer lengths, and expected picture-field counts. There is also a chunked path: close a piece locally, transfer it, verify it, reclaim the space, and assemble the pieces in order afterward.&lt;/p&gt;
&lt;p&gt;This is where the long journal earns its keep. Each tape has records of the current files, commands, attempts, and results. The procedures explain which machine does what and what counts as completing a stage. A later Codex conversation can inspect that state and continue instead of needing me to remember every detail.&lt;/p&gt;
&lt;p&gt;I also use the shit out of goals in Codex for this. I give it an outcome to work toward, like getting a tape ingested, preserved, decoded, and reviewed, with the records updated. The goal says what I want finished; the repo&amp;rsquo;s instructions, procedures, and journal give it the details needed to keep working toward that. There are a lot of intermediate steps in this setup, and I do not want “the decoder started” to be mistaken for the job being done.&lt;/p&gt;
&lt;p&gt;The procedures change because the work changes. A failed mount leads to another check. A misleading preview changes the review instructions. Something that worked for a short test turns out to need a different approach for a whole tape.&lt;/p&gt;
&lt;p&gt;I am pretty amazed Codex can deal with all of this. I can ask it to help capture, preserve, decode, review, export, and update the records across this weird setup. I still have to physically put the tape in the VCR. There are limits to the agent thing. 😅&lt;/p&gt;
&lt;p&gt;And a process starting successfully is not successful preservation. Even finishing an encode does not establish that the source is safely retained, the audio is right, or the important sections were found. That is why the notes have become rather fussy about “done.”&lt;/p&gt;
&lt;h2 id="cheaper-does-not-mean-cheap"&gt;Cheaper does not mean cheap&lt;/h2&gt;
&lt;p&gt;People have been asking what this approach costs. I went back through my purchase emails, and even the partial breakdown is a reminder that an ordinary VCR does not make the whole project cheap.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th style="text-align: right"&gt;Cost / replacement allowance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sony SLV-688HF VCR, including shipping and tax&lt;/td&gt;
&lt;td style="text-align: right"&gt;$69.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capture computer, salvaged from the junk pile at work&lt;/td&gt;
&lt;td style="text-align: right"&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Samsung 980 1 TB NVMe scratch SSD, already on hand&lt;/td&gt;
&lt;td style="text-align: right"&gt;$0 additional spending; allow about $300 for a new 1 TB replacement today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capture-PC boot SSD, PCIe NVMe adapter, BNC cable, tap lead, and SMA/BNC adapters, grouped in one shipment&lt;/td&gt;
&lt;td style="text-align: right"&gt;$100.49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S-Video-to-BNC splitter shipment&lt;/td&gt;
&lt;td style="text-align: right"&gt;$8.77&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMA DC-block shipment&lt;/td&gt;
&lt;td style="text-align: right"&gt;$27.38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Three PCIe capture cards for CXADC, including one dud&lt;/td&gt;
&lt;td style="text-align: right"&gt;$90.02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JLCPCB amplifier prototype batch: five PCBs, two assembled&lt;/td&gt;
&lt;td style="text-align: right"&gt;About $140&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M5Stack Atom Lite ESP32 controller with built-in IR&lt;/td&gt;
&lt;td style="text-align: right"&gt;$14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3D-printed stand for aiming the IR controller at the VCR&lt;/td&gt;
&lt;td style="text-align: right"&gt;Cents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VCR power meter for transport-state monitoring&lt;/td&gt;
&lt;td style="text-align: right"&gt;About $14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Estimated additional spending for my setup&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;About $464&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;With a current replacement allowance for the scratch SSD&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;About $764&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Apart from the scratch SSD&amp;rsquo;s explicitly marked replacement allowance, these are historical purchase amounts for my setup, including things bought while experimenting, not a recommended shopping list. The three matching AliExpress card orders show $30.87, $31.48, and $27.67. One card was a dud, so I ended up buying three; that is my spending, not a requirement to buy three cards. The emails also carry a shipping-fee exclusion notice, so those amounts may not capture every charge.&lt;/p&gt;
&lt;p&gt;The amplifier batch is about $140 from memory, pending the final invoice. I am counting the whole batch rather than dividing it into a misleadingly cheap single-board price. The Amazon shipment totals also differ from the initial order confirmation. With the $14 IR controller, roughly $14 power meter, and cents for the printed stand, call this roughly $464, with some accounting still to reconcile.&lt;/p&gt;
&lt;p&gt;The scratch SSD was another thing I already had, not another free component somebody else can assume they will find. Current prices are wild. On September 13, 2026, a &lt;a href="https://www.bestbuy.com/product/samsung-990-evo-plus-ssd-1tb-pcie-gen-4x4--5x2-m-2-2280-speeds-up-to-7150-mb-s/J3ZYG2TVFS/sku/12161481"&gt;Best Buy marketplace listing for a Samsung 990 EVO Plus 1 TB&lt;/a&gt; showed $299.74, sold and shipped by Merchant Expo LLC. I am using roughly $300 as a current replacement allowance, before tax. That is a newer model, not the Samsung 980 actually in my machine, and one listing rather than the cheapest-drive claim or a market average.&lt;/p&gt;
&lt;p&gt;The capture computer came out of the junk pile at work. It cost me nothing. Maybe call it a $100 used computer if you had to find something similar, but that is my guess, not a checked price. Including both that computer estimate and the scratch SSD allowance puts this partial budget around $864. The separate boot SSD and PCIe adapter I bought are already counted in the shipment above; the 1 TB scratch drive is not bundled into that amount.&lt;/p&gt;
&lt;p&gt;The other computers already on hand, bulk storage, tools, label printer and its tape, smiley-face stickers, electricity, and AI usage are outside this subtotal too. Starting from nothing would be a different budget.&lt;/p&gt;
&lt;p&gt;For comparison, the TBC-equipped decks I found on eBay while writing this were roughly $700–$1,600, depending on model and claimed condition. A &lt;a href="https://www.ebay.com/itm/236911127606"&gt;JVC HR-S9911U listing marked sold showed $900&lt;/a&gt;, a &lt;a href="https://www.ebay.com/itm/327171572501"&gt;seller-described rebuilt HR-S7600U was listed at about $1,150&lt;/a&gt;, and a &lt;a href="https://www.ebay.com/itm/396906605292"&gt;Panasonic AG-1980 listing describing capacitor replacement showed about $1,600&lt;/a&gt;. Those last two are advertised prices, not verified sale amounts, and this is a small sample rather than a market average.&lt;/p&gt;
&lt;p&gt;My additional spending comes to around $464, helped by a free computer and a scratch SSD I already owned. Allowing for buying those two items brings this partial budget closer to $864, which overlaps the lower end of those TBC-deck examples. There is still a lot of assembly, troubleshooting, and existing equipment behind that number. The eBay prices are for the deck alone too, so neither side is a complete capture setup priced from scratch.&lt;/p&gt;
&lt;h2 id="still-growing"&gt;Still growing&lt;/h2&gt;
&lt;p&gt;This started as a way to avoid paying for a fancy VCR. It has become a small preservation workflow, with considerably more DIY than the price comparison alone suggests.&lt;/p&gt;
&lt;p&gt;I would not present it as a polished setup for somebody else to install. Parts work well, parts need babysitting, and the next tape can still expose another bad assumption.&lt;/p&gt;
&lt;p&gt;I am keeping my own pipeline repo and tape collection private, but you do not need my exact repo to try this. I think this post should give you a starting point for making your own: write down what your machines do, keep a journal, and build up scripts and checks as you go. Steal whichever tricks here are useful, adapt them to your setup, and add the ones you figure out yourself. That is pretty much how mine grew.&lt;/p&gt;
&lt;p&gt;But the payoff is that I can now put a tape in, start a new Codex thread in the repo, and give it an ingest goal.&lt;/p&gt;
&lt;p&gt;It reads the instructions and notes, checks that there is room for the capture, rewinds the tape through the IR controller, and watches the power meter to confirm the rewind has finished. It gets the capture running, sends Play, and records the whole tape, using the meter to detect end-of-tape. Then it can carry on with preserving the files on the NAS, decoding, reviewing, and writing up what it found. Capture everything first; sort out the contents afterward.&lt;/p&gt;
&lt;p&gt;There is a lot behind that little request, and it can still need my help when something goes wrong. But I no longer have to manually coordinate every command on every machine just to get through a tape. The scripts, checks, and long journal are what let a fresh thread pick up the job.&lt;/p&gt;
&lt;p&gt;That is the part I find amazing. This sat on my someday pile for about two years, and now I can toss in a tape and ask Codex to get started. I did not set out to build all of this. I wanted to see what was on the tapes.&lt;/p&gt;</description></item><item><title>Weftan, a backup plan that became a Rust rabbit hole</title><link>https://mindflakes.com/posts/2026/09/07/weftan-rust-rabbit-hole/</link><pubDate>Mon, 07 Sep 2026 10:34:51 -0700</pubDate><guid>https://mindflakes.com/posts/2026/09/07/weftan-rust-rabbit-hole/</guid><description>&lt;p&gt;Weftan started as a fun backup plan. If TALA died hard, could I still have the layout engine around in some usable form?&lt;/p&gt;
&lt;p&gt;TALA arranges diagrams for D2. Recovering its behavior and rebuilding it in Rust seemed like an interesting thing to try. That eventually became &lt;a href="https://github.com/nelsonjchen/weftan"&gt;Weftan&lt;/a&gt;, and then a larger work-in-progress project called &lt;code&gt;d2-rs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Weftan was faster than the Go version of TALA when that was an external proprietary binary. The slightly annoying part is that TALA built into D2 was faster still. So naturally I started thinking about rewriting more of the surrounding thing too.&lt;/p&gt;
&lt;h2 id="recovering-the-behavior"&gt;Recovering the behavior&lt;/h2&gt;
&lt;p&gt;The early work used TALA release artifacts: symbols, debugging information, disassembly, and comparisons with the executable. Reconstructed Go code informed the Rust implementation. This was recovery work, and the provenance matters; I did not independently invent the same layout engine by looking at a few pictures.&lt;/p&gt;
&lt;p&gt;Then TALA&amp;rsquo;s source became available in D2 v0.9.0. That changed the situation quite a bit. There was now an upstream source implementation to follow and compare against, instead of relying entirely on recovered behavior.&lt;/p&gt;
&lt;p&gt;There is a lot hidden inside “put the boxes somewhere sensible.” Containers, labels, edge routes, and the order in which equally reasonable choices get considered can all affect the final result. Two diagrams can look basically the same while disagreeing on a lot of coordinates.&lt;/p&gt;
&lt;p&gt;So the useful comparison was to give both implementations the same measured graph and seed, then compare the actual geometry. When something differed, there was a smaller question to investigate than “why is this picture weird?”&lt;/p&gt;
&lt;h2 id="matching-a-corpus"&gt;Matching a corpus&lt;/h2&gt;
&lt;p&gt;The recorded Weftan baseline reached exact matches on all 120 successful single-seed comparisons in the checked-in corpus. That included 2,964 boxes and 974 routes, along with their corresponding labels and node icons. One large-grid case hit the work limit in both engines and was recorded separately.&lt;/p&gt;
&lt;p&gt;I was happy with that. I also want to keep the size of the claim attached to it: those are the cases in that corpus, with that reference version and comparison setup. It does not mean every possible D2 input will behave identically.&lt;/p&gt;
&lt;p&gt;Being able to reproduce a mismatch is useful even before it is fixed. It gives the next round of work an actual target. Otherwise it is very easy to keep improving the appearance of one example without knowing whether another example got worse.&lt;/p&gt;
&lt;p&gt;The comparison machinery ended up being a useful result of the experiment in its own right.&lt;/p&gt;
&lt;h2 id="the-plugin-tax"&gt;The plugin tax&lt;/h2&gt;
&lt;p&gt;Weftan plugs into D2 as an external layout process. D2 has to pass a graph across that boundary and get the result back. Built-in TALA does not pay that same process and protocol cost.&lt;/p&gt;
&lt;p&gt;There are two different comparisons here. Against the old external proprietary Go TALA binary, Weftan was faster in my testing. Against TALA built into D2, the end-to-end measurements went the other way. The baseline changed, and that distinction matters.&lt;/p&gt;
&lt;p&gt;Plugin overhead is a plausible explanation for at least some of the gap with built-in TALA, but I have not isolated it well enough to say it accounts for the entire difference. “Rust” on the box does not make the complete workflow faster by itself.&lt;/p&gt;
&lt;p&gt;That leaves Weftan in a perfectly reasonable place as a fun experiment. The backup-plan motivation got me started, and I got a recovered engine, comparisons, and a much closer look at the behavior out of it.&lt;/p&gt;
&lt;p&gt;It also left me curious about removing that boundary altogether.&lt;/p&gt;
&lt;h2 id="following-upstream-in-rust"&gt;Following upstream in Rust&lt;/h2&gt;
&lt;p&gt;The next rabbit hole is &lt;code&gt;d2-rs&lt;/code&gt;: a pure-Rust implementation targeting native execution and WebAssembly, with the layout engines running in the same process. The TALA work from Weftan has moved into that effort.&lt;/p&gt;
&lt;p&gt;I am interested in following upstream behavior and exploring parallel execution where it makes sense. Having the surrounding pipeline in Rust should also make it easier to investigate costs without the external plugin boundary sitting in the middle of every layout.&lt;/p&gt;
&lt;p&gt;This is still under construction. There are working source-processing and rendering paths, but plenty of compatibility work remains. A working SVG example is a nice milestone; it does not establish that the renderer, all the diagram features, or the other engines are interchangeable with D2.&lt;/p&gt;
&lt;p&gt;I also do not have a speedup to announce for the rewrite. That is something to measure as it develops.&lt;/p&gt;
&lt;p&gt;For now, &lt;a href="https://github.com/nelsonjchen/weftan"&gt;Weftan is public&lt;/a&gt;. It was a fun backup plan, it taught me things, and it apparently gave me another project to work on.&lt;/p&gt;</description></item><item><title>Keeping comma hardware repair answers somewhere findable</title><link>https://mindflakes.com/posts/2026/09/06/comma-repair-answers/</link><pubDate>Sun, 06 Sep 2026 10:34:51 -0700</pubDate><guid>https://mindflakes.com/posts/2026/09/06/comma-repair-answers/</guid><description>&lt;p&gt;One of the things I keep working on is the &lt;a href="https://github.com/ophwug/docs"&gt;unofficial comma hardware documentation&lt;/a&gt;. Somebody has a broken device, people help investigate, and eventually there is an answer somewhere in Discord.&lt;/p&gt;
&lt;p&gt;Then somebody else has the same problem.&lt;/p&gt;
&lt;p&gt;Discord is an annoying place for the answer to live. Finding the right conversation is work, the useful detail might be several messages away from the original question, and a person searching the web may never find it at all.&lt;/p&gt;
&lt;p&gt;So I keep trying to get those cases into a public document that search engines and AI assistants can read. Especially AI assistants, because people already ask them how to fix things. I want the actual repair knowledge to be available when somebody asks about their broken comma.&lt;/p&gt;
&lt;p&gt;That is a big part of why I do it this way. An answer buried in Discord is not much help if the tool somebody is asking cannot get to it. A public document gives it something to find, read, and point back to. It does not guarantee a correct answer, but at least the specific cases and their limitations are there to work from.&lt;/p&gt;
&lt;h2 id="the-useful-part-is-the-follow-up"&gt;The useful part is the follow-up&lt;/h2&gt;
&lt;p&gt;The document is organized around cases: what the person sees, what might be wrong, and what has helped other people with that particular problem. It has grown quite a bit. I do not expect anybody to sit down and read the whole thing in order.&lt;/p&gt;
&lt;p&gt;Recent additions and updates include more fuse repair reports, a fastboot-only recovery case, and another cable gotcha. These are the small details that can save somebody a lot of time if they are findable at the right moment.&lt;/p&gt;
&lt;p&gt;Fuse cases are a good example of why the reports matter. A suspected fault and a confirmed repair are different pieces of information. Somebody coming back with “that fixed it” makes the original discussion much more useful. An unsuccessful repair is worth recording too, especially if the symptoms seemed to fit.&lt;/p&gt;
&lt;p&gt;The repair work belongs to the community members doing it and reporting their results. My contribution here is collecting the cases, keeping the references, and updating the guidance as more information comes in.&lt;/p&gt;
&lt;h2 id="similar-looking-problems-need-different-answers"&gt;Similar-looking problems need different answers&lt;/h2&gt;
&lt;p&gt;The fastboot-only case covers a specific recovery situation on the comma three and comma 3X. A device can still show up in fastboot while being unable to enter the EDL mode used by the current flasher. Seeing a USB device on the computer does not establish that the flasher can talk to it in the mode it needs.&lt;/p&gt;
&lt;p&gt;That distinction needs to stay attached to the recovery notes. Otherwise a workaround for a particular failure starts looking like a general recommendation for everybody.&lt;/p&gt;
&lt;p&gt;The comma four cable case is another good one. The included OBD-C cable does its intended installation job, but lacks the USB data lines needed for computer flashing. That is a pretty frustrating detail to discover while you are already trying to recover something.&lt;/p&gt;
&lt;p&gt;Neither case needs a second set of instructions here that will drift out of date. The maintained document has the details and references.&lt;/p&gt;
&lt;h2 id="constant-little-updates"&gt;Constant little updates&lt;/h2&gt;
&lt;p&gt;This work is mostly incremental. Another symptom, another result, a clearer explanation of which device a case applies to. Sometimes the useful update is just making it harder to confuse two similar situations.&lt;/p&gt;
&lt;p&gt;I like having a place where those answers can accumulate. Whether somebody finds it through a search engine or asks an AI to help, I want them to end up at the relevant case, with the references and pictures available to check. The answer should have somewhere to point back to.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/ophwug/docs"&gt;docs are here&lt;/a&gt;. If you try a remedy from them, please report back whether it worked. That follow-up is part of what makes the next version of the document better.&lt;/p&gt;
&lt;p&gt;I might need these docs myself soon. My own comma 3X did not boot when I tried powering it over USB. I have not figured out why yet, so I may get to find out how useful my own documentation is from the other side.&lt;/p&gt;</description></item><item><title>Could the Crazy Taxi Arcade TAS use one fewer passenger?</title><link>https://mindflakes.com/posts/2026/08/10/crazy-taxi-one-fewer-passenger/</link><pubDate>Mon, 10 Aug 2026 10:34:51 -0700</pubDate><guid>https://mindflakes.com/posts/2026/08/10/crazy-taxi-one-fewer-passenger/</guid><description>&lt;p&gt;I got curious about whether a Crazy Taxi tool-assisted speedrun could be slightly faster with one fewer passenger.&lt;/p&gt;
&lt;p&gt;I do not really have TAS skills. This was a “could that work?” rabbit hole that led to looking at passenger tables, destination selection, and the game clock. I have not made a faster run.&lt;/p&gt;
&lt;p&gt;The tips are where the real TAS money is. Chaining Crazy Throughs and drifts can pile up money while carrying a single passenger. &lt;a href="https://tasvideos.org/9374S"&gt;SJ&amp;rsquo;s run&lt;/a&gt; aims for the Crazy License, reaches $20,000 with four passengers, and uses the long Police Station trip for a huge combo chain. The run ends when the clock reaches zero. So the idea is to earn enough money from tips with fewer fares, then have less clock left to burn off.&lt;/p&gt;
&lt;p&gt;The specific idea starts after the first drop-off at &lt;strong&gt;Cable Car Stop TOP&lt;/strong&gt;, the plaza at the top of the hill. In &lt;a href="https://tasvideos.org/9374S"&gt;SJ&amp;rsquo;s four-passenger route&lt;/a&gt;, the next pickup there goes to &lt;strong&gt;West Side Beach&lt;/strong&gt;, followed by a passenger going to &lt;strong&gt;Popcorn Mania&lt;/strong&gt;, and then one going to the &lt;strong&gt;Police Station&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;My idea was to leave the plaza and grab the side-street passenger going to &lt;strong&gt;FILA&lt;/strong&gt; instead of taking the nearby passenger to the beach. FILA puts me near the next Police Station pickup, so I could go from that drop-off to the final passenger without doing the beach-to-Popcorn Mania fare in between. FILA appears as “Shoe Rack” in the Android data I used for the investigation.&lt;/p&gt;
&lt;p&gt;That is why three passengers seemed promising: skip a whole fare and its extra pickup and drop-off, while keeping the long Police Station trip for earning tips. The side-street detour and the changed route could affect traffic and combo opportunities, so I would still need to show that three passengers can earn enough. Fewer stops only help if the money works out too.&lt;/p&gt;
&lt;h2 id="the-clock-is-part-of-the-problem"&gt;The clock is part of the problem&lt;/h2&gt;
&lt;p&gt;Picking up a passenger adds time to the Arcade clock. A successful delivery can add more. So changing the passenger route also changes how much time the game hands back to you.&lt;/p&gt;
&lt;p&gt;The symbol-rich Android release was useful for investigating this. Its code shows a distance-based time allowance at pickup, with a multiplier that changes as play goes on, plus a delivery-rank bonus at drop-off.&lt;/p&gt;
&lt;p&gt;In the calculation in my notes, a proposed three-passenger route adds about 12.56 fewer seconds than the four-passenger reference route. That comparison assumes the early-game multiplier and the best delivery judgement for each passenger.&lt;/p&gt;
&lt;p&gt;That is less added game time, which made the idea interesting enough to investigate further. It is not a measured 12.56-second improvement in a TAS. Driving, passenger animations, and when the clock actually advances still have to be accounted for.&lt;/p&gt;
&lt;h2 id="which-destination-do-you-get"&gt;Which destination do you get?&lt;/h2&gt;
&lt;p&gt;The Android investigation also showed that passenger destination selection follows a counter. It cycles through three destination slots, with each slot lasting 600 passenger-manager updates, roughly ten seconds at 60 updates per second.&lt;/p&gt;
&lt;p&gt;The choice is latched when the passenger event activates. Waiting until boarding finishes to reason about it would put the timing at the wrong point.&lt;/p&gt;
&lt;p&gt;That makes route planning more interesting. Reaching a passenger at a different time can select a different destination, which changes the trip and the clock allowance. Picking three convenient dots on a map is not enough.&lt;/p&gt;
&lt;p&gt;To look at this spatially, I forked the &lt;a href="https://noclip.website/"&gt;noclip.website&lt;/a&gt; viewer and loaded the GameCube Arcade city map with the extracted pedestrian pickup locations overlaid on it. Credit to noclip.website for the viewer; my additions turned it into a research map for this passenger-routing rabbit hole. It is much easier to reason about a side-street pickup when I can actually see where it sits in the city.&lt;/p&gt;
&lt;p&gt;The extracted passenger records match the Android reference records, which is useful evidence for understanding the data. It does not prove that every timing behavior carries over to the GameCube executable.&lt;/p&gt;
&lt;h2 id="maybe-later"&gt;Maybe later&lt;/h2&gt;
&lt;p&gt;The next step would be to verify the relevant behavior in the GameCube version and try the route in an actual TAS workflow. There is still a gap between a promising calculation and inputs that produce a faster run.&lt;/p&gt;
&lt;p&gt;For now this is a curiosity with some code and notes behind it. I need to sit down with it when things are a bit less crazy and I have time to learn the TAS side properly. I might revisit it then. One fewer passenger sounded like a small question. Apparently it came with homework.&lt;/p&gt;</description></item><item><title>Agent-assisted bilingual subtitles for group watches</title><link>https://mindflakes.com/posts/2026/06/16/agent-assisted-bilingual-subtitles/</link><pubDate>Tue, 16 Jun 2026 15:35:00 -0700</pubDate><guid>https://mindflakes.com/posts/2026/06/16/agent-assisted-bilingual-subtitles/</guid><description>&lt;p&gt;I made some agent-assisted subtitle tools for group watches.&lt;/p&gt;
&lt;p&gt;The short version is that I wanted one subtitle track with English and Chinese at the same time. Some people read one language faster, some read the other faster, and switching subtitle tracks is not a group activity.&lt;/p&gt;
&lt;p&gt;No repo link for this one. The subtitle files are not mine to redistribute, and the interesting part is the workflow anyway.&lt;/p&gt;
&lt;h2 id="the-group-watch-problem"&gt;The group watch problem&lt;/h2&gt;
&lt;p&gt;The simple version sounds extremely simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take an English subtitle file.&lt;/li&gt;
&lt;li&gt;Take a Chinese subtitle file.&lt;/li&gt;
&lt;li&gt;Put both lines into one subtitle cue.&lt;/li&gt;
&lt;li&gt;Watch the thing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Unfortunately, subtitle files are not just text. They are text, cue numbers, timestamps, line breaks, release-specific offsets, missing cues, extra signage cues, and sometimes a drift that slowly changes across the whole movie.&lt;/p&gt;
&lt;p&gt;The first version I tried was basically a mux. If the English and Chinese files had matching cue numbers, use the English timestamp and stack the text together.&lt;/p&gt;
&lt;p&gt;That worked well enough for one case. It was also obviously fragile.&lt;/p&gt;
&lt;h2 id="the-annoying-version"&gt;The annoying version&lt;/h2&gt;
&lt;p&gt;The more interesting case had two subtitle tracks that did not line up with one fixed offset.&lt;/p&gt;
&lt;p&gt;If it were just &amp;ldquo;add 12 seconds to every Chinese cue,&amp;rdquo; this would not be worth writing about. But the offset changed over time. It started around one value and gradually moved toward another.&lt;/p&gt;
&lt;p&gt;So the script got more specific:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;parse both subtitle files&lt;/li&gt;
&lt;li&gt;treat the English track as the timing source&lt;/li&gt;
&lt;li&gt;search for the best Chinese offset in local windows&lt;/li&gt;
&lt;li&gt;smooth those offsets&lt;/li&gt;
&lt;li&gt;remap Chinese cues onto the English timeline&lt;/li&gt;
&lt;li&gt;merge Chinese text into the nearest overlapping English cue&lt;/li&gt;
&lt;li&gt;preserve Chinese-only cues as their own events&lt;/li&gt;
&lt;li&gt;write an alignment report for things that looked suspicious&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The report was the important part. I did not need the script to pretend everything was perfect. I needed it to tell me where to look.&lt;/p&gt;
&lt;p&gt;The useful output was not just the merged &lt;code&gt;.srt&lt;/code&gt;. It was also a short review list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;low-confidence merges&lt;/li&gt;
&lt;li&gt;Chinese-only cues&lt;/li&gt;
&lt;li&gt;English cues with no matching Chinese text&lt;/li&gt;
&lt;li&gt;local offset anchors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That made the workflow tolerable. Generate, skim the report, spot-check the weird timestamps, adjust the heuristics if needed, and regenerate.&lt;/p&gt;
&lt;h2 id="where-agents-helped"&gt;Where agents helped&lt;/h2&gt;
&lt;p&gt;This is exactly the kind of small, specific tool where agents are useful.&lt;/p&gt;
&lt;p&gt;I did not need a general subtitle product. I did not need a GUI. I did not need a library that handles every movie ever made. I needed one script for one group watch, and then a slightly better script for a different subtitle mess.&lt;/p&gt;
&lt;p&gt;An agent can help make that cheap enough to bother with.&lt;/p&gt;
&lt;p&gt;The first pass can be dumb:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;same cue number -&amp;gt; same timestamp -&amp;gt; Chinese line + English line
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then the next pass can be less dumb:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;local timing windows -&amp;gt; estimated offset -&amp;gt; remapped cues -&amp;gt; overlap match -&amp;gt; review report
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The agent also made it easier to keep the code disposable. I did not have to lovingly design a subtitle framework. I could just ask for the thing I needed, run it, look at the bad spots, and ask for a better report.&lt;/p&gt;
&lt;p&gt;If you want to do the same thing, point &lt;a href="https://developers.openai.com/codex/"&gt;Codex&lt;/a&gt;, &lt;a href="https://code.claude.com/docs/en/overview"&gt;Claude Code&lt;/a&gt;, or &lt;a href="https://antigravity.google/"&gt;Google Antigravity&lt;/a&gt; at this post and your own subtitle files. The post is basically the spec: one timing source, one translated track, local offset search, merged output, and a review report. That is enough for an agent to build the throwaway script for your specific case.&lt;/p&gt;
&lt;p&gt;Anyway, it worked for the group watch. We had a very good time, nobody was lost or left behind!&lt;/p&gt;</description></item><item><title>codex-status-json</title><link>https://mindflakes.com/posts/2026/06/16/codex-status-json/</link><pubDate>Tue, 16 Jun 2026 15:03:00 -0700</pubDate><guid>https://mindflakes.com/posts/2026/06/16/codex-status-json/</guid><description>&lt;p&gt;I made a small &lt;a href="https://github.com/nelsonjchen/codex-status-command"&gt;Rust CLI that prints Codex status as JSON&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The short version is that I wanted Codex conversations to know my account, model, and quota state without opening the interactive TUI and reading &lt;code&gt;/status&lt;/code&gt; like a person.&lt;/p&gt;
&lt;p&gt;This is mostly useful with Codex goals. I am on the &lt;a href="https://developers.openai.com/codex/pricing"&gt;Pro 20x plan&lt;/a&gt;, which is the $200/month tier, so sometimes I really do have quota to burn. If I can read the remaining quota first, I can make a goal with an actual target instead of guessing how ambitious I should be.&lt;/p&gt;
&lt;h2 id="the-missing-command"&gt;The missing command&lt;/h2&gt;
&lt;p&gt;Codex has &lt;code&gt;/status&lt;/code&gt; in the interactive CLI. It shows useful stuff: account, model, context, writable roots, and rate limits.&lt;/p&gt;
&lt;p&gt;That is fine when I am sitting in the TUI. It is less fine when I want another Codex conversation to ask the same question.&lt;/p&gt;
&lt;p&gt;There is an upstream issue asking for this exact kind of thing: &lt;a href="https://github.com/openai/codex/issues/10233"&gt;a non-interactive JSON/headless replacement for &lt;code&gt;/status&lt;/code&gt;&lt;/a&gt;. The issue is still open as I write this. There is also &lt;code&gt;codex doctor --json&lt;/code&gt;, which is useful, but that is a diagnostic report. It is not the same as &amp;ldquo;tell me my account/model/quota status right now.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So I made &lt;code&gt;codex-status-json&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="the-first-version-was-worse"&gt;The first version was worse&lt;/h2&gt;
&lt;p&gt;The first version did what you would expect from a tool born out of impatience: it opened Codex in a PTY, sent &lt;code&gt;/status&lt;/code&gt;, waited for the panel to settle, captured the terminal output, stripped ANSI escapes, and parsed the text.&lt;/p&gt;
&lt;p&gt;It worked. It was also clearly a bad long-term place to live.&lt;/p&gt;
&lt;p&gt;Terminal scraping is annoying because every little UI change can break you. The parser has to care about box drawing, timing, update notices, trust prompts, terminal width, and whatever else the TUI happens to render that day.&lt;/p&gt;
&lt;p&gt;Still, it was useful. It gave me JSON like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;schema_version&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;account&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;user@example.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;plan&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Pro&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;raw&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;user@example.com (Pro)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;gpt-5.5&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;details&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;reasoning high&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;raw&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;gpt-5.5 (reasoning high)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;buckets&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;limits&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;5h&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;remaining_percent&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;99&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;resets&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;14:44&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;reset_at_unix&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1780350290&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;weekly&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;remaining_percent&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;70&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;resets&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;08:23 on 7 Jun&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;reset_at_unix&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1780845815&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That was enough to unblock the thing I wanted.&lt;/p&gt;
&lt;h2 id="the-less-cursed-version"&gt;The less cursed version&lt;/h2&gt;
&lt;p&gt;A few days later, I switched the default backend to Codex&amp;rsquo;s &lt;a href="https://developers.openai.com/codex/app-server"&gt;app-server&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That is a much better source than scraping the TUI. The tool starts:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;codex app-server --listen stdio://
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then it sends JSON-RPC calls for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;account/read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;account/rateLimits/read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config/read&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That gives it the same kind of account, model, and quota information without pretending a terminal status panel is an API.&lt;/p&gt;
&lt;p&gt;Important caveat: Codex app-server is documented, but it is also described as experimental and may change. So this is still not a supported &lt;code&gt;codex status --json&lt;/code&gt;. It is just a more sensible workaround than screen scraping.&lt;/p&gt;
&lt;p&gt;The PTY backend is still there as a fallback:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;codex-status-json --backend pty
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But the default is now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;codex-status-json --backend app-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="this-is-a-bridge"&gt;This is a bridge&lt;/h2&gt;
&lt;p&gt;I do not expect this to be the forever answer.&lt;/p&gt;
&lt;p&gt;Codex&amp;rsquo;s usage and billing surface is still moving. OpenAI has already introduced things like &lt;a href="https://developers.openai.com/codex/pricing"&gt;banked rate-limit resets&lt;/a&gt;, so the status shape is not just &amp;ldquo;two counters and call it done.&amp;rdquo; There are plans, windows, reset times, secondary buckets, and whatever else comes next.&lt;/p&gt;
&lt;p&gt;So I can see why OpenAI might not want to commit to a stable status JSON contract yet. That is fine. I still wanted something local that worked now.&lt;/p&gt;
&lt;p&gt;This is the same kind of bridge as a lot of these small tools: use the available pieces, make the annoying gap tolerable, and be happy if the official thing eventually replaces it.&lt;/p&gt;
&lt;h2 id="the-repo"&gt;The repo&lt;/h2&gt;
&lt;p&gt;The project is here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/codex-status-command"&gt;https://github.com/nelsonjchen/codex-status-command&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Install it locally with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cargo install --path .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The README has the options and limitations. The tests include a captured &lt;code&gt;/status&lt;/code&gt; fixture so the text parser can at least fail loudly when Codex changes its display.&lt;/p&gt;
&lt;p&gt;I would still rather have an official &lt;code&gt;codex status --json&lt;/code&gt;. Until then, this is small, local, and useful enough.&lt;/p&gt;</description></item><item><title>Moving Gmail Send mail as to Cloudflare SMTP</title><link>https://mindflakes.com/posts/2026/06/16/gmail-send-mail-as-cloudflare-smtp/</link><pubDate>Tue, 16 Jun 2026 14:13:00 -0700</pubDate><guid>https://mindflakes.com/posts/2026/06/16/gmail-send-mail-as-cloudflare-smtp/</guid><description>&lt;p&gt;I just finished moving my Gmail &amp;ldquo;Send mail as&amp;rdquo; setup completely to &lt;a href="https://developers.cloudflare.com/email-service/api/send-emails/smtp/"&gt;Cloudflare Email Service&amp;rsquo;s native SMTP submission&lt;/a&gt;. I can send out emails under my mindflakes.com domain.&lt;/p&gt;
&lt;p&gt;The short version is that I had built a small &lt;a href="https://github.com/nelsonjchen/smtp-2-cf-email-service"&gt;Rust SMTP-to-Cloudflare REST API relay&lt;/a&gt; in April. Today I noticed Cloudflare had shipped native SMTP submission.&lt;/p&gt;
&lt;p&gt;Part of the appeal here is minimizing vendors. Cloudflare already hosts the domain, handles incoming mail through &lt;a href="https://blog.cloudflare.com/introducing-email-routing/"&gt;Email Routing&lt;/a&gt;, and now can handle outgoing mail through &lt;a href="https://developers.cloudflare.com/email-service/get-started/send-emails/"&gt;Email Service&lt;/a&gt; too. For a personal domain, that can be cheap or free, and it is a nice alternative to paying for &lt;a href="https://workspace.google.com/pricing.html"&gt;Google Workspace&lt;/a&gt; when a personal Gmail account is otherwise fine.&lt;/p&gt;
&lt;h2 id="the-april-hack"&gt;The April hack&lt;/h2&gt;
&lt;p&gt;In April, I saw Cloudflare&amp;rsquo;s &lt;a href="https://blog.cloudflare.com/email-for-agents/"&gt;public beta announcement&lt;/a&gt; for Email Service and got excited about using Cloudflare for both inbound and outbound personal-domain mail.&lt;/p&gt;
&lt;p&gt;Then I got sad. I wanted Gmail to send mail for one of my domains through &lt;a href="https://developers.cloudflare.com/email-service/get-started/send-emails/"&gt;Cloudflare Email Service&lt;/a&gt;, but I did not see a native SMTP submission path I could just point Gmail at.&lt;/p&gt;
&lt;p&gt;Gmail&amp;rsquo;s &lt;a href="https://support.google.com/mail/answer/22370"&gt;Send mail as&lt;/a&gt; feature wants a normal authenticated SMTP server. You give it a hostname, a port, a username, and a password. Gmail talks SMTP. It does not want to be handed a REST API endpoint.&lt;/p&gt;
&lt;p&gt;At the time, the useful Cloudflare sending shape I had in front of me was the raw MIME REST API. Cloudflare had teased SMTP in the earlier private-beta era, but the public thing I could actually use was not SMTP.&lt;/p&gt;
&lt;p&gt;So I made &lt;code&gt;smtp2cf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It was intentionally narrow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;accept authenticated SMTP from Gmail&lt;/li&gt;
&lt;li&gt;write the raw MIME message to disk before returning &lt;code&gt;250 OK&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;forward that raw MIME to Cloudflare&amp;rsquo;s &lt;a href="https://developers.cloudflare.com/api/resources/email_sending/methods/send_raw"&gt;&lt;code&gt;send_raw&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;retry transient failures&lt;/li&gt;
&lt;li&gt;move permanent failures to a dead-letter directory&lt;/li&gt;
&lt;li&gt;stay small enough to run on a cheap little VPS with an IPv4 address, because that is what I already had&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mostly, I wanted to avoid operating a real mail server for a personal domain.&lt;/p&gt;
&lt;h2 id="the-temporary-bridge"&gt;The temporary bridge&lt;/h2&gt;
&lt;p&gt;This is also one of those places where &lt;a href="https://openai.com/codex/"&gt;Codex&lt;/a&gt; changed what felt reasonable to do.&lt;/p&gt;
&lt;p&gt;Before, I probably would have waited for Cloudflare&amp;rsquo;s rollout or grudgingly set up a heavier mail stack. But as far as I could tell, there was no useful public date for SMTP support. So I was impatient and made the bridge I wanted.&lt;/p&gt;
&lt;p&gt;My first &lt;code&gt;smtp2cf&lt;/code&gt; commit was April 18, 2026. I had the repo cleaned up and documented by April 20. It was not a forever project. It was a practical way to bridge the gap until Cloudflare shipped the official SMTP path.&lt;/p&gt;
&lt;p&gt;Cloudflare&amp;rsquo;s docs now have a changelog entry dated June 8, 2026: &lt;a href="https://developers.cloudflare.com/changelog/post/2026-06-08-smtp-submission/"&gt;Authenticated SMTP submission now available in beta&lt;/a&gt;. That means the relay did its job and can leave the live path.&lt;/p&gt;
&lt;h2 id="the-new-setup"&gt;The new setup&lt;/h2&gt;
&lt;p&gt;Cloudflare&amp;rsquo;s &lt;a href="https://developers.cloudflare.com/email-service/api/send-emails/smtp/"&gt;SMTP docs&lt;/a&gt; and Gmail&amp;rsquo;s &lt;a href="https://support.google.com/mail/answer/22370"&gt;Send mail as docs&lt;/a&gt; line up pretty directly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;host: &lt;code&gt;smtp.mx.cloudflare.net&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;port: &lt;code&gt;465&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;security: implicit TLS&lt;/li&gt;
&lt;li&gt;username: &lt;code&gt;api_token&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;password: a Cloudflare API token with Email Sending access&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I generated a token, changed the Gmail Send mail as SMTP settings, sent a test message, and watched it go through Cloudflare without my relay in the middle.&lt;/p&gt;
&lt;p&gt;That is the best kind of infrastructure migration: delete a moving part and then immediately wonder why you ever had to own it.&lt;/p&gt;
&lt;h2 id="the-now-historical-rust-thing"&gt;The now historical Rust thing&lt;/h2&gt;
&lt;p&gt;The repo is still here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/smtp-2-cf-email-service"&gt;https://github.com/nelsonjchen/smtp-2-cf-email-service&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is now mostly historical for me.&lt;/p&gt;
&lt;p&gt;I still like a few parts of it. The durable spool is the right instinct. Returning &lt;code&gt;250 OK&lt;/code&gt; before the message is safely written somewhere is how you earn a future debugging session. The Gmail header cleanup is gross but also a useful reminder that &amp;ldquo;raw MIME&amp;rdquo; does not always mean &amp;ldquo;whatever one mail system happened to emit is acceptable to another one.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="my-past-email-setups"&gt;My past email setups&lt;/h2&gt;
&lt;p&gt;This domain has had a few outgoing mail setups over the years.&lt;/p&gt;
&lt;p&gt;Way back, I used &lt;a href="https://www.dreamhost.com/hosting/email/"&gt;DreamHost&lt;/a&gt; for Gmail&amp;rsquo;s &lt;a href="https://support.google.com/mail/answer/22370"&gt;Send mail as&lt;/a&gt; SMTP server. That mostly worked, but it was still another provider in the path.&lt;/p&gt;
&lt;p&gt;Later, I used &lt;a href="https://www.zoho.com/mail/"&gt;Zoho Mail&lt;/a&gt; as a sort of middleman mailbox. Gmail would send as the domain through Zoho, and Zoho would receive mail and forward it onward. I was not happy with either side of that. Incoming forwarding would sometimes fail to take, delivery rates sucked on Zoho, sending email out through Zoho SMTP was a shitshow, and traffic from Zoho to Gmail was a shitshow too.&lt;/p&gt;
&lt;p&gt;The most annoying symptom was the delivery report email. I would get an email with a ZIP file of delivery-rate data, and the vibe was never &amp;ldquo;everything is fine.&amp;rdquo; It was more like &amp;ldquo;congratulations, your mail technically attempted to exist.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Then came &lt;code&gt;smtp2cf&lt;/code&gt;, the Rust relay in this post. That got me away from Zoho and let Gmail hand mail to my own tiny SMTP bridge, which then sent through Cloudflare&amp;rsquo;s raw MIME API. It was better because it was mine and inspectable, but it was still a moving part I had to run.&lt;/p&gt;
&lt;p&gt;This Cloudflare SMTP setup is the first version that feels like the shape I wanted all along: Gmail UI, personal domain, Cloudflare for the domain and mail plumbing, and no mailbox vendor sitting in the middle.&lt;/p&gt;
&lt;p&gt;Anyway, that is the trip report. I am happy this one is boring now.&lt;/p&gt;</description></item><item><title>Plainize Clip</title><link>https://mindflakes.com/posts/2026/06/14/plainize-clip/</link><pubDate>Sun, 14 Jun 2026 23:05:00 -0700</pubDate><guid>https://mindflakes.com/posts/2026/06/14/plainize-clip/</guid><description>&lt;p&gt;I made &lt;a href="https://github.com/nelsonjchen/plainize-clip"&gt;Plainize Clip&lt;/a&gt;, a tiny macOS app that cleans the current clipboard and quits.&lt;/p&gt;
&lt;p&gt;The short version is that I still wanted the old &lt;a href="https://www.macworld.com/article/195102/plainclip2.html"&gt;Plain Clip&lt;/a&gt; shape: copy weird text, launch the app, paste something boring.&lt;/p&gt;
&lt;p&gt;Not a clipboard manager. Not a menu bar app. Not a background process sitting around in RAM forever. I launch it through Spotlight, it rewrites the pasteboard as plain text, and then it exits.&lt;/p&gt;
&lt;h2 id="the-annoying-clipboard-problem"&gt;The annoying clipboard problem&lt;/h2&gt;
&lt;p&gt;Copying text on macOS is often not just copying text.&lt;/p&gt;
&lt;p&gt;Sometimes it brings formatting. Sometimes it brings weird invisible characters. Sometimes it brings smart quotes, hard-wrapped lines, non-breaking spaces, tabs, and whatever else got dragged along from a PDF, web page, chat app, or ticketing system.&lt;/p&gt;
&lt;p&gt;Usually I do not want a whole clipboard workflow. I just want the pasteboard to stop being annoying.&lt;/p&gt;
&lt;p&gt;So Plainize Clip does the small version:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the general pasteboard.&lt;/li&gt;
&lt;li&gt;If there is text, clean it.&lt;/li&gt;
&lt;li&gt;Write back plain text only.&lt;/li&gt;
&lt;li&gt;Quit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That is the whole normal launch path.&lt;/p&gt;
&lt;h2 id="launch-clean-quit"&gt;Launch, clean, quit&lt;/h2&gt;
&lt;p&gt;The important behavior is that Plainize Clip is faceless.&lt;/p&gt;
&lt;p&gt;There is no Dock icon to manage after the cleanup. There is no persistent process. There is no history database. If the current pasteboard has text, it cleans it with the saved preferences and replaces the pasteboard contents with text.&lt;/p&gt;
&lt;p&gt;The prod was Apple&amp;rsquo;s &lt;a href="https://support.apple.com/en-us/102527"&gt;Rosetta&lt;/a&gt; clock. Apple says Rosetta will remain available as a general-purpose Intel app translation tool through macOS 27, and then becomes limited starting with macOS 28. So an Intel-only utility like the old Plain Clip had a stronger &amp;ldquo;you should probably stop depending on this forever&amp;rdquo; vibe.&lt;/p&gt;
&lt;p&gt;Choosing Swift/AppKit/SwiftUI was not a deep architectural statement. I just wanted to follow Apple&amp;rsquo;s current recommendations for a small Mac app.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/nelsonjchen/plainize-clip/releases"&gt;GitHub releases&lt;/a&gt; are unsigned, so macOS does the usual &amp;ldquo;unknown developer&amp;rdquo; dance. Apple&amp;rsquo;s support page documents the &lt;a href="https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unknown-developer-mh40616/mac"&gt;Open Anyway&lt;/a&gt; flow. This is annoying, but paying Apple for a Developer ID certificate for a small personal clipboard cleaner is also annoying.&lt;/p&gt;
&lt;h2 id="reverse-engineering-because-codex-made-it-cheap"&gt;Reverse-engineering because Codex made it cheap&lt;/h2&gt;
&lt;p&gt;I also ended up reverse-engineering the old app more than I normally would have.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developers.openai.com/codex/"&gt;Codex&lt;/a&gt; let me reverse-engineer stuff I otherwise would not have lifted a finger for. The local &lt;code&gt;plain-clip-re&lt;/code&gt; workspace became the notebook for it: static inventory, Ghidra headless exports, behavior notes, and then a recovered map of what the old app actually did.&lt;/p&gt;
&lt;p&gt;The useful bits were not mysterious. They were just tedious:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;normal launch cleans the pasteboard and quits&lt;/li&gt;
&lt;li&gt;holding Shift at launch opens preferences&lt;/li&gt;
&lt;li&gt;the command-line wrapper has switches for the same cleanup options&lt;/li&gt;
&lt;li&gt;the cleaner reads string content from the general pasteboard&lt;/li&gt;
&lt;li&gt;after cleaning, it replaces the pasteboard with string-only content&lt;/li&gt;
&lt;li&gt;the pipeline handles line endings, whitespace, hard wraps, blank lines, smart quotes, tabs, Unicode normalization, and ASCII conversion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That was enough to rebuild the behavior intentionally instead of just making &amp;ldquo;a clipboard cleaner, probably.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Plainize Clip is still a separate implementation. I did not reuse the old app&amp;rsquo;s code. The reverse-engineering work was mostly there to answer boring compatibility questions without relying on memory.&lt;/p&gt;
&lt;h2 id="the-cleanup-switches"&gt;The cleanup switches&lt;/h2&gt;
&lt;p&gt;Plainize Clip has preferences because text cleanup is personal enough to need switches.&lt;/p&gt;
&lt;p&gt;The defaults do the stuff I usually want: trim whitespace, remove empty-line noise, join hard-wrapped prose, replace tabs with spaces, collapse repeated spaces, remove invisible control characters, and turn smart quotes and typographic dashes into simpler punctuation.&lt;/p&gt;
&lt;p&gt;There are also options for Unicode normalization and ASCII conversion. The ASCII path tries to romanize non-Latin scripts before filtering, so it does not turn a clipboard full of CJK, Korean, Arabic, Hebrew, or Cyrillic text into an empty string. It is best-effort, not magic.&lt;/p&gt;
&lt;p&gt;Preferences open by holding Shift while launching, or from Terminal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;open -a &lt;span style="color:#e6db74"&gt;&amp;#34;/Applications/Plainize Clip.app&amp;#34;&lt;/span&gt; --args --preferences
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save the settings and it runs one cleanup pass immediately. Cancel does not.&lt;/p&gt;
&lt;h2 id="the-plain-clip-context"&gt;The Plain Clip context&lt;/h2&gt;
&lt;p&gt;Plainize Clip borrows the spirit of &lt;a href="https://www.macworld.com/article/195102/plainclip2.html"&gt;Plain Clip&lt;/a&gt; by &lt;a href="https://www.bluem.net/"&gt;Carsten Blüm&lt;/a&gt;: stay invisible, do one job, quit.&lt;/p&gt;
&lt;p&gt;It is independent from Plain Clip and is not affiliated with Blüm.&lt;/p&gt;
&lt;p&gt;The context is that I liked Plain Clip, but the old &lt;a href="https://web.archive.org/web/20250331212359/https://www.bluem.net/files/plain-clip-2.5.2.zip"&gt;archived 2.5.2 build&lt;/a&gt; I had found was Intel-only, the source release situation was unclear, and Blüm&amp;rsquo;s current site no longer listed those legacy Mac apps by default. His site says older macOS apps moved out of the normal public listing, and that interested users can contact him for specific versions.&lt;/p&gt;
&lt;p&gt;Blüm later clarified to me that Plain Clip is still in daily use by him and that he has resumed work on it, including an Apple Silicon build. As far as I know, that newer build is still an on-request thing rather than something posted publicly.&lt;/p&gt;
&lt;p&gt;My approach was different: Plainize Clip is open on GitHub from the start. It remains my separate modern build of the shape I wanted.&lt;/p&gt;
&lt;h2 id="the-repo"&gt;The repo&lt;/h2&gt;
&lt;p&gt;The project is here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/plainize-clip"&gt;https://github.com/nelsonjchen/plainize-clip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The releases are here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/plainize-clip/releases"&gt;https://github.com/nelsonjchen/plainize-clip/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The README has the download notes, unsigned-app warning, preferences, CLI wrapper, build command, and tests.&lt;/p&gt;</description></item><item><title>Blocking spammy caller names with VoIP.ms Call Hunting</title><link>https://mindflakes.com/posts/2026/05/20/voip-ms-caller-name-blocker/</link><pubDate>Wed, 20 May 2026 19:30:00 -0700</pubDate><guid>https://mindflakes.com/posts/2026/05/20/voip-ms-caller-name-blocker/</guid><description>&lt;p&gt;I made a small &lt;a href="https://github.com/nelsonjchen/cname_blocker_voip"&gt;Rust VoIP.ms CNAME blocker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The short version is that I wanted to block a caller by name instead of by phone number.&lt;/p&gt;
&lt;p&gt;This sounds like it should be an option in &lt;a href="https://voip.ms/"&gt;VoIP.ms&lt;/a&gt;. For all the features VoIP.ms has, this is not one of them. So I made one for cheap.&lt;/p&gt;
&lt;h2 id="the-missing-filter"&gt;The missing filter&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://voip.ms/"&gt;VoIP.ms&lt;/a&gt; has a pretty capable &lt;a href="https://wiki.voip.ms/article/CallerID_Filtering"&gt;CallerID Filtering&lt;/a&gt; feature. You can filter on specific caller ID numbers, phone book groups, anonymous callers, calls that do not match the North American number format, STIR/SHAKEN attestation level, and wildcard patterns like area-code-ish blocks.&lt;/p&gt;
&lt;p&gt;That is all useful, but these asshole spam callers and scammers do not politely stick to one number. They rotate numbers. Blocking one just means the next call comes from another one. What I wanted to block on was the caller ID name.&lt;/p&gt;
&lt;p&gt;VoIP.ms has CNAM support too, but as far as I can tell it is for displaying names, not filtering on them. Their blog post on &lt;a href="https://voip.ms/blog/how-to-stop-spam-calls-voip/"&gt;stopping spam calls&lt;/a&gt; talks about filtering numbers, area codes, and anonymous callers, while describing CNAM as a way to identify callers before picking up. The &lt;a href="https://wiki.voip.ms/article/Caller_ID"&gt;Caller ID wiki page&lt;/a&gt; describes incoming Caller ID name lookup as an optional per-DID setting that can display a caller name for US and Canadian callers.&lt;/p&gt;
&lt;p&gt;There is also a &lt;a href="https://community.voip.ms/t/blocking-based-on-caller-id/2883"&gt;VoIP.ms Community Forum thread&lt;/a&gt; where someone asked for exactly this: blocking repeated callers by CNAME/CNAM because the caller used a pile of different numbers. A VoIP.ms staff reply said the feature had been suggested before and that there was no ETA.&lt;/p&gt;
&lt;p&gt;So, yes, this is apparently not just me being weird. At least not uniquely weird.&lt;/p&gt;
&lt;h2 id="the-call-hunting-trick"&gt;The Call Hunting trick&lt;/h2&gt;
&lt;p&gt;VoIP.ms has another feature called &lt;a href="https://wiki.voip.ms/article/Call_Hunting"&gt;Call Hunting&lt;/a&gt;. A DID can try multiple destinations in order.&lt;/p&gt;
&lt;p&gt;So I made a tool that registers to VoIP.ms as a SIP endpoint and sits first in a &lt;a href="https://wiki.voip.ms/article/Call_Hunting"&gt;Call Hunting&lt;/a&gt; group. It checks the incoming caller name and then does one of two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the caller does not match the block list, it rejects the call with &lt;code&gt;486 Busy Here&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the caller matches the block list, it answers, plays SIT tones plus a disconnected-style message, and hangs up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;486 Busy Here&lt;/code&gt; part is the whole trick. For normal calls, VoIP.ms treats the first destination as busy and keeps going to the next &lt;a href="https://wiki.voip.ms/article/Call_Hunting"&gt;Call Hunting&lt;/a&gt; member, which is the real phone path. The blocker gets out of the way.&lt;/p&gt;
&lt;p&gt;For blocked calls, the blocker answers the call. Once it answers, the hunt stops. My actual phone never rings.&lt;/p&gt;
&lt;h2 id="why-twilio-lookup-got-involved"&gt;Why Twilio Lookup got involved&lt;/h2&gt;
&lt;p&gt;The first version matched the caller name that VoIP.ms sent in the SIP call.&lt;/p&gt;
&lt;p&gt;Then the field I wanted was not the field I got.&lt;/p&gt;
&lt;p&gt;For the PCH-style caller I cared about, the VoIP.ms-provided name showed up as a generic city/state-style value. That is not useful enough to block on. Meanwhile, T-Mobile Caller ID showed &lt;code&gt;PCH&lt;/code&gt; for the same number.&lt;/p&gt;
&lt;p&gt;Searching for &lt;a href="https://www.google.com/search?q=PCH+phone+number+scam"&gt;&lt;code&gt;PCH phone number scam&lt;/code&gt;&lt;/a&gt; gets you Publishers Clearing House, plus warnings about PCH impersonator scams. &lt;a href="https://scamreport.pch.com/"&gt;PCH has its own scam-report page&lt;/a&gt; saying scammers misuse the PCH name to pretend people won prizes and demand money or personal information. The &lt;a href="https://consumer.ftc.gov/consumer-alerts/2023/12/hang-pch-impersonators"&gt;FTC also has an alert&lt;/a&gt; specifically about PCH impersonators.&lt;/p&gt;
&lt;p&gt;I do not know where T-Mobile got that name, and I am not claiming Twilio uses the same source. My understanding is that CNAM is not one magic database anyway. Different carriers and lookup providers can have different data, different caches, and different levels of freshness. Twilio&amp;rsquo;s database may simply be more thorough for this case. VoIP.ms was not giving me the useful label, so I added optional Twilio Lookup.&lt;/p&gt;
&lt;p&gt;If configured, the blocker can ask &lt;a href="https://www.twilio.com/docs/lookup/v2-api"&gt;Twilio Lookup&lt;/a&gt; for the caller name and match against that instead. If Twilio does not return a useful name or the lookup fails, the call is allowed through.&lt;/p&gt;
&lt;p&gt;The matching itself is intentionally boring:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BLOCK_CNAME_PATTERNS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;pch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those patterns are case-insensitive and match on token boundaries, so &lt;code&gt;pch&lt;/code&gt; can match &lt;code&gt;PCH&lt;/code&gt; without accidentally matching some longer unrelated word. There is also a regex option for stranger cases.&lt;/p&gt;
&lt;h2 id="the-repo"&gt;The repo&lt;/h2&gt;
&lt;p&gt;The project is here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/cname_blocker_voip"&gt;https://github.com/nelsonjchen/cname_blocker_voip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is not a PBX. It registers outbound to VoIP.ms, checks caller name information, optionally asks Twilio Lookup, returns &lt;code&gt;486 Busy Here&lt;/code&gt; for normal calls, and only answers calls it wants to block.&lt;/p&gt;
&lt;p&gt;I expect this exact piece of software to be useful mostly for myself, but I do hope people use the code as a starting point. After I posted it in the VoIP.ms thread, &lt;a href="https://community.voip.ms/t/blocking-based-on-caller-id/2883/10"&gt;someone asked&lt;/a&gt; about using it for a simple &amp;ldquo;press 1 to continue&amp;rdquo; style flow instead of dropping the call. I do not need that, and the code is not designed for it, but it is not a ridiculous direction. With an agent like &lt;a href="https://openai.com/codex/"&gt;Codex&lt;/a&gt; pointed at the code and tests, that kind of custom call flow seems very reachable. In a very small and specific way, this program is already acting like a focused PBX.&lt;/p&gt;
&lt;p&gt;It is written in Rust, and I deployed it to my TrueNAS box. Resident memory is around 5 MB.&lt;/p&gt;
&lt;p&gt;Codex 5.4 Medium helped write a lot of it.&lt;/p&gt;
&lt;p&gt;The README has the actual setup steps.&lt;/p&gt;
&lt;p&gt;The tests do not need real VoIP.ms credentials either. There is a local fake registrar/integration test path that sends matching and non-matching calls, checks that blocked calls get answered, checks that normal calls get &lt;code&gt;486 Busy Here&lt;/code&gt;, and verifies RTP audio makes it through.&lt;/p&gt;
&lt;p&gt;Anyway, it works for the annoying thing I wanted to stop.&lt;/p&gt;
&lt;p&gt;I still think VoIP.ms should offer caller-name filtering directly. Until then, this does the job.&lt;/p&gt;</description></item><item><title>Unofficial MakerWorld PMM OpenSCAD Reference</title><link>https://mindflakes.com/posts/2026/05/04/makerworld-pmm-openscad-reference/</link><pubDate>Mon, 04 May 2026 08:15:00 -0700</pubDate><guid>https://mindflakes.com/posts/2026/05/04/makerworld-pmm-openscad-reference/</guid><description>&lt;p&gt;I made an &lt;a href="https://github.com/nelsonjchen/unofficial-makerworld-parametric-model-maker-openscad-docs"&gt;unofficial MakerWorld PMM OpenSCAD reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The short version is that I wanted &lt;a href="https://openai.com/codex/"&gt;Codex&lt;/a&gt; to help me make MakerWorld-customizable &lt;a href="https://openscad.org/"&gt;OpenSCAD&lt;/a&gt; models, and I did not want it guessing from old forum posts, UI behavior, and whatever I happened to remember at 1 AM.&lt;/p&gt;
&lt;p&gt;This started because I wanted to use agents on my own OpenSCAD projects without re-teaching the same MakerWorld-specific weirdness every time.&lt;/p&gt;
&lt;h2 id="the-documentation-problem"&gt;The documentation problem&lt;/h2&gt;
&lt;p&gt;MakerWorld&amp;rsquo;s &lt;a href="https://forum.bambulab.com/t/parametric-model-maker-v1-1-0-major-ui-refresh/203564"&gt;Parametric Model Maker&lt;/a&gt; is genuinely useful. &lt;a href="https://openscad.org/"&gt;OpenSCAD&lt;/a&gt; plus a web customizer is a great fit for 3D-printable stuff where the interesting part is &amp;ldquo;same idea, slightly different dimensions.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;If you have not run into it before, &lt;a href="https://openscad.org/"&gt;OpenSCAD&lt;/a&gt; is CAD by writing code. You write variables, modules, and geometry operations, and it turns that into a model. That makes it very good for customizable prints, and very annoying when a target platform has extra rules.&lt;/p&gt;
&lt;p&gt;Unfortunately, the OpenSCAD side of PMM is documented in a very internet way. Some of it is in release posts. Some of it is in support replies. Some of it is in the actual web app. Some of it is people trying things and reporting what happened.&lt;/p&gt;
&lt;p&gt;There is a Bambu forum thread literally titled &lt;a href="https://forum.bambulab.com/t/any-documentation-on-parametric-model-maker/230605"&gt;Any Documentation on Parametric Model Maker?&lt;/a&gt;. The first post asks where to find the requirements for making customizable models. That felt familiar.&lt;/p&gt;
&lt;p&gt;For a human, scattered docs are annoying. For a coding agent, they are a great way to get plausible garbage. It might write OpenSCAD that looks fine locally but misses MakerWorld&amp;rsquo;s actual rules. It might invent a PMM feature because it saw something sort of similar somewhere else.&lt;/p&gt;
&lt;p&gt;I do not want an agent inventing &lt;code&gt;// preview[...]&lt;/code&gt; as a PMM feature. I want it to know that &lt;code&gt;// color&lt;/code&gt; is a thing, &lt;code&gt;// font&lt;/code&gt; is a thing, &lt;code&gt;mw_plate_N()&lt;/code&gt; is a thing, and &lt;code&gt;default.svg&lt;/code&gt; is not just some random example filename.&lt;/p&gt;
&lt;p&gt;So I made a reference.&lt;/p&gt;
&lt;h2 id="what-i-collected"&gt;What I collected&lt;/h2&gt;
&lt;p&gt;The repo is basically a practical map of the PMM OpenSCAD surface:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://nelsonjchen.github.io/unofficial-makerworld-parametric-model-maker-openscad-docs/pmm-openscad-api/"&gt;PMM OpenSCAD API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;an &lt;a href="https://nelsonjchen.github.io/unofficial-makerworld-parametric-model-maker-openscad-docs/agent-workflow/"&gt;agent workflow&lt;/a&gt; for converting normal OpenSCAD into PMM-ready OpenSCAD&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nelsonjchen.github.io/unofficial-makerworld-parametric-model-maker-openscad-docs/gotchas/"&gt;gotchas&lt;/a&gt; that agents are likely to mess up&lt;/li&gt;
&lt;li&gt;compatibility rules&lt;/li&gt;
&lt;li&gt;public source snapshots&lt;/li&gt;
&lt;li&gt;patterns and checklists&lt;/li&gt;
&lt;li&gt;a generated docs site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The small examples are the important ones:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-scad" data-lang="scad"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;accent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;#FF0000&amp;#34;&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// color
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;font_name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Roboto&amp;#34;&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// font
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;module&lt;/span&gt; mw_plate_1() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// printable plate here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;module&lt;/span&gt; mw_assembly_view() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// preview-only assembly here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;svg_file &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;default.svg&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;None of that is hard once you know it. The problem is making sure the agent knows it before it starts &amp;ldquo;helping.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I also made a &lt;a href="https://nelsonjchen.github.io/unofficial-makerworld-parametric-model-maker-openscad-docs/font-index/"&gt;PMM font index&lt;/a&gt;. It has 1881 MakerWorld PMM font families and 8267 exact PMM font strings. This got more elaborate than I expected, but fonts are exactly the kind of thing where an agent can confidently choose something that works on your machine and then does not work where the model actually runs.&lt;/p&gt;
&lt;h2 id="why-agent-first"&gt;Why agent-first&lt;/h2&gt;
&lt;p&gt;This is not meant to replace the official PMM UI or be a polished tutorial for someone&amp;rsquo;s first customizable model. It is mostly a pile of receipts and instructions for agents.&lt;/p&gt;
&lt;p&gt;Point &lt;a href="https://openai.com/codex/"&gt;Codex&lt;/a&gt; or another coding agent at the repo, then ask it to adapt a &lt;code&gt;.scad&lt;/code&gt; file for MakerWorld. The repo gives it rules to retrieve before it starts editing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;flatten local includes when PMM probably will not have them&lt;/li&gt;
&lt;li&gt;do not remove bundled PMM libraries like BOSL2 just because arbitrary local includes are risky&lt;/li&gt;
&lt;li&gt;use PMM upload defaults like &lt;code&gt;default.svg&lt;/code&gt; and &lt;code&gt;default.stl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;// color&lt;/code&gt; and &lt;code&gt;// font&lt;/code&gt; only where those controls are intended&lt;/li&gt;
&lt;li&gt;treat multi-plate output as a publishing decision, not just a module name&lt;/li&gt;
&lt;li&gt;cite whether a claim came from an official app endpoint, official release, employee reply, community report, or inference&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last part matters. I do not want a pile of agent-generated confidence. I want the agent to say why it thinks something is true.&lt;/p&gt;
&lt;h2 id="the-repo"&gt;The repo&lt;/h2&gt;
&lt;p&gt;The project is here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/unofficial-makerworld-parametric-model-maker-openscad-docs"&gt;https://github.com/nelsonjchen/unofficial-makerworld-parametric-model-maker-openscad-docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The generated docs site is here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://nelsonjchen.github.io/unofficial-makerworld-parametric-model-maker-openscad-docs/"&gt;https://nelsonjchen.github.io/unofficial-makerworld-parametric-model-maker-openscad-docs/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is unofficial, not endorsed by Bambu Lab or MakerWorld, and not a license grant for any font, library, model, or web asset. It is practical documentation from public sources and reverse-engineering.&lt;/p&gt;
&lt;p&gt;It is also probably incomplete or wrong in places. PMM keeps changing, and some of this stuff only becomes obvious after someone trips over it.&lt;/p&gt;
&lt;p&gt;Anyway, this mostly exists so my agents stop making the same MakerWorld OpenSCAD mistakes. If it saves someone else from digging through forum threads while trying to publish a customizable model, great.&lt;/p&gt;</description></item><item><title>Cisco Catalyst 9800-CL on GCP: what a fight</title><link>https://mindflakes.com/posts/2026/03/20/cisco-catalyst-9800-cl-on-gcp-what-a-fight/</link><pubDate>Fri, 20 Mar 2026 13:15:00 -0800</pubDate><guid>https://mindflakes.com/posts/2026/03/20/cisco-catalyst-9800-cl-on-gcp-what-a-fight/</guid><description>&lt;p&gt;I finally wrote up my notes on getting a modern &lt;a href="https://www.cisco.com/c/en/us/products/wireless/catalyst-9800-cl-wireless-controller-cloud/index.html"&gt;Cisco Catalyst 9800-CL&lt;/a&gt; running on &lt;a href="https://cloud.google.com/"&gt;Google Cloud Platform&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The short version is that this was much more of a fight than I expected.&lt;/p&gt;
&lt;p&gt;Cisco&amp;rsquo;s &lt;a href="https://www.cisco.com/c/en/us/products/collateral/wireless/catalyst-9800-cl-wireless-controller-cloud/nb-06-cat9800-cl-cloud-wirel-data-sheet-ctp-en.pdf"&gt;public GCP-facing material&lt;/a&gt; still nudges people toward &lt;a href="https://www.cisco.com/c/dam/en/us/td/docs/wireless/controller/9800/9800-cloud/deployment/c9800-cl-gcp-deployment-guide.pdf"&gt;Google Cloud Marketplace&lt;/a&gt;, but the Marketplace offerings I found were outrageously old: &lt;code&gt;16.12.1&lt;/code&gt; and &lt;code&gt;16.12.2s&lt;/code&gt; from the &lt;code&gt;2019-2020&lt;/code&gt; era, plus &lt;code&gt;17.2.1&lt;/code&gt; and &lt;code&gt;17.3.5a&lt;/code&gt; from the &lt;code&gt;2020-2021&lt;/code&gt; era.&lt;/p&gt;
&lt;p&gt;That is a pretty bad situation if what you actually want is a modern WLC on GCP. The version I actually worked from was &lt;code&gt;17.15.04d&lt;/code&gt;, which Cisco&amp;rsquo;s software portal lists with a release date of &lt;code&gt;19-Dec-2025&lt;/code&gt;. Cisco&amp;rsquo;s public guidance still points at a path that appears stuck on software families from years ago, and it was not a good starting point for getting a current controller running.&lt;/p&gt;
&lt;p&gt;The current downloadable image path is possible, but it turned out to involve more image surgery and boot-path archaeology than I had hoped for.&lt;/p&gt;
&lt;p&gt;I put the actual handoff-style guide here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nelsonjchen/c9800-cl-gcp-setup"&gt;Cisco Catalyst 9800-CL on GCP guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That guide covers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;where to get the &lt;code&gt;qcow2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;why Marketplace is not a great baseline right now&lt;/li&gt;
&lt;li&gt;what actually worked to get the controller reachable&lt;/li&gt;
&lt;li&gt;how I validated it was really up&lt;/li&gt;
&lt;li&gt;how to adopt an AP into the supported public-cloud path&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One of the main conclusions is that Cisco&amp;rsquo;s built-in GCP bootstrap path seems to create real first-boot state that the appliance expects, which helps explain why simply editing &lt;code&gt;/varied/iosxe_config.txt&lt;/code&gt; offline was not enough on a pristine raw image.&lt;/p&gt;
&lt;p&gt;Anyway: what a fight. But at least the notes are in one place now.&lt;/p&gt;</description></item><item><title>Bambu Lab Store Filament Tracker</title><link>https://mindflakes.com/posts/2026/02/11/bambu-lab-store-filament-tracker/</link><pubDate>Wed, 11 Feb 2026 20:10:00 -0800</pubDate><guid>https://mindflakes.com/posts/2026/02/11/bambu-lab-store-filament-tracker/</guid><description>&lt;p&gt;I&amp;rsquo;ve been running a &lt;a href="https://bambulab.com/"&gt;Bambu Lab&lt;/a&gt; Store filament tracker at &lt;a href="https://bbltracker.com/"&gt;bbltracker.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/bbltracker.com.png" alt="bbltracker dashboard"&gt;&lt;/p&gt;
&lt;p&gt;I recently got into &lt;a href="https://en.wikipedia.org/wiki/3D_printing"&gt;3D printing&lt;/a&gt;. A lot of what I print is practical: last-millimeter adapters, things like &lt;a href="https://en.wikipedia.org/wiki/Robotic_vacuum_cleaner"&gt;robot vacuum&lt;/a&gt; ramps around the house, and other tiny fixes that make daily life less annoying.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://bambulab.com/"&gt;Bambu Lab&lt;/a&gt; released the &lt;a href="https://bambulab.com/en-us/p2s"&gt;P2S&lt;/a&gt;, their flagship midrange printer, and I picked one up after hearing how polished their ecosystem is. People call them the Apple of 3D printers, and I can see why. The hardware is solid, but the software and ecosystem are really what make it shine.&lt;/p&gt;
&lt;p&gt;There is also a bit of a format war around &lt;a href="https://en.wikipedia.org/wiki/Fused_filament_fabrication"&gt;filament&lt;/a&gt;. Bambu sells filament with &lt;a href="https://en.wikipedia.org/wiki/Radio-frequency_identification"&gt;RFID&lt;/a&gt; tags that are not easy for third parties to mimic. The RFID flow makes loading filament easy and automatically applies Bambu-tuned profiles. Some people feel those profiles run too fast, but for my functional prints, they have worked great.&lt;/p&gt;
&lt;h2 id="where-the-frustration-started"&gt;Where the frustration started&lt;/h2&gt;
&lt;p&gt;I like having color options, and color can be functional too. But stock has been rough for a while, even after the holiday season.&lt;/p&gt;
&lt;p&gt;That rough stock situation (and the need for a tracker) is really a symptom of how popular Bambu has gotten, especially over the holidays:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/bbl-handy-chart.webp" alt="Handy chart showing Bambu popularity spike over the holidays"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://www.reddit.com/r/BambuLab/comments/1r01yoo/comment/o4hrtwx"&gt;Reddit comment&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Then there is the constant bulk-sale mechanic on the &lt;a href="https://us.store.bambulab.com/?skr=yes"&gt;Bambu Lab Store&lt;/a&gt;: buy 4+, get a strong discount, buy more, save more. In practice, that falls apart when the combinations you want are never all in stock at the same time.&lt;/p&gt;
&lt;p&gt;At the time, the store UI also made this harder than it needed to be. Out-of-stock options were not clearly crossed out, so building a cart felt like guesswork.&lt;/p&gt;
&lt;p&gt;That made me angry, but in a constructive way. So I built a tracker.&lt;/p&gt;
&lt;h2 id="what-the-tracker-does"&gt;What the tracker does&lt;/h2&gt;
&lt;p&gt;At a high level, the tracker monitors store inventory over time, keeps historical snapshots, and renders a dashboard so I can quickly answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is actually in stock right now?&lt;/li&gt;
&lt;li&gt;What appears stable versus constantly draining?&lt;/li&gt;
&lt;li&gt;What was the last major restock spike?&lt;/li&gt;
&lt;li&gt;Are there ETA hints for restocks?&lt;/li&gt;
&lt;li&gt;Is this likely to go out of stock soon?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I started with only the &lt;a href="https://us.store.bambulab.com/?skr=yes"&gt;US store&lt;/a&gt;, then expanded to &lt;a href="https://eu.store.bambulab.com/?skr=yes"&gt;EU&lt;/a&gt;, &lt;a href="https://ca.store.bambulab.com/?skr=yes"&gt;CA&lt;/a&gt;, &lt;a href="https://uk.store.bambulab.com/?skr=yes"&gt;UK&lt;/a&gt;, &lt;a href="https://au.store.bambulab.com/?skr=yes"&gt;AU&lt;/a&gt;, and &lt;a href="https://asia.store.bambulab.com/?skr=yes"&gt;Asia&lt;/a&gt; coverage.&lt;/p&gt;
&lt;p&gt;I also reused a stock-quantity verification trick I had learned while documenting &lt;a href="https://github.com/commaai/openpilot"&gt;openpilot&lt;/a&gt;&amp;rsquo;s &lt;a href="https://github.com/optskug/docs"&gt;Toyota security key journey&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="building-it-fast-and-keeping-it-moving"&gt;Building it fast (and keeping it moving)&lt;/h2&gt;
&lt;p&gt;I used a lot of &lt;a href="https://antigravity.google/"&gt;Google Antigravity&lt;/a&gt; while building this. &lt;a href="https://en.wikipedia.org/wiki/Web_scraping"&gt;Web scraping&lt;/a&gt; is tedious work, and &lt;a href="https://en.wikipedia.org/wiki/Large_language_model"&gt;large language models&lt;/a&gt; helped a lot with iteration speed and the constant tweaks.&lt;/p&gt;
&lt;p&gt;The codebase is not pretty in every corner, but LLMs have made it very workable to maintain and improve continuously.&lt;/p&gt;
&lt;p&gt;Later, I added depletion-rate estimation so the tracker can make a rough guess about when something might go out of stock.&lt;/p&gt;
&lt;h2 id="things-that-happened-after-launch"&gt;Things that happened after launch&lt;/h2&gt;
&lt;p&gt;After I released it and posted it on &lt;a href="https://www.reddit.com/r/BambuLab/"&gt;/r/BambuLab&lt;/a&gt;, a bunch of interesting things happened:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bambu nerfed the original stock-count hole the tracker used.&lt;/li&gt;
&lt;li&gt;For a while, counts were capped at 200 in the US view, which reduced visibility.&lt;/li&gt;
&lt;li&gt;Later, that cap moved to 400 in the &lt;a href="https://us.store.bambulab.com/?skr=yes"&gt;US store&lt;/a&gt;. Maybe my tracker&amp;rsquo;s utility was actually a factor in that decision? I do see traffic from China, where there is no Bambu Lab Store of the same codebase as far as I can see.&lt;/li&gt;
&lt;li&gt;Bambu improved their UI and added clearer out-of-stock cross-outs.&lt;/li&gt;
&lt;li&gt;I added more quality-of-life indicators, including &amp;ldquo;stable stock,&amp;rdquo; ETA context, and restock-spike visibility.&lt;/li&gt;
&lt;li&gt;I added a &lt;a href="https://gofund.me/b1be0586b"&gt;GoFundMe&lt;/a&gt; and a nice patron funded a custom domain and hosting for the tracker.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="backend-at-a-glance"&gt;Backend at a glance&lt;/h2&gt;
&lt;p&gt;Keeping this online reliably has mostly been a deployment and operations problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run"&gt;Google Cloud Run&lt;/a&gt; is heavily used for scheduled jobs, scaling, and reliability.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pages.cloudflare.com/"&gt;Cloudflare Pages&lt;/a&gt; hosts the output and leaves room for future expansion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you print a lot and buy filament in batches, this has been much better than manually refreshing product pages and hoping for the best.&lt;/p&gt;</description></item><item><title>Surviving a power outage with fiber ONT and PoE setup (Frontier FOX222)</title><link>https://mindflakes.com/posts/2025/07/06/surviving-a-power-outage-with-fiber-ont-and-poe-setup-frontier-fox222/</link><pubDate>Sun, 06 Jul 2025 18:12:00 -0800</pubDate><guid>https://mindflakes.com/posts/2025/07/06/surviving-a-power-outage-with-fiber-ont-and-poe-setup-frontier-fox222/</guid><description>&lt;p&gt;&lt;img src="https://assets.mindflakes.com/fiber-poe/fox222.jpg" alt="fox222"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://assets.mindflakes.com/fiber-poe/CA25U16V2.jpg" alt="power supply"&gt;&lt;/p&gt;
&lt;p&gt;For some reason, it&amp;rsquo;s been a bit hard to find information on how to setup my particular fiber ONT so that it can survive a power outage with a PoE setup.&lt;/p&gt;
&lt;p&gt;While of course, one could power a whole house with solar and batteries, I wanted to find a way to keep my fiber ONT and router powered during a power outage without having to invest in a full home battery system.&lt;/p&gt;
&lt;p&gt;Costco recently &lt;a href="https://slickdeals.net/f/18368635-ecoflow-river-3-plus-wireless-boost-combo-219"&gt;had on sale a small EcoFlow RIVER 3 Plus Portable Power Station&lt;/a&gt;, which I installed into my home network setup with the power station being next to my router.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been wonderful as an UPS!&lt;/p&gt;
&lt;p&gt;My setup is a bit peculiar as my Frontier FOX222 ONT is a bit far from the power station, but the power station is next to the router and there is an Ethernet cable running from the ONT to the router. The RIVER 3 is also next to the router.&lt;/p&gt;
&lt;p&gt;The only way to provide power to the ONT without also having to plumb AC backed by the power station: A PoE injector on the Ethernet cable between the ONT and the router and a splitter at the ONT end to convert the PoE to 16V DC power for the ONT.&lt;/p&gt;
&lt;p&gt;The FOX222 ONT is powered by a 16V DC power supply. There is an &amp;ldquo;Auxiliary Power Supply&amp;rdquo; port on the side of the power supply. Unfortunately, there is no printed information on what acceptable inputs are acceptable for this port. Nor is there documentation on the power supply itself on acceptable voltages. And size, that took forever and was inconsistent.&lt;/p&gt;
&lt;p&gt;Information is also a bit scattered and/or some people have done permanent modications. Some &lt;a href="https://www.instructables.com/I-211M-L-ONT-Enable-Data-While-on-Battery-Power/"&gt;sources have even said 12V is all you need but this is wrong&lt;/a&gt;! The information may be applicable to a different ONT model.&lt;/p&gt;
&lt;p&gt;Through some trial and error and quite some Amazon returns, I&amp;rsquo;ve have an Amazon checklist of the requirements for what is needed.&lt;/p&gt;
&lt;p&gt;My environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontier FOX222 Fiber ONT (Optical Network Terminal)&lt;/li&gt;
&lt;li&gt;Cyberpower CA25U16V2 Power Supply&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need a PoE injector that can provide at least 30W. The power supply itself at the end of the chain is rated for 16V at 1.6A but it is unlikely to use the full 1.6A.
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3Iubo63"&gt;TRENDnet Gigabit Power Over Ethernet Plus Injector, PoE+ (30W)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You need a PoE splitter that can provide 16V DC output or something close to it. The FOX222 ONT requires 16V DC input. It will not run with 12V DC input. The 18V DC output is within range and works.
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/4ew8v0C"&gt;LINOVISION 30W Gigabit PoE Splitter to DC 5/9/12/18V Output, Wide Voltage Input&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The power supply connector has a DIN-like connector on one end. It must be modified so that when there is no AC power, the ONT will still power on and provide data.
&lt;ul&gt;
&lt;li&gt;The modification is easily reversible with a small piece of alumnium foil.&lt;/li&gt;
&lt;li&gt;&lt;img src="https://assets.mindflakes.com/fiber-poe/power-supply-plug-modification.webp" alt="Modification Image"&gt;&lt;/li&gt;
&lt;li&gt;Image from &lt;a href="https://www.instructables.com/I-211M-L-ONT-Enable-Data-While-on-Battery-Power/"&gt;https://www.instructables.com/I-211M-L-ONT-Enable-Data-While-on-Battery-Power/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If not modified, apparently the ONT will refuse to power on the data port. I did not care to try.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;Auxiliary plug to the the power supply is a 4.0mm x 1.7mm barrel connector. I don&amp;rsquo;t know why some some sources say it&amp;rsquo;s a 4.5mm x 3.0mm barrel connector. Or &lt;a href="https://www.instructables.com/I-211M-L-ONT-Enable-Data-While-on-Battery-Power/"&gt;have other bizzare sizes such as &amp;ldquo;1.3mm x 3.5mm&amp;rdquo;.&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/4eKLUgX"&gt;4.0mm x 1.7mm barrel connector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;re adding some Ethernet cable segments. Some small thin cables is fine.
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/4nyWHyP"&gt;Monoprice&amp;rsquo;s SlimRun cables seem to be excellent. I have a few 1 ft patch cables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Plug the PoE injector into the wall and connect it to the router on the WAN side. Use the short Ethernet cables to add small segments as needed.&lt;/li&gt;
&lt;li&gt;Connect the PoE splitter to the ONT on the other end of the Ethernet cable. Use the short Ethernet cables to add small segments as needed.&lt;/li&gt;
&lt;li&gt;The LINOVISION PoE splitter has a 5.5mm x 2.1mm barrel connector male as an output. Connect it to the PoE adapter&amp;rsquo;s cable with the 4.0mm x 1.7mm barrel connector.&lt;/li&gt;
&lt;li&gt;Plug the 4.0mm x 1.7mm barrel connector into the &amp;ldquo;Auxiliary Power Supply&amp;rdquo; port on the Cyberpower CA25U16V2 power supply.&lt;/li&gt;
&lt;li&gt;Take out the wire between the ONT and the power supply and modify the plug as shown in the image above. This is done by stuffing in a small piece of aluminum foil into the barrel connector. It apparently shorts some pins together to allow the ONT to power on and provide data when there is no AC power.
&lt;ul&gt;
&lt;li&gt;&lt;img src="https://assets.mindflakes.com/fiber-poe/power-supply-plug-modification.webp" alt="Modification Image"&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Unplug the AC cord from the Cyberpower CA25U16V2 power supply.&lt;/li&gt;
&lt;li&gt;Plug in the DC barrel connector into the power supply&amp;rsquo;s &amp;ldquo;Auxiliary Power Supply&amp;rdquo; port.
&lt;ul&gt;
&lt;li&gt;&lt;img src="https://assets.mindflakes.com/fiber-poe/auxilllary_power_supply.jpg" alt="Auxiliary Power Supply"&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;It should power on!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do not plug in the AC cord!&lt;/strong&gt; as if you plug it in, the ONT will reboot. This is actually bad. If the AC power goes out, the ONT will reboot. Just leave it powered by the DC power supply exclusively and from the PoE injector.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Setup Image:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;img src="https://assets.mindflakes.com/fiber-poe/setup.jpg" alt="Setup Image"&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Operation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Leave the AC cord from the power supply unplugged. If you plug it in, power outages will disrupt service. You&amp;rsquo;re welcome to test this yourself once you have the setup working completely on DC power.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Things I don&amp;rsquo;t know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does this work the with the FRX523? It&amp;rsquo;s apparently some sort of successor to the FOX222.&lt;/li&gt;
&lt;li&gt;Will there by long term issues with the ONT being powered by a 18V DC power supply?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Comments:&lt;/p&gt;
&lt;p&gt;Questions and suggestions are welcome to &lt;a href="https://bsky.app/profile/mindflakes.com"&gt;https://bsky.app/profile/mindflakes.com&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.dmdtech.org/2024/11/30/power-your-frontier-and-others-ont-w-poe/"&gt;https://www.dmdtech.org/2024/11/30/power-your-frontier-and-others-ont-w-poe/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gave a hint that 16V is an absolute requirement&lt;/li&gt;
&lt;li&gt;Provided a hint to the LINOVision PoE splitter&amp;rsquo;s 18V output being acceptable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.instructables.com/I-211M-L-ONT-Enable-Data-While-on-Battery-Power/"&gt;https://www.instructables.com/I-211M-L-ONT-Enable-Data-While-on-Battery-Power/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provided the image above&lt;/li&gt;
&lt;li&gt;Implies that the power supply is happy to take a 12V DC input but this is wrong for the FOX222 ONT, at least.&lt;/li&gt;
&lt;li&gt;Plug size is wrong, it&amp;rsquo;s 4.0mm x 1.7mm!&lt;/li&gt;
&lt;li&gt;Only thing right seems to be the modification of the power supply plug with aluminum foil&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Talky Pet Watcher, a cat watching AI</title><link>https://mindflakes.com/posts/2025/02/01/talky-pet-watcher-a-cat-watching-ai/</link><pubDate>Sat, 01 Feb 2025 06:08:55 +0000</pubDate><guid>https://mindflakes.com/posts/2025/02/01/talky-pet-watcher-a-cat-watching-ai/</guid><description>&lt;p&gt;&lt;img src="https://mindflakes.com/images/talky-pet-watcher.png" alt="Cat Watch"&gt;&lt;/p&gt;
&lt;p&gt;Github Project: &lt;a href="https://github.com/nelsonjchen/talky_pet_watcher/"&gt;talky_pet_watcher&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m allergic to cats 😢. But a friend asked me to watch their cat. My family was more than willing to help but to be safe, I wanted to also monitor the cat to make sure she didn&amp;rsquo;t get into any shenanigans that were too much.&lt;/p&gt;
&lt;p&gt;With some cheap &lt;a href="https://www.tp-link.com/us/home-networking/cloud-camera/"&gt;TP-Link Tapo&lt;/a&gt; cameras, I set up a system to watch the cat remotely and ensure she was safe while I could not be next to her. It&amp;rsquo;s a &lt;a href="https://telegram.org/"&gt;Telegram&lt;/a&gt; bot that also sends me a message whenever the cat is doing something interesting.&lt;/p&gt;
&lt;p&gt;It watches multiple cameras and tries to aggregate the data to provide insights on the cat&amp;rsquo;s behavior.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the project description:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Talky Pet Watcher is a tool to watch a series of &lt;a href="https://www.onvif.org/"&gt;ONVIF&lt;/a&gt; webcams and their streams. It captures video clips, uploads them to Google AI for processing, and uses a generative model to create captions and identify relevant clips. The results are then delivered to a Telegram channel. This project is designed to help pet owners keep an eye on their pets and share interesting moments with friends.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This was slapped together in a few days, as I only watched the cat for two weeks.&lt;/p&gt;
&lt;p&gt;I was also interested in &lt;a href="https://gemini.google.com/"&gt;Google Gemini&lt;/a&gt; and how cheap they claimed to be for analyzing video. I figured that this would be a good way to test it out.&lt;/p&gt;
&lt;p&gt;What went well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The system was able to capture interesting moments effectively.&lt;/li&gt;
&lt;li&gt;It was able to concantate multiple camera&amp;rsquo;s POV to provide interesting views.&lt;/li&gt;
&lt;li&gt;The cameras were cheap!&lt;/li&gt;
&lt;li&gt;Putting the results on Telegram was a good way to share the results with the cat&amp;rsquo;s owner and friends.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What did not go well (and there are many!):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I did not implement history, so the system basically reported the same thing over and over again.&lt;/li&gt;
&lt;li&gt;Getting reliable clips from multiple cameras when motion was detected was iffy due to the TP-Link Tapo camera&amp;rsquo;s iffy web servers. It would stall and halt a lot.&lt;/li&gt;
&lt;li&gt;The Google Gemini AI could only analyze video clips at 1FPS. For an agile kitten, this can sometimes make wrong assumptions about what the cat is doing.&lt;/li&gt;
&lt;li&gt;Implementation wise, &lt;a href="https://bun.sh/"&gt;&lt;code&gt;bun&lt;/code&gt;&lt;/a&gt; was very crashy and I had to restart it a lot.&lt;/li&gt;
&lt;li&gt;The connection to the camera also had to be restarted a lot.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If I had to do it again:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use something that can pull from a NVR by relative timestamps. There was some Rust NVR but it was too complicated for me to set up in the small amount of time I had.&lt;/li&gt;
&lt;li&gt;Implement a history system and maybe some memory bank system.&lt;/li&gt;
&lt;li&gt;And so so much more! 😅&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>comma three faux touch keyboard</title><link>https://mindflakes.com/posts/2024/12/13/comma-three-faux-touch-keyboard/</link><pubDate>Fri, 13 Dec 2024 22:02:00 -0800</pubDate><guid>https://mindflakes.com/posts/2024/12/13/comma-three-faux-touch-keyboard/</guid><description>&lt;h1 id="-comma-three-faux-touch-keyboard"&gt;🦾 comma three Faux-Touch keyboard&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Long arms for those of us with short arms from birth or those who can&amp;rsquo;t afford arm extension surgery!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/nelsonjchen/c3-touchkey-keyboard/assets/5363/d9617916-2442-4287-b430-709dad173da8" alt="touchkey keyboard demo"&gt;&lt;/p&gt;
&lt;p&gt;Built a faux-touch keyboard for the comma three using a CH552G microcontroller. The keyboard is off the shelf and reprogrammed to emulat a touchscreen.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a bit of a hack but it certainly beats &lt;a href="https://en.wikipedia.org/wiki/Touchscreen#%22Gorilla_arm%22"&gt;gorilla arm&lt;/a&gt; syndrome while driving.&lt;/p&gt;
&lt;p&gt;For $5 of hardware, it&amp;rsquo;s very hard to beat.&lt;/p&gt;
&lt;p&gt;The journey started in January 2024 and ended in May 2024. Though it, I had to struggle with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CH552G programming&lt;/li&gt;
&lt;li&gt;Linux USB HID Touchscreen protocol&lt;/li&gt;
&lt;li&gt;Instructions and documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s also been through that period of unintended usage. The &lt;a href="https://github.com/FrogAi/FrogPilot"&gt;Frogpilot&lt;/a&gt; project has had users who were surprisingly interested in using it as an alternative for some GM vehicles where there is no ACC button. They use the keyboard to touch the screen where ACC would be.&lt;/p&gt;
&lt;p&gt;Part of the work also meant upstreaming to the the &lt;a href="https://github.com/wagiminator/MCU-Templates"&gt;CH552G community&lt;/a&gt;. While I doubt it&amp;rsquo;ll have any users, it&amp;rsquo;s nice to give back to the community. There is now a touchscreen library for the CH552G.&lt;/p&gt;
&lt;p&gt;The project is open source and can be found at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/c3-faux-touch-keyboard"&gt;https://github.com/nelsonjchen/c3-faux-touch-keyboard&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a nice readme that explains how to build and flash the firmware.&lt;/p&gt;</description></item><item><title>comma three faux touch keyboard</title><link>https://mindflakes.com/projects/c3-faux-touch-keyboard/</link><pubDate>Fri, 13 Dec 2024 00:00:00 +0000</pubDate><guid>https://mindflakes.com/projects/c3-faux-touch-keyboard/</guid><description>&lt;h1 id="-comma-three-faux-touch-keyboard"&gt;🦾 comma three Faux-Touch keyboard&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Long arms for those of us with short arms from birth or those who can&amp;rsquo;t afford arm extension surgery!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/nelsonjchen/c3-touchkey-keyboard/assets/5363/d9617916-2442-4287-b430-709dad173da8" alt="touchkey keyboard demo"&gt;&lt;/p&gt;
&lt;p&gt;Built a faux-touch keyboard for the comma three using a CH552G microcontroller. The keyboard is off the shelf and reprogrammed to emulat a touchscreen.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a bit of a hack but it certainly beats &lt;a href="https://en.wikipedia.org/wiki/Touchscreen#%22Gorilla_arm%22"&gt;gorilla arm&lt;/a&gt; syndrome while driving.&lt;/p&gt;
&lt;p&gt;For $5 of hardware, it&amp;rsquo;s very hard to beat.&lt;/p&gt;
&lt;p&gt;The journey started in January 2024 and ended in May 2024. Though it, I had to struggle with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CH552G programming&lt;/li&gt;
&lt;li&gt;Linux USB HID Touchscreen protocol&lt;/li&gt;
&lt;li&gt;Instructions and documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s also been through that period of unintended usage. The &lt;a href="https://github.com/FrogAi/FrogPilot"&gt;Frogpilot&lt;/a&gt; project has had users who were surprisingly interested in using it as an alternative for some GM vehicles where there is no ACC button. They use the keyboard to touch the screen where ACC would be.&lt;/p&gt;
&lt;p&gt;Part of the work also meant upstreaming to the the &lt;a href="https://github.com/wagiminator/MCU-Templates"&gt;CH552G community&lt;/a&gt;. While I doubt it&amp;rsquo;ll have any users, it&amp;rsquo;s nice to give back to the community. There is now a touchscreen library for the CH552G.&lt;/p&gt;
&lt;p&gt;The project is open source and can be found at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/c3-faux-touch-keyboard"&gt;https://github.com/nelsonjchen/c3-faux-touch-keyboard&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a nice readme that explains how to build and flash the firmware.&lt;/p&gt;</description></item><item><title>Replicate.com openpilot Replay Clipper</title><link>https://mindflakes.com/posts/2023/10/12/replicate.com-openpilot-replay-clipper/</link><pubDate>Thu, 12 Oct 2023 22:28:00 -0800</pubDate><guid>https://mindflakes.com/posts/2023/10/12/replicate.com-openpilot-replay-clipper/</guid><description>&lt;p&gt;&lt;img src="https://github.com/nelsonjchen/op-replay-clipper/assets/5363/7a9cf016-2012-4acd-90e9-9cdec6bf96c1" alt="Web capture_8-11-2023_9551_replicate com"&gt;&lt;/p&gt;
&lt;p&gt;The replay clipper has been ported to &lt;a href="https://replicate.com"&gt;Replicate.com&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://replicate.com/nelsonjchen/op-replay-clipper"&gt;https://replicate.com/nelsonjchen/op-replay-clipper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Along with it comes a slew of upgrades and updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPU accelerated&lt;/strong&gt; decoding, rendering, and encoding. NVIDIA GPUs are provided to the Replicate environment and greatly speed up the clipper.&lt;/li&gt;
&lt;li&gt;Rapid fast downloading of clips. Instead of relying on &lt;code&gt;replay&lt;/code&gt; to handle downloads sequentially in a non-parallel manner, we use the parfive library to download underlying data in parallel.&lt;/li&gt;
&lt;li&gt;Comma Connect URL Input. No need to mentally calculate the starting time and length. Just copy and paste the URL from Comma Connect.&lt;/li&gt;
&lt;li&gt;Video/UI-less mode. Don&amp;rsquo;t want UI? You can have it.&lt;/li&gt;
&lt;li&gt;360 mode. Render 360 clips&lt;/li&gt;
&lt;li&gt;Forward Upon Wide. Render clips with the forward clip upon the wide clip&lt;/li&gt;
&lt;li&gt;Richer error messages to help pinpoint issues.&lt;/li&gt;
&lt;li&gt;No more having to manage GitHub Codespaces. Replicate handles all the setup and cleanup for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, there is a cost. It&amp;rsquo;s a very small cost but technically Replicate.com is not free. Expect to drop a cent per render. Thankfully, you have a lot a trial credits to burn through and the clipper can run on a free-ish tier.&lt;/p&gt;</description></item><item><title>Colorized-Project: A Project-Color restoration.</title><link>https://mindflakes.com/posts/2023/02/01/colorized-project-a-project-color-restoration./</link><pubDate>Wed, 01 Feb 2023 22:28:00 -0800</pubDate><guid>https://mindflakes.com/posts/2023/02/01/colorized-project-a-project-color-restoration./</guid><description>&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/5363/217070588-7faccad0-890c-49df-a119-a0ad261cb9df.png" alt="screenshot_c235a4a9-429b-4564-bfef-24e4f12bd9a8"&gt;&lt;/p&gt;
&lt;p&gt;GitHub Link: &lt;a href="https://github.com/nelsonjchen/Colorized-Project"&gt;https://github.com/nelsonjchen/Colorized-Project&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On IntelliJ IDEs, I&amp;rsquo;ve been using &lt;a href="https://github.com/nowtilous/Project-Color"&gt;Project-Color&lt;/a&gt; to set a color for each project. This is useful for me because I have a lot of projects open at once, and it&amp;rsquo;s nice to be able to quickly identify which project is which.&lt;/p&gt;
&lt;p&gt;Unfortunately, Project-Color &lt;a href="https://github.com/nowtilous/Project-Color/issues/"&gt;hasn&amp;rsquo;t been updated in a while&lt;/a&gt;, and it doesn&amp;rsquo;t work with the latest versions of IntelliJ.&lt;/p&gt;
&lt;p&gt;After a few months of using IntelliJ without Project-Color, I decided to try to fix it. I&amp;rsquo;ve released the result as &lt;a href="https://github.com/nelsonjchen/Colorized-Project"&gt;Colorized-Project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thre are a few rough edges, but it&amp;rsquo;s mostly functional. I&amp;rsquo;ve been using it for a few weeks now, and it&amp;rsquo;s been working well. I&amp;rsquo;ve also submitted it to the JetBrains plugin repository as well.&lt;/p&gt;
&lt;p&gt;It still needs some polish like a nicer icon, and I&amp;rsquo;d like to add a few more features.&lt;/p&gt;</description></item><item><title>Colorized-Project</title><link>https://mindflakes.com/projects/colorized-project/</link><pubDate>Wed, 01 Feb 2023 00:00:00 +0000</pubDate><guid>https://mindflakes.com/projects/colorized-project/</guid><description>&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/5363/217070588-7faccad0-890c-49df-a119-a0ad261cb9df.png" alt="screenshot_c235a4a9-429b-4564-bfef-24e4f12bd9a8"&gt;&lt;/p&gt;
&lt;p&gt;GitHub Link: &lt;a href="https://github.com/nelsonjchen/Colorized-Project"&gt;https://github.com/nelsonjchen/Colorized-Project&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Remember your projects by color! Colorize the window somehow to differentiate your open projects. Updated and &lt;a href="https://github.com/nowtilous/Project-Color/issues/"&gt;maintained&lt;/a&gt; fork of Project-Color.&lt;/p&gt;
&lt;p&gt;Working on multiple projects simultaneously with Jetbrains products based on IntelliJ can be difficult, since they all look the same at a glance. This plugin solves that by giving the user the option to colorize each project&amp;rsquo;s window in a noticeable way.&lt;/p&gt;</description></item><item><title>GitHub Wiki SEE: Search Engine Enablement: Year One</title><link>https://mindflakes.com/posts/2022/10/13/github-wiki-see-search-engine-enablement-year-one/</link><pubDate>Thu, 13 Oct 2022 21:04:00 -0800</pubDate><guid>https://mindflakes.com/posts/2022/10/13/github-wiki-see-search-engine-enablement-year-one/</guid><description>&lt;p&gt;It&amp;rsquo;s been near a year since I supercharged &lt;a href="https://github-wiki-see.page"&gt;GitHub Wiki SEE&lt;/a&gt; with dynamically generating sitemaps.&lt;/p&gt;
&lt;p&gt;Since then a few things have changed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub has started to permit a select set of wikis to be indexed.&lt;/li&gt;
&lt;li&gt;They have not budged on letting un-publically editable wikis be indexed.&lt;/li&gt;
&lt;li&gt;There is now a star requirement of about 500, and it appears to be steadily decreasing.&lt;/li&gt;
&lt;li&gt;Many projects have reacted by moving or configuring their wikis to indexable platforms.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a result, traffic to &lt;a href="https://github-wiki-see.page"&gt;GitHub Wiki SEE&lt;/a&gt; has dropped off dramatically.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/5363/195682137-8f9fad93-2a68-4ca0-9c20-035eeba002a1.png" alt="stats"&gt;&lt;/p&gt;
&lt;p&gt;This is a good thing, as it means that GitHub is moving in the right direction.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still going to keep the service up, as it&amp;rsquo;s still useful for wikis that are not yet indexed and there are still about 400,000 wikis that aren&amp;rsquo;t indexed.&lt;/p&gt;
&lt;p&gt;Hopefully GitHub will continue to move in the right direction and allow all wikis to be indexed.&lt;/p&gt;</description></item><item><title>openpilot Replay Clipper</title><link>https://mindflakes.com/posts/2022/10/12/openpilot-replay-clipper/</link><pubDate>Wed, 12 Oct 2022 19:42:00 -0800</pubDate><guid>https://mindflakes.com/posts/2022/10/12/openpilot-replay-clipper/</guid><description>&lt;p&gt;End to End Longitudinal Control is currently an &amp;ldquo;extremely alpha&amp;rdquo; feature in &lt;a href="https://github.com/commaai/openpilot"&gt;openpilot&lt;/a&gt; that is the gateway to future functionality such as stopping for traffic lights and stop signs.&lt;/p&gt;
&lt;p&gt;Problem is, it&amp;rsquo;s hard to describe its current deficiencies without a video.&lt;/p&gt;
&lt;p&gt;So I made a tool to help make it easier to share clips of this functionality with a view into what openpilot is seeing, and thinking.&lt;/p&gt;
&lt;video controls&gt;
&lt;source src="https://user-images.githubusercontent.com/5363/188810452-47a479c4-fa9a-4037-9592-c6a47f2e1bb1.mp4
" type="video/mp4"&gt;
&lt;/video&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/op-replay-clipper/"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a bit heavy in resource use though. I was thinking of making it into a web service but I simply do not have enough time. So I made instructions for others to run it on services like &lt;a href="https://www.digitalocean.com/"&gt;DigitalOcean&lt;/a&gt;, where it is cheap.&lt;/p&gt;
&lt;p&gt;It is composed of a shell script and a Docker setup that fires up a bunch of processes and then kills them all when done.&lt;/p&gt;
&lt;p&gt;Hopefully this leads to more interesting clips being shared, and more feedback on the models that &lt;a href="https://comma.ai/"&gt;comma.ai&lt;/a&gt; can use.&lt;/p&gt;
&lt;p&gt;I later also ported this to Replicate &lt;a href="https://mindflakes.com/posts/2023/10/12/replicate.com-openpilot-replay-clipper/"&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Datasette-lite sqlite-httpvfs experiment POC with the California Unclaimed Property database</title><link>https://mindflakes.com/posts/2022/10/11/datasette-lite-sqlite-httpvfs-experiment-poc-with-the-california-unclaimed-property-database/</link><pubDate>Tue, 11 Oct 2022 20:36:00 -0800</pubDate><guid>https://mindflakes.com/posts/2022/10/11/datasette-lite-sqlite-httpvfs-experiment-poc-with-the-california-unclaimed-property-database/</guid><description>&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/5363/195697437-2331acfb-fedd-478f-8596-28d6a1809339.png" alt="screenshot"&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a web browser version of datasette called datasette-lite which runs on Python ported to WASM with Pyodide which can load SQLite databases.
I grafted the enhanced lazyFile implementation from emscripten and then from this implementation to datasette-lite relatively recently as a curious test. Threw in a 18GB CSV from CA&amp;rsquo;s unclaimed property records here&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.sco.ca.gov/upd_download_property_records.html"&gt;https://www.sco.ca.gov/upd_download_property_records.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;into a FTS5 Sqlite Database which came out to about 28GB after processing:&lt;/p&gt;
&lt;p&gt;POC, non-merging Log/Draft PR for the hack:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/datasette-lite/pull/49"&gt;https://github.com/simonw/datasette-lite/pull/49&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can run queries through to datasette-lite if you URL hack into it and just get to the query dialog, browsing is kind of a dud at the moment since datasette runs a count(*) which downloads everything.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://datasette-lite-lab.mindflakes.com/index.html?url=https://datasette-lite-lab.mindflakes.com/sdb/2022-10-02_93eff57de3573985_ca_unclaimed_property.sqlite#/2022-10-02_93eff57de3573985_ca_unclaimed_property?sql=SELECT+*+FROM+records+WHERE+records.owner_name+MATCH+%22Elon+Musk%22+ORDER+BY+CAST%28CURRENT_CASH_BALANCE+AS+FLOAT%29++DESC%3B"&gt;Elon Musk&amp;rsquo;s CA Unclaimed Property&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Still, not bad for a $0.42/mo hostable cached CDN&amp;rsquo;d read-only database. It&amp;rsquo;s on Cloudflare R2, so there&amp;rsquo;s no BW costs.&lt;/p&gt;
&lt;p&gt;Celebrity gawking is one thing, but the real, useful thing that this can do is search by address. If you aren&amp;rsquo;t sure of the names, such as people having multiple names or nicknames, you can search by address and get a list of properties at a location. This is one thing that the &lt;a href="https://ucpi.sco.ca.gov/en/Property/SearchIndex"&gt;California Unclaimed Property site can&amp;rsquo;t do&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I am thinking of making this more proper when R2 introduces lifecycle rules to delete old dumps. I could automate the process of dumping with GitHub Actions but I would like R2 to handle cleanup.&lt;/p&gt;</description></item><item><title>openpilot Replay Cliper</title><link>https://mindflakes.com/projects/openpilot-replay-clipper/</link><pubDate>Sat, 01 Oct 2022 00:00:00 +0000</pubDate><guid>https://mindflakes.com/projects/openpilot-replay-clipper/</guid><description>&lt;p&gt;Replicate: &lt;a href="https://replicate.com/nelsonjchen/op-replay-clipper"&gt;https://replicate.com/nelsonjchen/op-replay-clipper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/op-replay-clipper/"&gt;https://github.com/nelsonjchen/op-replay-clipper/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📽 Capture short ~30 second route clips with the openpilot UI included, and the route, seconds marker branded into it.&lt;/p&gt;
&lt;p&gt;Designed to try to encourage more interesting clips to be shared, especially with newer functionality such as &amp;ldquo;end to end longitudinal control&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Originally a bit heavy, so instructions were made for others to run it on services like DigitalOcean/GitHub Codespaces. Since then, the clipper has been ported to Replicate which provides GPUs and a nice WebUI.&lt;/p&gt;
&lt;p&gt;The UI rendering is composed of a shell script and a Docker setup that fires up a bunch of processes and then kills them all when done. On Replicate, it is wrapped by a Python script that handles the UI and non-UI processing of video and downloads.&lt;/p&gt;</description></item><item><title>Released Gargantuan Takeout Rocket</title><link>https://mindflakes.com/posts/2022/04/16/released-gargantuan-takeout-rocket/</link><pubDate>Sat, 16 Apr 2022 21:04:00 -0800</pubDate><guid>https://mindflakes.com/posts/2022/04/16/released-gargantuan-takeout-rocket/</guid><description>&lt;p&gt;Finally released GTR or Gargantuan Takeout Rocket.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/gtr"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Gargantuan Takeout Rocket (GTR) is a toolkit of guides and software to help you take out your data from &lt;a href="https://takeout.google.com/"&gt;Google Takeout&lt;/a&gt; and put it somewhere else safe easily, periodically, and fast to make it easy to do the right thing of backing up your Google account and related services such as your YouTube account or Google Photos periodically.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;It took a lot of time to research and to workaround the issues I found in &lt;a href="https://azure.microsoft.com/"&gt;Azure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also needed to take apart browser extensions and implement my own browser extension to facilitate the transfers.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://workers.cloudflare.com/"&gt;Cloudflare Workers&lt;/a&gt; was also used to work around issues with Azure&amp;rsquo;s API.&lt;/p&gt;
&lt;p&gt;All this combined, I was able to takeout 1.25TB of data in 3 minutes.&lt;/p&gt;
&lt;p&gt;Now I&amp;rsquo;m showing it around on Twitter, Discord, Reddit, and more to users who have used VPSes to backup Google Takeout or have expressed dismay at the lack of options for users who are looking to store archives on the cheap. The response from people who&amp;rsquo;ve opted to stay on Google has been good!&lt;/p&gt;
&lt;p&gt;There is also a project page with additional details &lt;a href="https://mindflakes.com/projects/gtr/"&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Gargantuan Takeout Rocket</title><link>https://mindflakes.com/projects/gtr/</link><pubDate>Sat, 16 Apr 2022 00:00:00 +0000</pubDate><guid>https://mindflakes.com/projects/gtr/</guid><description>&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/gtr"&gt;https://github.com/nelsonjchen/gtr&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Gargantuan Takeout Rocket (GTR) is a toolkit of guides and software to help you take out your data from &lt;a href="https://takeout.google.com/"&gt;Google Takeout&lt;/a&gt; and put it somewhere else safe easily, periodically, and fast to make it easy to do the right thing of backing up your Google account and related services such as your YouTube account or Google Photos periodically.&lt;/p&gt;
&lt;p&gt;Used to do this with a VPS. It was still too slow to backup 1.25TB. TOOK HOURS! Built a Rocket.&lt;/p&gt;
&lt;p&gt;It is comprised of a two major components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &amp;ldquo;GTR Proxy&amp;rdquo; Cloudflare Worker that HTTP3-izes the the Azure API and is capable of proxying base64 URLs to Google Takeout URLs.&lt;/li&gt;
&lt;li&gt;A browser extension that coordinates all this and constructs a job plan to execute.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two components are infinitely scaleable and can be used to backup Google Takeout at breathtaking, unprecedented speeds.&lt;/p&gt;
&lt;p&gt;The GTR repo contains a guide on how to setup all of this.&lt;/p&gt;
&lt;p&gt;A future revision or successor may target S3 or S3-like APIs. In particular, can we target &lt;a href="https://blog.cloudflare.com/introducing-r2-object-storage/"&gt;Cloudflare&amp;rsquo;s R2 when it comes out&lt;/a&gt;?&lt;/p&gt;</description></item><item><title>Cell Shield</title><link>https://mindflakes.com/posts/2021/07/28/cell-shield/</link><pubDate>Wed, 28 Jul 2021 18:47:00 -0800</pubDate><guid>https://mindflakes.com/posts/2021/07/28/cell-shield/</guid><description>&lt;p&gt;I built &lt;a href="https://cellshield.info"&gt;https://cellshield.info&lt;/a&gt; to scratch an itch. Why can&amp;rsquo;t I embed a
spreadsheet cell?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1MkUQwY33RY0gJwJtls48mjISBzPgd8noanZee-omezs/edit#gid=0&amp;amp;range=SAShowPost"&gt;
&lt;img src="https://shields.io/endpoint?url=https%3A%2F%2Fcellshield.info%2Fgs%3FspreadSheetId%3D1MkUQwY33RY0gJwJtls48mjISBzPgd8noanZee-omezs%26cellRange%3DC3" alt=""&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can click on that badge and see the sheet that is the data backing the
badge.&lt;/p&gt;
&lt;p&gt;It is useful for embedding a cell from an informal database that is a Google
Spreadsheet atop of some README, wiki, forum post, or web page. Spreadsheets are
still how many projects are managed and this can be used to embed a small
preview of important or attention grabbing data.&lt;/p&gt;
&lt;p&gt;It is a simple Go server that uses Google&amp;rsquo;s API to output JSON that
&lt;a href="https://shields.io"&gt;https://shields.io&lt;/a&gt; can read. Mix it in with some simple VueJS generator
front-end to take public spreadsheet URLS and tada.&lt;/p&gt;
&lt;p&gt;I picked to base my implementation on shields.io&amp;rsquo;s as they make really pretty
badges and have a lot of nice documentation and options.&lt;/p&gt;
&lt;p&gt;I run it on Cloud Run to keep the costs low and availability high. It also
utilizes Google&amp;rsquo;s implementation of authorization to get access to the Sheets API.&lt;/p&gt;
&lt;p&gt;The badge generator web UI can also make BBCode for embedding (along with modern
Markdown and so on).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve already used it for
&lt;a href="https://github.com/commaai/openpilot/wiki/Toyota-Lexus#2021-toyota-ecu-security-key-support-new-steering_lka--more"&gt;this&lt;/a&gt;
and &lt;a href="https://github.com/commaai/comma10k#comma10k"&gt;that&lt;/a&gt;. The former outputs a
running bounty total and the latter is community annotation progress.&lt;/p&gt;
&lt;p&gt;A major Game Boy development contest is using it labels to display the prize
pool amount: &lt;a href="https://itch.io/jam/gbcompo21"&gt;https://itch.io/jam/gbcompo21&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="why-i-built-this"&gt;Why I built this?&lt;/h2&gt;
&lt;p&gt;I built it for the &lt;a href="https://github.com/commaai/comma10k#comma10k"&gt;Comma10K&lt;/a&gt;
annotation project. I contributed the original badge which attempted to use a
script that was used in the repo to analyze the completion progress.
Unfortunately, this kept on diverging from the actual progress because the human
elements kept changing the structure and organization and I had the badge
removed. The only truth is what was on the spreadsheet.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1ZKqku0cAyWY0ELY5L2qsKYYYA2AMGbgAn4p53uoT3v8/edit#gid=0"&gt;&lt;img src="https://img.shields.io/endpoint?color=%2328B224&amp;amp;label=Completion%20Status&amp;amp;url=https%3A%2F%2Fcellshield.info%2Fgs%3FspreadSheetId%3D1ZKqku0cAyWY0ELY5L2qsKYYYA2AMGbgAn4p53uoT3v8%26cellRange%3Di1" alt="Completion Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="popularity"&gt;Popularity&lt;/h2&gt;
&lt;p&gt;Compared to my other projects, I don&amp;rsquo;t think the popularity will be too good for
this project though. I think it might have a chance to spread via word of mouth
but it&amp;rsquo;s not everyday that a large public collaborative project is born with a
Google Spreadsheet managing it. The name isn&amp;rsquo;t that great either as it probably
conflicts with anti-wireless hysteria. Also, it&amp;rsquo;s not like there&amp;rsquo;s an agreed
upon community of Google Spreadsheet users to share this knowledge with.&lt;/p&gt;
&lt;p&gt;I also shamelessly plugged it here but I think the question asker might be
deceased:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/57962813/how-to-embed-a-single-google-sheets-cells-text-content-into-a-web-page/68505801#68505801"&gt;https://stackoverflow.com/questions/57962813/how-to-embed-a-single-google-sheets-cells-text-content-into-a-web-page/68505801#68505801&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At least it was viewed 2,000 times over the last two years.&lt;/p&gt;
&lt;h2 id="maintenance"&gt;Maintenance&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s on Cloud Run. The cost to keep it running is low as it sometimes isn&amp;rsquo;t even
running and the Sheets V4 API it uses was recently declared
&lt;a href="https://www.protocol.com/enterprise/google-cloud-enterprise-apis"&gt;&amp;ldquo;enterprise-stable&amp;rdquo;&lt;/a&gt;.
Because of this, it will be super easy to maintain and cheap to run. I don&amp;rsquo;t
expect any more than $2 a month.&lt;/p&gt;</description></item><item><title>GitHub Wiki SEE: Search Engine Enablement</title><link>https://mindflakes.com/posts/2021/07/27/github-wiki-see-search-engine-enablement/</link><pubDate>Tue, 27 Jul 2021 18:12:00 -0800</pubDate><guid>https://mindflakes.com/posts/2021/07/27/github-wiki-see-search-engine-enablement/</guid><description>&lt;p&gt;I built this in March, but I figure I&amp;rsquo;ll write about this project better late
than never. I supercharged it in June out of curiosity to see how it would
perform. It&amp;rsquo;s just a ramble. I am not a good writer but I just wanted to write
stuff down.&lt;/p&gt;
&lt;p&gt;TLDR: I mirrored all of GitHub&amp;rsquo;s wiki with my own service to get the content on
Google and other search engines.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve used a search engine to search up something that could appear on
GitHub, you are using my service.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github-wiki-see.page/"&gt;https://github-wiki-see.page/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Did you know that GitHub wikis aren&amp;rsquo;t search engine optimized? In fact, GitHub
excluded their own wiki pages from the search results with their
&lt;a href="https://github.com/robots.txt"&gt;robots.txt&lt;/a&gt;.
&lt;a href="https://en.wikipedia.org/wiki/Robots_exclusion_standard"&gt;&amp;ldquo;robots.txt&amp;rdquo; is the mechanism that sites can use to indicate to search engines whether or not to index certain sections of the site&lt;/a&gt;.
If you search on Google or Bing, you won&amp;rsquo;t find any results unless your search
query terms are directly inside the URL.&lt;/p&gt;
&lt;p&gt;The situation with search engines is currently this. For example, if you wanted
to search for information relating to Nissan Leaf support of the openpilot
self-driving car project in their wiki, you could search for &amp;ldquo;nissan leaf
openpilot wiki&amp;rdquo;. Unfortunately, the search results would be empty and would
contain no results. If you searched for &amp;ldquo;nissan openpilot wiki&amp;rdquo;,
&amp;ldquo;&lt;a href="https://github.com/commaai/openpilot/wiki/Nissan%22"&gt;https://github.com/commaai/openpilot/wiki/Nissan&amp;quot;&lt;/a&gt; would show up correctly
because it has all the terms in it. The content of the GitHub wiki page is not
used for returning good results.&lt;/p&gt;
&lt;p&gt;It has been like this for at least 9 years.
&lt;a href="https://github.com/isaacs/github/issues/1683"&gt;GitHub responded that they have declined to remove it from &lt;code&gt;robots.txt&lt;/code&gt; as they believe it can be used for SEO abuse&lt;/a&gt;.
I find this quite unbelievable.
&lt;a href="https://developers.google.com/search/docs/advanced/guidelines/qualify-outbound-links"&gt;The mechanism to prevent this is to utilize &lt;code&gt;rel=&amp;quot;nofollow&amp;quot;&lt;/code&gt; on all user generated &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; links in the content and with newer guidance suggesting &lt;code&gt;rel=&amp;quot;nofollow ugc&amp;quot;&lt;/code&gt;&lt;/a&gt;.
By removing the content from the index, they have blinded a large portion of the
web&amp;rsquo;s users to the content in GitHub wiki pages.&lt;/p&gt;
&lt;p&gt;Until I made a pull request to
&lt;a href="https://docs.github.com/en/communities/documenting-your-project-with-wikis/about-wikis"&gt;add a notice to their documentation that wikis are not search engine optimized&lt;/a&gt;,
there was absolutely no official documentation or notice about this decision. I
still believe it is not enough as nothing in the main GitHub interface notes
this limitation.&lt;/p&gt;
&lt;p&gt;I believe there are many dedicated users and communities that are not aware of
this issue who are diligently contributing to GitHub wikis. This really
frustrated me. I was one of them. Many others and I contributed a lot to the
[comma.ai openpilot wiki][op_wiki] and it was maddening that their work and my
work were not indexed.&lt;/p&gt;
&lt;p&gt;I noticed that a lot of technical questions had Stack Overflow mirrors rank
highly and yet they weren&amp;rsquo;t Stack Overflow.&lt;/p&gt;
&lt;p&gt;So what I did was this: I made a proxy site without a &lt;code&gt;robots.txt&lt;/code&gt; that would
mirror GitHub wiki content to a site that did not have a restrictive robots.txt.
I put it up at &lt;a href="https://github-wiki-see.rs"&gt;https://github-wiki-see.rs&lt;/a&gt;. I called it GitHub Wiki SEE for
GitHub Wiki Search Engine Enablement. It&amp;rsquo;s kind of a twisted wording of SEO as I
see it as enabling and not even &amp;ldquo;optimizing&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="whats-the-technology-behind-it"&gt;What&amp;rsquo;s the technology behind it?&lt;/h2&gt;
&lt;p&gt;The current iteration is a &lt;a href="https://github.com/actix/actix-web"&gt;Rust Actix Web&lt;/a&gt; application. It parses
the URL and makes requests out to GitHub to get the content. It then scrapes the
content for relevant HTML and reformats it to be more easily crawl-able. It then
serves the content to the search engine and users who come in from a search
engine with a link to the project page and reasoning at the bottom as a static
pinned element. Users aren&amp;rsquo;t meant to read that page and are only meant to
browse to the original GitHub page.&lt;/p&gt;
&lt;p&gt;With these choices, I could meet these requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hosted it on Google Cloud Run just in case it doesn&amp;rsquo;t become popular.&lt;/li&gt;
&lt;li&gt;Made it easy to deploy.&lt;/li&gt;
&lt;li&gt;Made it cheap.&lt;/li&gt;
&lt;li&gt;Made it small to run on Cloud Run.&lt;/li&gt;
&lt;li&gt;Made it low maintenance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another considering was that it needed to not get rate limited by GitHub. To do
this, I made the server return an error and quit itself on Cloud Run if it
detected a rate limiting response to cycle to a new server with a new client IP
address. Unfortunately, this limited the number of requests each server could
handle at a time to a minimum of one.&lt;/p&gt;
&lt;h2 id="supercharging-it"&gt;Supercharging it&lt;/h2&gt;
&lt;p&gt;I originally built the project in March. I monitored the
&lt;a href="https://search.google.com/search-console/about"&gt;Google Search Console&lt;/a&gt; like a hawk to see how the openpilot stuff I had
contributed to was responding to the proxy setup. Admittedly, it got a few users
to click through here and there and I was pretty happy. For a month or two, I
was pretty satisfied. However, I kept on thinking about the
&lt;a href="https://github.com/isaacs/github/issues/1683"&gt;rest of the users who lamented this situation&lt;/a&gt;. While some did take
my offer to post a link somewhere on their blog, README, or tweet, the adoption
wasn&amp;rsquo;t as much as I had hoped.&lt;/p&gt;
&lt;p&gt;I had learned about &lt;a href="https://www.gharchive.org/"&gt;GH Archive&lt;/a&gt; which is a project that archived
every event of GitHub since about 2012. I also learned that the project also
mirrors its archive on &lt;a href="https://cloud.google.com/bigquery/"&gt;BigQuery&lt;/a&gt;. BigQuery allows you to search large
datasets with SQL. Using BigQuery, I was able to determine that about 2,500
GitHub Wiki pages get edited every day. In a week, about 17,000. A month? 66,000
pages. My furor at the blocking of indexing increased.&lt;/p&gt;
&lt;p&gt;I decided to throw $45 at the issue and run a big mega-query and queried all of
GH Archive. Over the lifetime of GH Archive, 4 million pages were edited. I
dumped this into a queue and processed it with Google Cloud Functions to run the
&lt;code&gt;HEAD&lt;/code&gt; query on all the pages in the list and re-exported it to BigQuery. 2
million pages were still there.&lt;/p&gt;
&lt;p&gt;You can find the results of this in these public tables on &lt;a href="https://cloud.google.com/bigquery/"&gt;BigQuery&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;github-wiki-see:show.checked_touched_wiki_pages_upto_202106&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Checked with &lt;code&gt;HEAD&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;github-wiki-see:show.touched_wiki_pages_upto_202106&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this &lt;code&gt;checked_touched_wiki_pages_upto_202106&lt;/code&gt; table, I generated 50MB of
compressed XML sitemaps for the GitHub Wiki SEE pages. I called them seed
sitemaps. The source for this can be found
&lt;a href="https://github.com/nelsonjchen/github-wiki-see-rs-sitemaps"&gt;in the sitemap generator repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;BigQuery is expensive, so I made another sitemap series for continuous updates.
An earlier version used BigQuery, but the new version runs a daily GitHub
Actions job that downloads the last week of events from GH Archive, processes
them for unique Wiki URLs and exports them to a sitemap. This handles any
ongoing GitHub Wiki changes. The source for this can also be found
&lt;a href="https://github.com/nelsonjchen/github-wiki-see-rs-sitemaps"&gt;in the sitemap generator repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-results"&gt;The results&lt;/h2&gt;
&lt;p&gt;I am getting thousands of clicks and millions of impressions every day. GitHub
Wiki SEE results are never at the top and average a position of about 29 or the
third page. The mirrored may not rank high and I don&amp;rsquo;t care as the original
content was never going to rank high anyway so the content appearing at all in
the search results of thousands of users even if it was sort of deep satisfies
me.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;All in all, a pretty fun project. I am really happy with the results.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My contributions to the openpilot wiki are now indexed.&lt;/li&gt;
&lt;li&gt;Everyone else&amp;rsquo;s contributions to their wikis are now indexed as well.&lt;/li&gt;
&lt;li&gt;I learned a few things or two about writing a Rust web app.&lt;/li&gt;
&lt;li&gt;I learned a lot about SEO&lt;/li&gt;
&lt;li&gt;I feel I&amp;rsquo;ve helped a lot of people who need to find a diamond in the rough and
the diamond isn&amp;rsquo;t in the URL.&lt;/li&gt;
&lt;li&gt;I feel a little warm that I have a high traffic site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have no intention of putting ads on the site. It costs me about $25 a month to
run but for the high traffic it gets, the peace of mind I&amp;rsquo;ve acquired, and the
good feelings of helping everybody searching Google, it is worth it.&lt;/p&gt;
&lt;p&gt;I did add decommissioning steps. If it comes to it, I&amp;rsquo;ll make the site just
directly redirect but after GitHub takes Wikis off their &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="things-i-learned"&gt;Things I Learned&lt;/h2&gt;
&lt;p&gt;This is a grab-bag of stuff I learned along the way.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sitemaps are a great way to get search engine traffic to your site.&lt;/li&gt;
&lt;li&gt;Large, multi-million page sitemaps can take many months to process. Consider
yourself lucky if it parses 50,000 URLs a day.
&lt;ul&gt;
&lt;li&gt;The Google Search Console saying &amp;ldquo;failed to retrieve&amp;rdquo; is a lie. It means to
say &amp;ldquo;processing&amp;rdquo; but this is apparently a long standing stupid bug.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GoogleBot is much more aggressive than Bing.
&lt;ul&gt;
&lt;li&gt;Google&amp;rsquo;s Index of my proxy site is probably a lot larger than Bing&amp;rsquo;s by a
magnitude or two. For every 30, 40, or even 100 GoogleBot requests, I get
one Bing request.&lt;/li&gt;
&lt;li&gt;This is important for serving DuckDuckGo users as it is their index.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The Rust Tokio situation is a bit of a pain as I am stuck on a version of
Tokio that is needed for stable Actix-Web. As a result, I cannot use modern
Tokio stuff that modern Rust crates or libraries use. I may port it to Rocket
or something in the future.&lt;/li&gt;
&lt;li&gt;A lot of people search for certification answers and test answers on GitHub
Wikis.&lt;/li&gt;
&lt;li&gt;GitHub Wikis and GitHub are no exception to Rule 34.&lt;/li&gt;
&lt;li&gt;Cloud Run is great for getting many IP addresses to use and to swap out if any
die. The outgoing IP address is extremely ephemeral and changing it is a
simple matter of simply exiting if a rate limit is detected.
&lt;ul&gt;
&lt;li&gt;This might not always work. Just exit again if so.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GCP&amp;rsquo;s UI is nicer for manual use compared to AWS.&lt;/li&gt;
&lt;li&gt;The built-in logging capabilities and sink of GCP products are also really
nice. I can easily re-export and/or stream logs to BigQuery or to a Pub Sub
topic for later analysis. I can also just use the viewer on the UI to see what
is going on.&lt;/li&gt;
&lt;li&gt;I get fairly free monitoring and graphing from the GCP ecosystem. The default
graphs are very useful.&lt;/li&gt;
&lt;li&gt;Rust web apps are really expensive to compile on Google Cloud Build. I had to
use 32-core machines to get it to compile in underneath the default timeout of
10 minutes. I eventually sped it up and made it cheaper to build by building
on a pre-built image along with a smaller instance but it is still quite
expensive.&lt;/li&gt;
&lt;li&gt;The traffic from the aggressive GoogleBot is impressively large. Thankfully, I
do not have to pay the bandwidth costs.&lt;/li&gt;
&lt;li&gt;My sloppy use of the HTML Parser may be causing errors but RAM is fairly
generous. It&amp;rsquo;s Rust too so I don&amp;rsquo;t really have to worry that much about
garbage collection or anything similar.&lt;/li&gt;
&lt;li&gt;Google actually follows links it sees in the pages. When I started, it did
not. Maybe it does it only on more popular pages.&lt;/li&gt;
&lt;li&gt;The Rust code is really sloppy, but it still works.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>GitHub Wiki SEE</title><link>https://mindflakes.com/projects/github-wiki-see/</link><pubDate>Thu, 01 Apr 2021 00:00:00 +0000</pubDate><guid>https://mindflakes.com/projects/github-wiki-see/</guid><description>&lt;p&gt;&lt;a href="https://github-wiki-see.page/"&gt;https://github-wiki-see.page/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A project to get GitHub Wikis indexed by Google Search.&lt;/p&gt;
&lt;p&gt;Explanation is right on the front page.&lt;/p&gt;
&lt;p&gt;If a search engine can&amp;rsquo;t see it, it may as well be invisible.&lt;/p&gt;
&lt;p&gt;Long-term project involving Rust, BigQuery, Cloudflare Workers, and lots of random hosting.&lt;/p&gt;
&lt;p&gt;Probably one more the more visited sites on the internet and a continous effort to keep costs low.&lt;/p&gt;
&lt;p&gt;Has caused GitHub to revisit their policy on a limited subset of Wikis meeting some criteria.&lt;/p&gt;
&lt;p&gt;Will continue until all Wikis are indexed even if they are publically editable or don&amp;rsquo;t meet some star count criteria.&lt;/p&gt;
&lt;p&gt;Personally, I just got really dismayed all this documentation I was doing on a Wiki wasn&amp;rsquo;t visible to Google. Then I realized it was a good idea to make it visible. Then I realized others don&amp;rsquo;t realize it was invisible at all. This was a problem.&lt;/p&gt;</description></item><item><title>The short lived life of Breakout Bots for Zoom Meetings</title><link>https://mindflakes.com/posts/2020/09/12/the-short-lived-life-of-breakout-bots-for-zoom-meetings/</link><pubDate>Sat, 12 Sep 2020 19:42:00 -0800</pubDate><guid>https://mindflakes.com/posts/2020/09/12/the-short-lived-life-of-breakout-bots-for-zoom-meetings/</guid><description>&lt;p&gt;With the pandemic happening, it&amp;rsquo;s been tough for many organizations to adapt. We&amp;rsquo;re all supposed to be together! One way organizations have been staying together is by using Zoom Meetings.&lt;/p&gt;
&lt;p&gt;At work, we&amp;rsquo;ve been using a knock-off reseller version called RingCentral Meetings. From looking at their competitors, Zoom has pulled out all the stops to make their meeting experience the most efficient, resiliant, rather cheapest, and reliable experience.&lt;/p&gt;
&lt;p&gt;One of these features is &amp;ldquo;Breakout Rooms&amp;rdquo;. With breakout rooms, users can subdivide their meetings to make mini-meetings from a bigger meeting. For most of the year, there has been an odd restriction on Breakout Meetings though.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You cannot go to another breakout room as an attendee without the host reassigning you unless you are a Host or a Co-Host.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Obviously, this can result in a lot of load upon the poor user who is desginated the host. Even the Co-Hosts can&amp;rsquo;t assign users to another breakout room.&lt;/p&gt;
&lt;p&gt;So I made a bot:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/BreakoutRoomsBotForZoomMeetings"&gt;https://github.com/nelsonjchen/BreakoutRoomsBotForZoomMeetings&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/5363/92406673-79d2e080-f0ed-11ea-9953-5b7704811d1c.gif" alt="the bot rename in action"&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s quite a hack, but it basically controls a web client that is a Host and assigns users based on chat commands and on attendees renaming themselves.&lt;/p&gt;
&lt;p&gt;Last night, I just finished finally optimizing the bot. It should be able to handle hundreds of users and piles of chat commands.&lt;/p&gt;
&lt;p&gt;This morning I learned that Zoom will add native support for switching:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.reddit.com/r/Zoom/comments/irkm82/selfselect_breakout_room/"&gt;https://www.reddit.com/r/Zoom/comments/irkm82/selfselect_breakout_room/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It was fun, but as brief as the bot existed, the main purposes are numbered. Maybe someone can reuse the code to make other Zoom Meeting add-on/bots?&lt;/p&gt;
&lt;h2 id="technical-and-learnings"&gt;Technical and Learnings&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I knew about this issue for many months but I never made a bot because there wasn&amp;rsquo;t an API. I waited until I was very disatisified with the situation and then made the bot. I should have just done it, without the API.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Thankfully, by doing this quickly, I only really spent like a week of time on this total. With some of the tooling I used, I didn&amp;rsquo;t have to spend too long debugging the issues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The original implementation of this tool was a copy and paste into Chrome&amp;rsquo;s Console. This is a much worse experience for users than a Chrome extension. &lt;a href="https://github.com/khusmann"&gt;Kyle Husmann&lt;/a&gt; contributed a fork that extensionized the bot and it is totally something I&amp;rsquo;ll be stealing for future hacks like this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Zoom got to where they are by doing things their competitors did not and rather quickly. This feature/feedback response is great and is why they&amp;rsquo;ve been crushing. I feel this is a feature that would have taken their competitors possibly a year to implement. At this time, Microsoft Teams is also looking to implement Breakout Rooms.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Zoom&amp;rsquo;s web client uses React+Redux. They also use the Redux-Thunk middleware. They do stick some side-effects in places where things shouldn&amp;rsquo;t but whatever, it works. It did make integration very hard if not impossible at this layer for my bot. However, subscribing to state changes was and is very reliable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://rxjs-dev.firebaseapp.com/"&gt;RxJS&lt;/a&gt; is a nice implementation of the Reactive Pattern with Observables and stuff like with RX.NET. Without this, I could not fit the bot in so few lines of code with the reliability and usability that it currently has.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;With &lt;a href="https://rxjs-dev.firebaseapp.com/"&gt;RxJS&lt;/a&gt; and Redux&amp;rsquo;s store, I was able to subscribe to internal Redux state changes and transform them into streams of events. For example, the bot transform the streams of renames and chat message requests into a common structure that it then merges to be processed by later operators. Lots of code is shared and I&amp;rsquo;m able to rearrange and transform at will.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The local override functionality is great if you want to inject Redux Web Tools into an existing minified application. If you beautify the code, you can inject dev tools too. Here&amp;rsquo;s the line of code I replaced to inject Redux Dev Tools into Zoom&amp;rsquo;s web client.&lt;/p&gt;
&lt;p&gt;Previous:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;, &lt;span style="color:#a6e22e"&gt;s&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Object(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;compose&lt;/span&gt;)(Object(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;applyMiddleware&lt;/span&gt;)(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;, &lt;span style="color:#a6e22e"&gt;s&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; window.&lt;span style="color:#a6e22e"&gt;__REDUX_DEVTOOLS_EXTENSION_COMPOSE__&lt;/span&gt;(Object(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;applyMiddleware&lt;/span&gt;)(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This was super helpful in seeing how the Redux state changes when performing actions in a nice GUI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/farzher/fuzzysort"&gt;fuzzysort&lt;/a&gt; is great! I&amp;rsquo;m quite surprised at how unsurprising the results can be when looking up rooms by a partial or cut up name.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The underlying websocket connection is available globally as &lt;code&gt;commandSocket&lt;/code&gt;. If you observe the commands, they are just JSON and you can inject your own commands to control the meeting programatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Since Breakout Room status is sent back via WebSocket, the UI will automatically update. I did not have to go through the Redux store to update the Breakout Room UI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UI Automation of Breakout Room UI: ~100ms vs webSocket command: 2ms. Wow!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chat messages are encrypted and decrypted client-side before going out on the Websocket interface. This probably doesn&amp;rsquo;t stop Zoom from reading the chat messages since they are the ones who gave you the keys. I could have reimplemented the encryption in the bot but after running some replay attacks, the UI did not update. Without the UI updating, I didn&amp;rsquo;t feel it was safe for the host to not know what the bot sent out on their behalf.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Even if I had implemented the chat message encryption, the existing UI automation of chat messages hovered around 30ms. The profiler also showed that the overhead of automation wasn&amp;rsquo;t that much and much of the chat message encryption contributed to the 30ms. It wouldn&amp;rsquo;t have been much of a performance win if I implemented it myself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Possible Security-ish Issue&lt;/strong&gt; - Since it isn&amp;rsquo;t an acceptable bounty issue, and hosts can simply disable chat, I will disclose is possible to DOS the native client by simply spamming chat. If there is too much text, the native clients will lock up and require a restart. Native clients don&amp;rsquo;t drop old chat logs. Web clients don&amp;rsquo;t have this issue as they will drop old chat automatically. Last but not least, it is possible for the Host to disable chat in many ways to alleviate such an attack. I wasn&amp;rsquo;t sure if to let my load_test bot out there or not because of this but after reflection, I think it&amp;rsquo;s OK.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://rxjs-dev.firebaseapp.com/"&gt;RxJS&lt;/a&gt; is MVP. Or rather the Reactive stuff is MVP. If you can get it into a stream, it&amp;rsquo;s super powerful. Best yet, you can reuse your experience from other ecosystems. I was previously using Rx.NET for some stuff at work. I&amp;rsquo;ll be definitely looking at using the Python version of this in the future.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Though it did seem the differing Reactive stuff have different operators. The baselines are still great though and you can always port operators from one to another implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rx is really helped if you can use TypeScript. Unfortuntately, I never got to this stage.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, this was fun! Now onward to the next thing.&lt;/p&gt;</description></item><item><title>Reverse RDP into Windows on GitHub Actions</title><link>https://mindflakes.com/posts/2019/12/07/reverse-rdp-into-windows-on-github-actions/</link><pubDate>Sat, 07 Dec 2019 17:24:55 +0000</pubDate><guid>https://mindflakes.com/posts/2019/12/07/reverse-rdp-into-windows-on-github-actions/</guid><description>&lt;p&gt;Ever wonder what the Desktop of the Windows Runners on GitHub Actions looks like?&lt;/p&gt;
&lt;p&gt;Or perhaps you&amp;rsquo;re missing the ability to &lt;a href="https://www.appveyor.com/docs/how-to/rdp-to-build-worker/"&gt;RDP into build agents like on Appveyor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wrote some steps that use ngrok to make a reverse tunnel possible. I also turned on RDP if it wasn&amp;rsquo;t on already and set a password.&lt;/p&gt;
&lt;p&gt;Take a look here!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/reverse-rdp-windows-github-actions"&gt;https://github.com/nelsonjchen/reverse-rdp-windows-github-actions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/reverse-rdp-desktop.png" alt="GitHub Actions Desktop"&gt;&lt;/p&gt;</description></item><item><title>Stop Sims 2 Purple Soup or Pink Flashing Crashes on Windows 10 and Modern Hardware with D9VK</title><link>https://mindflakes.com/posts/2019/10/02/stop-sims-2-purple-soup-or-pink-flashing-crashes-on-windows-10-and-modern-hardware-with-d9vk/</link><pubDate>Wed, 02 Oct 2019 06:08:55 +0000</pubDate><guid>https://mindflakes.com/posts/2019/10/02/stop-sims-2-purple-soup-or-pink-flashing-crashes-on-windows-10-and-modern-hardware-with-d9vk/</guid><description>&lt;p&gt;Wrote a guide on how to fix the Purple Soup or Pink Flashing Crashes for
The Sims 2 on Windows 10 1903+ on powerful gaming computers. It&amp;rsquo;s great to make
this accessible again to gamers with powerful gaming computers.&lt;/p&gt;
&lt;p&gt;Guide Link: &lt;a href="https://docs.google.com/document/d/19JMr-FQSU3AlF7Kyvcrr7awTiHBDQSUNLG6qfaI6rOs/edit#"&gt;https://docs.google.com/document/d/19JMr-FQSU3AlF7Kyvcrr7awTiHBDQSUNLG6qfaI6rOs/edit#&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;General idea: Replace Microsoft DirectX 9 with D9VK.&lt;/p&gt;
&lt;p&gt;D9VK is actually actively developed and fixes bugs. It also translates graphics
to a stack that is more open and stable.&lt;/p&gt;
&lt;p&gt;So this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/sims2-purple-soup.jpg" alt="sims 2 purple soup"&gt;&lt;/p&gt;
&lt;p&gt;Turns to this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/thesims2_d9vk_rtx2080_win101903.jpg" alt="Fixed and proof of running"&gt;&lt;/p&gt;</description></item><item><title>Fast and lightweight headless Qt Installer from Qt Mirrors: aqtinstall</title><link>https://mindflakes.com/posts/2019/06/02/fast-and-lightweight-headless-qt-installer-from-qt-mirrors-aqtinstall/</link><pubDate>Sun, 02 Jun 2019 22:02:00 -0800</pubDate><guid>https://mindflakes.com/posts/2019/06/02/fast-and-lightweight-headless-qt-installer-from-qt-mirrors-aqtinstall/</guid><description>&lt;p&gt;As part of my work on the &lt;a href="https://github.com/debauchee/barrier/"&gt;Barrier project&lt;/a&gt;, we needed a way to build it with the [Qt installer]from an online CI/CD service. On some platforms, such as &lt;a href="https://www.appveyor.com/docs/windows-images-software/"&gt;Appveyor, Qt may be preinstalled&lt;/a&gt;. But on some other platforms such as &lt;a href="https://azure.microsoft.com/en-us/services/devops/pipelines/"&gt;Azure Pipelines&lt;/a&gt;, Qt may not be pre-installed. Nor on Travis. Nor on CircleCI. For Linux and macOS, there is a saving grace. The Qt libraries can be installed headlessly from their operating system repositories such as &lt;code&gt;apt&lt;/code&gt; or &lt;code&gt;brew&lt;/code&gt;. But this luxury does not exist as well on Windows. While Chocolatey does offer some Qt packages, they tend to ancient or non-official.&lt;/p&gt;
&lt;p&gt;There is a toolkit called &lt;a href="https://github.com/hasboeuf/cuteci"&gt;CuteCI&lt;/a&gt; that let you use the Qt Installer and script the GUI headlessly. It&amp;rsquo;s janky and big though. Additionally, to pin versions, you must download 3 to 5 GB of Qt Installer to install a pinned version of Qt.&lt;/p&gt;
&lt;p&gt;So, I found this: &lt;a href="https://lnj.gitlab.io/post/qli-installer/"&gt;https://lnj.gitlab.io/post/qli-installer/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Apparently it&amp;rsquo;s possible to mimic the Qt Installer from a Python script.&lt;/p&gt;
&lt;p&gt;To save it from possible bit-rot, &lt;a href="https://github.com/nelsonjchen/qli-installer"&gt;I forked it to GitHub&lt;/a&gt;. I added parallel downloading, Windows support, and a CI system. I pushed Azure-Pipelines and each commit could cause 100 jobs to run. Across all the various platforms. Azure Pipelines did not break a sweat.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/hasboeuf/cuteci"&gt;CuteCI&lt;/a&gt; is still extremely useful if you must use the official installer with official support. But the speeds with &lt;a href="https://github.com/nelsonjchen/qli-installer"&gt;my fork&lt;/a&gt; were insane. It would normally take many minutes to install Qt with CuteCI. My QLI-Installer fork? 10 or 20 seconds.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But it turns out there&amp;rsquo;s an even better installer than mine!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/miurahr/aqtinstall/"&gt;https://github.com/miurahr/aqtinstall/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aqtinstall&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Classy reusable Python structure&lt;/li&gt;
&lt;li&gt;On PyPi. You can do &lt;code&gt;pip install aqtinstall&lt;/code&gt; and install &lt;code&gt;aqtinstall&lt;/code&gt; for immediate use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is also another fork of &lt;code&gt;qli-installer&lt;/code&gt;. The CI system was a in a bit of a sorry state though. So I contributed matrix generated jobs to test &lt;code&gt;aqtinstall&lt;/code&gt; across Mac, Windows, and Linux. @miurahr was able to dial down the installation and testing madness too and ramp it up in some places. Actual Qt applications are built to test if &lt;code&gt;aqtinstall&lt;/code&gt; work. Additionally, there was the insight that mobile platforms generally always require the latest Qt version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So, if you want a fast and lightweight headless Qt installer that installs from Qt Mirrors and is continually tested, use &lt;code&gt;aqtinstall&lt;/code&gt;!&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Non-encumbered Windows Bonjour SDK DLLs and Libs</title><link>https://mindflakes.com/posts/2019/06/01/non-encumbered-windows-bonjour-sdk-dlls-and-libs/</link><pubDate>Sat, 01 Jun 2019 19:15:00 -0800</pubDate><guid>https://mindflakes.com/posts/2019/06/01/non-encumbered-windows-bonjour-sdk-dlls-and-libs/</guid><description>&lt;p&gt;This was one of those adventures where you find a problem and it snowballs from there.&lt;/p&gt;
&lt;p&gt;As part of my work on the &lt;a href="https://github.com/debauchee/barrier/"&gt;Barrier project&lt;/a&gt;, we needed a way to build it with the &lt;a href="https://developer.apple.com/bonjour/"&gt;Bonjour SDK&lt;/a&gt; from an online CI/CD service. &lt;a href="https://en.wikipedia.org/wiki/Bonjour_(software)"&gt;Bonjour&lt;/a&gt; is used by Barrier to discover other barrier hosts on the same network. On Windows, the SDK files are needed. Other platforms natively come with the library or similar APIs already preinstalled.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s an issue. The SDK is behind a login wall on Apple&amp;rsquo;s site. Rehosting it might be possible but it might violate some agreement within the installer. Luckily, the sources are open and are under very permissive licenses.&lt;/p&gt;
&lt;p&gt;To save some work, I reused the XBMC project&amp;rsquo;s fork and made &lt;a href="https://github.com/nelsonjchen/mDNSResponder"&gt;my fork of the SDK&lt;/a&gt;. I didn&amp;rsquo;t change any of XBMC&amp;rsquo;s changes, if any. I added a CI system to build the necessary libraries in the same manner that Apple did.&lt;/p&gt;
&lt;p&gt;So, if you need a free, Cloud CI system accessible Apple Windows Bonjour SDK, look at the releases in &lt;a href="https://github.com/nelsonjchen/mDNSResponder"&gt;my repo&lt;/a&gt;! The library files are there and users can fork my build to build the stubs their own way as well.&lt;/p&gt;</description></item><item><title>Barrier CI System WIP</title><link>https://mindflakes.com/posts/2019/05/30/barrier-ci-system-wip/</link><pubDate>Thu, 30 May 2019 22:02:00 -0800</pubDate><guid>https://mindflakes.com/posts/2019/05/30/barrier-ci-system-wip/</guid><description>&lt;p&gt;This was one of those adventures where you find a problem and it snowballs from there.&lt;/p&gt;
&lt;p&gt;I wanted to use &lt;a href="https://github.com/debauchee/barrier/"&gt;barrier&lt;/a&gt; which is a tool for sharing mouses between computers. It is a fork of Synergy, which had gone commercial.&lt;/p&gt;
&lt;p&gt;So, I noticed on their &lt;a href="https://github.com/debauchee/barrier/"&gt;GitHub repo&lt;/a&gt; that the builds for various platforms was inconsistent. OK, maybe I can help fix their CI systems.&lt;/p&gt;
&lt;p&gt;More worringly, there was some Appveyor CI system configured. It was not configured with the &lt;code&gt;appveyor.yml&lt;/code&gt; method and the build process was non-transparent. It was also failing.&lt;/p&gt;
&lt;p&gt;My current CI system of choice for open source projects is &lt;a href="https://azure.microsoft.com/en-us/services/devops/pipelines/"&gt;Microsoft&amp;rsquo;s Azure Pipelines&lt;/a&gt;. For open source projects on GitHub, they&amp;rsquo;ll provide 10 parallel jobs of &lt;em&gt;Mac&lt;/em&gt;, Linux, and &lt;em&gt;Windows&lt;/em&gt;. &lt;em&gt;Mac&lt;/em&gt; and &lt;em&gt;Windows&lt;/em&gt; usually only show up as premium options or are severely rate-limited. As far as I can tell, whatever cluster Microsoft has created for their service, it&amp;rsquo;s really not limited by capacity.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/debauchee/barrier/issues/293"&gt;The good news is that they were fans of it and I was able to move them.&lt;/a&gt; It&amp;rsquo;s still a work in progress. The work done here has draw in some general &amp;ldquo;improve the ecosystem&amp;rdquo; enhancements which I will elaborate on in future posts.&lt;/p&gt;</description></item><item><title>An Android Accessibility Service for Eventbrite Organizers: PassShout</title><link>https://mindflakes.com/posts/2019/03/15/an-android-accessibility-service-for-eventbrite-organizers-passshout/</link><pubDate>Fri, 15 Mar 2019 19:15:00 -0800</pubDate><guid>https://mindflakes.com/posts/2019/03/15/an-android-accessibility-service-for-eventbrite-organizers-passshout/</guid><description>&lt;p&gt;The Scene:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s Saturday. And it&amp;rsquo;s like so many people have free time on that date.&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;re manning a convention center ticket check-in booth.&lt;/li&gt;
&lt;li&gt;Your eyesight just isn&amp;rsquo;t the best. After looking at hundreds, if not
thousands of them, it can be a blur.&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;re using Android.&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s a long line and people have &lt;em&gt;different&lt;/em&gt; pass or ticket types,
sometimes even if they are in the same group.&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;re processing thousands of people.&lt;/li&gt;
&lt;li&gt;You might not even have a table.&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;re using Eventbrite Organizer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have a tool for you! It&amp;rsquo;s called &lt;a href="https://github.com/nelsonjchen/PassShout/"&gt;PassShout&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition to a chime, it&amp;rsquo;ll read out loud the pass/ticket type.&lt;/p&gt;
&lt;p&gt;This greatly reduces operational mistakes. PassShout is part of an
implementation of a method in occupational safety called
&lt;a href="https://en.wikipedia.org/wiki/Pointing_and_calling"&gt;Pointing and Calling&lt;/a&gt;. &lt;a href="https://en.wikipedia.org/wiki/Pointing_and_calling"&gt;PAC&lt;/a&gt; has been
&lt;a href="https://www.atlasobscura.com/articles/pointing-and-calling-japan-trains"&gt;adopted widely thoughout Asia and the NYC Metro&lt;/a&gt; to
reduce incidents on trains systems. By calling out the type, things move
faster and mistakes are reduced.&lt;/p&gt;
&lt;h2 id="implementation"&gt;Implementation&lt;/h2&gt;
&lt;p&gt;This is the first Android app I&amp;rsquo;ve written in Kotlin. It&amp;rsquo;s amazing what the
Android Accessibility API offers for third party programs to make things
more accessible. Unfortunately, there does not seem to be an equivalent
for iOS.&lt;/p&gt;
&lt;p&gt;Kotlin is really easy to read and write. I can see how it&amp;rsquo;s not Scala and
is closer to a usable modern Java. It&amp;rsquo;s basically the Swift of Android.&lt;/p&gt;</description></item><item><title>NC QEMU</title><link>https://mindflakes.com/projects/nc-qemu/</link><pubDate>Tue, 25 Dec 2018 00:00:00 +0000</pubDate><guid>https://mindflakes.com/projects/nc-qemu/</guid><description>&lt;p&gt;&lt;a href="https://dev.azure.com/nelsonjchen/NC%20QEMU/_build/latest?definitionId=6?branchName=master"&gt;&lt;img src="https://dev.azure.com/nelsonjchen/NC%20QEMU/_apis/build/status/nelsonjchen.nc-qemu?branchName=master" alt="Build Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="links"&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Azure Pipelines Build View: &lt;a href="https://dev.azure.com/nelsonjchen/NC%20QEMU/_build"&gt;https://dev.azure.com/nelsonjchen/NC%20QEMU/_build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub Repo: &lt;a href="https://github.com/nelsonjchen/nc-qemu/"&gt;https://github.com/nelsonjchen/nc-qemu/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Releases Download Link: &lt;a href="https://github.com/nelsonjchen/nc-qemu/releases"&gt;https://github.com/nelsonjchen/nc-qemu/releases&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Is this outdated and not matching the latest release of QEMU? Contact me.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="changes"&gt;Changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Azure Pipeline to build &lt;code&gt;x86_x64&lt;/code&gt; target QEMU in MSYS2 on Windows&lt;/li&gt;
&lt;li&gt;Upgraded to Capstone with fixes that allow building in MSYS2.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="about"&gt;About&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;NC QEMU&amp;rdquo; is a very lightly augmented and experimental fork of QEMU to build on
Azure Pipelines and with &lt;a href="https://docs.microsoft.com/en-us/virtualization/api/"&gt;Windows Hypervisor Platform (WHPX)&lt;/a&gt; support for
users who care the most about running the &lt;code&gt;x86_64&lt;/code&gt; target fast. This build only
cares about the &lt;code&gt;x86_64&lt;/code&gt; target and other targets are not built.&lt;/p&gt;
&lt;p&gt;The output is a zip file with some DLLs and &lt;code&gt;qemu-system-x86_64.exe&lt;/code&gt;. This QEMU
distribution can be run from a folder by itself. Alternatively,
&lt;code&gt;qemu-system-x86_64.exe&lt;/code&gt; can be dropped into &lt;a href="https://qemu.weilnetz.de/"&gt;QEMU for Window&lt;/a&gt;&amp;rsquo;s
installation directory at &lt;code&gt;C:\Program Files\qemu&lt;/code&gt;, replacing the existing
version. &lt;em&gt;Some features like USB network redirection might be missing though&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The pipeline provides a differently compiled QEMU compared to &lt;a href="https://qemu.weilnetz.de/"&gt;QEMU for
Windows&lt;/a&gt;&amp;rsquo;s own &lt;a href="https://qemu.weilnetz.de/doc/BUILD.txt"&gt;build instructions&lt;/a&gt; which are
cross-compiled on Linux. Notably, NC QEMU is built on a Microsoft-provided copy
of Windows in MSYS2 with access to the Windows SDK headers for WHPX distributed
by Microsoft. The &lt;a href="https://mingw-w64.org/doku.php"&gt;MingW64 Toolkit&lt;/a&gt; which QEMU for Windows is built
with has unfortunately currently not reproduced the WHPX headers in a free
software manner. Users who want a QEMU for Windows with WHPX support but aren&amp;rsquo;t
licensed nor want to accept the terms of installing the Windows 10 SDK are out
of luck.&lt;/p&gt;
&lt;p&gt;Another benefit is that this project provides a declaratively made and
executable pipeline that builds QEMU for Windows. Fork, setup an Azure Pipeline,
and adjust if needed. This can be considered a bit &amp;ldquo;executable documentation&amp;rdquo;
for building a QEMU x86_64 target of this nature. The build logs are public and
can be used as reference. For users having trouble building or configuring their
systems to build QEMU, this reproducible setup can be quite useful.&lt;/p&gt;
&lt;h2 id="why-qemuwhpx-for-x86_64"&gt;Why QEMU+WHPX for &lt;code&gt;x86_64&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Absolutely not exhaustive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Acceleration is great. You can reach near-native speeds with acceleration.&lt;/li&gt;
&lt;li&gt;WHPX is native to the OS. No foreign kernel drivers/modules/extensions needed.&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s great for users who don&amp;rsquo;t have access to &lt;a href="https://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager-intel-haxm"&gt;Intel&amp;rsquo;s HAX&lt;/a&gt; because they
either want Hyper-V and/or AMD support. With AMD CPUs being multi-core bargain
monsters, developers and power users on AMD are becoming more numerous.&lt;/li&gt;
&lt;li&gt;QEMU+WHPX boots Windows ISOs. &lt;a href="https://github.com/intel/haxm/issues/20"&gt;HAX currently does not.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;QEMU is easier to hack on, script, and developed by many organizations, not
one. WHPX support was &lt;em&gt;contributed by Microsoft&lt;/em&gt; but is probably most useful for
Google&amp;rsquo;s QEMU-based Android emulators!&lt;/li&gt;
&lt;li&gt;Users hacking on or working with QEMU in Windows can bring their work to Linux
KVM accelerated systems easier. And vice-versa.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="packer-whpx-workaround"&gt;Packer WHPX workaround&lt;/h2&gt;
&lt;p&gt;The executables provided here do work for &lt;a href="https://www.packer.io/docs/builders/qemu.html"&gt;Packer&lt;/a&gt;. Simply add the
directory &lt;code&gt;with qemu-system-x86_64.exe&lt;/code&gt; to the &lt;code&gt;PATH&lt;/code&gt;. Packer does not currently
recognize &lt;code&gt;whpx&lt;/code&gt; as a valid argument for the &lt;code&gt;acceleration&lt;/code&gt; key. The workaround
is to add it to &lt;code&gt;qemu-args&lt;/code&gt; as a manual argument.&lt;/p&gt;
&lt;p&gt;Additionally, the &lt;code&gt;cpu&lt;/code&gt; argument of QEMU does not support &lt;code&gt;cpu=host&lt;/code&gt; for &lt;code&gt;whpx&lt;/code&gt;.
Specify something supported manually.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Orbital (PS4 Emulator) Build instructions:
&lt;a href="https://github.com/AlexAltea/orbital/wiki/Building"&gt;https://github.com/AlexAltea/orbital/wiki/Building&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Gooey and PyInstaller for easy to write, distribute, and use scripts!</title><link>https://mindflakes.com/posts/2018/12/15/gooey-and-pyinstaller-for-easy-to-write-distribute-and-use-scripts/</link><pubDate>Sat, 15 Dec 2018 20:36:00 -0800</pubDate><guid>https://mindflakes.com/posts/2018/12/15/gooey-and-pyinstaller-for-easy-to-write-distribute-and-use-scripts/</guid><description>&lt;p&gt;What is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easy to write&lt;/li&gt;
&lt;li&gt;Easy to distribute and portable&lt;/li&gt;
&lt;li&gt;Super Powerful&lt;/li&gt;
&lt;li&gt;Cross Platform&lt;/li&gt;
&lt;li&gt;Puts computer novices and professionals on the same level?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think it&amp;rsquo;s a combination of &lt;a href="https://github.com/chriskiehl/Gooey"&gt;Gooey&lt;/a&gt; and &lt;a href="https://github.com/pyinstaller/pyinstaller"&gt;PyInstaller&lt;/a&gt;. If
you put those two together, you get all those above.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s Python, so it&amp;rsquo;s easy to write. There are so many guides online and a wide
assortment of libraries. Most importantly, you get libraries that have a
strong eye towards ergonomics. Python is a wonderful beginner&amp;rsquo;s language that
some people even call an executable psuedolanguage.&lt;/li&gt;
&lt;li&gt;PyInstaller bundles applications into a single folder or exe so it&amp;rsquo;s easy to
distribute.&lt;/li&gt;
&lt;li&gt;You get the full force of Python&amp;rsquo;s language and its ecosystem behind you.&lt;/li&gt;
&lt;li&gt;Python is cross platform, but Gooey&amp;rsquo;s GUI is also as well. Don&amp;rsquo;t be expecting
to make &amp;ldquo;Destiny&amp;rdquo;-style GUIs with it. But do be expecting a surprising amount
of native-ness.&lt;/li&gt;
&lt;li&gt;While PyInstaller does package the application up into a portable Python
script, the existing command line invokation is still left for professionals.
Simply add &lt;code&gt;--ignore-gooey&lt;/code&gt; to the list of arguments. Now it&amp;rsquo;s just a plain
old script.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href="https://github.com/nelsonjchen/gooey-pyinstaller-demo"&gt;Gooey PyInstaller Demo&lt;/a&gt; was presented at a DesertPy meetup in August
of 2018. It&amp;rsquo;s a project showing the development of a simple argument parsing
script all the way up to a portable GUI. &lt;del&gt;Future improvements to the demo include
some CI scripts and configurations.&lt;/del&gt; &lt;em&gt;This was completed on 2018-12-15&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Demo Project Link: &lt;a href="https://github.com/nelsonjchen/gooey-pyinstaller-demo"&gt;https://github.com/nelsonjchen/gooey-pyinstaller-demo&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Switched to Hugo</title><link>https://mindflakes.com/posts/2015/11/29/switched-to-hugo/</link><pubDate>Sun, 29 Nov 2015 00:37:59 -0800</pubDate><guid>https://mindflakes.com/posts/2015/11/29/switched-to-hugo/</guid><description>&lt;p&gt;&lt;em&gt;Wow&lt;/em&gt;! I think it has been nearly two years since I last updated this blog.
I have decided to change the blog a bit and port it over to &lt;a href="http://hugo.spf13.com"&gt;hugo&lt;/a&gt; which is
a static site generator written in Go. It is &lt;em&gt;very&lt;/em&gt; fast compared to &lt;a href="http://nanoc.ws/"&gt;nanoc&lt;/a&gt;
which I was using. I do not know if I quite agree that this is just as
customizable as &lt;a href="http://nanoc.ws/"&gt;nanoc&lt;/a&gt; but it is clearly faster. I was not taking too much
advantage of the previous &lt;a href="http://foundation.zurb.com"&gt;Foundation&lt;/a&gt; based flexibility of the &lt;a href="http://nanoc.ws/"&gt;nanoc&lt;/a&gt;
site and I believe it may had become a burden. Also, bitrot was beginning to
seriously set in. Luckily, it is a static site but it does make some tears shed
when you have to pin some really old gems and use a really old version of Ruby
to build and deploy the site. Unless the kernel syscalls change dramatically,
I will probably be able to compile with this version of &lt;a href="http://hugo.spf13.com"&gt;hugo&lt;/a&gt; indefinitely.&lt;/p&gt;
&lt;p&gt;Anyway, the &lt;code&gt;nanoc&lt;/code&gt; versions troubles me no more. I am using a &lt;a href="http://hugo.spf13.com"&gt;hugo&lt;/a&gt; version
of the site now. As an improvement, I have also setup automatic deployment of
my &lt;code&gt;master&lt;/code&gt; branch to Google&amp;rsquo;s storage and configured CloudFlare to provide TLS
and all the goodies that come with that. I guess I could also start using
something like &lt;a href="http://prose.io"&gt;prose.io&lt;/a&gt; to write my posts as well. I doubt I will go that
far! But, it is very nice to know that is possible.&lt;/p&gt;
&lt;p&gt;I am now just using a theme called &lt;a href="https://github.com/enten/hyde-y"&gt;hyde-y&lt;/a&gt; which is off-the-shelf. Of
course, I did customize it a little bit. &lt;a href="http://hugo.spf13.com"&gt;hugo&lt;/a&gt;&amp;rsquo;s overriding system is plain
and simple to use.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why would anyone want summaries on the front page? Just give the complete
content.&lt;/li&gt;
&lt;li&gt;The titles of the posts on the front page should have the same information.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I addressed all that, and while it was a bit of a hunt, it was fairly clear
what needed to be done.&lt;/p&gt;
&lt;p&gt;I do not think I will go the full mile with customizing &lt;code&gt;hugo&lt;/code&gt; like I did with
&lt;code&gt;nanoc&lt;/code&gt;. The complexity was hurting more than it helped. I guess I have
switched over to being more minimalist since I concieved the original site.
&lt;a href="http://foundation.zurb.com"&gt;Foundation&lt;/a&gt; is great if you need the kitchen sink and everything but it is
really overkill. It was also another maintenance burden with its rather
relentless upgrade schedule. I am taking it slow from here on out.&lt;/p&gt;</description></item><item><title>Running Windows XP using Libvirt inside Ubuntu inside VMWare Fusion</title><link>https://mindflakes.com/posts/2014/01/06/running-windows-xp-using-libvirt-inside-ubuntu-inside-vmware-fusion/</link><pubDate>Mon, 06 Jan 2014 18:09:02 -0700</pubDate><guid>https://mindflakes.com/posts/2014/01/06/running-windows-xp-using-libvirt-inside-ubuntu-inside-vmware-fusion/</guid><description>&lt;p&gt;So I&amp;rsquo;m investigating running Windows XP inside &lt;a href="https://libvirt.org/"&gt;&lt;code&gt;libvirt&lt;/code&gt;&lt;/a&gt; in Ubuntu on my MacBook for continuous integration testing
purposes with &lt;a href="https://www.jenkins.io/"&gt;Jenkins&lt;/a&gt;. I tried for many hours to get it working but Windows would not boot up or it would get stuck
on the &lt;code&gt;NTLDR cannot be found&lt;/code&gt; issue. As it turns out, I did not enable &amp;ldquo;Enable hypervisor applications in this
virtual machine&amp;rdquo;. I was under the assumption that I would not need to check that box as I wrongly assumed &lt;a href="https://www.qemu.org/"&gt;&lt;code&gt;qemu&lt;/code&gt;&lt;/a&gt;
would handle all the needed machine translation and that an error with bootup is not the fault of lacking the ability
to use a CPU feature to emulate something or the disk emulation being borked. With five different ISOs of various
pedigrees, I tried installing Windows XP with &lt;a href="https://virt-manager.org/"&gt;&lt;code&gt;virt-manager&lt;/code&gt;&lt;/a&gt; and all of them failed to bootup with various disk image
formats like &lt;code&gt;qcow2&lt;/code&gt; or &lt;code&gt;raw&lt;/code&gt;. It was only until I checked that checkbox did it then work.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s my surprise for today. I really wished I checked that checkbox earlier! It determines if your machine can
bootup and only using &lt;code&gt;qemu&lt;/code&gt; without &lt;a href="https://linux-kvm.org/"&gt;&lt;code&gt;kvm&lt;/code&gt;&lt;/a&gt; will not cut it for booting Windows XP inside libvirt inside Ubuntu inside
&lt;a href="https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion"&gt;VMware Fusion&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>BeamNG DRIVE on Mac OS X with CrossOver</title><link>https://mindflakes.com/posts/2013/08/03/beamng-drive-on-mac-os-x-with-crossover/</link><pubDate>Sat, 03 Aug 2013 16:06:37 -0700</pubDate><guid>https://mindflakes.com/posts/2013/08/03/beamng-drive-on-mac-os-x-with-crossover/</guid><description>&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt; 2015-11-28: Hey BeamNG forum users. These instructions worked for me way
back when I was using CrossOver. You might be able to get similar or better results
with Wineskin Winery or some other Wine wrapper. And those are &lt;em&gt;free&lt;/em&gt; too!
I don&amp;rsquo;t run BeamNG on my local laptop anymore nowadays but I &lt;a href="(http://lg.io/2015/07/05/revised-and-much-faster-run-your-own-highend-cloud-gaming-service-on-ec2.html)"&gt;Game on Amazon EC2&lt;/a&gt;
and it works fairly well with BeamNG at a very minimal cost.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/beamng-crossover.jpg" alt="beamng on crossover"&gt;&lt;/p&gt;
&lt;p&gt;Gotta make this quick. This post will change.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how to get &lt;a href="http://www.beamng.com/drive/"&gt;BeamNG Drive&lt;/a&gt; working in Mac OS X. I&amp;rsquo;m sure there are equivilent instructions for &lt;a href="http://wineskin.urgesoftware.com/tiki-index.php?page=Wineskin%2C+Play+your+favorite+Windows+games+on+Mac+OS+X+without+needing+Microsoft+Windows"&gt;Wineskin&lt;/a&gt;
but I have no time to find out.&lt;/p&gt;
&lt;p&gt;Make a new bottle in Crossover. Make sure it is a Windows Vista bottle. Run the Wine configuration setting inside it
and set the version to Windows 7 in one of the sheets. Enable the Mac Driver so you don&amp;rsquo;t run it through X11.&lt;/p&gt;
&lt;p&gt;Install the Modern DirectX runtime into it. Install BeamNG into it. Mute your speakers. Change the audio options to
use OpenAL for output. Unmute speakers. Enjoy.&lt;/p&gt;
&lt;p&gt;So far this works for the tech demo. I&amp;rsquo;m going to try it on Alpha later.&lt;/p&gt;
&lt;p&gt;UPDATE: It works on Alpha. Yay!&lt;/p&gt;</description></item><item><title>Mount USB devices in Virtualbox-based Vagrant</title><link>https://mindflakes.com/posts/2013/07/24/mount-usb-devices-in-virtualbox-based-vagrant/</link><pubDate>Wed, 24 Jul 2013 17:03:05 -0700</pubDate><guid>https://mindflakes.com/posts/2013/07/24/mount-usb-devices-in-virtualbox-based-vagrant/</guid><description>&lt;p&gt;If you want to mount USB in &lt;a href="http://virtualbox.org"&gt;Virtualbox&lt;/a&gt;, you have to do this &lt;a href="https://gist.github.com/nelsonjchen/c09924feb0830415a1d0#file-solution"&gt;solution&lt;/a&gt;. At
the time of this post, a typical Google search for this would go to a solution
at advocated using the &lt;code&gt;attachusb&lt;/code&gt; command in &lt;code&gt;VboxManage&lt;/code&gt; in the &lt;code&gt;Vagrantfile&lt;/code&gt;
provider customization section. This will not work because the VM is off at the
time of bootup. It looks like the original author of this gist did not repost
his solution back to the thread.&lt;/p&gt;
&lt;p&gt;The solution, in other words, is to use USB filters to automatically connect
devices to your VM. USB filters can be added while the machine is powered off.
They&amp;rsquo;ll be applied upon boot or, in this case, &lt;code&gt;vagrant up&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>My current vagrant setup</title><link>https://mindflakes.com/posts/2013/06/22/my-current-vagrant-setup/</link><pubDate>Sat, 22 Jun 2013 20:09:42 -0700</pubDate><guid>https://mindflakes.com/posts/2013/06/22/my-current-vagrant-setup/</guid><description>&lt;p&gt;I firmly believe that &lt;a href="http://vagrantup.com"&gt;Vagrant&lt;/a&gt; is the quickest way from nothing to a running and preconfigured development
environment on any machine and especially Macs. For me, the &lt;a href="http://docs.vagrantup.com/v2/why-vagrant/"&gt;&amp;lsquo;works on my machine&amp;rsquo; problem&lt;/a&gt; is the biggest reason I
run Vagrant. Day-to-day though, Vagrant is probably the easiest to use UI for &lt;a href="https://www.virtualbox.org/%E2%80%8E"&gt;Virtualbox&lt;/a&gt;. If my work actually
had money to give me for the &lt;a href="http://www.vagrantup.com/vmware"&gt;VMWare plugin&lt;/a&gt;, I believe it would be a better UI for VMWare Fusion as well.&lt;/p&gt;
&lt;h3 id="the-basics"&gt;The Basics&lt;/h3&gt;
&lt;p&gt;This is enough to get started with Vagrant and to reap the rewards.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.virtualbox.org/%E2%80%8E"&gt;Virtualbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.vagrantup.com/vmware"&gt;Vagrant&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s great and all, but these are the basics. At the very least, you&amp;rsquo;ll be able to bring up some boxes that don&amp;rsquo;t
require special plugins up.&lt;/p&gt;
&lt;h3 id="frills"&gt;Frills&lt;/h3&gt;
&lt;p&gt;You don&amp;rsquo;t need these but I do! I usually build my &lt;a href="http://www.vagrantup.com/vmware"&gt;Vagrant&lt;/a&gt; boxes with &lt;a href="http://www.opscode.com/chef/"&gt;Opscode Chef&lt;/a&gt;,
a configuration management system. For reference, a Chef cookbook is a series of statements about how a machine
should be setup.&lt;/p&gt;
&lt;p&gt;Most of these frills are plugins. To build the boxes I make, you&amp;rsquo;ll usually have to install or use these.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://berkshelf.com/"&gt;Berkshelf&lt;/a&gt; is a dependency resolution manager for Chef. I use Berkshelf as a gem along with the corresponding
&lt;a href="https://github.com/riotgames/vagrant-berkshelf"&gt;vagrant plugin&lt;/a&gt;. With this, when I run &lt;code&gt;berks cookbook &amp;lt;name&amp;gt;&lt;/code&gt;, I can make a Virtual Machine that can be created
and destroyed quickly from scratch for whatever purpose. I could do &lt;code&gt;vagrant init&lt;/code&gt; but &lt;code&gt;berks cookbook&lt;/code&gt; has it beat
by creating a directory structure that&amp;rsquo;s pretty much a Chef cookbook. Even if I don&amp;rsquo;t intend to redistribute said cookbook,
the VM made is perfectly fine for tryout purposes.&lt;/li&gt;
&lt;li&gt;I like using the &lt;a href="https://github.com/opscode/bento"&gt;Opscode Bento&lt;/a&gt; boxes. They are minimal and they have already been uploaded to S3 on Opscode&amp;rsquo;s
dime. In a &lt;code&gt;Vagrantfile&lt;/code&gt;, you can set the box URL for a Vagrant basebox to be downloaded. These boxes are great.&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t use the Opscode Bento boxes without the &lt;a href="https://github.com/schisamo/vagrant-omnibus"&gt;Vagrant Omnibus plugin&lt;/a&gt;. Those boxes &lt;em&gt;do not include Chef&lt;/em&gt;
so you must install Chef at runtime.&lt;/li&gt;
&lt;li&gt;Just so you don&amp;rsquo;t get warnings about the Virtualbox additions being out of date,
there&amp;rsquo;s a &lt;a href="https://github.com/opscode/bento"&gt;Vagrant plugin to automatically update the guest additions if needed&lt;/a&gt;. This one is really &lt;em&gt;optional&lt;/em&gt;
and it&amp;rsquo;s use just surpresses that warning you get if you bring up a vagrant box with old guest additions.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="the-future"&gt;The Future&lt;/h3&gt;
&lt;p&gt;In the future, I&amp;rsquo;ll like to be able to test my boxes to make sure they stay working as the things they pull from the
internet change. For this, there&amp;rsquo;s &lt;a href="https://github.com/opscode/test-kitchen"&gt;Test Kitchen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, it&amp;rsquo;s still really cutting edge. However, there are &lt;a href="https://github.com/opscode/guard-kitchen"&gt;guard plugins&lt;/a&gt; and this &lt;a href="http://starkandwayne.com/articles/2013/05/07/tdd-your-devops-with-test-kitchen/"&gt;Youtube Video and
blog post&lt;/a&gt;. That video is very much a must see for anybody who appreciates TDD.&lt;/p&gt;
&lt;p&gt;Full integration testing on your own laptop is very attractive to me. I keep mine plugged in and I find it disturbing
that the rest of the cores on this MacBook Pro just lie cool.&lt;/p&gt;
&lt;p&gt;And also, maybe if I get some cash, I might drop some money on the VMWare plugin and VMWare Fusion. If I want to
simulate multiple servers at a time and heavier loads, it would make that much faster.&lt;/p&gt;</description></item><item><title>A Windows "QuickStart"</title><link>https://mindflakes.com/posts/2013/05/26/a-windows-quickstart/</link><pubDate>Sun, 26 May 2013 20:33:11 -0700</pubDate><guid>https://mindflakes.com/posts/2013/05/26/a-windows-quickstart/</guid><description>&lt;p&gt;I paid Swish $25 and $8 shipping for &lt;a href="http://www.swish.com/details/devkit/"&gt;this&lt;/a&gt; two months ago. I think I had my
share donated to the Khan Academy.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/quickstart-box.jpg" alt="The box"&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s the &amp;ldquo;Windows Quickstart Kit for Mac Developers&amp;rdquo;. It includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A full Windows 8 Pro license&lt;/li&gt;
&lt;li&gt;A Parallels Desktop license&lt;/li&gt;
&lt;li&gt;A USB Stick with an ISO on it with a real Microsoft Certificate of Authenticity. Genuine Windows!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;m not sure why Microsoft saw fit to spend two months packaging a &amp;ldquo;quickstart&amp;rdquo;. Why Microsoft did not see fit to
provide this quickstart through digital distribution is beyond me. They could have emailed a link to some keys and
ISOs and maybe even let a distributor like Digital River do this. Instead, they saw fit to contract this work out to
a physical media distribution company. It seems with these charity-ware deals that the best way to donate money is to
allocate a large chunk to some physical distributor.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m so stoked to try and develop for Windows Phone. I&amp;rsquo;m going to virtualize Windows 8 and develop a native app for
a platform on which I have no way to run natively. I&amp;rsquo;m also going to jump over some chairs in celebration.&lt;/p&gt;
&lt;p&gt;Nuh uh, virtualization is cheating and is really anti-dogfood. If I have to boot up a RAM hungry OS to RUN the app,
it&amp;rsquo;s a failure. If I have to develop the app inside a virtualized OS, that would be even more of a failure since I
would have to bear with the performance and non-nativeness penalty. There&amp;rsquo;s no way a good app can come out with so
many barriers like that. Google realized that its Eclipse toolkit was a problem and
&lt;a href="http://developer.android.com/sdk/installing/studio.html"&gt;switched&lt;/a&gt;
to IntelliJ IDEA to reduce their barriers and gain an awesome IDE in the meantime. I highly doubt Microsoft will
provide a free cross-platform and light SDK. The technical barriers are just too high. It&amp;rsquo;s totally not like their
Xbox where game developers are used to putting up with shit like that. Mobile developers are in general more finicky
and prefer native tools. I&amp;rsquo;m not sure if this is still the case now but the Android team saw fit at one point to
maintain the Android compilation toolchain for the entire OS &lt;em&gt;on&lt;/em&gt; OS X. You can compile a Linux system on OS X!&lt;/p&gt;
&lt;p&gt;With that said, this cool cardboard box is all that remains. It would be well designed if it never existed. It&amp;rsquo;s way
too late for that though.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s made up of two big pieces. After ripping off the plastic wrapper, there&amp;rsquo;s a windows logo cutout sleeve thing to
keep the box with a flap closed. It&amp;rsquo;s a pretty retail package for something that is only sold online.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/quickstart-box-two-pieces.jpg" alt="two pieces"&gt;&lt;/p&gt;
&lt;p&gt;When you open the box, you have a welcome card with some very basic instructions on the back on the left and some
coupon card things on the right.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/quickstart-box-open-box.jpg" alt="open box"&gt;&lt;/p&gt;
&lt;p&gt;Beneath the welcome card is a USB drive. It has a Microsoft certificate of authenticity on it,
so you know its real and has real Microsoft binary bits. There&amp;rsquo;s also a tracking ID on it too. The Microsoft logo is
embedded in plastic on the back and not some cheap printer job. It&amp;rsquo;s certainly done with style to make the USB drive
seem to be actually worth something.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/quickstart-box-beneath-the-card.jpg" alt="beneath the card"&gt;&lt;/p&gt;
&lt;p&gt;On the USB Drive is just an ISO. There are no other files The drive itself isn&amp;rsquo;t bootable. It is formatted with NTFS
though. At the very least, it&amp;rsquo;s readable on all platforms. It also enumerates as a device of the name Windows 8 Pro
in system profiler. Quite a Matroska doll. I guess having an ISO is easier than mounting a USB drive in a virtual
machine in terms of instructional material.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/quickstart-box-drive-contents.jpg" alt="drive contents"&gt;&lt;/p&gt;
&lt;p&gt;The cards on the right had keys on them. They&amp;rsquo;re business card sized and feel like them too.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/quickstart-box-key-cards.jpg" alt="key cards"&gt;&lt;/p&gt;
&lt;p&gt;On the back were some keys. Why they felt the need to kill all these trees and grow soy beans to produce the ink for
printing on said dead tree material to wrap around these numbers is beyond me.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mindflakes.com/images/quickstart-box-keycodes.jpg" alt="key codes"&gt;&lt;/p&gt;
&lt;p&gt;It took two months for these two alphanumeric strings to get here. The lack of timeliness on delivering these two
codes and an ISO does not look well for Microsoft&amp;rsquo;s Windows efforts at all. It would take 30 minutes to illegally
obtain these materials. 30 minutes is far less than two months. Oh, I almost forgot,
they don&amp;rsquo;t even include Parallels in the package. You will have to go download that from Parallel&amp;rsquo;s site.
What a &amp;ldquo;quickstart&amp;rdquo; indeed.&lt;/p&gt;</description></item><item><title>Here's a compiled version of Keycastr.</title><link>https://mindflakes.com/posts/2013/05/22/heres-a-compiled-version-of-keycastr./</link><pubDate>Wed, 22 May 2013 13:02:12 -0700</pubDate><guid>https://mindflakes.com/posts/2013/05/22/heres-a-compiled-version-of-keycastr./</guid><description>&lt;blockquote&gt;
&lt;p&gt;Update: Keycastr is currently maintained at &lt;a href="https://github.com/keycastr/keycastr"&gt;github.com/keycastr/keycastr&lt;/a&gt;. Please use that repository first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I just compiled a version of keycastr and put it here since I could not find a precompiled version that&amp;rsquo;s relatively
new. The release artifacts are available here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nelsonjchen/keycastr/releases"&gt;Keycastr releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Keycastr is used for presentational demos. It&amp;rsquo;s free and the source is here in this &lt;a href="https://github.com/nelsonjchen/keycastr"&gt;GitHub repo&lt;/a&gt;. You will need a
modern version of Xcode to compile it.&lt;/p&gt;
&lt;p&gt;The version that is compiled is by &lt;a href="https://github.com/creemama"&gt;creemama&lt;/a&gt;. I only blindingly updated the settings
with Xcode recommendations. This &amp;ldquo;works for me&amp;rdquo; on OS X 10.8.3.&lt;/p&gt;</description></item><item><title>Boot Camp, Windows 7, and USB Installation Gotchas</title><link>https://mindflakes.com/posts/2013/05/13/boot-camp-windows-7-and-usb-installation-gotchas/</link><pubDate>Mon, 13 May 2013 15:23:28 -0700</pubDate><guid>https://mindflakes.com/posts/2013/05/13/boot-camp-windows-7-and-usb-installation-gotchas/</guid><description>&lt;p&gt;A few days ago, I decided to pull the trigger and install Windows on my Retina
MacBook Pro at a LAN Party. It&amp;rsquo;s a perfectly fine machine with a decent
graphics card and CPU. I started Boot Camp Assistant and followed through with
the instructions to install from the USB drive using the ISO of Windows 7 I had
on my Desktop. &lt;em&gt;If you follow Apple&amp;rsquo;s steps to the letter, your installation of
Windows will never proceed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you do follow the steps, you will most certainly get an error of &amp;ldquo;Setup was
unable to create a new system partition or locate an existing system partition.
&amp;quot; when you select the BootCamp partition that you had just formatted in the
Windows installer on your Mac. This can occur on PCs too but basically the
issue is that the USB drive is initialized as another valid bootable disk. If
you were to Google this, you will find
&lt;a href="http://social.technet.microsoft.com/Forums/en-US/w7itproinstall/thread/9e18e169-f77e-4026-b22f-f602e670d55c/"&gt;solutions&lt;/a&gt;
online that involve yanking out the USB drive and reinserting it before
pressing &amp;ldquo;Install Now&amp;rdquo; on the Windows 7 installation screen. This &amp;ldquo;Install Now&amp;rdquo;
screen is a big centered button that prompts your to install now and the arrow
is set inside a blue circle that&amp;rsquo;s like a gem.&lt;/p&gt;
&lt;p&gt;This is where Apple&amp;rsquo;s helpfulness can get in your way. By default, Boot Camp
Assistant copies the Boot Camp support files to your USB drive. It also adds an
&lt;code&gt;Autounattend.xml&lt;/code&gt; file to the root of your drive to automatically install Boot
Camp support drivers and utilities in Windows. With &lt;code&gt;Autounattend.xml&lt;/code&gt; present,
you will not have the opportunity to yank out and reinsert the USB drive before
continuing. Instead, the setup just proceeds as if that button was pressed.
While this is a good file for disc installations, it causes a massive problem
for USB installation which is most likely to be the case for all new Macs from
now on since they do not include disc drives by default. If you try to ask
Apple to &amp;lsquo;fix&amp;rsquo; this, they will just blame Microsoft which admittedly isn&amp;rsquo;t
blame free either.&lt;/p&gt;
&lt;p&gt;To solve this problem, move or delete &lt;code&gt;Autounattend.xml&lt;/code&gt; on the root of the USB
drive and proceed with the normal steps afterwards . After Windows is done
installing and since &lt;code&gt;Autounattend.xml&lt;/code&gt; was nullified, you will have to run the
Boot Camp support installer from the BootCamp folder on the USB drive.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a shame Apple&amp;rsquo;s instructions just do not work. The fix required would
probably mean removing &lt;code&gt;Autounattend.xml&lt;/code&gt; functionality for USB Drive
installations. In the meantime, this worked for me.&lt;/p&gt;</description></item><item><title>Reddit Link: LEARN VERSION CONTROL if you are in UCSB CS!</title><link>https://mindflakes.com/posts/2013/05/07/reddit-link-learn-version-control-if-you-are-in-ucsb-cs/</link><pubDate>Tue, 07 May 2013 16:19:28 -0700</pubDate><guid>https://mindflakes.com/posts/2013/05/07/reddit-link-learn-version-control-if-you-are-in-ucsb-cs/</guid><description>&lt;p&gt;Here&amp;rsquo;s a comment I wrote on /r/UCSantaBarbara a month ago. Learning Git is
a very valuable tool for any aspiring or lazy programmer.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.reddit.com/r/UCSantaBarbara/comments/1b93tr/ucsb_cs_faq/"&gt;UCSB CS FAQ comment thread&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At my current place of somewhat employment, the candidates for our internship
offering job did not know how to use Git. I hope my post will have changed
something.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re new to Git, &lt;a href="https://git-scm.com/"&gt;git-scm.com&lt;/a&gt; is still a good starting point.&lt;/p&gt;</description></item><item><title>And suddenly, nearly a decade later, a blog appears! Again.</title><link>https://mindflakes.com/posts/2013/04/14/and-suddenly-nearly-a-decade-later-a-blog-appears-again./</link><pubDate>Sun, 14 Apr 2013 22:05:53 -0700</pubDate><guid>https://mindflakes.com/posts/2013/04/14/and-suddenly-nearly-a-decade-later-a-blog-appears-again./</guid><description>&lt;p&gt;I&amp;rsquo;m ashamed to say I&amp;rsquo;ve neglected this domain and site for 9 years. In that
time I&amp;rsquo;ve gone through high school and university in that time. In each period,
I&amp;rsquo;ve attempted &lt;a href="http://web.archive.org/web/*/http://mindflakes.com"&gt;putting up blogs, forums, another blog, and other similar
nonsense&lt;/a&gt;. I did not follow
through with whatever resolutions I may have made to keep the site maintained
or write down the latest results from my last screwing around with my projects.
Basically, for the past 9 years, I don&amp;rsquo;t really have much to show for it other
than being able to rattle off some helpful anecdote from experiences I have
never written down here and there.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remember kids, the only difference between screwing around and Science is
writing it down.&lt;/p&gt;
&lt;p&gt;&amp;ndash; Adam Savage, &lt;a href="http://www.youtube.com/watch?v=BSUMBBFjxrY"&gt;Video&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I thought I was conducting Science for the past few years; in reality, I&amp;rsquo;ve
only been really screwing around.&lt;/p&gt;
&lt;p&gt;Most of my experiments in the past few years have also ended in spectacular
failures. I never wrote about my failures even during the times I had a blog
available. This is where my fails turned into an epic fail. I recently watched
a &lt;a href="http://www.ted.com/talks/ben_goldacre_battling_bad_science.html"&gt;TED talk&lt;/a&gt;
on this and learned that this phenomenon is called research bias. I should not
have withheld what I had learned from failing. My memory of my failures only
fade away until someone mentioned it and I rattle off some anecdote. This
obviously won&amp;rsquo;t scale and does not do much to help me demonstrate any skills
when I can&amp;rsquo;t even get my foot in the door.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to change that with the launch of my new site. I&amp;rsquo;ll be honest here,
this is very much a career move. However, it is also a personal one for reasons
I&amp;rsquo;ve outlined above. I can&amp;rsquo;t let those many hours of screwing around go to
waste! Prospective employers and/or clients love to hire people who do
Science, not screwing around. And I think this site will fix that.&lt;/p&gt;
&lt;p&gt;My new site is designed is designed so much as to be a portfolio and as a blog.
My portfolio is small as I have little to show and my blog has a meager amount
of posts. As a foundation though, the site is solid and is totally ready to
showcase more of my skills.&lt;/p&gt;
&lt;h3 id="because-of-hope"&gt;Because of Hope&lt;/h3&gt;
&lt;p&gt;I started the project to fix up my personal site in September 2012.&lt;/p&gt;
&lt;p&gt;At that time I had heard about this static site generator called &lt;code&gt;nanoc&lt;/code&gt;. I had
taken down previous versions of this site since I could not be bothered to
maintain the PHP beast that is called Wordpress. Also, there was little hope of
me hacking on Wordpress to change its default style to something that better
demonstrates my skills. Stock Wordpress themes just do not exhibit confidence
in web development. Also, learning PHP is just something I&amp;rsquo;me not too
interested in doing. The horror stories of PHP may be FUD, but I&amp;rsquo;ve experienced
and witnessed them myself in a past lifetime. &lt;code&gt;nanoc&lt;/code&gt; answers both of these
problems. I can hack Ruby and static HTML pages on a server do not have moving
parts. &lt;code&gt;nanoc&lt;/code&gt; is written in Ruby and generates static HTML pages for
uploading onto a server. Compared to a beast like Wordpress, it is also very
transparent. The nanoc guys can better explain their kind of product (static
site generators) &lt;a href="http://nanoc.ws/about/#why-static"&gt;here&lt;/a&gt;. I did have
a problem in that I had absolutely no experience with &lt;code&gt;nanoc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In September 2012, I answered a request for help from an organization
named Because of Hope at the beginning of my last quarter at UCSB. Because of
Hope is an organization that helps children and women in Uganda through
sustainable methods. Here&amp;rsquo;s the email that was sent to the UCSB Computer
Science jobs list:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My name is Natalie Lemonnier and I graduated from UCSB in 2011. Since
graduating, two of my friends and I have started a nonprofit here in
Goleta called Because of Hope (BOH) that is working to empower women in
Uganda. I am emailing you because our nonprofit is getting ready to
launch a new Student Sponsorship campaign, which will require making
some changes to our website, and I was wondering if you knew of anyone
(student, faculty or otherwise) who might be interested in helping us
make these changes. I am not sure if websites are your area of focus,
but I just wanted to contact you and see if you could point us in the
direction of someone who might be able to help.&lt;/p&gt;
&lt;p&gt;Originally, our friend built our site from scratch. However, he is no
long working with us and none of us have any web background to make the
necessary changes. I believe that some of the fine-tuning will be
relatively easy, while other issues may be more time intensive.&lt;/p&gt;
&lt;p&gt;However, because we don&amp;rsquo;t have web background, we are not really sure
of the exact time needed to complete the desired changes. If you or
anyone else you know would be interested in volunteering to help us, or
would like to learn more about our situation, we would greatly
appreciate it.&lt;/p&gt;
&lt;p&gt;Because we have only been operational for a year, no one in our
organization currently gets paid, so unfortunately it&amp;rsquo;s not in the
budget to pay someone to help us with our website. But we are hoping to
find someone who would be understanding of our situation and wiling to
help us further our mission! I know UCSB is getting ready to start up,
so please feel free to pass on this information to students you think
would be willing and able to get involved. Please let us know if anyone
comes to mind! Thank you for your time, and please feel free to contact
me via phone if that would be at all more convenient for you! (818)
620-8847. Happy Fall Quarter :)&lt;/p&gt;
&lt;p&gt;(If you would like to learn more about our organization or view our
site, please feel free at &lt;a href="http://www.becauseofhope.org"&gt;http://www.becauseofhope.org&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Best of Wishes,&lt;/p&gt;
&lt;p&gt;Natalie Lemonnier&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, I needed some experience doing a &lt;code&gt;nanoc&lt;/code&gt; site, something nice to put on my
résumé, and something to satisfy and fulfill the &amp;ldquo;help poor children in Uganda&amp;rdquo;
request in the &lt;code&gt;Vim&lt;/code&gt; text editor. Because of Hope needed someone to maintain
their site. With my résumé as sparse as it is, I called up Natalie and offered
to help.&lt;/p&gt;
&lt;p&gt;I met up with them and took a look at the setup. They had a $20/month VPS to
serve one HTML file. The rest of the &amp;ldquo;pages&amp;rdquo; were actually linked tumblr posts.
I told them upfront I wanted to test out some tools on their problem before
I ran with it on my own personal site. In the meantime, I would maintain their
current handcrafted site and check out any technical details such as the domain
name, Google Apps, or the VPS itself.&lt;/p&gt;
&lt;p&gt;I had &lt;code&gt;nanoc&lt;/code&gt; chosen out, but what about the rest? I checked out the
then-current website&amp;rsquo;s code and it had some custom 980px grid system.
I couldn&amp;rsquo;t be bothered to try and understand it. I did try to understand it
enough to add a donations page that they immediately needed for their upcoming
fundraiser.&lt;/p&gt;
&lt;p&gt;On the bright side, all the mockups that Nikki Day of BOH did were in
Photoshop. They were far more ambitious than what the current site&amp;rsquo;s current
implementation. The custom 980px grid system was not up the the task. In
retrospect, it would have been thrown out anyway. I shudder at what beast
I would have had to slay if the original plans had gone through.&lt;/p&gt;
&lt;p&gt;I chose to use &lt;a href="http://foundation.zurb.com/"&gt;Zurb Foundation&lt;/a&gt;. Zurb Foundation
is a front-end framework that is far more flexible than many other frameworks
out there. This was the the kind of Framework that could handle Nikki&amp;rsquo;s
designs. I would also get free functionality such as drop-down menus, pretty
buttons, photo carousel, and first-class support for SCSS, an extension of CSS.
Zurb Foundation&amp;rsquo;s use of SCSS was very instrumental in adapting the design of
Zurb Foundation for Nikki&amp;rsquo;s designs. Compared to its competitor Twitter
Bootstrap, there is far more control over elements on the page, especially the
buttons. Also, I got a mobile site for free! BOH didn&amp;rsquo;t even ask for it but it
came with it!&lt;/p&gt;
&lt;p&gt;Ever since from that time, I&amp;rsquo;ve been working on BOH&amp;rsquo;s &lt;code&gt;nanoc&lt;/code&gt; site. I learned
how to better compose layouts, the Rules DSL of nanoc, and the whole &lt;code&gt;nanoc&lt;/code&gt;
ecosystem. I was also working with it to integrate Zurb Foundation into the
project.&lt;/p&gt;
&lt;p&gt;Remember that $20/month VPS? It has since been replaced with Amazon S3. I also
tossed in Cloudflare in front. BOH can take on a DDOS attack of the most epic
proportions. Why would anyone attack a non-profit is beyond me but the overkill
just tickles my funny bone. And the cost is free to near pennies! This is
something only a static site can do. As a vote of confidence in S3, &lt;a href="http://blog.apps.npr.org/2013/02/14/app-template-redux.html"&gt;NPR used
S3 for transferring data in its election
app&lt;/a&gt;. I&amp;rsquo;m not
saying that BOH will become as big as the presedential election, but it can
definitely handle that much traffic if need be and certianly more than
a $20/month VPS. Honestly, I&amp;rsquo;m just glad I don&amp;rsquo;t have to manage a Linux server
to achieve this goal. It&amp;rsquo;s really upload and forget.&lt;/p&gt;
&lt;p&gt;Also, in doing &lt;code&gt;nanoc&lt;/code&gt;, I now realize how much of a pain in the ass it is to
update the original site. There was so much copy-paste! How do people do all
these busy work?&lt;/p&gt;
&lt;p&gt;I learned a lot from doing the BOH site and I am continuing to work on it in my
spare time. It&amp;rsquo;s source code is versioned in a Git repository so I can
experiment on it all I want. It&amp;rsquo;s also open source in a &lt;a href="https://github.com/becauseofhope/because-of-hope"&gt;GitHub
repository&lt;/a&gt;. If anybody wants
to peruse it and reuse parts of it, they are more than welcome to do so. I plan
to keep it in sync with anything that is a &lt;code&gt;nanoc&lt;/code&gt; best-practice and update it
with new pages.&lt;/p&gt;
&lt;h3 id="mindflakes"&gt;Mindflakes&lt;/h3&gt;
&lt;p&gt;Okay, so I&amp;rsquo;ve helped a non-profit get their site stuff in shape and beyond.&lt;/p&gt;
&lt;p&gt;What about my personal site?&lt;/p&gt;
&lt;p&gt;Ever since I&amp;rsquo;ve started the BOH site project, I&amp;rsquo;ve probably made 3 false starts
in creating mindflakes.com. I wager I wasn&amp;rsquo;t confident enough in my &lt;code&gt;nanoc&lt;/code&gt;
knowledge in the previous starts. I had given up or I did not have enough time
to really sit down and really start work on my site. Also, I was working part
time at a local company in the area at this time. I came back home so exhausted
from looking through piles of Ruby and Python that I really did not want to put
my finger to the keyboard.&lt;/p&gt;
&lt;p&gt;Maybe the fourth time was the charm? I didn&amp;rsquo;t start the mindflakes
project this time. What I did instead was start an &lt;a href="https://github.com/crazysim/nanoc-foundation-blog"&gt;example/bootstrap
project&lt;/a&gt; for a blog that
used Zurb Foundation and &lt;code&gt;nanoc&lt;/code&gt;. I put some of the best-practices I knew into
there and learned some more best practices from various sources around the web.
I&amp;rsquo;m especially proud that the project is completely tied to Zurb Foundation as its
Javascript is sourced from its gem. There were plenty of Bootstrap examples out
there but I believe that Zurb Foundation better fits with the &lt;code&gt;nanoc&lt;/code&gt;
philosophy in letting power come out in the right places when needed.&lt;/p&gt;
&lt;p&gt;By distilling the framework from the content of my site, I was able to work
away from any pressure in having a nice site to present. Just having a good
framework to build upon is much more gratifying.&lt;/p&gt;
&lt;p&gt;Not only that, I now have a free reusable framework that I may be able to reuse
for client projects that don&amp;rsquo;t involve my site. I don&amp;rsquo;t have to extract magic
or best practice out of my site. I can just rebase anything off of the
framework with the way I like it. I plan to contribute anything I think is
relevant while building my own site back to the framework.&lt;/p&gt;
&lt;p&gt;This is my first long blog post since ever. I wrote this to test out this blog.
As of this post, Mindflakes.com is still very much out-of-the-box even though
I just built the box. I will customize it and make it look pretty enough to
really showcase my work over the next few sessions. I&amp;rsquo;m going to have to decide
what to do about images and what to do with the flexibility that Zurb
Foundation offers me.&lt;/p&gt;
&lt;p&gt;As for what to write, I think I have that settled.&lt;/p&gt;</description></item></channel></rss>