Posts in Shell (20 found)
Manuel Moreale 5 days ago

Bix Frankonis

This week on the People and Blogs series we have an interview with Bix Frankonis, whose blog can be found at bix.blog . Tired of RSS? Read this in your browser or sign up for the newsletter . The People and Blogs series is supported by Brennan Kenneth Brown and the other 129 members of my "One a Month" club. If you enjoy P&B, consider becoming one for as little as 1 dollar a month. My name is Bix, and I’m a straight, white, middle-aged, cisgender man born in upstate New York who now lives in the St. Johns neighborhood of Portland, Oregon—my hometown since 1997 and the longest I’ve lived anywhere since becoming an adult. I’m actually-autistic and multiply otherwise disabled, and remain, as I’ve been for most of my life, financially dependent upon my remaining parent. (If that’s for some reason not enough, my homepage will tell you more than you possibly could want to know, and a previous birthday post serves as the first part of my, and my blog’s, manifesto.) “Bix” is a descendent and derivation of an online handle I’d once had that became my everyday name and then, in 2018, my legal one . To a large degree the modern era of my blogging is dedicated to posting through the above realities both present and future. I live alone except for a gray and white domestic shorthair cat named Meru after the protagonist of the comic book Mind MGMT. I’ve been online since a dialup gopher server run by public libraries in upstate New York allowed me to upload a file of Twin Peaks symbolism to an FTP server in Australia and telnet into an internet BBS based in New York City called MindVox . In the mid-90s, along with two other people I ran a large and cumbersome online petition effort against the Communications Decency Act which inspired a more rigorous one from the Center for Democracy and Technology, and which landed me in the pages of Rolling Stone . In the late-90s, I ran an Internet cafe , or more accurately ran it into the ground for reasons I now know to be the unaccommodated and unmitigated autism, since I wasn’t diagnosed until 2016. In the early-2000s, I blogged original political reporting (also at the time called “stand-alone journalism”) here in Portland that was widely-read in local government circles and got me profiled in The Oregonian and cited in two books. If you traveled in Firefly fandom circles, you probably at least once found yourself on one or another fansite I’d put together in my own decade or so. Finally, for a time in the mid-2010s, I project managed a nonprofit herd of urban goats here in Portland. These days, life mostly is just about listening to, reading, and watching things, and, of course, the blogging . As with most autistic people, habit and routine are foundational and self-regulating, and so every day I get in an hour of reading at a neighborhood coffeeshop; once a week I take myself for breakfast out (also in the neighborhood); and— fatigue willing —once a month I try to get across town to Oregon Zoo (where I’ll also indulge in my intermittent but long-standing photography hobby ) but over the past year this hasn’t happened all that often, much to my increasing chagrin. The current iteration of my blog goes back to 2019 when I received the bix.blog domain as part of Automattic’s “dotblogger” program (you can read my pitch for it), although I consider my actual modern blogging era to start the year before, in 2018 when I started blogging about my 2016 autism diagnosis on Medium. This current era includes earlier this year having had my twenty-fifth blogging anniversary (a post which also serves as the second part of my manifesto), since I’ve been blogging in some form since early in the year 2000—usually personal blogging but occasionally something subject-specific, using many different kinds of blogging software, hosting and self-hosted solutions, and domains. Since I cannot for certain remember what was my very first blogging, it’s not clear to me whether I was motivated to represent myself personally online or whether my first blog was project-specific—even though the latter undoubtedly still was infused by my personality. It’s unlikely that I’ve ever blogged in any kind of dry, “professional” tone and so, in that sense, it’s all personal blogging. As the late Christopher Locke once said, for better or worse, “Voice is what happens when you shitcan the cover-up.” These days, blogging also (at least in theory) includes the longterm project of working toward restoring as much of those two and a half decades as possible, using categories to designate a post’s original domain. It’s a positively gargantuan task —not least because I don’t have archives of everything and some things will need to be re-created post-by-post using the Wayback Machine. As I noted in my IndieWeb Carnival post on self-expression, blogging very much is a coping mechanism, without which I’d only be even more lost, despite the continually recurring mixed feelings I have about it because on the matter of ego (yet another reference to my manifesto post). One of the things that interests me about the restoration project is learning how that coping mechanism functioned for previous versions of me . Simply put: I blog when I cannot not blog. Depending on the post and how time-sensitive it is or isn’t, I might jot things down in Apple Notes (as I did with my initial pass at answers to these questions) before creating and saving a draft in Markdown on my laptop. Typically speaking, though, many if not most posts are written in one sitting, with a post now and then set aside for a second look later that day or the next morning before actually posting it. It’s not unusual for me to spot typos or remember something I forgot to add within hours after a post goes live, in which cases I will make edits. In most cases where I need to come back to post to add something days later, I include an Addenda section at the bottom where I include those updates. Any farther out than that, and mostly I just write a new post. It’s very rare for me to have posts “banked” for posting at a later date, like I know some other bloggers do, since publicly posting something is the final step to getting that thing out of my head where it’s been taking up space—and also because blogging for me is an ongoing process of self-narration (and self-belief ), which for me necessarily means it’s got to be happening in real-time. For that same reason of self-narration, many of my posts necessarily link previous posts somehow relevant to the post at hand. While writing, I’ll often have such posts in mind but don’t bother to do the work of actually adding the links to them until the post is substantively written. Those linked posts then carry a “referring posts” section. In this way, my blog partakes of a tiny bit of “digital garden” magic (the digital garden being the other popular way in which people who make personal websites organize them) by helping to tie together my thinking on specific matters over time. My blog, then, becomes (somewhat like my phone ) an external component of my autistic or otherwise-addled brain. As for what motivates me and the question of what I actually blog about : in the end anything and everything I write can’t help but be about myself—whether the specifics of what I’m writing about happen to be blogging itself, or a movie or television show I recently watched, or autism research, or the politics of solidarity. Over the past year or two, I’ve become especially interested in how important it is for us to spend time letting each other know that we are seen and we are heard. (There’s nothing quite like blogging a movie no one’s seen, one that’s emphatically about being seen and heard, and—this part, too, is in my manifesto post—having it make the filmmakers’ day.) All of this is subject to the whims of fatigue or, as has been the case lately, autistic burnout —which is why I’ve not been blogging as much as I usually do, and why, in fact, it took me nearly two months just to answer these questions. Never before have I felt such cognitive paralysis and claustrophobia when attempting to write, which as you can imagine is simply terrific when writing is one of your self-regulating activities. Only very rarely can I write outside of the house—say, at one of my neighborhood coffeeshops. As I’ve returned to again and again, my blogging is a sort of writing myself into existence and claiming the space I take up in the world, and this is a sensitive mindset that’s, perhaps ironically, best protected by being alone and home instead of up and about and subject to the stressors of being autistic and anxious out in the world. This in part is because the “spotlight effect” is real, and if I’m writing at a coffeeshop I can only do it with my back against the wall. (I mean that literally, not metaphorically.) It’s extremely rare, although not completely unheard of, for me to have anything else going on around me, like a television show or music, when I am writing. If I do feel the need for music, it’s generally going to be something instrumental like LoFi Girl playlists or the soundtracks to Station Eleven or The Fallout (of all things). It’s fairly common, at least when it comes to my longer posts, and almost surely when it comes to my more discursive ones, to fall into hyperfocus . If you’re autistic otherwise neurodivergent and know this state, this usually means looking up after an hour or two and realizing you’re light-headed from forgetting to eat lunch and with a very pressing need to go to the bathroom—themselves two things perhaps better realized at home than at a neighborhood coffeeshop. Early this year, I migrated to 11ty after several false starts looking at various static site generators and failing to come to terms with them—despite the fact that once upon a time in the mid-2000s I self-hosted MoveableType on an OpenBSD box over my home DSL, so it’s not like I’m incapable of understanding things. Right now, posts are written in Panda, the stand-alone editor from the makers of Bear (the Markdown notes app, not the blogging service), on my MacBook Air where I have my local 11ty install. Recently, I switched from manually uploading the built site directory to Netlify to using continuous deploy via pushing to GitHub, after a timezone snag with the latter process finally was resolved. For the rare post that includes an image or two, I currently host those on some.pics, a service of omg.lol, because my blog previously was on their weblog.lol service and it’s just easier to keep doing that for now. I’m still a Flickr Pro member, so at some point I might switch over to them, since that’s where all my photography is anyway, except that, even more rarely, sometimes I’m posting a graphic instead of a photo, and those I do not also have on Flickr. (This is one way in which I miss the ease of an actual blogging CMS, but there currently aren’t any such tools that don’t frustrate me past my point of patience. When I win the lottery, I will pay someone to build me one that does everything I need, and only what I need. Ironically, all these years decades later, Blogger and MoveableType still had the right idea: a CMS that publishes a static site.) There might have been some early iteration of my blogging which was done manually, but if so it’s lost to the severely deficient autobiographical memory and no archive exists. The earliest blogging software I would have used would have been Blogger, but (as noted) I spent many years self-hosting MoveableType over my home DSL, before moving on the WordPress for at least as long. Along the way I’ve tried many different things, from TypePad to Tumblr, micro.blog to weblog.lol, Proseful to (the other) Bear to Pika. I think I even very briefly used really simple a shell script on a VPS. (Full disclosure: I fully admit to an ethical conflict when it comes to “bullshit bots”, or generative so-called AI. I dislike their misuse of copyrights, I dislike their climate impact, I dislike removing cognitive friction from creation, and I dislike that ultimately these bots will just keep narrowing the breadth of human knowledge and expression and everything becomes increasingly self-referential. Nonetheless, I’ve used ChatGPT and GitHub Copilot when needing to solve specific technical challenges, especially when converting archived posts from previous blogs, or to create features such as my “on this day” and “recently read blogs” widgets. I am not a coder, and while I often can understand, for lack of a better way to say it, the story of a piece of code and make tiny, piecemeal adjustments to existing code because of that, I cannot myself code from scratch. My “excuse” in the end is inevitably a selfish one: the blog, in many ways, is all I have, and all that will be left of me when I am gone that said “I was here”, and I need it to function in a certain way. I’m up front about this, because people have a right to call me a hypocrite. That said, as I recently announced , it is my intention not to use these tools going forward, although any existing code will remain in use barring a clear route to replacement.) This is a pretty good example of the type of question I don’t know how to answer. I started blogging within a particular context and at a particular time, and that context and time, and their circumstances and people, are ancestors to who I am today. I don’t know how to blank out that past to imagine how I’d do it now absent that history. More generally, if we take this question as advice to others: there’s a saying (of a provenance I’m not even going to attempt to trace) that the best time to plant a tree was twenty years ago—but the second-best time is now. I think we are right now living in the second-best time to start a blog, because there’s a clear interest percolating in trying to re-center blogs in a way that hasn’t been seen since before the combined rise of content marketing and Twitter, as seen by the advent of sites such as ooh.directory and blogroll.org . Perhaps all we’re really lacking is something along the lines of Technorati and the other services that once existed to help us see not merely who is linking to what URLs but who is linking to whom . This unmet need, I feel, needs to be addressed if blogging truly is to become once again a blogosphere—or, more likely in this day and age, a whole, diverse, plural set of blogospheres . Two years ago, I ran with an idea Kevin Lawver had of blogging as the empathy engine of the web, and in today’s increasingly authoritarian environment we need more than ever as much public solidarity between and among whole persons as we feel we safely can put online. We have a real chance to reclaim an internet where we are people , not users. For anyone reading these interviews who isn’t themselves yet blogging: please start a blog. We need you here. (If someone is looking for some passionate motivation, I suggest watching Pump Up the Volume , the 1990 film that is the patron movie of blogging despite pre-dating blogging itself. “Talk hard.”) It currently does not cost me anything to run my blog beyond the domain which isn’t due for renewal until 2030, nor does it make any money. I’m technically part of One a Month Club , but I don’t really promote that beyond a site badge and in the footer of my RSS feed. However, I cap membership numbers because above a certain threshold it would affect my eligibility for SNAP and Medicaid benefits here in the U.S. without actually providing enough support to make up for those losses. (I’d never actually reach those kinds of membership numbers anyway, but I’d rather be safe than sorry.) More generally, people of course can do whatever they want with their personal blogs. That’s what makes them personal. I’d assume that I’m more inclined to expect personal blogs not to be behind outright paywalls, but your mileage may vary. I don’t have any inherent objection to blogging as a side-hustle, but blogs that specifically try to hustle readership behind paywalls or otherwise cumbersome hoops will tend to feel much less personal to me. That said, I’ll readily admit to an outright bias against anyone whose primary purpose is “content marketing” or growth hacking, or who obsesses over things like SEO, because I believe that the focus on these things is part of what helped push blogs to the edges of the internet and mainstream irrelevancy around the same time that Twitter not only consumed the subset idea of microblogging but also made it explicitly—and frictionlessly—social. Recommendations are always difficult for me because my brain dislikes ranking things or people. It’s why I don’t rate books on Goodreads and why I don’t rate movies on Letterboxd and simply mark the things I enjoyed. It’s also why I don’t maintain a blogroll, although I certainly did back in the OG blogging days. So, the first part of my answer here is going to be a bit of a cheat, if nonetheless a responsive one. At the bottom of the front page of my blog, above the four links to places to find more blogs, is what I’ve referred to as a “bloglog” (what others lately have taken to calling a “postroll”). In my case it lists the ten blog posts by other people that I’ve actually read most recently (or, at least, as of the last time my site was built)—and it’s also available as an RSS feed . It all runs off an Instapaper tag, and I’m sure there’s plenty of blogs there for people to discover. That said, I will offer a short list that splits the difference between some fellow OG bloggers, a couple of more recent finds, and some of the newer self-tagged “word vomit” bloggers on sites like Bear. This is less about placing any of these bloggers above any others in my estimation than about making some suggestions that help expand the types of bloggers represented in this series. Here I’ll just crib a couple of projects from the “sites” section of my homepage. Since late 2001, around the time that so-called “warblogging” became a thing, I’ve been hosting an ad- and cruft-free, minimalist presentation of Mark Twain’s The War Prayer which often finds itself shared with students by teachers. (It’s also on my longest-running domain.) Two years ago I returned to the internet the complete archive of a shipyard workers zine from World War II, updated my research into it, and this year I finally turned over the originals to Oregon Historical Society. Finally, although Joss Whedon became quite evidently problematic, I remain a fan of one of his unmade scripts , which I wrote about nearly a decade ago because I appreciate its (workable? impractical?) ideal that there are no expendable people. (If I can tack on a postscript of sorts: given my eternal struggle with my own ego , I thank Manu for inviting me to participate in People and Blogs.) Now that you're done reading the interview, go check the blog and subscribe to the RSS feed . If you're looking for more content, go read one of the previous 123 interviews . Make sure to also say thank you to Andrea Contino and the other 129 supporters for making this series possible. Absurd Pirate Elaine (my mom, who’s been blogging as long as I have) Jessamyn West Shelley Powers

0 views
Harper Reed 1 weeks ago

Remote Claude Code: programing like it was the early 2000s

