<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en" xmlns="http://www.w3.org/2005/Atom">
    <link href="https://simonsafar.com/index.xml" rel="self"></link>
    <link href="https://simonsafar.com" rel="alternate" type="text/html" hreflang="en"></link>
    <title>Simon Safar</title>
    <subtitle>All recent entries from simonsafar.com</subtitle>
    <id>https://simonsafar.com/index.xml</id>
    <updated>2024-02-19T21:01:07.008735-08:00</updated>
    <entry>
        <title type="html">Fixing Samba Again</title>
        <link href="https://simonsafar.com/2024/fixing_samba_again/"></link>
        <id>https://simonsafar.com/2024/fixing_samba_again/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2024-02-18T16:00:00.000000-08:00</published>
        <updated>2024-02-18T16:00:00.000000-08:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2024/fixing_samba_again/">
            
        &lt;h1&gt; Fixing Samba Again &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2024/02/19 &lt;/div&gt;

        &lt;p&gt;
          Apparently, file sharing between Linux and Windows is a thing that &lt;i&gt;should&lt;/i&gt; be working but somehow is especially prone to breaking. Given how there is Kerberos involved, this is not especially surprising (yes I have &lt;a href=&quot;/2021/ssh_auth_debugging&quot;&gt;debugged this before&lt;/a&gt;). I did decide to write a post about it though, just in case someone needs this particular solution... but also to demonstrate how you can dig into supposedly hard to fix things.
        &lt;/p&gt;

        &lt;h1&gt;How it started&lt;/h1&gt;

        &lt;p&gt;
          Well, it was an upgrade of my file server from Chimaera to Dadelaus (... these are &lt;a href=&quot;https://ww.devuan.org&quot;&gt;Devuan&lt;/a&gt; versions roughly corresponding to Debian Bullseye &amp;amp; Bookworm, except without systemd. We... don&apos;t particularly like systemd.) Everything went remarkably smoothly... until the point when I tried to connect to it using my Windows machine.
        &lt;/p&gt;

        &lt;p&gt;
          I don&apos;t even particularly remember what the actual error message was... Windows is not especially good at providing useful ones. So... out of the toolkit comes &lt;code&gt;tcpdump&lt;/code&gt;!
        &lt;/p&gt;

        &lt;p&gt;
          
        &lt;/p&gt;




      
        </summary>
    </entry>
    <entry>
        <title type="html">Python REPLs anywhere</title>
        <link href="https://simonsafar.com/2024/python_interact/"></link>
        <id>https://simonsafar.com/2024/python_interact/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2024-02-14T16:00:00.000000-08:00</published>
        <updated>2024-02-14T16:00:00.000000-08:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2024/python_interact/">
            
        &lt;h1&gt; Python REPLs anywhere &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2024/02/15 &lt;/div&gt;

        &lt;div style=&quot;background-color: #775544; border: 2px solid #4444aa; padding: 1em; margin: 1em;&quot;&gt;
          (Status: might need more testing; this might have been written overly too quickly.)
        &lt;/div&gt;

        &lt;p&gt;
          Programming languages that have an interactive prompt (REPL: a &amp;quot;read-eval-print loop&amp;quot;) are nice because you can try out your code interactively, without having to stop &amp;amp; recompile everything; you can just type in your functions one by one and see whether they work.
        &lt;/p&gt;

        &lt;p&gt;
          Except... no one really wants to type in functions one by one. This is why Jupyter notebooks exist: they let you push this story a little bit further; you can at least edit your functions in cells, so no re-typing / up-arrow-pressing needed. But then, once you have a decent amount of code, you&apos;d want to write an Actual Python Program. And how do you test those with REPLs?
        &lt;/p&gt;

        &lt;h1&gt;PDB&lt;/h1&gt;

        &lt;p&gt;
          Well, round one: PDB the debugger exists. You just

          &lt;code&gt;&lt;pre&gt;
import pdb
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          at the beginning of the code, and then you can drop in a breakpoint with

          &lt;code&gt;&lt;pre&gt;
pdb.set_trace()
          &lt;/pre&gt;&lt;/code&gt;

          It&apos;s fairly easy to do &amp;amp; it&apos;s more convenient to look at program state with it vs. a lot of print statements.
        &lt;/p&gt;

        &lt;p&gt;
          A less known feature of PDB is that you can &lt;b&gt;get a full interactive shell prompt&lt;/b&gt; with the &amp;quot;i&amp;quot; command. While the &lt;code&gt;(pdb)&lt;/code&gt; one is also OK for printing things, this one is less picky about what it evaluates. Thus... you now have a full REPL at the spot in your code where you actually need one, not just outside.
        &lt;/p&gt;

        &lt;h1&gt;code.interact()&lt;/h1&gt;

        &lt;p&gt;
          In case you want to drop into an interactive shell right away... you can use the actual module Python uses for interactive shells!
        &lt;/p&gt;

        &lt;p&gt;
          Try

          &lt;code&gt;&lt;pre&gt;
import code

# ... and then in your code

code.interact()
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          This will give you the nice interactive prompt... except it&apos;s literally just that, without any local variables you have. Pretty useless, but it&apos;s a good starting point.
        &lt;/p&gt;

        &lt;p&gt;
          You can improve it by
          &lt;code&gt;&lt;pre&gt;
code.interact(local=locals())
          &lt;/pre&gt;&lt;/code&gt;

          which gives you local variables... not the symbols in your module though. To fix that, you can just also add them:
        &lt;/p&gt;

        &lt;p&gt;
          &lt;code&gt;&lt;pre&gt;
code.interact(local={**locals(), **sys.modules[__name__].__dict__})
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          This way you can test out functions too.
        &lt;/p&gt;


         &lt;p class=&quot;smallgray&quot;&gt;
          ... comments welcome, either in &lt;a href=&quot;mailto:simon@simonsafar.com&quot;&gt;email&lt;/a&gt; or on the (eventual) Mastodon post &lt;a href=&quot;https://fosstodon.org/@ssafar&quot;&gt; on Fosstodon&lt;/a&gt;.
        &lt;/p&gt;

      
        </summary>
    </entry>
    <entry>
        <title type="html">Logging Into Things</title>
        <link href="https://simonsafar.com/2024/auth_providers/"></link>
        <id>https://simonsafar.com/2024/auth_providers/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2024-01-29T16:00:00.000000-08:00</published>
        <updated>2024-01-29T16:00:00.000000-08:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2024/auth_providers/">
            
        &lt;h1&gt; Logging Into Things &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2024/01/30 &lt;/div&gt;

        &lt;i&gt;(... in which we&apos;re returning to the practice of plentiful but not overly high quality posts.)&lt;/i&gt;


        &lt;p&gt;
          There is that alternate universe where everyone knows what public key cryptography is. Where you can identify yourself without remembering 23 different passwords that you need to type into various custom text boxes on websites. Where phone numbers aren&apos;t a reasonable way of identifying people.
        &lt;/p&gt;

        &lt;p&gt;
          &lt;img src=&quot;phone_and_sim_card.jpg&quot; alt=&quot;a phone, a sim card, and a cloud in the background with google logos&quot;&gt;
          &lt;br&gt;
          &lt;i&gt;(please don&apos;t insist on finding deep meaning in this image.)&lt;/i&gt;
        &lt;/p&gt;

        &lt;p&gt;
          Then, there is our current world, where... well, if you want to prove you&apos;re a unique user, you&apos;re using one of 3-4 big authentication providers... or you go back to the telcos, and have them demonstrate that you have a phone number. It... kind of makes sense, from a spam filtering perspective, but it&apos;s still not too great.
        &lt;/p&gt;

        &lt;p&gt;
          Wouldn&apos;t it be cool if we had Actual Infrastructure that handed out physical devices to people, which they could then use to identify themselves?
        &lt;/p&gt;

        &lt;p&gt;
          ... actually, could it turn out that we do have such infrastructure already?
        &lt;/p&gt;

        &lt;h1&gt;SIM cards exist.&lt;/h1&gt;

        &lt;p&gt;
          If there is a sort of opposite to a cypherpunk utopia with holding your own keys, it&apos;s probably looking a lot like telcos. There is an impressive cultural difference between them and freedom-minded internet people: in telco-land, things are directed by central authorities, the fancy application logic is in the network itself (vs. the smart endpoint model of the internet), and of course, you&apos;re going to be charged for this by the minute. However, despite them resisting the idea of becoming dumb pipes, this eventually happened, so we&apos;re now in the world of...
        &lt;/p&gt;

        &lt;p&gt;
          ... using either Google or Facebook as authentication providers.
        &lt;/p&gt;

        &lt;p&gt;
          Who then require you to have a phone number.
        &lt;/p&gt;

        &lt;p&gt;
          But then there is something else here. If you think back of the concept of having a physical device, carefully handed out to everyone, to prove their identity in a cryptographic way...
        &lt;/p&gt;

        &lt;p&gt;
          ... this is actually what SIM cards are.
        &lt;/p&gt;

        &lt;p&gt;
          Of course, in a true telco fashion, we can&apos;t actually use them for crypto; we have to go through the circuitous route of phone numbers &amp;amp; text messages. I do find it remarkably ironic though, that...
        &lt;/p&gt;

        &lt;h1&gt;... phones do VoIP?&lt;/h1&gt;

        &lt;p&gt;
          &lt;i&gt;(disclaimer: don&apos;t take me up too much on the technical details)&lt;/i&gt;
        &lt;/p&gt;

        &lt;p&gt;
          Once upon a time, mobile phones did voice as an Actual Thing. If you wanted to call someone, you&apos;d get a digital channel, end to end, allocated by the network, which you could push bits through at a given bitrate. You add an audio codec and you gloss over some details; you get a voice call!
        &lt;/p&gt;

        &lt;p&gt;
          Eventually, people wanted mobile internet. The kind where you aren&apos;t charged by the minute. So... with 3G, we had circuit switched voice calls still (with pre-allocated pipes), plus packet networking. Very different. See, that&apos;s why we, the telco, charge you by the minute for voice calls!
        &lt;/p&gt;

        &lt;p&gt;
          By the time we got to 4G / LTE though, things have changed. The amount of data traffic exceeded voice by a lot &amp;amp; got up to pretty good quality / latency levels too; why even add dedicated infrastructure for voice calls? Especially if... we can switch back to 3G whenever someone calls you. (... this was seriously happening at some point.) Or... since 3G networks are being phased out already too... here is the idea: just do voice calls over the LTE packet network!
        &lt;/p&gt;

        &lt;p&gt;
          (Like everyone else started doing on the internet somewhat earlier.)
        &lt;/p&gt;

        &lt;p&gt;
          This is VoLTE, a.k.a. Voice over LTE. It sounds like Advanced Telco Magic (of which there is still a decent amount; it&apos;s not like we can reuse open standards in a simple way that doesn&apos;t give us opportunity to add our own patents). On the other hand: what&apos;s underneath is really just SIP calls, behind a VPN.
        &lt;/p&gt;

        &lt;p&gt;
          (Mostly. Yet again glossing over some details.)
        &lt;/p&gt;

        &lt;p&gt;
          Given how all this works, the existence of &amp;quot;wifi calling&amp;quot; is not even that surprising. The basic scheme is the same: you have a packet network connection already, through which you run a VPN (IPSec actually) connection. The trick is that you &lt;i&gt;use the SIM card&lt;/i&gt; as the root of the crypto that lets you log into the VPN! From that on, the network already knows who you are; the rest is just plain VoIP calls.
        &lt;/p&gt;

        &lt;p&gt;
          The point is... we do have a neat, distributed authentication mechanism that almost everyone has an instance of, distributed between multiple companies, which can (could!) be used to log into things &amp;amp; sign stuff. It&apos;s doable.
        &lt;/p&gt;

        &lt;p&gt;
          Why are we then still relying on things like... &lt;i&gt;credit card numbers&lt;/i&gt;?
        &lt;/p&gt;
      
        </summary>
    </entry>
    <entry>
        <title type="html">Bookmarks</title>
        <link href="https://simonsafar.com/2024/bookmarks/"></link>
        <id>https://simonsafar.com/2024/bookmarks/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2024-01-21T16:00:00.000000-08:00</published>
        <updated>2024-01-21T16:00:00.000000-08:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2024/bookmarks/">
            
        &lt;h1&gt; Bookmarks &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2024/01/22 &lt;/div&gt;

        &lt;p&gt;
          Computer science has some problems that have been solved already in better ways... and despite this, we keep solving them in ways that are worse than those. Mostly because the better solutions would involve controlling code / infrastructure / etc. that inventors of worse solutions do not have control over.
        &lt;/p&gt;

        &lt;p&gt;
          Browser bookmarks are one of these things.
        &lt;/p&gt;

        &lt;p&gt;
          The typical bookmark UI involves a tree of bookmarks that you can open. You can also search for them from the browser title bar. Optimally, they can be organized in a hierarchy. They... really don&apos;t do a lot. Better browsers do have a reasonable way of managing them, too.
        &lt;/p&gt;

        &lt;p&gt;
          &lt;img src=&quot;firefox_bookmarks.PNG&quot; alt=&quot;the firefox bookmark manager window&quot;&gt;
        &lt;/p&gt;


        &lt;h1&gt; They should be files.&lt;/h1&gt;

        &lt;p&gt;
          As in... yes, we could reimplement a hierarchical file browser, but... on at least &lt;i&gt;some&lt;/i&gt; operating systems... we already have something that closely resembles a hierarchical file browser.
        &lt;/p&gt;

        &lt;p&gt;
          (... it&apos;s the hierarchical file browser.)
        &lt;/p&gt;

        &lt;p&gt;
          &lt;img src=&quot;as_files.png&quot; alt=&quot;a Windows folder, with url files&quot;&gt;
        &lt;/p&gt;

        &lt;p&gt;
          As it turns out, most operating systems even have an &amp;quot;internet shortcut&amp;quot; file type, which, when double-clicked, will open the page it points to. They are really easy to create, too: you just grab the little icon left of the URL bar &amp;amp; drop it into a file browser / desktop / etc.
        &lt;/p&gt;

        &lt;p&gt;
          (The above directory was generated by just dropping some of the bookmarks into a random directory.)
        &lt;/p&gt;

        &lt;p&gt;
          Isn&apos;t this great?
        &lt;/p&gt;

        &lt;p&gt;
          Nevertheless, you might also have noticed that, despite the seeming similarities, the directory of URL files is &lt;i&gt;significantly uglier&lt;/i&gt; than the in-browser variant. The UI is also... somewhat less convenient.
        &lt;/p&gt;

        &lt;h1&gt;So why exactly...?&lt;/h1&gt;

        &lt;p&gt;
          &lt;img src=&quot;nicer_files.png&quot; alt=&quot;the same files, as nicer icons&quot;&gt;
        &lt;/p&gt;

        &lt;p&gt;
          Here. They are now prettier. With very little extra effort.
        &lt;/p&gt;

        &lt;p&gt;
          As it turns out, you can pick icons for them! It&apos;s pretty efficient, too: all they store is a path &amp;amp; an icon ID, so you can either use the system-provided ones (to generate a quaint 90s feel) or supply your own.
        &lt;/p&gt;

        &lt;p&gt;
          Note that there is no such functionality in the Firefox bookmark manager. (Likely because no one thought you&apos;d need such functionality.) The icon you get there is that of the actual website.
        &lt;/p&gt;

        &lt;p&gt;
          &lt;img src=&quot;firefox_bookmark_icon.png&quot; alt=&quot;icons are from websites.&quot;&gt;
        &lt;/p&gt;

        &lt;p&gt;
          Which is... actually likely a better default than a Firefox icon. It&apos;s actually so good that no one bothered implementing an icon picker.
        &lt;/p&gt;

        &lt;p&gt;
          This is not a big loss. But then... you could have gotten one for free?
        &lt;/p&gt;

        &lt;h1&gt;Bookmark sync&lt;/h1&gt;

        &lt;p&gt;
          Browsers can sync bookmarks. With... themselves. As long as you sign in with a Google / Firefox / etc. account. Before this was a thing, you would (you probably still do?) have extensions explicitly for syncing bookmarks.
        &lt;/p&gt;

        &lt;p&gt;
          But... if they&apos;re files...
        &lt;/p&gt;

        &lt;p&gt;
          ... just throw them into Dropbox? Or rsync them? Or throw them onto a network drive?
        &lt;/p&gt;

        &lt;h1&gt;Dynamic bookmarks&lt;/h1&gt;

        &lt;p&gt;
          Ever wanted a list of your top 10 pending pull requests available from your browser?
        &lt;/p&gt;

        &lt;p&gt;
          URL files are super simple:
        &lt;/p&gt;

        &lt;p&gt;
          &lt;code&gt;&lt;pre&gt;
[InternetShortcut]
URL=https://simonsafar.com/2024/not_using_terminals/
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          ... even if you add an icon:
          &lt;code&gt;&lt;pre&gt;
[InternetShortcut]
URL=https://simonsafar.com/2024/not_using_terminals/
IconIndex=22
HotKey=0
IDList=
IconFile=C:\Windows\System32\shell32.dll
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          ... so if browsers actually used these instead of their own database, you could super easily generate a list of them.
        &lt;/p&gt;

        &lt;h1&gt;Automated processing&lt;/h1&gt;

        &lt;p&gt;
          You press Ctrl-D to bookmark something. It creates a file.
        &lt;/p&gt;

        &lt;p&gt;
          Some background process notices the file &amp;amp; archives the full page for you. Or makes an mp3 out of the video. Or downloads the paper pdf, sends it to your tablet, extracts all references and calls GPT-4 to make some &lt;a href=&quot;https://apps.ankiweb.net/&quot;&gt;Anki&lt;/a&gt; cards out of it for you to review.
        &lt;/p&gt;

        &lt;p&gt;
          The conclusion being: &lt;a href=&quot;https://catern.com/reuse.html&quot;&gt;reusing existing abstractions&lt;/a&gt; is nice. And although turning everything into files is not necessarily always a good idea, we could possibly &lt;a href=&quot;http://simonsafar.com/2022/tcp_port_files/&quot;&gt;turn more things into files&lt;/a&gt; before we run out of the good ones.
        &lt;/p&gt;


      
        </summary>
    </entry>
    <entry>
        <title type="html">On Not Using Terminals</title>
        <link href="https://simonsafar.com/2024/not_using_terminals/"></link>
        <id>https://simonsafar.com/2024/not_using_terminals/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2024-01-16T16:00:00.000000-08:00</published>
        <updated>2024-01-16T16:00:00.000000-08:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2024/not_using_terminals/">
            
        &lt;h1&gt; On Not Using Terminals &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2024/01/17 &lt;/div&gt;

        &lt;img src=&quot;teletype.jpg&quot; alt=&quot;a teletype terminal&quot;&gt;

        &lt;p&gt;
          The typical hierarchy of computer users involves two levels: those who use UNIX terminals a lot, and... everyone else, who &lt;i&gt;should&lt;/i&gt; but doesn&apos;t.
        &lt;/p&gt;

        &lt;p&gt;
          Said hierarchy is present in many minds of users of UNIX / Linux machines; actually, with PowerShell, even Windows users are slowly getting convinced that the True Way of using a computer involves opening one (or many) terminal windows, typing long commands (extra points for pipelines), and staring at the results as they scroll forth. This, due to its simplicity, is clearly superior to any other way in which one could use a computer... and this especially includes clicking on things.
        &lt;/p&gt;

        &lt;p&gt;
          With this, I present today&apos;s Mostly Weird Fact:
        &lt;/p&gt;

        &lt;p&gt;
          &lt;b&gt;I don&apos;t actually use terminals &lt;i&gt;that&lt;/i&gt; much.&lt;/b&gt; (Despite working a whole lot with Linux computers.)
        &lt;/p&gt;

        &lt;p&gt;
          In this post (and some of the followups?), I&apos;ll show how this might actually be nice, even.
        &lt;/p&gt;


        &lt;h2&gt;Is this yet another weird Emacs post?&lt;/h2&gt;

        &lt;!-- &lt;h2&gt;(There might be a catch)&lt;/h2&gt; --&gt;

        &lt;p&gt;
          This statement is technically true. For the record, if you &lt;i&gt;do&lt;/i&gt; want terminals, Emacs is a mostly excellent replacement for &lt;code&gt;tmux&lt;/code&gt; or &lt;code&gt;screen&lt;/code&gt;; you can start an instance on your server &amp;amp; use &lt;code&gt;emacsclient&lt;/code&gt; to reconnect to your set of terminal buffers.
        &lt;/p&gt;

        &lt;p&gt;
          This is, however, &lt;i&gt;not&lt;/i&gt; what I mean. Terminals within Emacs are still terminals, with a prompt, and an infinitely long piece of printer paper above, with all the things your previous commands printed.
        &lt;/p&gt;

        &lt;p&gt;
          There are other ways though.
        &lt;/p&gt;

        &lt;h2&gt;Just not running commands&lt;/h2&gt;

        &lt;p&gt;
          I&apos;m wondering how much of UNIX terminal usage is a combination of &lt;code&gt;cd&lt;/code&gt; and &lt;code&gt;ls&lt;/code&gt;. They are excellent tools; I&apos;m pretty sure there are also neat enhanced versions (e.g. a &lt;code&gt;cd&lt;/code&gt; with a stack of past directories).
        &lt;/p&gt;

        &lt;p&gt;
          As it happens, Emacs has a mode for &amp;quot;editing&amp;quot; directories; it&apos;s called &lt;code&gt;dired&lt;/code&gt;. If you open a directory instead of a file, you get something like this:

          &lt;img src=&quot;dired_with_post_dir.png&quot; alt=&quot;a screenshot of dired&quot;&gt;
        &lt;/p&gt;

        &lt;p&gt;
          It&apos;s just like the output of &lt;code&gt;ls&lt;/code&gt;. But then it&apos;s much better! It&apos;s just like another Emacs buffer with an open file; you can do whatever you can in those: search for text, move around, copy parts, etc. It&apos;s also much faster than exploring directories with plain &lt;code&gt;ls&lt;/code&gt;:
          &lt;ul&gt;
            &lt;li&gt;Up a directory: just hit &amp;quot;^&amp;quot;&lt;/li&gt;
            &lt;li&gt;Open a subdir: hit &amp;quot;Enter&amp;quot; on it&lt;/li&gt;
            &lt;li&gt;&amp;quot;w&amp;quot; copies the name of the file, even without having to select it (... I&apos;m literally using this right now to copy the image file name into the HTML file)&lt;/li&gt;
            &lt;li&gt;&amp;quot;C-u 0 w&amp;quot; copies the full path of a file&lt;/li&gt;
            &lt;li&gt;You can also just &lt;i&gt;edit&lt;/i&gt; (as in &amp;quot;type into the middle of&amp;quot;) file names (and do search-replace on them) if you hit &amp;quot;C-x C-q&amp;quot; (commit with &amp;quot;C-c C-c&amp;quot;)&lt;/li&gt; 
          &lt;/ul&gt;
        &lt;/p&gt;

        &lt;p&gt;
          If you just want to peek into a subdir, you can have it dump the contents into the same buffer:
          &lt;br&gt;
          
          &lt;img src=&quot;dired_with_expanded_post_dir.png&quot; alt=&quot;a screenshot of dired with a subdir open&quot;&gt;
        &lt;/p&gt;

        &lt;p&gt;
          But... most importantly, for our Quest of Avoiding Terminals: you can run commands on them!
        &lt;/p&gt;

        &lt;h2&gt;But actually still running commands&lt;/h2&gt;

        &lt;p&gt;
          You might have thought that we&apos;ll go all the way &amp;amp; will avoid running UNIX commands altogether. Well... we&apos;re definitely not going that far. Being able to run commands is actually nice; it&apos;s just we don&apos;t have to do it in a terminal (a.k.a. infinite piece of printer paper) all the time.
        &lt;/p&gt;

        &lt;p&gt;
          As it happens, it&apos;s super easy to run commands in Emacs: &amp;quot;M-!&amp;quot; (&amp;quot;M&amp;quot; is Alt, basically) gets you a prompt:
          &lt;img src=&quot;sync_date.png&quot; alt=&quot;typing date into the minibuffer&quot;&gt;
          &lt;br&gt;
          ... hit Enter &amp;amp; you get the results:
          &lt;br&gt;
          &lt;img src=&quot;sync_date_result.png&quot; alt=&quot;results showing in the minibuffer&quot;&gt;
          &lt;br&gt;
          ... also showcasing the fact that it&apos;s non-interactive: it was probably trying to ask for a new one, but we closed its input on it.
        &lt;/p&gt;

        &lt;p&gt;
          This is nice if you just want to run something quick, maybe look at the results, and have them disappear once you do anything else. (If you really want to, you can still dig them up from the &amp;quot;*Messages*&amp;quot; buffer.)
        &lt;/p&gt;

        &lt;p&gt;
          You can also run these commands async, either by lauching them as such by &amp;quot;M-&amp;amp;&amp;quot; or just by putting an &amp;amp; at the end of the command. Here is an example, with preparing a new command while a previous round of it is still running:
        &lt;/p&gt;

        &lt;p&gt;
          &lt;img src=&quot;async_shell_command.png&quot; alt=&quot;running a shell command in the background, with a for loop&quot;&gt;
        &lt;/p&gt;

        &lt;p&gt;
          The difference is that the synchronous ones will block everything &amp;amp; just display their results ephemerally; for the async ones, the output gets written into a buffer, in which you can search, copy, etc, since they are just like any other buffer / open file in Emacs. (... there might be a recurring theme here.)
        &lt;/p&gt;

        &lt;p&gt;
          Finally, you can use &lt;code&gt;dired&lt;/code&gt; (the directory editor) to pick files &amp;amp; run commands on them. You can just go to a file and hit &amp;quot;!&amp;quot;... or you can select (mark) multiple (press &amp;quot;m&amp;quot;) and hit &amp;quot;!&amp;quot; after, to get an entire list of files:
        &lt;/p&gt;

        &lt;p&gt;
          &lt;img src=&quot;running_file_in_dired.png&quot;&gt;
        &lt;/p&gt;

        &lt;p&gt;
          After typing a command, results are shown just like in the above case.
          &lt;br&gt;
          &lt;img src=&quot;running_file_in_dired_result.png&quot;&gt;
        &lt;/p&gt;


        &lt;h2&gt;Halfway conclusions&lt;/h2&gt;

        &lt;p&gt;
          OK so we can... navigate around &amp;amp; kinda run commands? In a weird way? Without a terminal? And sometimes it&apos;s quicker?
        &lt;/p&gt;

        &lt;p&gt;
          Which is nice for some things. Quick &amp;amp; simple commands, like checking the date? Sure. Converting some random images to jpegs while looking at a directory listing? Possibly. But then we have our ssh sessions and long-running compilations and countless other cases, when one-offs are just inconvenient.
        &lt;/p&gt;

        &lt;p&gt;
          So... coming up in the followup post: compilation mode, some things about ssh and some other things about shell scripts!
        &lt;/p&gt;

        &lt;!--  &lt;p class=&quot;smallgray&quot;&gt; --&gt;
        &lt;!--   ... comments welcome, either in &lt;a href=&quot;mailto:simon@simonsafar.com&quot;&gt;email&lt;/a&gt; or on the (eventual) Mastodon post &lt;a href=&quot;https://fosstodon.org/@ssafar&quot;&gt; on Fosstodon&lt;/a&gt;. --&gt;
        &lt;!-- &lt;/p&gt; --&gt;

      
        </summary>
    </entry>
    <entry>
        <title type="html">Voice recognition with Emacs</title>
        <link href="https://simonsafar.com/2024/whisper/"></link>
        <id>https://simonsafar.com/2024/whisper/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2024-01-09T16:00:00.000000-08:00</published>
        <updated>2024-01-09T16:00:00.000000-08:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2024/whisper/">
            
        &lt;h1&gt; Voice recognition with Emacs &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2024/01/10 &lt;/div&gt;

        &lt;p&gt;
          &lt;i&gt;... because Emacs is the best for quick hacks to integrate with basically anything. Originally from 2023/09/03, edited a bit now because... we shall start posting again!&lt;/i&gt;
        &lt;/p&gt;

        &lt;!-- &lt;div style=&quot;background-color: #775544; border: 2px solid #4444aa; padding: 2em; margin: 1em;&quot;&gt; --&gt;
        &lt;!--   This is post no. 32 for &lt;a href=&quot;https://kevq.uk/&quot;&gt;Kev Quirk&apos;s&lt;/a&gt;  &lt;a href=&quot;https://100daystooffload.com/&quot;&gt;#100DaysToOffload&lt;/a&gt; challenge. The point is to write &lt;i&gt;many&lt;/i&gt; things, not to write &lt;i&gt;good&lt;/i&gt; ones. Please adjust quality expectations accordingly :) --&gt;
        &lt;!-- &lt;/div&gt; --&gt;


        &lt;h1&gt; Enter Whisper &lt;/h1&gt;

        &lt;p&gt;
          Being able to just talk to computers and have them understand what you&apos;re saying is something that I&apos;ve been thinking about for a long time. There used to be systems that did work somewhat, but they were Windows-only, expensive, and hard to integrate with anything.

        &lt;/p&gt;

        &lt;p&gt;
          At some point though, rather abruptly, OpenAI released Whisper, a basically open-source voice recognition model that happens to be remarkably good. Good to the point of... you stop thinking about how good it is since it never really presents an issue. You can run it locally if you have a reasonably beefy machine, or, alternatively, they also provide an online service API that is dirt cheap if you are not planning on talking to it all day.
        &lt;/p&gt;

        &lt;p&gt;
          On the other hand, the current user interface is not especially great. They don&apos;t even have a good command line tool that you could just throw an mp3 file at, let alone something that that you could use interactively to save yourself from a bunch of typing.
        &lt;/p&gt;

        &lt;p&gt;
          Wouldn&apos;t it be great though to have a nice text editor we could integrate it into? One that is relatively easy to extend?

        &lt;/p&gt;

        &lt;h1&gt;The Emacs way&lt;/h1&gt;

        &lt;p&gt;
          What we would want is a button which, when pressed, would start recording your voice; when you press it again, it could inject it into the current buffer. (In case you were wondering: buffer is Emacs lingo for basically an open file.)
        &lt;/p&gt;

        &lt;p&gt;
          So what is the most hacky, simplistic way you can imagine making this happen? Let&apos;s just use using ffmpeg to record some sound in the background when the button is pressed. When it&apos;s pressed again, we just interrupt ffmpeg and pick up the file it has produced.
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;
(defun ssafar--launch-voice-ffmpeg ()
  (unless ssafar-openai-token
    (error &amp;quot;No API token available&amp;quot;))
  (let* ((proc (start-process-shell-command
                &amp;quot;voice ffmpeg&amp;quot; &amp;quot;*ffmpeg*&amp;quot;
                &amp;quot;ffmpeg -y -t 60 -f pulse -i default /home/simon/voice_temp.mp3&amp;quot;)))
    (setq ssafar-voice-ffmpeg-process proc)
    (message &amp;quot;recording...&amp;quot;)
    (set-process-sentinel proc &apos;ssafar--voice-ffmpeg-sentinel)))
        &lt;/code&gt;&lt;/pre&gt;

        &lt;p&gt;
          We put up a 60 second upper limit for how long it can run. This is typically enough for dictating a couple of sentences, but prevents it from accidentally recording 3 hours and then sending it out to OpenAI for processing.
        &lt;/p&gt;
        &lt;p&gt;
          The above code launches the process in the background; it also saves the reference to the running process to &amp;quot;ssafar-voice-ffmpeg-process&amp;quot;... which is just a global variable, essentially.
        &lt;/p&gt;
        &lt;p&gt;
          As for what the actual button does...
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;
(defun ssafar-launch-or-commit-voice ()
  (interactive)
  (if (not ssafar-voice-ffmpeg-process)
      (ssafar--launch-voice-ffmpeg)
    (progn
      (interrupt-process ssafar-voice-ffmpeg-process)
      ;; technically, it might not be dead yet, but... eventually.
      )))

        &lt;/code&gt;&lt;/pre&gt;

        &lt;p&gt;
          We just check whether our global variable for FFmpeg process is set or not. If this hasn&apos;t been set yet, we launch a new process. Meanwhile, if it exists already, we interrupt it &amp;amp; clean it up.
        &lt;/p&gt;

        &lt;p&gt;
          Background processes in Emacs can have so-called sentinels, which are functions that get called when anything changes in the processes state, for example it dies, finishes, and so on. Here&apos;s ours.
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;
(defun ssafar--voice-ffmpeg-sentinel (proc event)
  ;; reset the variable
  (setq ssafar-voice-ffmpeg-process nil)
  (cond
   ;; interrupt is ok... why 255 is there is a question, but... oh well
   ((memq (process-exit-status proc) &apos;(2 255))
    (message &amp;quot;finished; calling whisper...&amp;quot;)
    (ssafar-voice-call-whisper)) ;; calling the actual service if everything went well with ffmpeg
   (t (message &amp;quot;... something went wrong with the process&amp;quot;))))
        &lt;/code&gt;&lt;/pre&gt;

        &lt;p&gt;
          As for the service call:
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;
(defun ssafar-openai-transcription (audio-path)
  &amp;quot;Send an audio file to OpenAI for transcription.
AUDIO-PATH should be the full path to the audio file.&amp;quot;
  (unless ssafar-openai-token
    (error &amp;quot;No API token available&amp;quot;))

  (let ((command (format &amp;quot;curl -s https://api.openai.com/v1/audio/transcriptions \
-H \&amp;quot;Authorization: Bearer %s\&amp;quot; \
-H \&amp;quot;Content-Type: multipart/form-data\&amp;quot; \
-F file=\&amp;quot;@%s\&amp;quot; \
-F model=\&amp;quot;whisper-1\&amp;quot;&amp;quot;
                         ssafar-openai-token
                         audio-path)))
    (json-read-from-string
     (let ((default-directory &amp;quot;~&amp;quot;)) ;; so that it doesn&apos;t try doing stuff with tramp
       (shell-command-to-string command)
       ))))

        &lt;/code&gt;&lt;/pre&gt;

        &lt;p&gt;
          Given how Whisper wants a multi-part post request, we don&apos;t really bother using the built-in Emacs version. Just call out to curl directly.
        &lt;/p&gt;
        &lt;p&gt;
          The actual code that calls this gives it the mp3 file that was recorded by ffmpeg, and calls a callback once done.
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;
(defun ssafar-voice-call-whisper ()
  &amp;quot;Sends the current output file to whisper&amp;quot;
  (setq ssafar-last-voice-output
        (ssafar-openai-transcription
         &amp;quot;/home/simon/voice_temp.mp3&amp;quot;))
  (let ((the-text  (cdr (assoc &apos;text ssafar-last-voice-output))))
    (if ssafar-current-voice-callback
        (funcall ssafar-current-voice-callback the-text)
      (message the-text))))
        &lt;/code&gt;&lt;/pre&gt;

        &lt;p&gt;
          This callback was set up when we first launched the enter process and it depends on whether we are in Emacs proper or some other X window. (As it happens, Emacs is also my window manager this time, but it would probably also work without, as long as we get some global hotkeys routed to it.) This way, if we&apos;re in an actual Emacs buffer, we inject the text; otherwise, we emulate keypresses that e.g. a browser can catch, so that all this works even if what we&apos;re looking at is another, non-Emacs app.
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;
(defun ssafar--voice-callback-dwim (text)
  &amp;quot;Do something reasonable depending on the mode we&apos;re in&amp;quot;
  (cond
   ((derived-mode-p &apos;exwm-mode)
    (start-process-shell-command
     &amp;quot;xdotool&amp;quot; nil nil
     (format &amp;quot;xdotool type --clearmodifiers %s&amp;quot;
             (shell-quote-argument text))))
   (t (insert text)))
  (message &amp;quot;(whisper done)&amp;quot;))
        &lt;/code&gt;&lt;/pre&gt;

        &lt;p&gt;
          As a final touch, you can make all this happen while you hit a particular key.
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;
(global-set-key (kbd &amp;quot;&lt;f12&gt;&amp;quot;) &apos;ssafar-launch-or-commit-voice)
        &lt;/f12&gt;&lt;/code&gt;&lt;/pre&gt;


        &lt;h1&gt;Conclusion&lt;/h1&gt;

        &lt;p&gt;
          Is this the optimal way of doing this? Probably not. On the other hand, the total amount of code required to do this is really not a lot, and compared to just a plain shell script, this actually gives some feedback to you about which state the process is in. For example, you do get the little &amp;quot;recording...&amp;quot; notification message while it&apos;s running; I did find this fairly useful instead of trying to figure out whether I have pressed the button an odd or an even number of times.
        &lt;/p&gt;
        &lt;p&gt;
          There are plenty of opportunities to improve on this: for example, currently they cannot handle additional recorded speech while processing the previous chunk. Also, ffmpeg does have a habit of splitting off the last second of audio when interrupted, apparently. (Might be just my fault of me interrupting it in a stupid way though.)
        &lt;/p&gt;
        &lt;p&gt;
          Nevertheless, as it turns out, having a voice recognition engine tightly integrated into your text editor is immensely useful. For example, this article was written mainly using this tool, making it way faster to finish. (As it turns out, most humans are way better at talking than typing, even if they are pretty good at typing.)
        &lt;/p&gt;
         &lt;p class=&quot;smallgray&quot;&gt;
          ... comments welcome, either in &lt;a href=&quot;mailto:simon@simonsafar.com&quot;&gt;email&lt;/a&gt; or on the (eventual) Mastodon post &lt;a href=&quot;https://fosstodon.org/@ssafar&quot;&gt; on Fosstodon&lt;/a&gt;.
        &lt;/p&gt;

      
        </summary>
    </entry>
    <entry>
        <title type="html">Debian Packages for Everything!</title>
        <link href="https://simonsafar.com/2023/dpkg/"></link>
        <id>https://simonsafar.com/2023/dpkg/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2023-02-24T16:00:00.000000-08:00</published>
        <updated>2023-02-24T16:00:00.000000-08:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2023/dpkg/">
            
        &lt;h1&gt; Debian Packages for Everything! &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 02/25/2023 &lt;/div&gt;

        &lt;p&gt;
          &lt;i&gt;... in which /usr/local is no more. Also featuring large amounts of laziness with minor surprises.&lt;/i&gt;
        &lt;/p&gt;

        &lt;h1&gt; The problem &lt;/h1&gt;

        &lt;p&gt;
          You clone a git repo from GitHub. (... yes I know we&apos;re promoting Microsoft products here; we&apos;re not trying to solve All The Problems right now.) You compile it. And then... &lt;code&gt;make install&lt;/code&gt;.
        &lt;/p&gt;

        &lt;p&gt;
          It then dumps everything into &lt;code&gt;/usr/local&lt;/code&gt;. Which... kinda works... unless you want to remove it, figure out where it came from, install it on other machines, etc.
        &lt;/p&gt;

        &lt;p&gt;
          Ideally, you&apos;d make a Debian package for it. You imagine making a Debian package for it. And... then... you realize that it&apos;s quite some work, so you just... don&apos;t.
        &lt;/p&gt;

        &lt;h1&gt;checkinstall&lt;/h1&gt;

        &lt;p&gt;
          Have you ever heard of &lt;a href=&quot;http://checkinstall.izto.org/&quot;&gt;checkinstall&lt;/a&gt;? It&apos;s a tool that does... almost what we&apos;d want: it has you run &lt;code&gt;make install&lt;/code&gt;, watches what happens, and packs the results into a Debian package.
        &lt;/p&gt;

        &lt;p&gt;
          It&apos;s not a lot harder than doing &lt;code&gt;make install&lt;/code&gt; (or whatever command it takes to install) itself.
        &lt;/p&gt;

        &lt;p&gt;
          Of course, you still have to give it a package name, description, etc. on the command line:
          &lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;~/some_git_repo/ $ checkinstall --pkgname some_git_repo \
      --pkgsource https://github.com/someone/some_git_repo \
      --pkgarch=amd64 \
      make install&lt;/code&gt;&lt;/pre&gt;
        &lt;/p&gt;

        &lt;p&gt;
          This is still some work, so...
        &lt;/p&gt;

        &lt;h1&gt;The shell script&lt;/h1&gt;

        &lt;p&gt;
          ... let&apos;s write a shell script that fetches metadata from GitHub &amp;amp; fills them out!
        &lt;/p&gt;

        &lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;#!/bin/bash

# Get the URI of the local Git repository
URI=$(git remote get-url origin)

# Extract the owner and repository name from the URI
OWNER=$(echo $URI | cut -d &amp;quot;/&amp;quot; -f 4)
REPO=$(echo $URI | cut -d &amp;quot;/&amp;quot; -f 5 | cut -d &amp;quot;.&amp;quot; -f 1)

# Retrieve information about the repository from the GitHub API
API_RESULT=$(curl https://api.github.com/repos/$OWNER/$REPO)

# Extract the name of the repository
NAME=$(echo $API_RESULT | jq -r &apos;.name&apos;)

# Extract the description of the repository
echo $API_RESULT | jq -r &apos;.description&apos; &amp;gt;description-pak

# Extract the latest release tag of the repository
TAG=$(curl https://api.github.com/repos/$OWNER/$REPO/releases/latest | jq -r &apos;.tag_name&apos;)

echo &amp;quot;Tag is: $TAG&amp;quot;

# Build the checkinstall command
echo checkinstall \
  --pkgname=$NAME \
  --pkgversion=$TAG \
  --pkgrelease=1 \
  --pkggroup=default \
  --pkgsource=$URI \
  --pkgarch=all \
  --maintainer=$OWNER \
  --install=no \
  --showinstall=yes \
  $@
&lt;/code&gt;&lt;/pre&gt;

        &lt;p&gt;
          We use the version hash as a &amp;quot;version&amp;quot; so that even if we come across the package later, we&apos;ll be able to tell where it came from.
        &lt;/p&gt;

        &lt;p&gt;
          Obviously, it&apos;s not a &lt;i&gt;true&lt;/i&gt;, well-working Debian package; it doesn&apos;t have any dependencies added, it assumes that it works on any architecture, etc. Definitely not publishable right away... but... it&apos;s better than just dumping stuff in &lt;code&gt;/usr/local&lt;/code&gt;!
        &lt;/p&gt;

        &lt;h1&gt;... but... a shell script?&lt;/h1&gt;

        &lt;p&gt;
          Here is a Python script doing the same thing! With some minor fixes (e.g. for missing tags):
        &lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import subprocess
import json
import sys

# Get the URI of the local Git repository
uri = subprocess.check_output([&amp;quot;git&amp;quot;, &amp;quot;remote&amp;quot;, &amp;quot;get-url&amp;quot;, &amp;quot;origin&amp;quot;]).decode(&amp;quot;utf-8&amp;quot;).strip()

# Extract the owner and repository name from the URI
owner, repo = uri.split(&amp;quot;/&amp;quot;)[-2:]
repo = repo.split(&amp;quot;.&amp;quot;)[0]

# Retrieve information about the repository from the GitHub API
api_result = json.loads(subprocess.check_output([&amp;quot;curl&amp;quot;, &amp;quot;https://api.github.com/repos/{}/{}&amp;quot;.format(owner, repo)]).decode(&amp;quot;utf-8&amp;quot;))

# Extract the name of the repository
name = api_result[&amp;quot;name&amp;quot;]

# Extract the description of the repository
with open(&amp;quot;description-pak&amp;quot;, &amp;quot;w&amp;quot;) as f:
    f.write(api_result[&amp;quot;description&amp;quot;])

# Extract the latest release tag of the repository
try:
    tag = json.loads(subprocess.check_output([&amp;quot;curl&amp;quot;, &amp;quot;https://api.github.com/repos/{}/{}/releases/latest&amp;quot;.format(owner, repo)]).decode(&amp;quot;utf-8&amp;quot;))[&amp;quot;tag_name&amp;quot;]
except:
    tag = subprocess.check_output([&amp;quot;git&amp;quot;, &amp;quot;rev-parse&amp;quot;, &amp;quot;HEAD&amp;quot;]).decode(&amp;quot;utf-8&amp;quot;).strip()[:10]

print(&amp;quot;Tag is: {}&amp;quot;.format(tag))

# Build the checkinstall command
print(&amp;quot;echo checkinstall \\\n  --pkgname={} \\\n  --pkgversion={} \\\n  --pkgrelease=1 \\\n  --pkggroup=default \\\n  --pkgsource={} \\\n  --pkgarch=all \\\n  --maintainer={} \\\n  --install=no \\\n  --showinstall=yes \\\n  {}&amp;quot;.format(name, tag, uri, owner, &amp;quot; &amp;quot;.join(sys.argv[1:])))
        &lt;/code&gt;&lt;/pre&gt;




        &lt;p class=&quot;smallgray&quot;&gt;
          ... comments welcome, either in &lt;a href=&quot;mailto:simon@simonsafar.com&quot;&gt;email&lt;/a&gt; or on the (eventual) Mastodon post &lt;a href=&quot;https://fosstodon.org/@ssafar&quot;&gt; on Fosstodon&lt;/a&gt;.
        &lt;/p&gt;


        &lt;h1&gt;OK so why &lt;i&gt;both&lt;/i&gt;?&lt;/h1&gt;

        &lt;p&gt;
          As in... if we have a shell script... why rewrite it in Python? Also... isn&apos;t this code... a little bit... ugly?
        &lt;/p&gt;

        &lt;p&gt;
          Well, actually: the fun part about all this code is who it was written by. It was... ChatGPT, mostly. And then OpenAI&apos;s code model turned it into Python.
        &lt;/p&gt;

        &lt;p&gt;
          In fact, &lt;i&gt;checkinstall&lt;/i&gt; itself was recommended by ChatGPT! And it also knows how to use it. Along with GitHub APIs. I just asked:

        &lt;/p&gt;

        &lt;p&gt;
          &lt;i&gt;how do I get the author, name, description etc. from a github repo?&lt;/i&gt;
        &lt;/p&gt;

        &lt;p&gt;
          followed by
        &lt;/p&gt;

        &lt;p&gt;
          &lt;i&gt;write a bash script that, based on the results of the above API, fills out a call to checkinstall (e.g. --pkgname, --pkgversion etc.)&lt;/i&gt;
        &lt;/p&gt;

        &lt;p&gt;
          It did mess up quoting &lt;code&gt;$DESCRIPTION&lt;/code&gt; first, and made up some arguments to &lt;code&gt;checkinstall&lt;/code&gt; that don&apos;t actually exist, but... it knows how to use it in general, and the resulting shell script is an excellent starting point (it&apos;s pretty much what you see above).
        &lt;/p&gt;

        &lt;p&gt;
          They&apos;re really nice tools. Also, in case you weren&apos;t worried about &lt;a href=&quot;https://en.wikipedia.org/wiki/Existential_risk_from_artificial_general_intelligence&quot;&gt;where this is going&lt;/a&gt;... maybe it&apos;s time to start doing so.
        &lt;/p&gt;

      
        </summary>
    </entry>
    <entry>
        <title type="html">Talking to your Android phone from anywhere</title>
        <link href="https://simonsafar.com/2022/adb_superpowers/"></link>
        <id>https://simonsafar.com/2022/adb_superpowers/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2022-07-16T17:00:00.000000-07:00</published>
        <updated>2022-07-16T17:00:00.000000-07:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2022/adb_superpowers/">
            
        &lt;h1&gt; Talking to your Android phone from anywhere &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2022/07/17 &lt;/div&gt;

        &lt;p&gt;
          &lt;i&gt;... how to &amp;quot;ssh&amp;quot; into your Android phone from a server far away?&lt;/i&gt;
        &lt;/p&gt;

        &lt;p&gt;
          (tl;dr: combine &lt;b&gt;adb tcpip&lt;/b&gt; with &lt;b&gt;Tailscale&lt;/b&gt;!)
        &lt;/p&gt;

        &lt;h1&gt;Why?&lt;/h1&gt;

        &lt;p&gt;
          Ever wished your Android phone was an Actual Computer? (... well, it is, it&apos;s just hiding it well.) Like... if only you could, say, write a cron job that checks where it is? Or just remote-desktop into it? While your processes aren&apos;t randomly killed because Android doesn&apos;t like them?
        &lt;/p&gt;

        &lt;h1&gt; ... ADB? &lt;/h1&gt;

        &lt;p&gt;
          I&apos;ll assume you know what ADB is. If not... you can &lt;a href=&quot;https://developer.android.com/studio/command-line/adb&quot;&gt;look it up&lt;/a&gt;: it&apos;s the standard way of debugging Android phones for developers. You can run commands in a shell, pull files, start apps, send keyboard events... many things that Android works &lt;i&gt;very hard&lt;/i&gt; to prevent arbitrary apps from doing. You don&apos;t even need root for most of it: you can just enable developer mode.
        &lt;/p&gt;

        &lt;p&gt;
          Except... there is a catch. It needs a USB cable between your computer &amp;amp; the phone. Which is fine if you just want to do Actual Development, but... it&apos;s slightly inconvenient if you just want to &lt;i&gt;rsync&lt;/i&gt; a few photos off it. It&apos;s, at least &lt;a href=&quot;../../2021/trivial_inconveniences/&quot;&gt;inconvenient&lt;/a&gt; enough to make you stop doing whatever you want to do regularly. It&apos;s also impossible to automate.
        &lt;/p&gt;

        &lt;h1&gt;Alternatives&lt;/h1&gt;

        &lt;p&gt;
          There are apps that launch an SSH server on the phone. The problem is that... they&apos;re &lt;i&gt;apps&lt;/i&gt;. Thus, they&apos;re a lot less powerful than ADB. (... also, I&apos;m not entirely sure that they&apos;d be allowed to keep running with an open port all the time).
        &lt;/p&gt;

        &lt;h1&gt;No USB!&lt;/h1&gt;

        &lt;p&gt;
          You can get around this though. Actually, Android supports &lt;i&gt;remote &lt;/i&gt; debugging, via a TCP port, which has the same capabilities as doing it over USB!
        &lt;/p&gt;

        &lt;p&gt;
          The catch is... you need to first enable this with, well, a USB cable. It&apos;s not hard though; once your phone is connected, you can just say

          &lt;code&gt;&lt;pre&gt;
~ $ adb tcpip [portnumber]
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          ... and then you can connect your computer to it with
        &lt;/p&gt;

        &lt;p&gt;
          &lt;code&gt;&lt;pre&gt;
~ $ adb connect [ip address]:[the port you picked above]
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          ... which will give you the same access as if you had an actual USB cable connected. Then, once you&apos;re done, you can disconnect with

          &lt;code&gt;&lt;pre&gt;
~ $ adb disconnect
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          You then only have to repeat this after a phone restart.
        &lt;/p&gt;

        &lt;p&gt;
          As expected though, you generally have to be on the same wifi network though; phone carriers are doing a good job at firewalling away open ports (in our case, it&apos;s probably for the better). So you can do stuff with your phone while you&apos;re at home, but...
        &lt;/p&gt;

        &lt;h1&gt;... enter Tailscale!&lt;/h1&gt;

        &lt;p&gt;
          As you might have noticed, I kinda &lt;a href=&quot;../../2021/trivial_inconveniences/&quot;&gt;like Tailscale&lt;/a&gt;. It&apos;s a VPN thing that &lt;i&gt;feels like&lt;/i&gt; you had actual LAN cabling connecting all your devices, no matter where they are.
        &lt;/p&gt;

        &lt;p&gt;
          Which... is... pretty much we&apos;d need for this.
        &lt;/p&gt;

        &lt;p&gt;
          And, as it turns out, the ADB TCP server on the phone &lt;i&gt;does&lt;/i&gt; happen to listen on the Tailscale IP, too. Which... you can talk to &lt;i&gt;any time&lt;/i&gt;. Even if you&apos;re sitting in a cafe with a laptop and your phone is still on LTE. Even if you&apos;re in a cafe and it&apos;s a server back home that&apos;s doing the connecting with a cron job.
        &lt;/p&gt;

        &lt;h1&gt;Practicalities&lt;/h1&gt;

        &lt;p&gt;
          Admittedly, I haven&apos;t tested this solution a lot yet... but regardless, here are some preliminary findings.
        &lt;/p&gt;

        &lt;p&gt;
          It &lt;b&gt;always works&lt;/b&gt;. You&apos;re now in the same class as big tech companies&apos; notification services: even if the phone is asleep, you can wake it up!
        &lt;/p&gt;

        &lt;p&gt;
          It also &lt;b&gt;looks kinda good on power&lt;/b&gt;. Having &lt;i&gt;adb tcpip&lt;/i&gt; on doesn&apos;t prevent the phone from sleeping. Apparently, connecting, even &lt;i&gt;leaving an actual shell open&lt;/i&gt;, doesn&apos;t prevent the phone from sleeping? 
        &lt;/p&gt;

        &lt;p&gt;
          My experiment for this was just to run a for loop in the shell with a &lt;i&gt;sleep 0.3&lt;/i&gt; in it; it becomes a &lt;i&gt;lot&lt;/i&gt; more choppy once the screen is not on (indicating that it&apos;s asleep most of the time). On the other hand... this &lt;i&gt;might&lt;/i&gt; prevent it from going into &lt;i&gt;deeper&lt;/i&gt; sleep levels; the jury is still out on how bad this is for, say, an entire day.
        &lt;/p&gt;

        &lt;h1&gt;... remote desktop?&lt;/h1&gt;

        &lt;p&gt;
          Well, yes, I mentioned that in the beginning. So, if you&apos;re interested: &lt;a href=&quot;https://github.com/Genymobile/scrcpy&quot;&gt;scrcpy&lt;/a&gt; is a thing. It&apos;s basically remote desktop (normally) over USB, with compressed video streaming of frames... but then it&apos;s using ADB for all of it, so you can do this with the above setup, too!
        &lt;/p&gt;

        &lt;p&gt;
          (... it&apos;s also open source, and looks reasonably non-overcomplicated.)
        &lt;/p&gt;

         &lt;p class=&quot;smallgray&quot;&gt;
          ... comments welcome, either in &lt;a href=&quot;mailto:simon@simonsafar.com&quot;&gt;email&lt;/a&gt; or on the (eventual) Mastodon post &lt;a href=&quot;https://fosstodon.org/@ssafar&quot;&gt; on Fosstodon&lt;/a&gt;.
        &lt;/p&gt;

      
        </summary>
    </entry>
    <entry>
        <title type="html">Fancifying the Command Line with JSON</title>
        <link href="https://simonsafar.com/2022/new_command_line/"></link>
        <id>https://simonsafar.com/2022/new_command_line/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2022-04-16T17:00:00.000000-07:00</published>
        <updated>2022-04-16T17:00:00.000000-07:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2022/new_command_line/">
            
        &lt;h1&gt; Fancifying the Command Line with JSON &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2022/04/17 &lt;/div&gt;

        &lt;p&gt;
          &lt;i&gt;... tl;dr: JSON fits in well with command line piping!&lt;/i&gt;
        &lt;/p&gt;

        &lt;div style=&quot;background-color: #775544; border: 2px solid #4444aa; padding: 2em; margin: 1em;&quot;&gt;
          This is technically no longer part of &lt;a href=&quot;https://kevq.uk/&quot;&gt;Kev Quirk&apos;s&lt;/a&gt;  &lt;a href=&quot;https://100daystooffload.com/&quot;&gt;#100DaysToOffload&lt;/a&gt; challenge. The point, nevertheless,  is &lt;i&gt;still&lt;/i&gt; to write &lt;i&gt;many&lt;/i&gt; things, not to write &lt;i&gt;good&lt;/i&gt; ones. Please adjust quality expectations accordingly :)
        &lt;/div&gt;


        &lt;p&gt;
          I recently came across this &lt;a href=&quot;https://jvns.ca/blog/2022/04/12/a-list-of-new-ish--command-line-tools/&quot;&gt;article&lt;/a&gt; on some new-ish command line tools on &lt;a href=&quot;https://news.ycombinator.com/item?id=31009313#31060833&quot;&gt;HN&lt;/a&gt;. Which did serve as a reminder for the existence of &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt;, the JSON query / processing / piping tool.
        &lt;/p&gt;

        &lt;p&gt;
          You can combine this with e.g. &lt;a href=&quot;https://kellyjonbrazil.github.io/jc/&quot;&gt;jc&lt;/a&gt;, which can turn outputs of various command line tools to json, and achieve something like this:
        &lt;/p&gt;

        &lt;p&gt;
          &lt;code class=&quot;language-javascript&quot;&gt;&lt;pre&gt;
$ jc ls
[{&amp;quot;filename&amp;quot;: &amp;quot;battleship.png&amp;quot;}, {&amp;quot;filename&amp;quot;: &amp;quot;index.html&amp;quot;}, {&amp;quot;filename&amp;quot;: &amp;quot;liblist.png&amp;quot;}]
$ jc ls -l | jq
[
  {
    &amp;quot;filename&amp;quot;: &amp;quot;battleship.png&amp;quot;,
    &amp;quot;flags&amp;quot;: &amp;quot;-rw-r--r--&amp;quot;,
    &amp;quot;links&amp;quot;: 1,
    &amp;quot;owner&amp;quot;: &amp;quot;simon&amp;quot;,
    &amp;quot;group&amp;quot;: &amp;quot;simon&amp;quot;,
    &amp;quot;size&amp;quot;: 20947,
    &amp;quot;date&amp;quot;: &amp;quot;Jan 24 18:11&amp;quot;
  },
  {
    &amp;quot;filename&amp;quot;: &amp;quot;index.html&amp;quot;,
    &amp;quot;flags&amp;quot;: &amp;quot;-rw-r--r--&amp;quot;,
    &amp;quot;links&amp;quot;: 1,
    &amp;quot;owner&amp;quot;: &amp;quot;simon&amp;quot;,
    &amp;quot;group&amp;quot;: &amp;quot;simon&amp;quot;,
    &amp;quot;size&amp;quot;: 15608,
    &amp;quot;date&amp;quot;: &amp;quot;Mar 28 21:46&amp;quot;
  },
  {
    &amp;quot;filename&amp;quot;: &amp;quot;liblist.png&amp;quot;,
    &amp;quot;flags&amp;quot;: &amp;quot;-rw-r--r--&amp;quot;,
    &amp;quot;links&amp;quot;: 1,
    &amp;quot;owner&amp;quot;: &amp;quot;simon&amp;quot;,
    &amp;quot;group&amp;quot;: &amp;quot;simon&amp;quot;,
    &amp;quot;size&amp;quot;: 16763,
    &amp;quot;date&amp;quot;: &amp;quot;Feb 5 13:00&amp;quot;
  }
]
$ jc ls -l | jq &apos;.[] | {.filename, .size}&apos;
{
  &amp;quot;name&amp;quot;: &amp;quot;battleship.png&amp;quot;,
  &amp;quot;date&amp;quot;: &amp;quot;Jan 24 18:11&amp;quot;
}
{
  &amp;quot;name&amp;quot;: &amp;quot;index.html&amp;quot;,
  &amp;quot;date&amp;quot;: &amp;quot;Mar 28 21:46&amp;quot;
}
{
  &amp;quot;name&amp;quot;: &amp;quot;liblist.png&amp;quot;,
  &amp;quot;date&amp;quot;: &amp;quot;Feb 5 13:00&amp;quot;
}
          &lt;/pre&gt;&lt;/code&gt;
        &lt;/p&gt;

        &lt;p&gt;
          Apart from the little chaos with jq and having to expand the JSON array into individual elements it can act on (the &amp;quot;.[]&amp;quot; part), it&apos;s a lot easier to handle (and less prone to breaking) than the standard awk + sed + grep combo.
        &lt;/p&gt;

        &lt;p&gt;
          One might even argue that just by doing JSON you can get most of the benefits of Powershell (the &amp;quot;passing objects around instead of just plain text&amp;quot; part), without the downsides (... of your tools not &lt;i&gt;actually&lt;/i&gt; just dumping text into pipes, so they&apos;re somewhat... harder to implement?). Not having used Powershell a lot though, there might be a lot of things I&apos;m missing here.
        &lt;/p&gt;

        &lt;p&gt;
          Nevertheless... this is yet another instance where, instead of custom borderline-human-readable syntax, we use something more standard, obtaining a lot more flexibility. Just like &lt;a href=&quot;../../2021/syntax&quot;&gt;with programming languages&lt;/a&gt;!
        &lt;/p&gt;

         &lt;p class=&quot;smallgray&quot;&gt;
          ... comments welcome, either in &lt;a href=&quot;mailto:simon@simonsafar.com&quot;&gt;email&lt;/a&gt; or on the (eventual) Mastodon post &lt;a href=&quot;https://fosstodon.org/@ssafar&quot;&gt; on Fosstodon&lt;/a&gt;.
        &lt;/p&gt;

      
        </summary>
    </entry>
    <entry>
        <title type="html">Day 100</title>
        <link href="https://simonsafar.com/2022/day_100/"></link>
        <id>https://simonsafar.com/2022/day_100/</id>
        <author>
            <name>Simon Safar</name>
        </author>
        <published>2022-03-29T17:00:00.000000-07:00</published>
        <updated>2022-03-29T17:00:00.000000-07:00</updated>
        <summary type="html" xml:base="https://simonsafar.com/2022/day_100/">
            
        &lt;h1&gt; Day 100 &lt;/h1&gt;
        &lt;div class=&quot;date&quot;&gt; 2022/03/30 &lt;/div&gt;

        &lt;div style=&quot;background-color: #775544; border: 2px solid #4444aa; padding: 2em; margin: 1em;&quot;&gt;
          This is post no. 100 for &lt;a href=&quot;https://kevq.uk/&quot;&gt;Kev Quirk&apos;s&lt;/a&gt;  &lt;a href=&quot;https://100daystooffload.com/&quot;&gt;#100DaysToOffload&lt;/a&gt; challenge. The point is to write &lt;i&gt;many&lt;/i&gt; things, not to write &lt;i&gt;good&lt;/i&gt; ones. Please adjust quality expectations accordingly :)
        &lt;/div&gt;

        &lt;p&gt;
          So it has come to this.
        &lt;/p&gt;

        &lt;p&gt;
          (... relevant &lt;a href=&quot;https://xkcd.com/1022/&quot;&gt;xkcd&lt;/a&gt;.)
        &lt;/p&gt;

        &lt;p&gt;
          If I&apos;m counting correctly (I&apos;m... hoping I&apos;m counting correctly?), this is the 100th article of the series that started very close to a year ago, with &lt;a href=&quot;../../2021/sockets&quot;&gt;Sockets: part 1&lt;/a&gt;. And... as it turns out, having a &lt;i&gt;hundred&lt;/i&gt; articles solves the problem of &amp;quot;my site is a pointless, empty ghosttown with posts with the word &apos;test&apos; in their title&amp;quot; fairly reasonably well!
        &lt;/p&gt;

        &lt;p&gt;
          For some background on what this entire thing is... there is also the &lt;a href=&quot;../../2021/halfway&quot;&gt;article written halfway through&lt;/a&gt;. But... of course, the 100th of all of them has to be yet another retrospective! So here it is.
        &lt;/p&gt;

        &lt;p&gt;
          &lt;img src=&quot;beeminder.png&quot; tag=&quot;a graph from beeminder, showing progress&quot;&gt;
        &lt;/p&gt;

        &lt;h1&gt;Some conclusions&lt;/h1&gt;

        &lt;p&gt;
          &lt;b&gt;&lt;a href=&quot;https://beeminder.com/&quot;&gt;Beeminder&lt;/a&gt; &lt;i&gt;works&lt;/i&gt;&lt;/b&gt;. At least for me. Being threatened with having to pay them $30 if you don&apos;t finish today&apos;s article, even though you&apos;re tired and it&apos;s late in the evening and you &lt;i&gt;really&lt;/i&gt; don&apos;t want to, does wonders. &lt;i&gt;Sometimes&lt;/i&gt; I did end up getting ahead, just for fun (... kinda the green dots?), but most of the time, it was &amp;quot;last minute&amp;quot; posts.
        &lt;/p&gt;

        &lt;p&gt;
          At the same time though... often, I really didn&apos;t end up having the time to do something Actually Interesting and write about it. Some of the posts that I did like a decent amount were about e.g. &lt;a href=&quot;../hammer&quot;&gt;hacking some MIDI stuff with Wireshark&lt;/a&gt;... or &lt;a href=&quot;../smtp&quot;&gt;talking to an SMTP server by hand&lt;/a&gt; and writing about it; doing this needs a lot of time than &amp;quot;let&apos;s write something in half an hour before falling asleep&amp;quot;. Instead, there was a lot of &amp;quot;weird philosophy speculation about the nature of programming&amp;quot;, which might not have lead anywhere interesting.
        &lt;/p&gt;

        &lt;p&gt;
          Nevertheless... &lt;b&gt;consistency gets you fairly far&lt;/b&gt;. Sure, not all articles ended up being especially fancy, but whenever &lt;i&gt;I had the chance / mood to do something fancy&lt;/i&gt;, there &lt;i&gt;was&lt;/i&gt; an article, because I was writing one every 2-3-4 days, so the process of creating one has been worked out fairly well.
        &lt;/p&gt;

        &lt;p&gt;
          At the same time... at some point, I stopped posting every article on my &lt;a href=&quot;https://fosstodon.org/web/@ssafar&quot;&gt;Mastodon account&lt;/a&gt;, since... I didn&apos;t think many of them made it over the quality bar. Which is still a lot better than &lt;i&gt;not writing&lt;/i&gt; them because of speculation on them not making it over the quality bar.
        &lt;/p&gt;

        &lt;h1&gt;Was it worth it?&lt;/h1&gt;

        &lt;p&gt;
          Hell yes.
        &lt;/p&gt;

        &lt;p&gt;
          Much has been written about how writing ideas down helps actually &lt;i&gt;thinking&lt;/i&gt; about them. Even if no one ends up reading them. So getting in the habit of doing this is nice already (even though it&apos;s been a specific, mostly computer-y subset of ideas so far).
        &lt;/p&gt;

        &lt;p&gt;
          But then... my article on &lt;a href=&quot;../../2021/lisp_scripting&quot;&gt;writing shell scripts in Lisp&lt;/a&gt; has been surely visited a lot of times... just by me, as a reference. Not sure whether there were &lt;i&gt;additional&lt;/i&gt; visitors, but this was already worth it. (... update: apparently, it&apos;s getting visits from google.com, of all places!)
        &lt;/p&gt;

        &lt;p&gt;
          On the other hand, my &lt;a href=&quot;../../2021/emacs_mobile&quot;&gt;Emacs OS Mobile&lt;/a&gt; article made it into the &lt;a href=&quot;https://linmob.net/linbits-57-weekly-linux-phone-news-week31-32/&quot;&gt;Linux on Mobile newsletter&lt;/a&gt;! How cool is that?
        &lt;/p&gt;

        &lt;p&gt;
          That article was, by the way, the most popular one, by number of visits. Runner-ups include &lt;a href=&quot;../../2021/web_vitals&quot;&gt;Web Vitals&lt;/a&gt; (in which it turns out that this site isn&apos;t super fast, after all... we might have fixed it with a new front page though?), the one on &lt;a href=&quot;../smtp&quot;&gt;SMTP&lt;/a&gt;, the &lt;a href=&quot;../../2021/amazon_prime&quot;&gt;Amazon Prime&apos;s evilness&lt;/a&gt; one, and &lt;a href=&quot;../../2021/self_signed&quot;&gt;one trying to make certificates slightly more intuitive&lt;/a&gt;. Also, stuff on &lt;a href=&quot;../../2021/potter_api&quot;&gt;copyright&lt;/a&gt;, &lt;a href=&quot;../../2021/tabs&quot;&gt;tabs, &lt;a href=&quot;../2022&quot;&gt;Signal&lt;/a&gt; and &lt;a href=&quot;../../2021/discord&quot;&gt;Discord&lt;/a&gt;.
        &lt;/a&gt;&lt;/p&gt;

        &lt;h1&gt;What&apos;s next?&lt;/h1&gt;

        &lt;p&gt;
          Well... first of all, yet another round of thanks to &lt;a href=&quot;https://kevq.uk/&quot;&gt;Kev&lt;/a&gt; for &lt;a href=&quot;https://100daystooffload.com/&quot;&gt;making up this idea&lt;/a&gt;!
        &lt;/p&gt;

        &lt;p&gt;
          And... no, the current cadence of articles is &lt;i&gt;definitely&lt;/i&gt; not going to continue. I&apos;ll need more time to actually &lt;i&gt;make them interesting&lt;/i&gt;.
        &lt;/p&gt;

        &lt;p&gt;
          On the other hand... they&apos;re also not going to &lt;i&gt;stop&lt;/i&gt; coming entirely. Having an Actual Website with Actual Articles is something that I now know how to do; although I won&apos;t have &amp;quot;100 days&amp;quot; to blame for low quality, I hopefully have learned already not to care a lot. I do agree with Joels &lt;a href=&quot;https://joelchrono12.xyz/blog/day-100-actually/&quot;&gt;101th article&lt;/a&gt; on this: it&apos;s just getting started!
        &lt;/p&gt;

        &lt;p&gt;
          On the other hand... not having to keep them coming will hopefully let me fix a few things. In no particular order:
        &lt;/p&gt;

        &lt;p&gt;
          ... can we make an ActivityPub feed of all the articles so that anyone from the Fediverse can follow them? and then boost good ones from my actual account?
        &lt;/p&gt;

        &lt;p&gt;
          Also... &lt;i&gt;actually posting them somewhere&lt;/i&gt; might be a good idea sometimes, too.
        &lt;/p&gt;

        &lt;p&gt;
          Along with... maybe a redesign for the actual articles, not just the title page?
        &lt;/p&gt;

        &lt;p&gt;
          But most importantly: this idea of &lt;b&gt;consistently producing a ton of questionable-quality stuff to make some cool things in the process&lt;/b&gt; seems generalizable to... a lot of &lt;i&gt;other&lt;/i&gt; things, too! So &lt;i&gt;that&lt;/i&gt; part might continue. Maybe it will be large amounts of weird synth / piano songs; maybe finally learning Blender reasonably well; maybe writing some Actual Software (yes I do have some Lisp-related plans there).
        &lt;/p&gt;

        &lt;p&gt;
          Finally: thank you for everyone who ended up reading &lt;i&gt;any&lt;/i&gt; of the many, many articles... hope you found something fun / interesting somewhere!
        &lt;/p&gt;

         &lt;p class=&quot;smallgray&quot;&gt;
          ... comments welcome, either in &lt;a href=&quot;mailto:simon@simonsafar.com&quot;&gt;email&lt;/a&gt; or on the (eventual) Mastodon post &lt;a href=&quot;https://fosstodon.org/@ssafar&quot;&gt; on Fosstodon&lt;/a&gt;.
        &lt;/p&gt;

      
        </summary>
    </entry>
</feed>