So so many friends have asked me how I use Claude Code from my phone. I am always a bit surprised, because a lot of this type of work I have been doing for nearly 25 years (or more!) and I always forget that it is partially a lost art. This is how it used to be. We didn’t have fancy IDEs, and fancy magic to deploy stuff. We had to ssh (hah. Telnet!) into a machine and work with it through the terminal. It ruled. It was a total nightmare. It was also a lot of fun. One of my favorite parts of the early 2000s was hanging out in IRC channels and just participating in the most ridiculous community of tech workers. A very very fun time. #corporate on efnet! There is a lot of nostalgia for that time period - but for the most part the tooling sucked. The new IDEs, and magic deploy systems have made it so that you do not have to deal with a terminal to get shit done. And then… Claude Code sashays into the room and fucks up the vibe. Or creates a new vibe? Who knows. Anyway. We are all using terminals now and it is hilarious and fun. So let’s vibe. Thinking about terminals probably, Leica M11, 12/2025 The conversation I have with people about Claude Code start normally, and almost without exception end with “I wish I could do this from my phone.” Well.. I am here to tell you that it is easy! And accessible! First things first - there are a couple really neat startups that are solving this in a very different way that I work. I think they are awesome. My favorite example of this is superconductor (great name!). They allow you to run and instantiate a bunch of agents (Claude Codex, Amp, Codex, etc) and interact with them remotely. They are also a really great team! Another example is happy coder . An open source magical app that connects to your Claude Code. It is theoretically pretty good, and I know some people who love it. I couldn’t get it to work reliably. One of my core values is: I want to just ssh into shit . That is kind of one of my general hobbies. Can I ssh into this thing? If yes, then I am happy. If no, then how can I make it so I can ssh into it. When it came to figuring out how to use Claude Code on my phone, the obvious answer was: ssh into my computer from my phone, and run claude . Turns out this is pretty straight forward. My workstation: Let’s break it down. I use an iPhone, so I will be talking about iPhone apps. There are good android apps to do this too! There are maybe 4 things you need to solve for: As a form of tldr, here are my personal answers: Let’s break it down: You will need to access your workstation from anywhere. I use a Mac and linux boxes for this. Linux is easy: Just make sure openssh-server is installed. Test that you can ssh into it - and bam. Typically if you are using a box from a Claude provider, this is built into the program. Macs are a bit harder. You need to enable ssh , and then for extra credit you need to enable screen sharing . Once this is done you should theoretically be able to remotely connect to your computer. It is very important you try to connect to it from another computer that is on the same network. Figure out your local IP (192.168.xxx.yyy), and then ssh to your local IP from another machine (or from the same machine). As long as you can connect to it - then the next step will be super easy. If you can’t connect to it, ask chatgpt wtf is going on. Once you can reliably SSH into your machine, then it is time to get Tailscale working. There are a few alternatives (zero tier, etc) and I am sure they are good. Tailscale is friends, and they are awesome. Having used them since before they launched, I can promise that it is a life changer. Install the Tailscale client on all your machines. Tailscale will magically create a network that only you have access to (or anyone else you add to your network). You can then access any of your machines from any of your machines. I.e. your phone can instantly connect to your workstation while you workstation is in Chicago, and your phone is in Tokyo. You don’t have to poke a hole in a firewall, do magical networking, or learn how to do magical networking. It just works. It is a beautiful product. There are is a deep bench of Tailscale features that you should check out eventually - but for today, just use it for networking. Since you were able to ssh into your machine before (I hope!) - now you can test it with your fancy new Tailscale ip address or magic name. And you can do that from any device that is on your Tailscale network. Like.. your phone! This means network is solved! This is where some personal preference comes in. You now need to pick a terminal client that you like to use, and feels good to use. Lots of my friends like prompt , and termius . Both are great choices. I personally really like blink . It is a bit nerdier, and when you open it, it just drops you into a shell. Immediately. No interface, no nonsense. Just a shell. It is a wild app. You can use their blink build product to host a lil dev server for yourself! I wanted to use their build product - but the default and unchangeable user was and I cannot being myself to seriously use a product that drops you into a server as the root user. lol Anyway, blink is for me! And since you set up Tailscale, and ssh you can just type and it will magically connect. you can use the command in blink to set up keys, and hosts, etc. highly worthwhile. Now you are inside of your workstation! Now you can really rip some tokens! I checked the build status RIGHT AFTER THIS SHOT, Leica M11, 01/2026 Tools! You could just navigate to the directory that your Claude project live, and run Claude. But then when you phone went to sleep or whatever - your ssh client may disconnect. And you would have to redo the connection, run Claude –continue, and live this life of lots of typing. We don’t use AI tools to type more! There are three tools that are super helpful: If you are using SSH a lot you need to set up some SSH keys, and then push them around to all your servers. I am not going to tell you how to do that, since you should already have keys somewhere to integrate with source code repositories. If you want to generate new, or have questions - the terminal clients may help you. My guess is that you already have some. Couple tips: Mosh is from a forgotten time (2012!) when the internet was slow, and connections were spotty. What mosh does is allow your “fragile” ssh connection to roam around with you. You use it just like ssh: . But now when you shut your laptop, or forget about your phone - the connection will pop back up when you surface it again. It allows the connection to survive a lot of the various environmental things that would normally derail a ssh connection. This RULES. I was on a train the other day and totally lost internet while we were in a tunnel. Then we emerged and internet came back. My ssh (really mosh) session just paused for a moment, and then BAM! Was back and Claude was telling me it had deleted my entire workstation, and was going to the beach! There are some gotchas about ssh-agent, keys and mosh that I won’t get into. If things are weird, just google it or as chatgpt. Tbh, I am a screen guy. But it is 2026 and TMUX is a better choice. It allows you to have a long running terminal process that you can reattach to. This is helpful even without a remote connection. It also acts as a multiplexer - allowing for multiple terminal sessions in a single terminal window. You can have 7 Claude Codes running simultaneously and just tab through them as needed. TMUX is what a lot of the “Claude Code orchestration” hacks are built upon.You should check them out . I haven’t yet found one that works how I want it - even know there are some good ones! I just want to use regular old TMUX, and a bunch of weird helpers. My TMUX config is here: harperreed/dotfiles/.TMUX.conf . Be forewarned, that the key combos are wacky! TMUX is the key that allows me to run a dozen Claude Code instances, and then walk away from my workstation, pick up my phone and continue hacking. To make things consistent, and easier I have a few scripts that really tie the room together. First, I have my claude code aliases: These allow me to start or pick up my last work. You are dangerously skipping permissions, right? Another helpful script is this one to help me unlock my keychain: On a Mac, Claude Code stores its api key in your keychain, then it requires you to unlock your keychain to work. This also has the added benefit of unlocking your ssh keys if they are using the keychain for your ssh-agent. My TMUX starter script is really handy. I just type and it magically starts a new named session, or attaches to the named session already. This script specifically names my sessions based on the workstation I use it from. This allows me to see what computer I am in via the terminal title. My workflow is: Now you can tell Claude to do weird shit from your phone 24 hours a day. It rules. Don’t do it while driving. Thank you for using RSS. I appreciate you. Email me terminal client workstation network: Tailscale client: blink workstation: Mac with constant power and fast internet tools: TMUX , some magic scripts, and Claude Code Keys/identity use a password to unlock your key! use an ssh agent to make that process not horrible on a Mac you can have your key be unlocked by your keychain (which is also where your Claude Code api key is!) ssh into my workstation burn tokens

0 views

Can I finally start using Wayland in 2026?

Wayland is the successor to the X server (X11, Xorg) to implement the graphics stack on Linux. The Wayland project was actually started in 2008, a year before I created the i3 tiling window manager for X11 in 2009 — but for the last 18 years (!), Wayland was never usable on my computers. I don’t want to be stuck on deprecated software, so I try to start using Wayland each year, and this articles outlines what keeps me from migrating to Wayland in 2026. For the first few years, Wayland rarely even started on my machines. When I was lucky enough for something to show up, I could start some toy demo apps in the demo compositor Weston. Around 2014, GNOME started supporting Wayland. KDE followed a few years later. Major applications (like Firefox, Chrome or Emacs) have been slower to adopt Wayland and needed users to opt into experimental implementations via custom flags or environment variables, until very recently, or — in some cases, like — still as of today. Unfortunately, the driver support situation remained poor for many years. With nVidia graphics cards, which are the only cards that support my 8K monitor , Wayland would either not work at all or exhibit heavy graphics glitches and crashes. In the 2020s, more and more distributions announced looking to switch to Wayland by default or even drop their X11 sessions , and RHEL is winding down their contributions to the X server . Modern Linux distributions like Asahi Linux (for Macs, with their own GPU driver!) clearly consider Wayland their primary desktop stack, and only support X11 on a best-effort basis. So the pressure to switch to Wayland is mounting! Is it ready now? What’s missing? I’m testing with my lab PC, which is a slightly upgraded version of my 2022 high-end Linux PC . I describe my setup in more details in stapelberg uses this: my 2020 desk setup . Most importantly for this article, I use a Dell 8K 32" monitor (resolution: 7680x4320!), which, in my experience, is only compatible with nVidia graphics cards (I try other cards sometimes). Hence, both the lab PC and my main PC contain an nVidia GPU: (In case you’re wondering why I use the older card in my PC: I had a crash once where I suspected the GPU, so I switched back from the 4070 to my older 3060.) For many years, nVidia drivers were entirely unsupported under Wayland. Apparently, nVidia refused to support the API that Wayland was using, insisting that their EGLStreams approach was superior. Luckily, with nVidia driver 495 (late 2021), they added support for GBM (Generic Buffer Manager). But, even with GBM support, while you could now start many Wayland sessions, the session wouldn’t run smoothly: You would see severe graphics glitches and artifacts, preventing you from getting any work done. The solution for the glitches was explicit sync support: because the nVidia driver does not support implicit sync (like AMD or Intel), Wayland (and wlroots, and sway) needed to get explicit sync support . Sway 1.11 (June 2025) and wlroots 0.19.0 are the first version with explicit sync support. With the nVidia driver now working per se with Wayland, unfortunately that’s still not good enough to use Wayland in my setup: my Dell UP3218K monitor requires two DisplayPort 1.4 connections with MST (Multi Stream Transport) and support. This combination worked just fine under X11 for the last 8+ years. While GNOME successfully configures the monitor with its native resolution of 7680x4320@60, the monitor incorrectly shows up as two separate monitors in sway. The reason behind this behavior is that wlroots does not support the property (issue #1580 from 2019) . Luckily, in 2023, contributor sent draft merge request !4154 , which adds support for the property. But, even with the patch, my monitor would not work correctly: The right half of the monitor would just stay black. The full picture is visible when taking a screenshot with , so it seems like an output issue. I had a few exchanges about this with starting in August 2025 (thanks for taking a look!), but we couldn’t figure out the issue. A quarter later, I had made good experiences regarding debugging complex issues with the coding assistant Claude Code (Opus 4.5 at the time of writing), so I decided to give it another try. Over two days, I ran a number of tests to narrow down the issue, letting Claude analyze source code (of sway, wlroots, Xorg, mesa, …) and produce test programs that I could run manually. Ultimately, I ended up with a minimal reproducer program (independent of Wayland) that shows how the DRM property does not work on nVidia (but does work on Intel, for example!): I posted a bug report with a video in the nVidia forum and hope an nVidia engineer will take a look! Crucially, with the bug now identified, I had Claude implement a workaround: copy the right half of the screen (at ) to another buffer, and then display that buffer , but with . With that patch applied, for the first time, I can use Sway on my 8K monitor! 🥳 By the way, when I mentioned that GNOME successfully configures the native resolution, that doesn’t mean the monitor is usable with GNOME! While GNOME supports tiled displays, the updates of individual tiles are not synchronized, so you see heavy tearing in the middle of the screen, much worse than anything I have ever observed under X11. GNOME/mutter merge request !4822 should hopefully address this. During 2025, I switched all my computers to NixOS . Its declarative approach is really nice for doing such tests, because you can reliably restore your system to an earlier version. To make a Wayland/sway session available on my NixOS 25.11 installation, I added the following lines to my NixOS configuration file ( ): I also added the following Wayland-specific programs to : Note that activating this configuration kills your running X11 session, if any. Just to be sure, I rebooted the entire machine after changing the configuration. With this setup, I spent about one full work day in a Wayland session. Trying to actually get some work done uncovers issues that might not show in casual testing. Most of the day was spent trying to fix Wayland issues 😅. The following sections explain what I have learned/observed. Many years ago, when Wayland became more popular, people asked on the i3 issue tracker if i3 would be ported to Wayland. I said no: How could I port a program to an environment that doesn’t even run on any of my computers? But also, I knew that with working a full-time job, I wouldn’t have time to be an early adopter and shape Wayland development. This attitude resulted in Drew DeVault starting the Sway project around 2016, which aims to be a Wayland version of i3. I don’t see Sway as competition. Rather, I thought it was amazing that people liked the i3 project so much that they would go through the trouble of creating a similar program for other environments! What a nice compliment! 😊 Sway aims to be compatible with i3 configuration files, and it mostly is. If you’re curious, here is what I changed from the Sway defaults, mostly moving key bindings around for the NEO keyboard layout I use, and configuring / blocks that I formerly configured in my file : I encountered the following issues with Sway: I don’t know how I can configure the same libinput settings that I had before. See for what I have on X11. Sway’s available settings do not seem to match what I used before. The mouse cursor / pointer seems laggy, somehow?! It seems to take longer to react when I move the trackball, and it also seems to move less smoothly across the screen. Simon Ser suspects that this might be because hardware cursor support might not work with the nVidia drivers currently. No Xwayland scaling: programs started via Xwayland are blurry (by default) or double-scaled (when setting ). This is a Sway-specific limitation: KDE fixed this in 2022 . From Sway issue #2966 , I can tell that Sway developers do not seem to like this approach for some reason, but that’s very unfortunate for my migration: The backwards compatibility option of running older programs through Xwayland is effectively unavailable to me. Sometimes, keyboard shortcuts seem to be executed twice! Like, when I focused the first of five Chrome windows in a stack and moved that window to another workspace, two windows would be moved instead of one. I also see messages like this one (not exactly correlated with the double-shortcut problem, though): …and that seems wrong to me. My high-end Linux PC certainly isn’t slow by any measure. When I first started GTK programs like GIMP or Emacs, I noticed all fonts were way too large! Apparently, I still had some scaling-related settings that I needed to reset like so: Debugging tip: Display GNOME settings using (stored in ). Some programs like apparently need an explicit environment variable, otherwise they run in Xwayland. Weird. I also noticed that font rendering is different between X11 and Wayland! The difference is visible in Chrome browser tab titles and the URL bar, for example: At first I thought that maybe Wayland defaults to different font-antialiasing and font-hinting settings, but I tried experimenting with the following settings (which default to and ), but couldn’t get things to render like they did before: Update : Thanks to Hugo for pointing out that under Wayland, GTK3 ignores the configuration file and uses dconf exclusively! Setting the following dconf setting makes the font rendering match: The obvious replacement for is . I quickly ran into a difference in architecture between the two programs: i3lock shows a screen locker window. When you kill i3lock, the screen is unlocked. When you kill swaylock, you end up in a Red Screen Of Death . To get out of this state, you need to restart swaylock and unlock. You can unlock from the command line by sending to the process. This was very surprising to me, but is by (Wayland) design! See Sway issue #7046 for details, and this quote from the Wayland protocol : “The compositor must stop rendering and provide input to normal clients. Instead the compositor must blank all outputs with an opaque color such that their normal content is fully hidden.” OK, so when you start via SSH for testing, remember to always unlock instead of just cancelling with Ctrl+C. And hope it never crashes. I used to start via a wrapper script, which turns off the monitor (input wakes it up): With Wayland, the DPMS behavior has to be implemented differently, with : The i3 window manager can be extended via its IPC interface (interprocess communication) . I use a few small tools that use this interface. I noticed the following issues when using these tools with Sway: Tools using the Go package need a special socket path hook currently . We should probably include transparent handling in the package to ease the transition. Tools started with from the Sway config unexpectedly keep running even when you exit Sway ( ) and log into a new session! My workspace-populate-for-i3 did not work: My wsmgr-for-i3 worked partially: On X11, I use the rxvt-unicode (URxvt) terminal emulator. It has a couple of quality-of-life features that I don’t want to lose, aside from being fast and coming with a minimal look: In earlier experiments, I tried Alacritty or Kitty, but wasn’t happy with either. Thanks to anarcat’s blog post “Wayland: i3 to Sway migration” , I discovered the terminal emulator , which looks like a really nice option! I started a config file to match my URxvt config, but later I noticed that at least some colors don’t seem to match (some text lines with green/red background looked different). I’m not sure why and have not yet looked into it any further. I noticed the following issues using : Pressing Ctrl+Enter (which I seem to do by mistake quite a bit) results in escape sequences, whereas URxvt just treats Ctrl+Enter like Enter. This can be worked around in your shell (Zsh, in my case), see foot issue #628 for details. Double-clicking on part of a URL with the mouse selects the URL (as expected), but without the scheme prefix! Annoying when you do want to use the mouse. I can hold Ctrl to work around this, which will make select everything under the pointer up to, and until, the next space characters. Starting in results in not having color support for programs running inside the session. Probably a terminfo-related problem somehow…? I can also reproduce this issue with GNOME terminal. But with URxvt or xterm , it works. Selecting text highlights the text within the line, but not the entire line. This is different from other terminal emulators I am used to, but I don’t see an option to change it. Here’s a screenshot showing after triple-clicking on the right of “kthreadd”: But triple-clicking on an echo output line highlights only the contents, not the whole line: I find Emacs’s Wayland support rather disappointing. The standard version of Emacs only supports X11, so on Sway, it starts in Xwayland. Because Sway does not support scaling with Xwayland, Emacs shows up blurry (top/background window): Native Wayland support (bottom/foreground window) is only available in the Emacs version ( on NixOS). used to be a separate branch, but was merged in Emacs 29 (July 2023). There seem to be issues with on X11 (you get a warning when starting Emacs-pgtk on X11), so there have to be two separate versions for now… Unfortunately, the text rendering looks different than native X11 text rendering! The line height and letter spacing seems different: I’m not sure why it’s different! Does anybody know how to make it match the old behavior? Aside from the different text rendering, the other major issue for me is input latency: Emacs-pgtk feels significantly slower (less responsive) than Emacs. This was reported on Reddit multiple times ( thread 1 , thread 2 ) and Emacs bug #71591 , but there doesn’t seem to be any solution. I’ll also need a solution for running Emacs remotely. Thus far, I use X11 forwarding over SSH (which works fine and with low latency over fiber connections). I should probably check out waypipe, but have not yet had a chance. When starting Chrome and checking the debug page, things look good: But rather quickly, after moving and resizing browser windows, the GPU process dies with messages like the following and, for example, WebGL is no longer hardware accelerated: Of course, using a browser without hardware acceleration is very frustrating, especially at high resolutions. Starting Chrome with seems to work around the GPU process exiting, but Chrome still does not feel as smooth as on X11. Another big issue for me is that Sway does not open Chrome windows on the workspace on which I closed them. Support for tracking and restoring the EWMH atom was added to i3 in January 2016 and to Chrome in May 2016 and Firefox in March 2020 . I typically have 5+ workspaces and even more Chrome windows at any given point, so having to sort through 10+ Chrome windows every day (when I boot my work computer) is very annoying . Simon Ser said that this would be addressed with a new Wayland protocol ( , merge request !18 ). I work remotely a lot, so screen sharing is a table-stakes feature for me. I use screen sharing in my browser almost every day, in different scenarios and with different requirements. In X11, I am used to the following experience with Chrome. I click the “Window” tab and see previews of my windows. When I select the window and confirm, its contents get shared: To get screen sharing to work in Wayland/sway, you need to install and (the latter is specific to wlroots, which sway uses). With these packages set up, this is the behavior I see: This is a limitation of (and others) , which should be addressed with the upcoming Sway 1.12 release. I changed my NixOS configuration to use sway and wlroots from git to try it out. When I click on the “Window” tab, I see a chooser in which I need to select a window: After selecting the window, I see only that window’s contents previewed in Chrome: After confirming, I get another chooser and need to select the window again. Notably, there is no connection between the previewed window and the chosen window in this second step — if I chose a different window, that’s what will be shared: Now that window is screenshared (so the feature now works; nice!), but unfortunately in low resolution, meaning the text is blurry for my co-workers. I reported this as xdg-desktop-portal-wlr issue #364 and it seems like the issue is that the wrong scale factor is applied. The patch provided in the issue works for me. But, on a high level, the whole flow seems wrong: I shouldn’t see a chooser when clicking on Chrome’s “Window” tab. I should see previews of all windows. I should be able to select the window in Chrome, not with a separate chooser. I also noticed a very annoying glitch when output scaling is enabled: the contents of (some!) windows would “jump around” as I was switching between windows (in a tabbed or stacked container) or between workspaces. I first noticed this in the terminal emulator, where the behavior is as follows: I captured the following frame with my iPhone just as the content was moving a few pixels, shortly after switching focus to this window: Later, I also noticed that Chrome windows briefly show up blurry after switching . My guess is that because Sway sets the scale factor to 1 for invisible windows, when switching focus you see a scale-1 content buffer until the application provided its scale-3 content buffer. dunst supports Wayland natively. I tried dunst 1.13 and did not notice any issues. rofi works on Wayland since v2.0.0 (2025-09-01). I use rofi with rofimoji as my Emoji picker. For text input, instead of , seems to work. I didn’t notice any issues. Instead of my usual choice , I tried , but unfortunately ’s flag to select the window to capture is rather cumbersome to use (and captures in 1x scale). Does anyone have any suggestions for a good alternative? Finally I made some progress on getting a Wayland session to work in my environment! Before giving my verdict on this Wayland/sway experiment, let me explain that my experience on X11/i3 is really good. I don’t see any tearing or other artifacts or glitches in my day-to-day computer usage. I don’t use a compositor, so my input latency is really good: I once measured it to approximately 763 μs in Emacs on X11 with my custom-built keyboard (plus output latency), see kinX: latency measurement (2018) . So from my perspective, switching from this existing, flawlessly working stack (for me) to Sway only brings downsides. I observe new graphical glitches that I didn’t have before. The programs I spend most time in (Chrome and Emacs) run noticeably worse. Because of the different implementations, or because I need to switch programs entirely, I encounter a ton of new bugs. For the first time, an on-par Wayland experience seems within reach, but realistically it will require weeks or even months of work still. In my experience, debugging sessions quickly take hours as I need to switch graphics cards and rewire monitors to narrow down bugs. I don’t have the time to contribute much to fixing these numerous issues unfortunately, so I’ll keep using X11/i3 for now. For me, a Wayland/Sway session will be ready as my daily driver when: The lab PC contains a nVidia GeForce RTX 4070 Ti. The main PC contains a nVidia GeForce RTX 3060 Ti. I don’t know how I can configure the same libinput settings that I had before. See for what I have on X11. Sway’s available settings do not seem to match what I used before. The mouse cursor / pointer seems laggy, somehow?! It seems to take longer to react when I move the trackball, and it also seems to move less smoothly across the screen. Simon Ser suspects that this might be because hardware cursor support might not work with the nVidia drivers currently. No Xwayland scaling: programs started via Xwayland are blurry (by default) or double-scaled (when setting ). This is a Sway-specific limitation: KDE fixed this in 2022 . From Sway issue #2966 , I can tell that Sway developers do not seem to like this approach for some reason, but that’s very unfortunate for my migration: The backwards compatibility option of running older programs through Xwayland is effectively unavailable to me. Sometimes, keyboard shortcuts seem to be executed twice! Like, when I focused the first of five Chrome windows in a stack and moved that window to another workspace, two windows would be moved instead of one. I also see messages like this one (not exactly correlated with the double-shortcut problem, though): …and that seems wrong to me. My high-end Linux PC certainly isn’t slow by any measure. i3lock shows a screen locker window. When you kill i3lock, the screen is unlocked. When you kill swaylock, you end up in a Red Screen Of Death . To get out of this state, you need to restart swaylock and unlock. You can unlock from the command line by sending to the process. Tools using the Go package need a special socket path hook currently . We should probably include transparent handling in the package to ease the transition. Tools started with from the Sway config unexpectedly keep running even when you exit Sway ( ) and log into a new session! My workspace-populate-for-i3 did not work: Sway does not implement i3’s layout saving/restoring because Drew decided in 2017 that the feature is “too complicated and hacky for too little benefit” . Too bad. I have a couple of layouts I liked that I’ll need to replicate differently. Sway does not match workspace nodes with criteria. There’s pull request #8980 (posted independently, five days ago) to fix that. My wsmgr-for-i3 worked partially: Restoring workspaces ( ) worked. Sway’s command implementation does not seem to pick up workspace numbers from the target name. Backwards search through your scrollback (= command output) Opening URLs in your scrollback using keyboard shortcuts Opening a new terminal window in the same working directory Updating the terminal title from your shell Pressing Ctrl+Enter (which I seem to do by mistake quite a bit) results in escape sequences, whereas URxvt just treats Ctrl+Enter like Enter. This can be worked around in your shell (Zsh, in my case), see foot issue #628 for details. Double-clicking on part of a URL with the mouse selects the URL (as expected), but without the scheme prefix! Annoying when you do want to use the mouse. I can hold Ctrl to work around this, which will make select everything under the pointer up to, and until, the next space characters. Starting in results in not having color support for programs running inside the session. Probably a terminfo-related problem somehow…? I can also reproduce this issue with GNOME terminal. But with URxvt or xterm , it works. Selecting text highlights the text within the line, but not the entire line. This is different from other terminal emulators I am used to, but I don’t see an option to change it. Here’s a screenshot showing after triple-clicking on the right of “kthreadd”: But triple-clicking on an echo output line highlights only the contents, not the whole line: I can share a Chrome tab. I can share the entire monitor. I cannot share a specific window (the entire monitor shows up as a single window). Switch focus to another terminal by changing workspaces, or by switching focus within a stacked or tabbed container. The new terminal shows up with its text contents slightly offset. Within a few milliseconds, ’s text jumps to the correct position. Sway no longer triggers some key bindings twice some times (“ghost key presses”) I no longer see glitches when switching between windows or workspaces in Sway. Chrome is continuously hardware-accelerated. Chrome windows are restored to their previous workspace when starting. Emacs either: Runs via Xwayland and Sway makes scaling work. Or if its variant fixes its input latency issues and can be made to render text the same as before somehow.

0 views
Jason Scheirer 1 weeks ago

Learn To Live With The Defaults

Every deviation from default is slowing you down from getting started and making it harder to help others. I use about 5 distinct laptops/desktops on an average day, not to mention the VMs within them and other machines I shell into. Having a consistent experience is useful, but equally important is my ability to roll with the punches and get productive on a new computer without much ceremony. One thing I do to cope with this is a dotfiles repo with a dead-simple installation method , but also note how conservative it is. No huge vim plugin setup. Very minimal tmux config (which is still bad, and I’ll explain why later). Not a lot going on. Moving from the defaults to a custom setup might make you more effective in the immediate term, but it makes it harder long-term. You have additional complexity in terms of packages installed, keymaps, etc. that you need to reproduce regularly on every system you use. As I complained about in Framework Syndrome , flexible software just moves the problem along, it does not solve the problem. Having a tool that’s flexible enough to get out of the way so that you can solve the problem yourself is double-edged: it does not provide the solution you want, it provides an environment to implement your solution. This seems to mean that everyone new to the software will not see it as useful as it seems to you, right? To them it’s a blank slate, and is only useful with significant customization. This also affects teachability! With your hyper-customized setup you can’t be as effective a mentor or guide. One thing that makes it harder for me to advocate tmux to new devs is that I use one thing sightly idiomatically: coming from the older tool screen means I remap Ctl-B to Ctl-A for consistency. This has bitten me many a time! One example: Once I had set up a shared VM at work and had long-running tasks in tmux that my teammates could check in on. The entire setup was stymied by the fact that nobody but me could use tmux due to that one customization I had set up. Learn to lean in and be as functional as possible with the default setup. A kitted-out vim is great but learn the basics as muscle memory. Prefer tools with good defaults over good enough tools with the flexibility to make them as good as the ones with good defaults.

0 views
Justin Duke 2 weeks ago

mise hooks

I've written before about having a standardized set of commands across all my projects. One of the things that helps with that is mise hooks. I've actually been migrating from justfiles to mise tasks over the past few months. mise already manages my tool versions, so consolidating task running into the same tool means one less thing to install and one less config file per project. The syntax is nearly identical—you're just writing shell commands with some metadata—and mise tasks have a few nice extras like file watching and automatic parallelization. mise has a handful of lifecycle hooks that you can use to run arbitrary scripts at various points: | Hook | When it runs | | --- | --- | | | Every time you change directories | | | When you first enter a project directory | | | When you leave a project directory | | | Before a tool is installed | | | After a tool is installed | I am not a fan of injecting scripts into things that you want to be as fast as humanly possible, but here's one exception: I use the hook to print a little welcome message when I into a project: This is a small thing, but it's a nice reminder of what commands are available when I'm jumping between projects. I'm always context-switching and I never remember what the right incantation is for any given project. This way, I get a little cheat sheet every time I enter a directory.

0 views
Blog System/5 2 weeks ago

ssh-agent broken in tmux? I've got you!

A little over two years ago, I wrote an article titled SSH agent forwarding and tmux done right . In it, I described how SSH agent forwarding works—a feature that lets a remote machine use the credentials stored in your local ssh-agent instance—and how using a console multiplexer like tmux or screen often breaks it. In that article, I presented the ssh-agent-switcher : a program I put together in a few hours to fix this problem. In short, ssh-agent-switcher exposes an agent socket at a stable location ( by default) and proxies all incoming credential requests to the transient socket that the sshd server creates on a connection basis. In this article, I want to formalize this project by presenting its first actual release, 1.0.0, and explain what has changed to warrant this release number. I put effort into creating this formal release because ssh-agent-switcher has organically gained more interest than I imagined as it is solving a real problem that various people have. When I first wrote ssh-agent-switcher, I did so to fix a problem I was having at work: we were moving from local developer workstations to remote VMs, we required SSH to work on the remote VMs for GitHub access, and I kept hitting problems with the ssh-agent forwarding feature breaking because I’m an avid user of tmux . To explain the problem to my peers, I wrote the aforementioned article and prototyped ssh-agent-switcher after-hours to demonstrate a solution. At the end of the day, the team took a different route for our remote machines but I kept using this little program on my personal machines. Because of work constraints, I had originally written ssh-agent-switcher in Go and I had used Bazel as its build system. I also used my own shtk library to quickly write a bunch of integration tests and, because of the Bazel requirement, I even wrote my first ruleset, rules_shtk , to make it possible. The program worked, but due to the apparent lack of interest, I considered it “done” and what you found in GitHub was a code dump of a little project I wrote in a couple of free evenings. Recently, however, ssh-agent-switcher stopped working on a Debian testing machine I run and I had to fix it. Luckily, someone had sent a bug report describing what the problem was: OpenSSH 10.1 had changed the location where sshd creates the forwarding sockets and even changed their naming scheme, so ssh-agent-switcher had to adapt. Fixing this issue was straightforward, but doing so made me have to “touch” the ssh-agent-switcher codebase again and I got some interest to tweak it further. My energy to work on side-projects like this one and to write about them comes from your support. Subscribe now to motivate future content! As I wanted to modernize this program, one thing kept rubbing me the wrong way: I had originally forced myself to use Go because of potential work constraints. As these requirements never became relevant and I “needed to write some code” to quench some stress, I decided to rewrite the program in Rust. Why, you ask? Just because I wanted to. It’s my code and I wanted to have fun with it, so I did the rewrite. Which took me into a detour. You see: while command line parsing in Rust CLI apps is a solved problem , I had been using the ancient getopts crate in other projects of mine out of inertia. Using either library requires replicating some boilerplate across apps that I don’t like, so… I ended up cleaning up that “common code” as well and putting it into a new crate aptly-but-oddly-named getoptsargs . Take a look and see if you find it interesting… I might write a separate article on it. Doing this rewrite also made me question the decision to use Bazel (again imposed by constraints that never materialized) for this simple tool: as much as I like the concepts behind this build system and think it’s the right choice for large codebases, it was just too heavy for a trivial program like ssh-agent-switcher. So… I just dropped Bazel and wrote a Makefile—which you’d think isn’t necessary for a pure Rust project but remember that this codebase includes shell tests too. With the Rust rewrite done, I was now on a path to making ssh-agent-switcher a “real project” so the first thing I wanted to fix were the ugly setup instructions from the original code dump. Here is what the project README used to tell you to write into your shell startup scripts: Yikes. You needed shell-specific logic to detach the program from the controlling session so that it didn’t stop running when you logged out, as that would have made ssh-agent-switcher suffer from the exact same problems as regular sshd socket handling. The solution to this was to make ssh-agent-switcher become a daemon on its own with proper logging and “singleton” checking via PID file locking. So now you can reliably start it like this from any shell: I suppose you could make systemd start and manage ssh-agent-switcher automatically with a per-user socket trigger without needing the daemonization support in the binary per se… but I do care about more than just Linux and so assuming the presence of systemd is not an option. With that done, I felt compelled to fix a zero-day TODO that kept causing trouble for people: a fixed-size buffer used to proxy requests between the SSH client and the forwarded agent. This limitation caused connections to stall if the response from the ssh-agent contained more keys than fit in the buffer. The workaround had been to make the fixed-size buffer “big enough”, but that was still insufficient for some outlier cases and came with the assumption that the messages sent over the socket would fit in the OS internal buffers in one go as well. No bueno. Fixing this properly required one of the following: adding threads to handle reads and writes over two sockets in any order, dealing with the annoying / family of system calls, or using an async runtime and library (tokio) to deal with the event-like nature of proxying data between two network connections. People dislike async Rust for some good reasons, but async is the way to get to the real fearless concurrency promise. I did not fancy managing threads by hand, and I did not want to deal with manual event handling… so async it was. And you know what? Switching to async had two immediate benefits: Handling termination signals with proper cleanup became straightforward. The previous code had to install a signal handler and deal with potential races in the face of blocking system calls by doing manual polling of incoming requests, which isn’t good if you like power efficiency. Using tokio made this trivial and in a way that I more easily trust is correct. I could easily implement the connection proxying by using an event-driven loop and not having to reason about threads and their terminating conditions. Funnily enough, after a couple of hours of hacking, I felt proud of the proxying algorithm and the comprehensive unit tests I had written so I asked Gemini for feedback, and… while it told me my code was correct, it also said I could replace it all with a single call to a primitive! Fun times. I still don’t trust AI to write much code for me, but I do like it a lot to perform code reviews. Even with tokio in the picture and all of the recent new features and fixes, the Rust binary of ssh-agent-switcher is still smaller (by 100KB or so) than the equivalent Go one and I trust its implementation more. Knowing that various people had found this project useful over the last two years, I decided to conclude this sprint by creating an actual “formal release” of ssh-agent-switcher. Formal releases require: Documentation, which made me write a manual page . A proper installation process, which made me write a traditional -like script because doesn’t support installing supporting documents. A tag and release number, which many people forget about doing these days but are critical if you want the code to be packaged in upstream OSes. And with that, ssh-agent-switcher 1.0.0 went live on Christmas day of 2025. pkgsrc already has a package for it ; what is your OS waiting for? 😉 In that article, I presented the ssh-agent-switcher : a program I put together in a few hours to fix this problem. In short, ssh-agent-switcher exposes an agent socket at a stable location ( by default) and proxies all incoming credential requests to the transient socket that the sshd server creates on a connection basis. In this article, I want to formalize this project by presenting its first actual release, 1.0.0, and explain what has changed to warrant this release number. I put effort into creating this formal release because ssh-agent-switcher has organically gained more interest than I imagined as it is solving a real problem that various people have. Some background When I first wrote ssh-agent-switcher, I did so to fix a problem I was having at work: we were moving from local developer workstations to remote VMs, we required SSH to work on the remote VMs for GitHub access, and I kept hitting problems with the ssh-agent forwarding feature breaking because I’m an avid user of tmux . To explain the problem to my peers, I wrote the aforementioned article and prototyped ssh-agent-switcher after-hours to demonstrate a solution. At the end of the day, the team took a different route for our remote machines but I kept using this little program on my personal machines. Because of work constraints, I had originally written ssh-agent-switcher in Go and I had used Bazel as its build system. I also used my own shtk library to quickly write a bunch of integration tests and, because of the Bazel requirement, I even wrote my first ruleset, rules_shtk , to make it possible. The program worked, but due to the apparent lack of interest, I considered it “done” and what you found in GitHub was a code dump of a little project I wrote in a couple of free evenings. New OpenSSH naming scheme Recently, however, ssh-agent-switcher stopped working on a Debian testing machine I run and I had to fix it. Luckily, someone had sent a bug report describing what the problem was: OpenSSH 10.1 had changed the location where sshd creates the forwarding sockets and even changed their naming scheme, so ssh-agent-switcher had to adapt. Fixing this issue was straightforward, but doing so made me have to “touch” the ssh-agent-switcher codebase again and I got some interest to tweak it further. My energy to work on side-projects like this one and to write about them comes from your support. Subscribe now to motivate future content! The Rust rewrite As I wanted to modernize this program, one thing kept rubbing me the wrong way: I had originally forced myself to use Go because of potential work constraints. As these requirements never became relevant and I “needed to write some code” to quench some stress, I decided to rewrite the program in Rust. Why, you ask? Just because I wanted to. It’s my code and I wanted to have fun with it, so I did the rewrite. Which took me into a detour. You see: while command line parsing in Rust CLI apps is a solved problem , I had been using the ancient getopts crate in other projects of mine out of inertia. Using either library requires replicating some boilerplate across apps that I don’t like, so… I ended up cleaning up that “common code” as well and putting it into a new crate aptly-but-oddly-named getoptsargs . Take a look and see if you find it interesting… I might write a separate article on it. Doing this rewrite also made me question the decision to use Bazel (again imposed by constraints that never materialized) for this simple tool: as much as I like the concepts behind this build system and think it’s the right choice for large codebases, it was just too heavy for a trivial program like ssh-agent-switcher. So… I just dropped Bazel and wrote a Makefile—which you’d think isn’t necessary for a pure Rust project but remember that this codebase includes shell tests too. Daemonization support With the Rust rewrite done, I was now on a path to making ssh-agent-switcher a “real project” so the first thing I wanted to fix were the ugly setup instructions from the original code dump. Here is what the project README used to tell you to write into your shell startup scripts: Yikes. You needed shell-specific logic to detach the program from the controlling session so that it didn’t stop running when you logged out, as that would have made ssh-agent-switcher suffer from the exact same problems as regular sshd socket handling. The solution to this was to make ssh-agent-switcher become a daemon on its own with proper logging and “singleton” checking via PID file locking. So now you can reliably start it like this from any shell: I suppose you could make systemd start and manage ssh-agent-switcher automatically with a per-user socket trigger without needing the daemonization support in the binary per se… but I do care about more than just Linux and so assuming the presence of systemd is not an option. Going async With that done, I felt compelled to fix a zero-day TODO that kept causing trouble for people: a fixed-size buffer used to proxy requests between the SSH client and the forwarded agent. This limitation caused connections to stall if the response from the ssh-agent contained more keys than fit in the buffer. The workaround had been to make the fixed-size buffer “big enough”, but that was still insufficient for some outlier cases and came with the assumption that the messages sent over the socket would fit in the OS internal buffers in one go as well. No bueno. Fixing this properly required one of the following: adding threads to handle reads and writes over two sockets in any order, dealing with the annoying / family of system calls, or using an async runtime and library (tokio) to deal with the event-like nature of proxying data between two network connections. Handling termination signals with proper cleanup became straightforward. The previous code had to install a signal handler and deal with potential races in the face of blocking system calls by doing manual polling of incoming requests, which isn’t good if you like power efficiency. Using tokio made this trivial and in a way that I more easily trust is correct. I could easily implement the connection proxying by using an event-driven loop and not having to reason about threads and their terminating conditions. Funnily enough, after a couple of hours of hacking, I felt proud of the proxying algorithm and the comprehensive unit tests I had written so I asked Gemini for feedback, and… while it told me my code was correct, it also said I could replace it all with a single call to a primitive! Fun times. I still don’t trust AI to write much code for me, but I do like it a lot to perform code reviews. Documentation, which made me write a manual page . A proper installation process, which made me write a traditional -like script because doesn’t support installing supporting documents. A tag and release number, which many people forget about doing these days but are critical if you want the code to be packaged in upstream OSes.

0 views
Justin Duke 3 weeks ago

Dotfiles

Fernando convinced me to publish (and, therefore, clean up) my dotfiles : I’m interested in people who customize their experience of computing. This is often derided as “ricing”. But agency is interesting. People who remake their environment to suit them are interesting. And I am endlessly curious about how people do this. I like reading people’s init.el, their custom shell scripts, their NixOS config. It’s even better if they have some obscure hardware e.g. some keyboard layout I’ve never heard of and a trackball with custom gestures. I put my dotfiles up on GitHub because I imagine someone will find them interesting. My dotfiles are quite boring, but now they are public .

0 views
Brain Baking 3 weeks ago

Getting Emacs And MacOS To Play Nice

What a nightmare. Yet another reason (previously the never-ending flow of bloat ) to switch back from MacOS to a proper *Nix environment. I thought installing an old editor would be as simple as issuing a single “fetch me that package, will you” command. But I was so very wrong. Expect more of these “Emacs Journey” posts in-between the regular ones. Apologies if they’re completely useless to you: I need to have this on record. First: there are multiple Emacs-es out there, which one do you want? There’s Aquamacs , the vanilla Emacs ( ) which you should avoid if you want Doom or Spacemacs, and a few more that are listed in the Doom Emacs Getting Started Guide . refused to compile and the official package is not on v30 yet. Others do not provide —confusingly, there’s two binaries: the you run inside a terminal as a non-GUI editor, and the GUI one. Ultimately, this worked (and natively compiled): But then you’re stuck with a symlink as an app which Alfred refuses to recognize. On top of that, the Pro Way (TM) to start Emacs is to spawn clients and have a daemon running in the background. Since I want to experiment and make use of the feature that enables me to load different configs, I created a custom launch agent for this called (my Emacs config collection is to be called bakemacs ): that baby and off we go. The second part, starting a client, is a shell script that Automator wrapped into a “native MacOS Application” to fool Alfred et al.: I learned the hard way that you can make use of in case the client can’t find the daemon in case something killed it. Or someone. Yes, you. Me? Stop pressing ! This sends a terminate command from the client to the daemon who owns it and kills that one as well. Just delete the frame instead. Since I can’t remember all that and the Q pressing is an old habit that Dies Hard (TM), just rewire the key: And then your spell checker breaks: : Searching for program: No such file or directory, . Of course a path issue. There are several options here, I went with Yet Another Package that spawns a shell and steals its so I can mange it externally: Are we there yet? No. The application icon is butt-ugly and slightly bigger than the others. Go to https://macosicons.com/ , search for “emacs”, get yourself a nice one, press “Get Info” on your Automator-generated app, replace that icon, replace the one from the Homebrew Cellar as well, and finally kill more MacOS stuff with fire: Some Emacs builds feature the icon with Homebrew build flags but this doesn’t fix our requirement to use , not . Are we there yet? No. Throw in a bunch of smooth sailing scroll commands that make Emacs a bit more trackpad-friendly: Are we there yet? That depends. Would you like to keep the icon in the dock? In that case, we’re still screwed as Mac thinks the client is another app and convincing it otherwise or using tricks like setting doesn’t seem to work. At this point, I got tired of fighting and gave up. For me, (Alfred) + (finding “Bakemacs”) and jamming enter is good enough. I guess I’ll also create an alias called to quickly whip up a client from a terminal, although I’ve heard you should be running terminals and browsers and Emacs inside Emacs. Scrolling still is a bit annoying because the point keeps on chugging along, making it for a less than optimally smooth experience. At least I managed to get image zooming via pinch zoom on a trackpad working—take that, Sublime! Related topics: / emacs / macos / By Wouter Groeneveld on 20 December 2025.  Reply via email .

0 views
xenodium 3 weeks ago

agent-shell 0.25 updates

It's been a little while since the last agent-shell blog post detailing changes , so we're naturally due another one detailing the latest features. A native Emacs shell to interact with any LLM agent powered by ACP ( Agent Client Protocol ). Let's go through the latest changes… The biggest change is the new experimental viewport/compose mode. While agent-shell's comint shell experience has its benefits, some folks may opt for a more familiar buffer experience, that is less shell-like. There are perhaps 3 defining characteristics in the new viewport/compose feature: A dedicated compose buffer : You get a full, multiline buffer dedicated to crafting prompts. I personally find this mode of operation more natural (no need to watch out for accidental submissions via RET), but also opens up the possibility to enable your favourite minor modes that may not play nice with . You can launch compose buffers via , edit your prompt, and when you're ready, submit with the familiar binding. Viewport : I've experimented with shell viewports before and also added a similar experience to chatgpt-shell . This compose/viewport UX quickly became my primary way of interacting with non-agent LLMs. This is a read-only buffer typically displaying the latest agent interaction. Use to navigate through current interaction items. Use to switch through pages/interactions. Auto-modal : Compose and viewport modes complement each other and offer automatic transition between read-only and editable (compose) buffers. From a viewport, you can always press to reply to the latest interaction. When replying, you automatically go into edit/compose mode. When submitting via , you automatically transition into viewport (read-only) mode. While you can use at any time to compose multi-line prompts and send from the shell, to get the hybrid experience, you need to enable with . From then on, will favor the experience. You can always jump between viewport and shell with . buffers now offer the ability to queue additional prompts if the agent is busy. Use and to queue and remove requests. You can now change models via ( ), when supported by the agent. For Anthropic users, we now have and to set default agent model and modes. You can view available values by expanding shell handshake items. If keen on using defaults for a different agent, please file a feature request . By default, launching via prompts users to select one of the supported agents. You can now skip this by setting your preferred agent ( thank you Jonathan ). While shell-maker automatically prompts users to save content when killing buffers, its integration was a little clunky with agents. Elle Najt's -specific implementation is now enable by default, saving Markdown transcripts to . When launching new shells, you should see a message like: You can always open the current transcript via . To disable the new transcript generation use: Jonathan Jin introduced , enabling folks to add MCP servers to their agents. For example: You can now search across shell nodes, including collapsed ones. When using isearch ( swiper too), matching nodes are now automatically expanded. When invoking , active region, flymake errors, dired and image buffers are now automatically considered and brought over to buffers to be included while crafting prompts. There's one more. From a viewport buffer, selecting a region and pressing "r" (for reply) brings the selection over to the compose buffer as blockquoted text. We've migrated Cursor agent support to use Mike Moore's ACP Adapter . Install with: Thank you to all contributors for these improvements! Beyond what's showcased, much love and effort's been poured into polishing the experience. Interested in the nitty-gritty? Have a look through the 122 commits since the last blog post. If agent-shell or acp.el are useful to you, please consider sponsoring development. LLM tokens aren't free, and neither is the time dedicated to building this stuff ;-) Paul Nelson built agent-shell-attention.el offering a mode line attention indicator. Julian Hirn built agent-review introducing a streamlined workflow. #106 : Use replace-buffer-contents instead of erase-buffer/insert #127 : No longer possible to select the Anthropic model used by Claude Code #142 : [bug] Crash during message streaming: markdown overlay receives nil positions #143 : gemini 0.17.1 requires authMethod to authenticate (fix by Andrea ) #144 : Make collapsible indicator keymap customizable #145 : Unable to enter Plan Mode until after first message is sent #150 : Warning (undo): Buffer 'Codex Agent @ …' undo info was 25664948 bytes long #154 : Gemini CLI doesn't need authorization if already logged in #125 : Fix error when viewing proposed diffs ( Nat Page ) #129 : Support setting a preferred agent config ( Jonathan Jin ) #138 : Enable specifying MCP server configurations via custom variable ( Jonathan Jin ) #139 : Document MCP server config functionality ( Jonathan Jin ) #140 : README: Add MELPA badge + expand install instructions ( Jonathan Jin ) #141 : Add keybindings to cycle/set session mode ( Jonathan Jin ) #149 : Use fixed-pitch for ascii art logos ( Mark A. Hershberger ) #155 : Give the new custom flag to gemini for skipping the authorization ( ccQpein )

0 views

Dismantling Defenses: Trump 2.0 Cyber Year in Review

The Trump administration has pursued a staggering range of policy pivots this past year that threaten to weaken the nation’s ability and willingness to address a broad spectrum of technology challenges, from cybersecurity and privacy to countering disinformation, fraud and corruption. These shifts, along with the president’s efforts to restrict free speech and freedom of the press, have come at such a rapid clip that many readers probably aren’t even aware of them all. President Trump has repeatedly claimed that a primary reason he lost the 2020 election was that social media and Big Tech companies had conspired to silence conservative voices and stifle free speech. Naturally, the president’s impulse in his second term has been to use the levers of the federal government in an effort to limit the speech of everyday Americans, as well as foreigners wishing to visit the United States. In September, Donald Trump signed a national security directive known as NSPM-7 , which directs federal law enforcement officers and intelligence analysts to target “anti-American” activity, including any “tax crimes” involving extremist groups who defrauded the IRS. According to extensive reporting by journalist Ken Klippenstein , the focus of the order is on those expressing “opposition to law and immigration enforcement; extreme views in favor of mass migration and open borders; adherence to radical gender ideology,” as well as “anti-Americanism,” “anti-capitalism,” and “anti-Christianity.” Earlier this month, Attorney General Pam Bondi issued a memo advising the FBI to compile a list of Americans whose activities “may constitute domestic terrorism.” Bondi also ordered the FBI to establish a “ cash reward system ” to encourage the public to report suspected domestic terrorist activity. The memo states that domestic terrorism could include “opposition to law and immigration enforcement” or support for “radical gender ideology.” The Trump administration also is planning to impose social media restrictions on tourists as the president continues to ramp up travel restrictions for foreign visitors. According to a notice from U.S. Customs and Border Protection (CBP), tourists — including those from Britain, Australia, France, and Japan — will soon be required to provide five years of their social media history. The CBP said it will also collect “several high value data fields,” including applicants’ email addresses from the past 10 years, their telephone numbers used in the past five years, and names and details of family members. Wired reported in October that the US CBP executed more device searches at the border in the first three months of the year than any other previous quarter. The new requirements from CBP add meat to the bones of Executive Order 14161 , which in the name of combating “foreign terrorist and public safety threats” granted broad new authority that civil rights groups warn could enable a renewed travel ban and expanded visa denials or deportations based on perceived ideology. Critics alleged the order’s vague language around “public safety threats,” creates latitude for targeting individuals based on political views, national origin, or religion. At least 35 nations are now under some form of U.S. travel restrictions . In February, Trump ordered  executive branch agencies to stop enforcing the  U.S. Foreign Corrupt Practices Act , which froze foreign bribery investigations, and even allows for “remedial actions” of past enforcement actions deemed “inappropriate.” The White House also disbanded the Kleptocracy Asset Recovery Initiative  and  KleptoCapture Task Force  — units which proved their value in corruption cases and in seizing the assets of sanctioned Russian oligarchs — and diverted resources away from investigating white-collar crime. Also in February, Attorney General Pam Bondi dissolved the FBI’s Foreign Influence Task Force , an entity created during Trump’s first term designed to counter the influence of foreign governments on American politics. In March 2025, Reuters reported that several U.S. national security agencies had halted work on a coordinated effort to counter Russian sabotage, disinformation and cyberattacks. Former President Joe Biden had ordered his national security team to establish working groups to monitor the issue amid warnings from U.S. intelligence that Russia was escalating a shadow war against Western nations. In a test of prosecutorial independence, Trump’s Justice Department ordered prosecutors to drop the corruption case against New York Mayor Eric Adams . The fallout was immediate: Multiple senior officials resigned in protest, the case was reassigned, and chaos engulfed the Southern District of New York (SDNY) – historically one of the nation’s most aggressive offices for pursuing public corruption, white-collar crime, and cybercrime cases. When it comes to cryptocurrency, the administration has shifted regulators at the U.S. Securities and Exchange Commission (SEC) away from enforcement to cheerleading an industry that has consistently been plagued by scams, fraud and rug-pulls. The SEC in 2025 systematically retreated from enforcement against cryptocurrency operators, dropping major cases against Coinbase , Binance , and others. Perhaps the most troubling example involves Justin Sun , the Chinese-born founder of crypto currency company Tron . In 2023, the SEC charged Sun with fraud and market manipulation. Sun subsequently invested $75 million in the Trump family’s World Liberty Financial (WLF) tokens, became the top holder of the $TRUMP memecoin, and secured a seat at an exclusive dinner with the president. In late February 2025, the SEC dropped its lawsuit. Sun promptly took Tron public through a reverse merger arranged by Dominari Securities, a firm with Trump family ties. Democratic lawmakers have urged the SEC to investigate what they call “concerning ties to President Trump and his family” as potential conflicts of interest and foreign influence. In October, President Trump pardoned Changpeng Zhao , the founder of the world’s largest cryptocurrency exchange Binance . In 2023, Zhao and his company pled guilty to failing to prevent money laundering on the platform. Binance paid a $4 billion fine, and Zhao served a four-month sentence. As CBS News observed last month, shortly after Zhao’s pardon application, he was at the center of a blockbuster deal that put the Trump’s family’s WLF on the map. “Zhao is a citizen of the United Arab Emirates in the Persian Gulf and in May, an Emirati fund put $2 billion in Zhao’s Binance,” 60 Minutes reported . “Of all the currencies in the world, the deal was done in World Liberty crypto.” SEC Chairman Paul Atkins has made the agency’s new posture towards crypto explicit, stating “most crypto tokens are not securities.” At the same time, President Trump has directed the Department of Labor and the SEC to expand 401(k) access to private equity and crypto — assets that regulators have historically restricted for retail investors due to high risk, fees, opacity, and illiquidity. The executive order explicitly prioritizes “curbing ERISA litigation,” and reducing accountability for fiduciaries while shifting risk onto ordinary workers’ retirement savings. At the White House’s behest, the U.S. Treasury in March suspended the Corporate Transparency Act , a law that required companies to reveal their real owners. Finance experts warned the suspension would bring back shell companies and “ open the flood gates of dirty money ” through the US, such as funds from drug gangs, human traffickers, and fraud groups. Trump’s clemency decisions have created a pattern of freed criminals committing new offenses, including Jonathan Braun , whose sentence for drug trafficking was commuted during Trump’s first term, was found guilty in 2025 of violating supervised release and faces new charges. Eliyahu Weinstein , who received a commutation in January 2021 for running a Ponzi scheme, was sentenced in November 2025 to 37 years for running a new Ponzi scheme. The administration has also granted clemency to a growing list of white-collar criminals: David Gentile , a private equity executive sentenced to seven years for securities and wire fraud (functionally a ponzi-like scheme), and Trevor Milton , the Nikola founder sentenced to four years for defrauding investors over electric vehicle technology. The message: Financial crimes against ordinary investors are no big deal. At least 10 of the January 6 insurrectionists pardoned by President Trump have already been rearrested, charged or sentenced for other crimes , including plotting the murder of FBI agents, child sexual assault, possession of child sexual abuse material and reckless homicide while driving drunk. The administration also imposed sanctions against the International Criminal Court (ICC). On February 6, 2025, Executive Order 14203 authorized asset freezes and visa restrictions against ICC officials investigating U.S. citizens or allies, primarily in response to the ICC’s arrest warrants for Israeli Prime Minister Benjamin Netanyahu over alleged war crimes in Gaza. Earlier this month the president launched the “ Gold Card ,” a visa scheme established by an executive order in September that offers wealthy individuals and corporations expedited paths to U.S. residency and citizenship in exchange for $1 million for individuals and $2 million for companies , plus ongoing fees. The administration says it is also planning to offer a “platinum” version of the card that offers special tax breaks — for a cool $5 million. President Trump campaigned for a second term insisting that the previous election was riddled with fraud and had been stolen from him. Shortly after Mr. Trump took the oath of office for a second time, he fired the head of the Cybersecurity and Infrastructure Security Agency (CISA) — Chris Krebs (no relation) — for having the audacity to state publicly that the 2020 election was the most secure in U.S. history. Mr. Trump revoked Krebs’s security clearances, ordered a Justice Department investigation into his election security work, and suspended the security clearances of employees at SentinelOne , the cybersecurity firm where Krebs worked as chief intelligence and public policy officer. The executive order was the first direct presidential action against any US cybersecurity company. Krebs subsequently resigned from SentinelOne, telling The Wall Street Journal he was leaving to push back on Trump’s efforts “to go after corporate interests and corporate relationships.” The president also dismissed all 15 members of the Cyber Safety Review Board (CSRB), a nonpartisan government entity established in 2022 with a mandate to investigate the security failures behind major cybersecurity events — likely because those advisors included Chris Krebs. At the time, the CSRB was in the middle of compiling a much-anticipated report on the root causes of Chinese government-backed digital intrusions into at least nine U.S. telecommunications providers. Not to be outdone, the Federal Communication Commission quickly moved to roll back a previous ruling that required U.S. telecom carriers to implement stricter cybersecurity measures. Meanwhile, CISA has lost roughly a third of its workforce this year amid mass layoffs and deferred resignations. When the government shutdown began in October, CISA laid off even more employees and furloughed 65 percent of the remaining staff, leaving only 900 employees working without pay. Additionally, the Department of Homeland Security has reassigned CISA cyber specialists to jobs supporting the president’s deportation agenda. As Bloomberg reported earlier this year, CISA employees were given a week to accept the new roles or resign, and some of the reassignments included relocations to new geographic areas. The White House has signaled that it plans to cut an additional $491 million from CISA’s budget next year , cuts that primarily target CISA programs focused on international affairs and countering misinformation and foreign propaganda. The president’s budget proposal justified the cuts by repeating debunked claims about CISA engaging in censorship. The Trump administration has pursued a similar reorganization at the FBI: The Washington Post reported in October that a quarter of all FBI agents have now been reassigned from national security threats to immigration enforcement. Reuters reported last week that the replacement of seasoned leaders at the FBI and Justice Department with Trump loyalists has led to an unprecedented number of prosecutorial missteps, resulting in a 21 percent dismissal rate of the D.C. U.S. attorney’s office criminal complaints over eight weeks, compared to a mere .5% dismissal rate over the prior 10 years. “These mistakes are causing department attorneys to lose credibility with federal courts, with some judges quashing subpoenas, threatening criminal contempt and issuing opinions that raise questions about their conduct,” Reuters reported. “Grand juries have also in some cases started rejecting indictments, a highly unusual event since prosecutors control what evidence gets presented.” In August, the DHS banned state and local governments from using cyber grants on services provided by the Multi-State Information Sharing and Analysis Center (MS-ISAC), a group that for more than 20 years has shared critical cybersecurity intelligence across state lines and provided software and other resources at free or heavily discounted rates. Specifically, DHS barred states from spending funds on services offered by the Elections Infrastructure ISAC , which was effectively shuttered after DHS pulled its funding in February. Cybersecurity Dive reports that the Trump administration’s massive workforce cuts, along with widespread mission uncertainty and a persistent leadership void, have interrupted federal agencies’ efforts to collaborate with the businesses and local utilities that run and protect healthcare facilities, water treatment plans, energy companies and telecommunications networks. The publication said the changes came after the US government eliminated CIPAC — a framework that allowed private companies to share cyber and threat intel without legal penalties. “Government leaders have canceled meetings with infrastructure operators, forced out their longtime points of contact, stopped attending key industry events and scrapped a coordination program that made companies feel comfortable holding sensitive talks about cyberattacks and other threats with federal agencies,” Cybersecurity Dive’s Eric Geller wrote. Both the National Security Agency (NSA) and U.S. Cyber Command have been without a leader since Trump dismissed Air Force General Timothy Haugh in April, allegedly for disloyalty to the president and at the suggestion of far-right conspiracy theorist Laura Loomer. The nomination of Army Lt. Gen. William Hartman for the same position fell through in October. The White House has ordered the NSA to cut 8 percent of its civilian workforce (between 1,500 and 2,000 employees). As The Associated Press reported in August, the Office of the Director of National Intelligence plans to dramatically reduce its workforce and cut its budget by more than $700 million annually. Director of National Intelligence Tulsi Gabbard said the cuts were warranted because ODNI had become “bloated and inefficient, and the intelligence community is rife with abuse of power, unauthorized leaks of classified intelligence, and politicized weaponization of intelligence.” The firing or forced retirements of so many federal employees has been a boon to foreign intelligence agencies. Chinese intelligence agencies, for example, reportedly moved quickly to take advantage of the mass layoffs, using a network of front companies to recruit laid-off U.S. government employees for “consulting work.” Former workers with the Defense Department’s Defense Digital Service who resigned en-masse earlier this year thanks to DOGE encroaching on their mission have been approached by the United Arab Emirates to work on artificial intelligence for the oil kingdom’s armed forces, albeit reportedly with the blessing of the Trump administration. President Trump has filed multibillion-dollar lawsuits against a number of major news outlets over news segments or interviews that allegedly portrayed him in a negative light, suing the networks ABC , the BBC , the CBS parent company Paramount , The Wall Street Journal , and The New York Times , among others. The president signed an executive order aimed at slashing public subsidies to PBS and NPR , alleging “bias” in the broadcasters’ reporting. In July, Congress approved a request from Trump to cut $1.1 billion in federal funding for the Corporation for Public Broadcasting , the nonprofit entity that funds PBS and NPR. Brendan Carr , the president’s pick to run the  Federal Communications Commission (FCC), initially pledged to “dismantle the censorship cartel and restore free speech rights for everyday Americans.” But on January 22, 2025, the FCC reopened complaints against ABC, CBS and  NBC  over their coverage of the 2024 election. The previous FCC chair had dismissed the complaints as attacks on the First Amendment and an attempt to weaponize the agency for political purposes. President Trump in February seized control of the White House Correspondents’ Association , the nonprofit entity that decides which media outlets should have access to the White House and the press pool that follows the president. The president invited an additional 32 media outlets, mostly conservative or right-wing organizations. According to the journalism group Poynter.org , there are three religious networks, all of which lean conservative, as well as a mix of outlets that includes a legacy paper, television networks, and a digital outlet powered by artificial intelligence.  Trump also barred The Associated Press from the White House over their refusal to refer to the Gulf of Mexico as the Gulf of America. Under Trump appointee Kari Lake , the U.S. Agency for Global Media moved to dismantle Voice of America , Radio Free Europe/Radio Liberty , and other networks that for decades served as credible news sources behind authoritarian lines. Courts blocked shutdown orders, but the damage continues through administrative leave, contract terminations, and funding disputes. President Trump this term has fired most of the people  involved in processing  Freedom of Information Act  (FOIA) requests for government agencies. FOIA is an indispensable tool used by journalists and the public to request government records, and to hold leaders accountable. Petitioning the government, particularly when it ignores your requests, often requires challenging federal agencies in court. But that becomes far more difficult if the most competent law firms start to shy away from cases that may involve crossing the president and his administration. On March 22, the president issued a memorandum  that directs heads of the Justice and Homeland Security Departments to “seek sanctions against attorneys and law firms who engage in frivolous, unreasonable and vexatious litigation against the United States,” or in matters that come before federal agencies. The Trump administration announced increased vetting of applicants for H-1B visas for highly skilled workers, with an internal State Department memo saying that anyone involved in “censorship” of free speech should be considered for rejection. Executive Order 14161 , issued in 2025 on “foreign terrorist and public safety threats,” granted broad new authority that civil rights groups warn could enable a renewed travel ban and expanded visa denials or deportations based on perceived ideology. Critics charged that the order’s vague language around “public safety threats” creates latitude for targeting individuals based on political views, national origin, or religion. At the beginning of this year, President Trump ordered staffers at the Consumer Financial Protection Bureau (CFPB) to stop most work. Created by Congress in 2011 to be a clearinghouse of consumer complaints, the CFPB has sued some of the nation’s largest financial institutions for violating consumer protection laws. The CFPB says its actions have put nearly $18 billion back in Americans’ pockets in the form of monetary compensation or canceled debts, and imposed $4 billion in civil money penalties against violators. The Trump administration said it planned to fire up to 90 percent of all CFPB staff, but a recent federal appeals court ruling in Washington tossed out an earlier decision that would have allowed the firings to proceed. Reuters reported this week that an employee union and others have battled against it in court for ten months, during which the agency has been almost completely idled. The CFPB’s acting director is Russell Vought , a key architect of the GOP policy framework Project 2025. Under Vought’s direction, the CFPB in May quietly withdrew a data broker protection rule intended to limit the ability of U.S. data brokers to sell personal information on Americans. Despite the Federal Reserve’s own post-mortem explicitly blaming Trump-era deregulation for the 2023 Silicon Valley Bank collapse, which triggered a fast-moving crisis requiring emergency weekend bailouts of banks, Trump’s banking regulators in 2025 doubled down. They loosened capital requirements, narrowed definitions of “unsafe” banking practices, and stripped specific risk categories from supervisory frameworks. The setup for another banking crisis requiring taxpayer intervention is now in place. The Privacy Act of 1974, one of the few meaningful federal privacy laws, was built on the principles of consent and separation in response to the abuses of power that came to light during the Watergate era. The law states that when an individual provides personal information to a federal agency to receive a particular service, that data must be used solely for its original purpose . Nevertheless, it emerged in June that the Trump administration has built a central database of all US citizens. According to NPR , the White House plans to use the new platform during upcoming elections to verify the identity and citizenship status of US voters. The database was built by the Department of Homeland Security and the Department of Governmental Efficiency and is being rolled out in phases to US states. Probably the biggest ungotten scoop of 2025 is the inside story of what happened to all of the personal, financial and other sensitive data that was accessed by workers at the so-called Department of Government Efficiency (DOGE). President Trump tapped Elon Musk to lead the newly created department, which was mostly populated by current and former employees of Musk’s various technology companies (including a former denizen of the cybercrime community known as the “Com” ). It soon emerged that the DOGE team was using artificial intelligence to surveil at least one federal agency’s communications for hostility to Mr. Trump and his agenda. DOGE employees were able to access and synthesize data taken from a large number of previously separate and highly guarded federal databases, including those at the Social Security Administration, the Department of Homeland Security, the Office of Personnel Management, and the U.S. Department of the Treasury. DOGE staffers did so largely by circumventing or dismantling security measures designed to detect and prevent misuse of federal databases, including standard incident response protocols, auditing, and change-tracking mechanisms. For example, an IT expert with the National Labor Relations Board (NLRB) alleges that DOGE employees likely downloaded gigabytes of data from agency case files in early March, using short-lived accounts that were configured to leave few traces of network activity. The NLRB whistleblower said the large data outflows coincided with multiple blocked login attempts from addresses in Russia, which attempted to use valid credentials for a newly-created DOGE user account. The stated goal of DOGE was to reduce bureaucracy and to massively cut costs — mainly by eliminating funding for a raft of federal initiatives that had already been approved by Congress. The DOGE website claimed those efforts reduced “wasteful” and “fraudulent” federal spending by more than $200 billion. However, multiple independent reviews by news organizations determined the true “savings” DOGE achieved was off by a couple of orders of magnitude, and was likely closer to $2 billion. At the same time DOGE was slashing federal programs, President Trump fired at least 17 inspectors general at federal agencies — the very people tasked with actually identifying and stopping waste, fraud and abuse at the federal level. Those included several agencies (such as the NLRB) that had open investigations into one or more of Mr. Musk’s companies for allegedly failing to comply with protocols aimed at protecting state secrets. In September, a federal judge found the president unlawfully fired the agency watchdogs , but none of them have been reinstated. Where is DOGE now? Reuters reported last month that as far as the White House is concerned, DOGE no longer exists , even though it technically has more than half a year left to its charter. Meanwhile, who exactly retains access to federal agency data that was fed by DOGE into AI tools is anyone’s guess. KrebsOnSecurity would like to thank the anonymous researcher NatInfoSec for assisting with the research on this story.

0 views
iDiallo 4 weeks ago

Paying for the rides I took 8 years ago

What does it mean when we say that investors are subsidizing the price of a service? We often hear that ChatGPT is not profitable, despite some users paying $20 a month, or others up to $200 a month. The business is still losing money despite everything we're paying. To stay afloat, OpenAI and other AI companies have to use money from their investors to cover operations until they find a way to generate sustainable income. Will these AI companies capture enough market share and attract enough paying customers to become profitable? Will they find the right formula or cheap enough hardware to be sustainable? Lucky for us, we have the benefit of hindsight. Not for AI companies, but for an adjacent company that relied entirely on investor funds to capture market share and survive: Uber. Uber is now a publicly traded company on the NASDAQ. They first became profitable in 2023, with a net income of $1.89 billion. In 2024, they generated $9.86 billion in profit. If you're wondering what their numbers looked like in 2022, it was a net loss of $9.14 billion. When they were losing money, that was investor money. They were doing everything in their power to crush the competition and remain the only player in town. Once they captured enough market share, they pulled a switcheroo. Their prices went from extremely affordable to just being another taxi company. I took my first Uber ride in 2016. I had car troubles, and taking the bus to work would have turned a 20-minute drive into three bus rides and an hour and 20 minutes of commuting. Instead, I downloaded Uber. Within minutes, my ride was outside waiting for me. I walked to the passenger side up front and opened the door, only to find a contraption I wasn't familiar with. The driver politely asked me to sit in the back. He was paraplegic. On the ride, we had a good conversation until he dropped me off at work. A notification appeared on my phone with the price: $3.00. That's how much it cost for a 5-mile drive. For reference, taking the bus would have cost $1.50 per ride. A day pass was $5.00 at the time. But with Uber, it was $3.00 and saved me a whole lot of time. I didn't even have to think about parking once I got to work. I didn't question it because, well, it was cheap and convenient. Throughout my time at that job, I took these rides to work. When I opened the app one day and the price was suddenly $10, I didn't even flinch. I closed the app and opened Lyft as an alternative. At most, I would pay $6. If it was too expensive, I would just spend another 20 minutes at work and wait for the surge to end and prices would go back down. This felt like a cheat code to life. At that point, I questioned whether it was even worth owning a car. Mind you, I live in Los Angeles, a city where you can't do much without a car and our transit system is nothing to brag about. Nobody made money, but everybody got paid. From time to time, I would wonder: if I'm paying those measly prices for transportation, how much is the driver making? Obviously, if Uber took its cut from the $3 ride, there wouldn't be much left for the driver. But my answer came from the drivers themselves. They loved Uber. Some of them said they could make up to $80,000 a year just driving. How many $3 rides does it take? You see, there were bonuses and goals they could reach. If they completed 100 rides in a timespan, they would qualify for a bonus. Something like an extra $500. If they did 300 rides, they could double the bonus. The whole thing was gamified. In the end, Uber was happy, the driver was happy, and the rider was happy. It was the same for Lyft. There were incentives everywhere. Nobody made money, but everybody got paid. This is what it looks like when investors subsidize the cost. So what does it look like when they stop subsidizing the cost? Well, in 2022, I took those same rides. From my old apartment to that job. Instead of $3, it cost around $24. That's an 8x increase. Ridesharing is the norm these days. People hardly take taxis anymore. The Ubers and Lyfts of the world have dominated the industry by making rides so cheap that they decimated the old guards. Now that they're the only players in town, they've jacked up the prices, and hardly anyone complains. We've already changed our habits. We've forgotten what the alternative looks like. This should serve as a preview for subsidized technologies like AI. Right now, everyone is offering it for free or at unsustainable prices. Companies are in a race to capture users, train us to integrate AI into our workflows, and make us dependent on their platforms. While I can see someone paying $20, $30, or even $60 for a rideshare in an emergency, I don't see average people paying $200 for a ChatGPT subscription. Even that is at a net loss. But that's exactly the point. Right now, it doesn't matter what we pay for these subscriptions. The goal for these companies is for AI to become essential to how we work, create, and think. Once these companies capture enough market share and eliminate alternatives, they'll have the same leverage Uber gained. They'll start with a modest price increase, maybe $25 becomes $40. Then $60. Then tiered pricing for different levels of capability. Before long, what feels optional today will feel mandatory, and we'll pay whatever they ask because we'll have built our lives around it. Imagine a future where completing a legal document requires access to agentic AI. Like you literally cannot do it unless you shell out a subscription to Gemini Ultra Pro Max Turbo. The subsidy era never lasts forever. Right now, whenever I have no choice but to take Uber, I'm paying back the remaining $21 dollars from those rides I took eight years ago. Today, venture capitalists are paying for your AI queries just like they paid for my rides. But it's not a charity. Enjoy it while it lasts, but don't forget that someone, eventually, will have to pay the real price. And that someone will be us.

0 views
Corrode 1 months ago

Rust for Linux

Bringing Rust into the Linux kernel is one of the most ambitious modernization efforts in open source history. The Linux kernel, with its decades of C code and deeply ingrained development practices, is now opening its doors to a memory-safe language. It’s the first time in over 30 years that a new programming language has been officially adopted for kernel development. But the journey is far from straightforward. In this episode, we speak with Danilo Krummrich, Linux kernel maintainer and Rust for Linux core team member, about the groundbreaking work of integrating Rust into the Linux kernel. Among other things, we talk about the Nova GPU driver, a Rust-based successor to Nouveau for NVIDIA graphics cards, and discuss the technical challenges and cultural shifts required for large-scale Rust adoption in the kernel as well as the future of the Rust4Linux project. CodeCrafters helps you become proficient in Rust by building real-world, production-grade projects. Learn hands-on by creating your own shell, HTTP server, Redis, Kafka, Git, SQLite, or DNS service from scratch. Start for free today and enjoy 40% off any paid plan by using this link . Rust for Linux is a project aimed at bringing the Rust programming language into the Linux kernel. Started to improve memory safety and reduce vulnerabilities in kernel code, the project has been gradually building the infrastructure, abstractions, and tooling necessary for Rust to coexist with the kernel’s existing C codebase. Danilo Krummrich is a software engineer at Red Hat and a core contributor to the Rust for Linux project. In January 2025, he was officially added as a reviewer to the RUST entry in the kernel’s MAINTAINERS file, recognizing his expertise in developing Rust abstractions and APIs for kernel development. Danilo maintains the and branches and is the primary developer of the Nova GPU driver, a fully Rust-based driver for modern NVIDIA GPUs. He is also a maintainer of RUST [ALLOC] and several DRM-related kernel subsystems. AOSP - The Android Open Source Project Kernel Mailing Lists - Where the Linux development happens Miguel Ojeda - Rust4Linux maintainer Wedson Almeida Filho - Retired Rust4Linux maintainer noveau driver - The old driver for NVIDIA GPUs Vulkan - A low level graphics API Mesa - Vulkan and OpenGL implementation for Linux vtable - Indirect function call, a source of headaches in nouveau DRM - Direct Rendering Manager, Linux subsystem for all things graphics Monolithic Kernel - Linux’ kernel architecture The Typestate Pattern in Rust - A very nice way to model state machines in Rust pinned-init - The userspace crate for pin-init rustfmt - Free up space in your brain by not thinking about formatting kunit - Unit testing framework for the kernel Rust core crate - The only part of the Rust Standard Library used in the Linux kernel Alexandre Courbot - NVIDIA employed co-maintainer of nova-core Greg Kroah-Hartman - Linux Foundation fellow and major Linux contributor Dave Airlie - Maintainer of the DRM tree vim - not even neovim mutt - classic terminal e-mail client aerc - a pretty good terminal e-mail client Rust4Linux Zulip - The best entry point for the Rust4Linux community Rust for Linux GitHub Danilo Krummrich on GitHub Danilo Krummrich on LinkedIn

0 views
Fernando Borretti 1 months ago

I Wish People Were More Public

Probably not a popular thing to say today. The zeitgeisty thing to say is that we should all log off and live terrible cottagecore solarpunk lives raising chickens and being mindful. I wish people were more online and more public. I have rarely wished the opposite. Consider this post addressed to you, the reader. I will often find a blog post on Hacker News that really resonates. And when I go to check the rest of the site there’s three other posts. And I think: I wish you’d write more! When I find someone whose writing I really connect with, I like to read everything they have written, or at least a tractable subset of their most interesting posts. If I like what I see, I reach out. This is one of the best things about writing online: your future friends will seek you out. And, from the other side, I have often written a post where, just before publishing, I would think: “who would want to read this? It’s too personal, obscure, idiosyncratic, probably a few people will unsubscribe to the RSS feed for this”. And always those are the posts where people email me to say they always thought the same thing but could never quite put it into words. I really value those emails. “I am understood” is a wonderful feeling. I try to apply a rule that if I do something, and don’t write about it—or otherwise generate external-facing evidence of it—it didn’t happen. I have built so many things in the dark, little experiments or software projects or essays that never saw the light of day. I want to put more things out. If it doesn’t merit an entire blog post, then at least a tweet. If I follow you on Twitter, and you have posted a picture of your bookshelf, I have probably scanned every book in it. This is why I appreciate Goodreads . Like many people I have been reading a lot less over the past ~5y, but since I made a Goodreads account earlier this year, I’ve read tens of books. Reading in public has helped to motivate me. You may say reading in public is performative. I say reading in private is solipsistic. Dante, in De Monarchia , writes: All men on whom the Higher Nature has stamped the love of truth should especially concern themselves in laboring for posterity, in order that future generations may be enriched by their efforts, as they themselves were made rich by the efforts of generations past. For that man who is imbued with public teachings, but cares not to contribute something to the public good, is far in arrears of his duty, let him be assured; he is, indeed, not “a tree planted by the rivers of water that bringeth forth his fruit in his season,” [ Psalms 1:3 ] but rather a destructive whirlpool, always engulfing, and never giving back what it has devoured. My default mode is solipsism. I read in private, build in private, learn in private. And the problem with that is self-doubt and arbitrariness. I’m halfway through a textbook and think: why? Why am I learning geology? Why this topic, and not another? There is never an a priori reason. I take notes, but why tweak the LaTeX if no-one, probably not even future me, will read them? If I stop reading this book, what changes? And doing things in public makes them both more real and (potentially) useful. If you publish your study notes, they might be useful to someone. Maybe they get slurped up in the training set of the next LLM, marginally improving performance. And Goodreads, for all its annoyances, is a uniquely tender social network. Finishing a book, and then seeing a friend mark it as “want to read”, feels like a moment of closeness. I have a friend who lived in Sydney, who has since moved away, and we don’t keep in touch too often, because the timezones are inconvenient, but occasionally she likes my book updates, and I like hers, and I will probably never read that avant-garde novel, but I’m glad she is reading it. It is like saying: “You exist. I exist. I remember. I wish you happiness.” Lots of people use spaced repetition , but most everyone’s flashcard collections are private. They exist inside a database inside an app like Anki or Mochi . You can export decks, but that’s not a living artifact but a dead snapshot, frozen in time. One reason I built hashcards : by using a Git repo of Markdown files as the flashcard database, you can trivially publish your deck to GitHub. My own flashcard collection is public. I hope that more people use hashcards and put their decks up on GitHub. The point is not that you can clone their repos (which is close to useless: you have to write your own flashcards) but because I’m curious what people are learning. Not the broad strokes, since we all want to learn thermo and econ and quantum chemistry and the military history of the Song dynasty and so on, but the minutiae. Why did you make a flashcard out of this Bible passage? Why does it resonate with you? Why do you care about the interpretation of that strange passage in Antigone ? Why did you memorize this poem? Computers mediate every aspect of our lives, yet most people use their computers the way they came out of the box. At most they might change the desktop background. Some people don’t even change the default icons on the macOS dock. Even most Linux users just use the stock configuration, e.g. GNOME on Fedora or whatever. I’m interested in people who customize their experience of computing. This is often derided as “ ricing ”. But agency is interesting. People who remake their environment to suit them are interesting. And I am endlessly curious about how people do this. I like reading people’s , their custom shell scripts, their NixOS config. It’s even better if they have some obscure hardware e.g. some keyboard layout I’ve never heard of and a trackball with custom gestures. I put my dotfiles up on GitHub because I imagine someone will find them interesting. And beyond my selfish curiosity there’s also the Fedorovist ancestor simulation angle: if you die and are not cryopreserved, how else are you going to make it to the other side of the intelligence explosion? Every tweet, blog post, Git commit, journal entry, keystroke, mouse click, every one of these things is a tomographic cut of the mind that created it.

0 views

I want a better build executor

This post is part 4/4 of a series about build systems . The market fit is interesting. Git has clearly won, it has all of the mindshare, but since you can use jj to work on Git repositories, it can be adopted incrementally. This is, in my opinion, the only viable way to introduce a new VCS: it has to be able to be partially adopted. If you've worked with other determinism-based systems, one thing they have in common is they feel really fragile, and you have to be careful that you don't do something that breaks the determinism. But in our case, since we've created every level of the stack to support this, we can offload the determinism to the development environment and you can basically write whatever code you want without having to worry about whether it's going to break something. In my last post , I describe an improved build graph serialization. In this post, I describe the build executor that reads those files. Generally, there are three stages to a build: There are a lot more things an executor can do than just spawning processes and showing a progress report! This post explores what those are and sketches a design for a tool that could improve on current executors. Ninja depends on mtimes, which have many issues . Ideally, it would take notes from and look at file attributes, not just the mtime, which eliminates many more false positives. I wrote earlier about querying the build graph . There are two kinds of things you can query: The configuration graph (what bazel calls the target graph ), which shows dependencies between "human meaningful" packages; and the action graph , which shows dependencies between files. Queries on the action graph live in the executor; queries on the configuration graph live in the configure script. For example, / , , and query the configuration graph; and query the action graph. Cargo has no stable way to query the action graph. Note that “querying the graph” is not a binary yes/no. Ninja's query language is much more restricted than Bazel's. Compare Ninja's syntax for querying “the command line for all C++ files used to build the target ” 2 : to Bazel's: Bazel’s language has graph operators, such as union, intersection, and filtering, that let you build up quite complex predicates. Ninja can only express one predicate at a time, with much more limited filtering—but unlike Bazel, allows you to filter to individual parts of the action, like the command line invocation, without needing a full protobuf parser or trying to do text post-processing. I would like to see a query language that combines both these strengths: the same nested predicate structure of Bazel queries, but add a new predicate that takes another predicate as an argument for complex output filtering: We could even go so far as to give this a jq-like syntax: For more complex predicates that have multiple sets as inputs, such as set union and intersection, we could introduce a operator: In my previous post , I talked about two main uses for a tracing build system: first, to automatically add dependency edges for you; and second, to verify at runtime that no dependency edges are missing. This especially shines when the action graph has a way to express negative dependencies, because the tracing system sees every attempted file access and can add them to the graph automatically. For prior art, see the Shake build system . Shake is higher-level than an executor and doesn't work on an action graph, but it has built-in support for file tracing in all three of these modes: warning about incorrect edges; adding new edges to the graph when they're detected at runtime; and finally, fully inferring all edges from the nodes alone . I would want my executor to only support linting and hard errors for missing edges. Inferring a full action graph is scary and IMO belongs in a higher-level tool, and adding dependency edges automatically can be done by a tool that wraps the executor and parses the lints. What's really cool about this linting system is that it allows you to gradually transition to a hermetic build over time, without frontloading all the work to when you switch to the tool. The main downside of tracing is that it's highly non-portable, and in particular is very limited on macOS. One possible alternative I've thought of is to do a buck2-style unsandboxed hermetic builds, where you copy exactly the specified inputs into a tempdir and run the build from the tempdir. If that fails, rerun the build from the main source directory. This can't tell which dependency edges are missing, but it can tell you a dependency is missing without fully failing the build. The downside to that is it assumes command spawning is a pure function, which of course it's not; anything that talks to a socket is trouble because it might be stateful. Tracing environment variable access is … hard. Traditionally access goes through the libc function, but it’s also possible to take an in a main function, in which case accesses are just memory reads. That means we need to trace memory reads somehow. On x86 machines, there’s something called PIN that can do this directly in the CPU without needing compile time instrumentation. On ARM there’s SPE , which is how works, but I’m not sure whether it can be configured to track 100% of memory accesses. I need to do more research here. On Linux, this is all abstracted by . I’m not sure if there’s equivalent wrappers on Windows and macOS. There’s also DynamicRIO , which supports a bunch of platforms, but I believe it works in a similar way to QEMU, by interposing itself between the program and the CPU, which comes with a bunch of overhead. That could work as an opt-in. One last way to do this is with a SIGSEGV signal handler , but that requires that environment variables are in their own page of memory and therefore a linker script. This doesn’t work for environment variables specifically, because they aren’t linker symbols in the normal sense, they get injected by the C runtime . In general, injecting linker scripts means we’re modifying the binaries being run and might cause unexpected build or runtime failures. Here I describe more concretely the tool I want to build, which I’ve named . It would read the constrained clojure action graph serialization format (Magma) that I describe in the previous post; perhaps with a way to automatically convert Ninja files to Magma. Like Ekam , Ronin would have a continuous rebuild mode (but unlike Bazel and Buck2, no background server). Like Shake, It would have runtime tracing, with all of options, to allow gradually transitioning to a hermetic build. And it would have bazel-like querying for the action graph, both through CLI arguments with an jq syntax and through a programmatic API. Finally, it would have pluggable backends for file watching, tracing, stat-ing, progress reporting, and checksums, so that it can take advantage of systems that have more features while still being reasonably fast on systems that don’t. For example, on Windows stats are slow, so it would cache stat info; but on Linux stats are fast so it would just directly make a syscall. Like Ninja, Ronin would keep a command log with a history of past versions of the action graph. It would reuse the bipartite graph structure , with one half being files and the other being commands. It would parse depfiles and dyndeps files just after they’re built, while the cache is still hot. Like , ronin would use a single-pass approach to support early cutoff. It would hash an "input manifest" to decide whether to rebuild. Unlike , it would store a mapping from that hash back to the original manifest so you can query why a rebuild happened. Tracing would be built on top of a FUSE file system that tracked file access. 3 Unlike other build systems I know, state (such as manifest hashes, content hashes, and removed outputs) would be stored in an SQLite database, not in flat files. Kinda. Ronin takes a lot of ideas from buck2. It differs in two major ways: The main advantage of Ronin is that it can slot in underneath existing build systems people are already using—CMake and Meson—without needing changes to your build files at all. In this post I describe what a build executor does, some features I would like to see from an executor (with a special focus on tracing), and a design for a new executor called that allows existing projects generating ninja files to gradually transition to hermetic builds over time, without a “flag day” that requires rewriting the whole build system. I don’t know yet if I will actually build this tool, that seems like a lot of work 5 😄 but it’s something I would like to exist in the world. In many ways Conan profiles are analogous to ninja files: profiles are the interface between Conan and CMake in the same way that ninja files are the interface between CMake and Ninja. Conan is the only tool I'm aware of where the split between the package manager and the configure step is explicit. ↩ This is not an apple to apples comparison; ideally we would name the target by the output file, not by its alias. Unfortunately output names are unpredictable and quite long in Bazel. ↩ macOS does not have native support for FUSE. MacFuse exists but does not support getting the PID of the calling process. A possible workaround would be to start a new FUSE server for each spawned process group. FUSE on Windows is possible through winfsp . ↩ An earlier version of this post read "Buck2 only supports non-hermetic builds for system toolchains , not anything else", which is not correct. ↩ what if i simply took buck2 and hacked it to bits,,, ↩ Resolving and downloading dependencies. The tool that does this is called a package manager . Common examples are , , Conan 1 , and the resolver . Configuring the build based on the host environment and build targets. I am not aware of any common name for this, other than maybe configure script (but there exist many tools for this that are not just shell scripts). Common examples are CMake, Meson, autotools, and the Cargo CLI interface (e.g. and ). Executing a bunch of processes and reporting on their progress. The tool that does this is called a build executor . Common examples are , , , and the phase of . It does not expect to be a top-level build system. It is perfectly happy to read (and encourages) generated files from a higher level configure tool. This allows systems like CMake and Meson to mechanically translate Ninja files into this new format, so builds for existing projects can get nice things. It allows you to gradually transition from non-hermetic to hermetic builds, without forcing you to fix all your rules at once, and with tracing to help you find where you need to make your fixes. Buck2 doesn’t support tracing at all. It technically supports non-hermetic builds, but you don't get many benefits compared to using a different build system, and it's still high cost to switch build systems 4 . In many ways Conan profiles are analogous to ninja files: profiles are the interface between Conan and CMake in the same way that ninja files are the interface between CMake and Ninja. Conan is the only tool I'm aware of where the split between the package manager and the configure step is explicit. ↩ This is not an apple to apples comparison; ideally we would name the target by the output file, not by its alias. Unfortunately output names are unpredictable and quite long in Bazel. ↩ macOS does not have native support for FUSE. MacFuse exists but does not support getting the PID of the calling process. A possible workaround would be to start a new FUSE server for each spawned process group. FUSE on Windows is possible through winfsp . ↩ An earlier version of this post read "Buck2 only supports non-hermetic builds for system toolchains , not anything else", which is not correct. ↩ what if i simply took buck2 and hacked it to bits,,, ↩

0 views
neilzone 1 months ago

Adding a button to Home Assistant to run a shell command

Now that I have fixed my garage door controller , I wanted to see if I could use it from within Home Assistant, primarily so that I can then have a widget on my phone screen. In this case, the shell command is a simple invocation to a remote machine. (I am not aware of a way to add a button to an Android home screen to run a shell command or bash script.) Adding a button to run a shell command or bash script in Home Assistant was pretty straightforward, following the Home Assistant documentation for shell commands . To my configuration.yaml file, I added something like: The quirk here was that reloading the configuration.yaml file from within the Home Assistant UI was insufficient. I needed to completely restart Home Assistant to pick up the changes. Once I had restarted Home Assistant, the shell commands were available. To add buttons, I needed to create “helpers”. I did this from the Home Assistant UI, via Settings / Devices & services / Helpers / Create helper". One helper for each button. After I had created each helper, I went back into the helper’s settings, to add zone information, so that it appeared in the right place in the dashboard. Having created the button/helper, and the shell command, I used an automation to link the two together. I did this via the Home Assistant UI, Settings / Automations & scenes. For the button, it was linked to a change in state of the button, with no parameters specified. The “then do” action is the shell command for the door in question.

0 views
Allen Pike 1 months ago

Why is ChatGPT for Mac So Good?

This year, even as Anthropic, Google, and others have challenged OpenAI’s model performance crown, ChatGPT’s lead as an end-user product has only solidified. On the Dithering podcast last week (paywalled) , Ben Thompson called out an aspect of why this is: I need someone to write the definitive article on why the ChatGPT Mac app is so good, and why everyone else is in dereliction of duty in doing these. Gemini 3 is reportedly coming this week. […] And I’m looking forward to it. I expect it to be good. And it’s just going to have to be so astronomically good for me to not use ChatGPT, precisely because the [Mac] app is so useful. A model is only as useful as its applications. As AI becomes multimodal and gets better at using tools, these interfaces are getting even more important – to the point that models’ apps now matter more than benchmarks . And while every major LLM has a mobile app, only three have a Mac app: Copilot, Claude, and ChatGPT. And of those, only one is truly good. Hold on – we’re diving in. ChatGPT for Mac is a nice app. It’s well-maintained, stable, performant, and pleasant to use. Over the last year and a half, OpenAI has brought most new ChatGPT features to the Mac app on day one, and even launched new capabilities exclusively for Mac, like Work with Apps . The app does a good job of following the platform conventions on Mac. That means buttons, text fields, and menus behave as they do in other Mac apps. While ChatGPT is imperfect on both Mac and web, both platforms have the finish you would expect from a daily-use tool. Meanwhile, the Mac apps for Claude and Microsoft’s “365 Copilot” are simply websites residing in an app’s shell, like a digital hermit crab. 365 Copilot is effectively a build of the Edge browser that only loads m365.cloud.microsoft , while Claude loads their web UI using the ubiquitous Electron framework. While the Claude web app works pretty well, it only takes a few minutes of clicking around Claude for Mac to find various app-specific UI bugs and bits of missing polish. As just one example: Mac apps can typically be moved by dragging the top corner of the window. Claude supports this too, but not when you have a chat open? Unsurprisingly, the Microsoft 365 Copilot app is even worse, and Gemini doesn’t have a Mac app at all. The desktop has not been a focus for the major AI labs thus far. The oddball here is the plain “Copilot” app, which is of course unrelated to the “365 Copilot” app other than sharing an icon, corporate parent, and name. Copilot for Mac is, it seems, a pared-down native Mac reproduction of the ChatGPT app with a bit of Microsoft UI flavor. It’s actually weirdly nice, although it’s missing enough features that it feels clearly behind ChatGPT and Claude. Fascinatingly, the Copilot app doesn’t allow you to sign in with a work account. For work – the main purpose of a desktop app – you must use the janky 365 Copilot web app. While this dichotomy might be confusing, it’s a perfect illustration of the longstanding tension that’s made cross-platform the norm for business apps. Cross-platform apps like Claude’s are, of course, cheaper to develop than native ones like OpenAI’s. But cost isn’t the most important tradeoff when these very well-capitalized companies decide whether to make their apps cross-platform. The biggest tradeoff is between polished UX and coordinated featurefulness . It’s easier to get a polished app with native APIs, but at a certain scale separate apps make it hard to rapidly iterate a complex enterprise product while keeping it in sync on each platform, while also meeting your service and customer obligations. So for a consumer-facing app like ChatGPT or the no-modifier Copilot, it’s easier to go native. For companies that are, at their core, selling to enterprises, you get Electron apps. This is not as bad as it sounds, because despite popular sentiment, Electron apps can be good apps. Sure, by default they’re janky web app shells. But with great care and attention and diligence and craft, they can be polished almost as well as native apps. While they might not feel native, Electron apps like Superhuman, Figma, Cursor, and Linear are delightful 1 . These apps are tools for work, and their teams invest in fixing rough edges, UI glitches, and squirrelly behaviour that might break users’ flow. Meanwhile, ChatGPT, despite being built on native tech, has its share of problems. These range from the small (the Personalization settings pane currently has two back-arrows instead of one) to the hilarious. View this post on Instagram A post shared by Allen Pike (@allenjpike) At the end of the day, the ChatGPT app for Mac is good because they care. They have a product-led growth model that justifies spending the resources, an organizational priority on user experience, and a team that can execute on that mission. Meanwhile, Anthropic’s been going hard on enterprise sales, so it’s not shocking they’ve neglected their desktop experience. It’s unlikely they have a big team of developers on the app who don’t care about these issues – they probably haven’t had many folks working on it at all. Still, I wouldn’t count out the possibility of a change in course here. While mobile is king, desktop is still where work happens. While OpenAI has acquired Sky to double down on desktop, Google has long been all-in on the browser. That leaves Anthropic as the challenger on desktop, with their latest models begging to be paired with well-crafted apps. While Anthropic could surprise everybody by dropping a native Mac app, I would bet against that. There’s a lot of headroom available to them just by investing in doing Electron well, mixing in bits of native code where needed, and hill-climbing from “website in shell” to “great app that happens to use web technology”. Just as ChatGPT’s unexpected success woke OpenAI to the opportunities of being more product-centric, the breakout hit of Claude Code might warm Anthropic to the importance of investing in delightful tools. Last year they brought on Mike Krieger as CPO , who certainly seems like he could rally a team in this direction given the chance. Until then, ChatGPT will reign supreme. We’ve done some Electron work at Forestwalk, and it was surprising how easy it was to cause classic Electron bugs like the whole app being a white square, the top navigation scrolling out of view, and the like. It was even more surprising how tractable it is to just refuse to tolerate these common issues, and put in the time to fix them one by one. It can be done.  ↩ We’ve done some Electron work at Forestwalk, and it was surprising how easy it was to cause classic Electron bugs like the whole app being a white square, the top navigation scrolling out of view, and the like. It was even more surprising how tractable it is to just refuse to tolerate these common issues, and put in the time to fix them one by one. It can be done.  ↩

0 views
xenodium 1 months ago

Rinku: CLI link previews

In my last Bending Emacs episode, I talked about overlays and used them to render link previews in an Emacs buffer. While the overlays merely render an image, the actual link preview image is generated by rinku , a tiny command line utility I built recently. leverages macOS APIs to do the actual heavy lifting, rendering/capturing a view off screen, and saving to disk. Similarly, it can fetch preview metadata, also saving the related thumbnail to disk. In both cases, outputs to JSON. By default, fetches metadata for you. In this instance, the image looks a little something like this: On the other hand, the flag generates a preview, very much like the ones you see in native macOS and iOS apps. Similarly, the preview renders as follows: While overlays is one way to integrate anywhere in Emacs, I had been meaning to look into what I can do for eshell in particular. Eshell is just another buffer , and while overlays could do the job, I wanted a shell-like experience. After all, I already knew we can echo images into an eshell buffer . Before getting to on , there's a related hack I'd been meaning to get to for some time… While we're all likely familiar with the cat command, I remember being a little surprised to find that offers an alternative elisp implementation. Surprised too? Go check it! Where am I going with this? Well, if eshell's command is an elisp implementation, we know its internals are up for grabs , so we can technically extend it to display images too. is just another function, so we can advice it to add image superpowers. I was pleasantly surprised at how little code was needed. It basically scans for image arguments to handle within advice and otherwise delegates to 's original implementation. And with that, we can see our freshly powered-up command in action: By now, you may wonder why the detour when the post was really about ? You see, this is Emacs, and everything compounds! We can now leverage our revamped command to give similar superpowers to , by merely adding an function. As we now know, outputs things to JSON, so we can use to parse the process output and subsequently feed the image path to . can also output link titles, so we can show that too whenever possible. With that, we can see the lot in action: While non-Emacs users are often puzzled by how frequently we bring user flows and integrations on to our beloved editor, once you learn a little elisp, you start realising how relatively easily things can integrate with one another and pretty much everything is up for grabs . Reckon and these tips will be useful to you? Enjoying this blog or my projects ? I am an 👉 indie dev 👈. Help make it sustainable by ✨ sponsoring ✨ Need a blog? I can help with that . Maybe buy my iOS apps too ;)

0 views
Susam Pal 1 months ago

Fizz Buzz with Cosines

Fizz Buzz is a counting game that has become oddly popular in the world of computer programming as a simple test of basic programming skills. The rules of the game are straightforward. Players say the numbers aloud in order beginning with one. Whenever a number is divisible by 3, they say 'Fizz' instead. If it is divisible by 5, they say 'Buzz'. If it is divisible by both 3 and 5, the player says both 'Fizz' and 'Buzz'. Here is a typical Python program that prints this sequence: Here is the output: fizz-buzz.txt . Can we make the program more complicated? The words 'Fizz', 'Buzz' and 'FizzBuzz' repeat in a periodic manner throughout the sequence. What else is periodic? Trigonometric functions! Perhaps we can use trigonometric functions to encode all four rules of the sequence in a single closed-form expression. That is what we are going to explore in this article, for fun and no profit. By the end, we will obtain a discrete Fourier series that can take any integer \( n \) and select the corresponding text to be printed. In fact, we will derive it using two different methods. First, we will follow a long-winded but hopefully enjoyable approach that relies on a basic understanding of complex exponentiation, geometric series and trigonometric functions. Then, we will obtain the same result through a direct application of the discrete Fourier transform. Before going any further, we establish a precise mathematical definition for the Fizz Buzz sequence. We begin by introducing a few functions that will help us define the Fizz Buzz sequence later. We define a set of four functions \( \{ s_0, s_1, s_2, s_3 \} \) for integers \( n \) by: \begin{align*} s_0(n) &= n, \\ s_1(n) &= \mathtt{Fizz}, \\ s_2(n) &= \mathtt{Buzz}, \\ s_3(n) &= \mathtt{FizzBuzz}. \end{align*} We call these the symbol functions because they produce every term that appears in the Fizz Buzz sequence. The symbol function \( s_0 \) returns \( n \) itself. The functions \( s_1, \) \( s_2 \) and \( s_3 \) are constant functions that always return the literal words \( \mathtt{Fizz}, \) \( \mathtt{Buzz} \) and \( \mathtt{FizzBuzz} \) respectively, no matter what the value of \( n \) is. We define a function \( f(n) \) for integer \( n \) by \[ f(n) = \begin{cases} 1 & \text{if } 3 \mid n \text{ and } 5 \nmid n, \\ 2 & \text{if } 3 \nmid n \text{ and } 5 \mid n, \\ 3 & \text{if } 3 \mid n \text{ and } 5 \mid n, \\ 0 & \text{otherwise}. \end{cases} \] The notation \( m \mid n \) means that the integer \( m \) divides the integer \( n, \) i.e. \( n \) is a multiple of \( m. \) Equivalently, there exists an integer \( c \) such that \( n = cm . \) Similarly, \( m \nmid n \) means that \( m \) does not divide \( n, \) i.e. \( n \) is not a multiple of \( m. \) This function covers all four conditions involved in choosing the \( n \)th item of the Fizz Buzz sequence. As we will soon see, this function tells us which of the four symbol functions produces the \( n \)th item of the Fizz Buzz sequence. For this reason, we call \( f(n) \) the index function. We now define the Fizz Buzz sequence as the sequence \[ (s_{f(n)}(n))_{n = 1}^{\infty} \] We can expand the first few terms of the sequence explicitly as follows: \begin{align*} (s_{f(n)}(n))_{n = 1}^{\infty} &= (s_{f(1)}(1), \; s_{f(2)}(2), \; s_{f(3)}(3), \; s_{f(4)}(4), \; s_{f(5)}(5), \; s_{f(6)}(6), \; s_{f(7)}(7), \; \dots) \\ &= (s_0(1), \; s_0(2), \; s_1(3), \; s_0(4), s_2(5), \; s_1(6), \; s_0(7), \; \dots) \\ &= (1, \; 2, \; \mathtt{Fizz}, \; 4, \; \mathtt{Buzz}, \; \mathtt{Fizz}, \; 7, \; \dots). \end{align*} Note how the function \( f(n) \) produces an index \( i \) which we then use to select the symbol function \( s_i(n) \) to produce the \( n \)th term of the sequence. This is precisely why we decided to call \( f(n) \) the index function while defining it in the previous section. Here we discuss the first method of deriving our closed form expression, starting with indicator functions and rewriting them using complex exponentials and cosines. Here is the index function \( f(n) \) from the previous section with its cases and conditions rearranged to make it easier to spot interesting patterns: \[ f(n) = \begin{cases} 0 & \text{if } 5 \nmid n \text{ and } 3 \nmid n, \\ 1 & \text{if } 5 \nmid n \text{ and } 3 \mid n, \\ 2 & \text{if } 5 \mid n \text{ and } 3 \nmid n, \\ 3 & \text{if } 5 \mid n \text{ and } 3 \mid n. \end{cases} \] This function helps us select another function \( s_{f(n)}(n) \) which in turn determines the \( n \)th term of the Fizz Buzz sequence. Our goal now is to replace this piecewise formula with a single closed-form expression. To do so, we first define indicator functions \( I_m(n) \) as follows: \[ I_m(n) = \begin{cases} 1 & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] The formula for \( f(n) \) can now be written as: \[ f(n) = \begin{cases} 0 & \text{if } I_5(n) = 0 \text{ and } I_3(n) = 0, \\ 1 & \text{if } I_5(n) = 0 \text{ and } I_3(n) = 1, \\ 2 & \text{if } I_5(n) = 1 \text{ and } I_3(n) = 0, \\ 3 & \text{if } I_5(n) = 1 \text{ and } I_3(n) = 1. \end{cases} \] Do you see a pattern? Here is the same function written as a table: Do you see it now? If we treat the values in the first two columns as binary digits and the values in the third column as decimal numbers, then in each row the first two columns give the binary representation of the number in the third column. For example, \( 3_{10} = 11_2 \) and indeed in the last row of the table, we see the bits \( 1 \) and \( 1 \) in the first two columns and the number \( 3 \) in the last column. In other words, writing the binary digits \( I_5(n) \) and \( I_3(n) \) side by side gives us the binary representation of \( f(n). \) Therefore \[ f(n) = 2 \, I_5(n) + I_3(n). \] We can now write a small program to demonstrate this formula: We can make it even shorter at the cost of some clarity: What we have obtained so far is pretty good. While there is no universal definition of a closed-form expression, I think most people would agree that the indicator functions as defined above are simple enough to be permitted in a closed-form expression. In the previous section, we obtained the formula \[ f(n) = I_3(n) + 2 \, I_5(n) \] which we then used as an index to look up the text to be printed. We also argued that this is a pretty good closed-form expression already. However, in the interest of making things more complicated, we must ask ourselves: What if we are not allowed to use the indicator functions? What if we must adhere to the commonly accepted meaning of a closed-form expression which allows only finite combinations of basic operations such as addition, subtraction, multiplication, division, integer exponents and roots with integer index as well as functions such as exponentials, logarithms and trigonometric functions. It turns out that the above formula can be rewritten using only addition, multiplication, division and the cosine function. Let us begin the translation. Consider the sum \[ S_m(n) = \sum_{k = 0}^{m - 1} e^{i 2 \pi k n / m}, \] where \( i \) is the imaginary unit and \( n \) and \( m \) are integers. This is a geometric series in the complex plane with ratio \( r = e^{i 2 \pi n / m}. \) If \( n \) is a multiple of \( m , \) then \( n = cm \) for some integer \( c \) and we get \[ r = e^{i 2 \pi n / m} = e^{i 2 \pi c} = 1. \] Therefore, when \( n \) is a multiple of \( m, \) we get \[ S_m(n) = \sum_{k = 0}^{m - 1} e^{i 2 \pi k n / m} = \sum_{k = 0}^{m - 1} 1^k = m. \] If \( n \) is not a multiple of \( m, \) then \( r \ne 1 \) and the geometric series becomes \[ S_m(n) = \frac{r^m - 1}{r - 1} = \frac{e^{i 2 \pi n} - 1}{e^{i 2 \pi n / m} - 1} = 0. \] Therefore, \[ S_m(n) = \begin{cases} m & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] Dividing both sides by \( m, \) we get \[ \frac{S_m(n)}{m} = \begin{cases} 1 & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] But the right-hand side is \( I_m(n). \) Therefore \[ I_m(n) = \frac{S_m(n)}{m} = \frac{1}{m} \sum_{k = 0}^{m - 1} e^{i 2 \pi k n / m}. \] We begin with Euler's formula \[ e^{i x} = \cos x + i \sin x \] where \( x \) is a real number. From this formula, we get \[ e^{i x} + e^{-i x} = 2 \cos x. \] Therefore \begin{align*} I_3(n) &= \frac{1}{3} \sum_{k = 0}^2 e^{i 2 \pi k n / 3} \\ &= \frac{1}{3} \left( 1 + e^{i 2 \pi n / 3} + e^{i 4 \pi n / 3} \right) \\ &= \frac{1}{3} \left( 1 + e^{i 2 \pi n / 3} + e^{-i 2 \pi n / 3} \right) \\ &= \frac{1}{3} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right). \end{align*} The third equality above follows from the fact that \( e^{i 4 \pi n / 3} = e^{i 6 \pi n / 3} e^{-i 2 \pi n / 3} = e^{i 2 \pi n} e^{-i 2 \pi n/3} = e^{-i 2 \pi n / 3} \) when \( n \) is an integer. The function above is defined for integer values of \( n \) but we can extend its formula to real \( x \) and plot it to observe its shape between integers. As expected, the function takes the value \( 1 \) whenever \( x \) is an integer multiple of \( 3 \) and \( 0 \) whenever \( x \) is an integer not divisible by \( 3. \) Similarly, \begin{align*} I_5(n) &= \frac{1}{5} \sum_{k = 0}^4 e^{i 2 \pi k n / 5} \\ &= \frac{1}{5} \left( 1 + e^{i 2 \pi n / 5} + e^{i 4 \pi n / 5} + e^{i 6 \pi n / 5} + e^{i 8 \pi n / 5} \right) \\ &= \frac{1}{5} \left( 1 + e^{i 2 \pi n / 5} + e^{i 4 \pi n / 5} + e^{-i 4 \pi n / 5} + e^{-i 2 \pi n / 5} \right) \\ &= \frac{1}{5} + \frac{2}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{2}{5} \cos \left( \frac{4 \pi n}{5} \right). \end{align*} Extending this expression to real values of \( x \) allows us to plot its shape as well. Once again, the function takes the value \( 1 \) at integer multiples of \( 5 \) and \( 0 \) at integers not divisible by \( 5. \) Recall that we expressed \( f(n) \) as \[ f(n) = I_3(n) + 2 \, I_5(n). \] Substituting these trigonometric expressions yields \[ f(n) = \frac{1}{3} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + 2 \cdot \left( \frac{1}{5} + \frac{2}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{2}{5} \cos \left( \frac{4 \pi n}{5} \right) \right). \] A straightforward simplification gives \[ f(n) = \frac{11}{15} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right). \] We can extend this expression to real \( x \) and plot it as well. The resulting curve takes the values \( 0, 1, 2 \) and \( 3 \) at integer points, as desired. Now we can write our Python program as follows: The keen-eyed might notice that the expression we obtained for \( f(n) \) is a discrete Fourier series. This is not surprising, since the output of a Fizz Buzz program depends only on \( n \bmod 15. \) Any function on a finite cyclic group can be written exactly as a finite Fourier expansion. In this section, we obtain \( f(n) \) using the discrete Fourier transform. It is worth mentioning that the calculations presented here are quite tedious to do by hand. Nevertheless, this section offers a glimpse of how such calculations are performed. By the end, we will arrive at exactly the same \( f(n) \) as before. There is nothing new to discover here. We simply obtain the same result by a more direct but more laborious method. If this doesn't sound interesting, you may safely skip the subsections that follow. We know that \( f(n) \) is a periodic function with period \( 15. \) To apply the discrete Fourier transform, we look at one complete period of the function using the values \( n = 0, 1, \dots, 14. \) Over this period, we have: \begin{array}{c|ccccccccccccccc} n & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 \\ \hline f(n) & 3 & 0 & 0 & 1 & 0 & 2 & 1 & 0 & 0 & 1 & 2 & 0 & 1 & 0 & 0 \end{array} The discrete Fourier series of \( f(n) \) is \[ f(n) = \sum_{k = 0}^{14} c_k \, e^{i 2 \pi k n / 15} \] where the Fourier coefficients \( c_k \) are given by the discrete Fourier transform \[ c_k = \frac{1}{15} \sum_{n = 0}^{14} f(n) e^{-i 2 \pi k n / 15}. \] for \( k = 0, 1, \dots, 14. \) The formula for \( c_k \) is called the discrete Fourier transform (DFT). The formula for \( f(n) \) is called the inverse discrete Fourier transform (IDFT). Let \( \omega = e^{-i 2 \pi / 15}. \) Then using the values of \( f(n) \) from the table above, the DFT becomes: \[ c_k = \frac{3 + \omega^{3k} + 2 \omega^{5k} + \omega^{6k} + \omega^{9k} + 2 \omega^{10k} + \omega^{12k}}{15}. \] Substituting \( k = 0, 1, 2, \dots, 14 \) into the above equation gives us the following Fourier coefficients: \begin{align*} c_{0} &= \frac{11}{15}, \\ c_{3} &= c_{6} = c_{9} = c_{12} = \frac{2}{5}, \\ c_{5} &= c_{10} = \frac{1}{3}, \\ c_{1} &= c_{2} = c_{4} = c_{7} = c_{8} = c_{11} = c_{13} = c_{14} = 0. \end{align*} Calculating these Fourier coefficients by hand can be rather tedious. In practice they are almost always calculated using numerical software, computer algebra systems or even simple code such as the example here: fizz-buzz-fourier.py . Once the coefficients are known, we can substitute them into the inverse transform introduced earlier to obtain \begin{align*} f(n) &= \sum_{k = 0}^{14} c_k \, e^{i 2 \pi k n / 15} \\[1.5em] &= \frac{11}{15} + \frac{2}{5} \left( e^{i 2 \pi \cdot 3n / 15} + e^{i 2 \pi \cdot 6n / 15} + e^{i 2 \pi \cdot 9n / 15} + e^{i 2 \pi \cdot 12n / 15} \right) \\ & \phantom{=\frac{11}{15}} + \frac{1}{3} \left( e^{i 2 \pi \cdot 5n / 15} + e^{i 2 \pi \cdot 10n / 15} \right) \\[1em] &= \frac{11}{15} + \frac{2}{5} \left( e^{i 2 \pi \cdot 3n / 15} + e^{i 2 \pi \cdot 6n / 15} + e^{-i 2 \pi \cdot 6n / 15} + e^{-i 2 \pi \cdot 3n / 15} \right) \\ & \phantom{=\frac{11}{15}} + \frac{1}{3} \left( e^{i 2 \pi \cdot 5n / 15} + e^{-i 2 \pi \cdot 5n / 15} \right) \\[1em] &= \frac{11}{15} + \frac{2}{5} \left( 2 \cos \left( \frac{2 \pi n}{5} \right) + 2 \cos \left( \frac{4 \pi n}{5} \right) \right) \\ & \phantom{=\frac{11}{15}} + \frac{1}{3} \left( 2 \cos \left( \frac{2 \pi n}{3} \right) \right) \\[1em] &= \frac{11}{15} + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right) + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right). \end{align*} This is exactly the same expression for \( f(n) \) we obtained in the previous section. We see that the Fizz Buzz index function \( f(n) \) can be expressed precisely using the machinery of Fourier analysis. To summarise, we have defined the Fizz Buzz sequence as \[ (s_{f(n)}(n))_{n = 1}^{\infty} \] where \[ f(n) = \frac{11}{15} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right). \] and \( s_0(n) = n, \) \( s_1(n) = \mathtt{Fizz}, \) \( s_2(n) = \mathtt{Buzz} \) and \( s_3(n) = \mathtt{FizzBuzz}. \) A Python program to print the Fizz Buzz sequence based on this definition was presented earlier. That program can be written more succinctly as follows: We can also wrap this up nicely in a shell one-liner, in case you want to share it with your friends and family and surprise them: We have taken a simple counting game and turned it into a trigonometric construction consisting of a discrete Fourier series with three cosine terms and four coefficients. None of this makes Fizz Buzz any easier. Quite the contrary. But it does show that every \( \mathtt{Fizz} \) and \( \mathtt{Buzz} \) now owes its existence to a particular set of Fourier coefficients. We began with the modest goal of making this simple problem more complicated. I think it is safe to say that we did not fall short. Read on website | #absurd | #python | #programming | #technology | #mathematics | #puzzle Definitions Symbol Functions Index Function Fizz Buzz Sequence From Indicator Functions to Cosines Indicator Functions Complex Exponentials Discrete Fourier Transform One Period of Fizz Buzz Fourier Coefficients Inverse Transform

0 views
W. Jason Gilmore 1 months ago

Toggling Between Fullscreen Editor and Terminal In VSCode and Cursor

November 18, 2025: This article was originally published on June 4, 2024 and later updated for clarity after I returned to it and couldn't figure out which file to modify! I've been a Vim user for decades however a few years ago I switched to VS Code and then subsequently Cursor for my web development work. When building modern web apps you'll spend almost as much time running shell commands as coding, so I need to have a terminal within easy reach at all times. In fact I typically keep several terminal tabs open, including one opened to the local MySQL instance, one running a worker, and one to execute various shell commands including those related to managing my Git repository. I want this transition between editor and terminal to be as seamless as possible and so I setup two keyboard shortcuts to help me quickly move back and forth between the two. Furthermore, the transition will always open the terminal in fullscreen mode so I'm not fighting with screen real estate on a laptop. To configure these shortcuts, open the keyboard shortcuts ( ) file in JSON mode and add the following entries: I've used for the toggling shortcut, however you can switch this to whatever you'd like. If you're running Windows I suppose you would change the shortcut to or something like that. Once defined, save the changes and then try using the keyboard shortcut to switch between the two. With the terminal maximized your VS Code enviroment will look like this: If you have any other VS Code screen optimization tips, I'd love to hear about them! Hit me up on Twitter at @wjgilmore .

0 views