<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.1.1">Jekyll</generator><link href="https://duckpond.ch/feed.xml" rel="self" type="application/atom+xml" /><link href="https://duckpond.ch/" rel="alternate" type="text/html" /><updated>2026-02-15T15:18:56+01:00</updated><id>https://duckpond.ch/feed.xml</id><title type="html">duckpond.ch</title><subtitle>Deep dive tech blog with ducks</subtitle><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><entry><title type="html">Mit Eidechsen war alles besser</title><link href="https://duckpond.ch/meta/2026/01/25/mit-eidechsen-war-alles-besser.html" rel="alternate" type="text/html" title="Mit Eidechsen war alles besser" /><published>2026-01-25T00:00:00+01:00</published><updated>2026-01-25T00:00:00+01:00</updated><id>https://duckpond.ch/meta/2026/01/25/mit-eidechsen-war-alles-besser</id><content type="html" xml:base="https://duckpond.ch/meta/2026/01/25/mit-eidechsen-war-alles-besser.html">&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: I am publishing the following article on behalf of a friend who
recently wrote it. I found the piece engaging and worth preserving, which is why
I decided to share it here. The article reflects the author’s personal views and
style; all opinions, interpretations, and conclusions expressed are solely those
of the original author and do not represent my own.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ein in der Antike verbreitetes Navigationshilfmittel war neben der Sonne und den restlichen Sternen
auch die gemeine Eidechse. Ein leicht zu übersehendes, aber nützliches Reptil. Auch vor Gericht
fanden Sie weiträumig Verwendung, wenn jemand einen Eid ablegen musste. Wenn man log, kackte
die Eidechse einem sofort auf die Hand. Das war damals genügend Abschreckung, denn Kleenex war
noch nicht erfunden.&lt;/p&gt;

&lt;p&gt;Auch als Politbarometer fanden Eidechsen schon Verwendung, denn häufig kommt die Süd- und
Osteidechse eher von rechts. Also rechts vor links, wegen der STVO, insbesondere auch in Italien,
seit Meloni und in Deutschland wegen der AFD.&lt;/p&gt;

&lt;p&gt;Kommt eine Eidechse dann von links geht man offensichtlich in die entgegengesetzte Richtung,
ist aber selten, dann muss die Eidechse ja warten, also zumindest die Deutsche. Die Viecher warten
aber nicht gerne. Die haben viel zu tun und haben’s eilig, könnte man denken, aber weit gefehlt. Sie
sind arbeitslos. Warum? Nun das ist ‘ne lange Geschichte. Also wo fangen wir an? Mit Navigation
eben.&lt;/p&gt;

&lt;p&gt;Navigation mittels Eidechsen ist heute nicht mehr sehr populär, seitdem es Kompasse und GPS
gibt. Bis vor 65 Millionen Jahren war das allerdings anders, denn die Eidechsen waren grösser, sehr
viel grösser. Nicht zu übersehen, gab es da nur eine Richtung:&lt;/p&gt;

&lt;h2 id=&quot;weg-von-der-eidechse&quot;&gt;Weg von der Eidechse!&lt;/h2&gt;

&lt;p&gt;Der Versuch der Integration dieser monströsen Tiere in tragbare Navigationsinstrumente ging natürlich
wegen des unvorteilhaften Formfaktors drastisch schief. Also musste ein Meteor her, damit die
Viecher endlich kleiner werden. Das führte dann schliesslich zu McDonalds, Microschrott, Trump,
Musk und einer Menge brauner Scheisse. Einer von denen musste derart oft gelogen haben, dass
alle Eidechsen der Welt spontan kacken mussten. Aus olfaktorischen Gründen wurden sie von der
Jurisdiktion dann ausgeschlossen. Das war ein schwerer Schlag für die Eidechseninnung und natürlich
auch ein grosser Einkommensverlust. Ihre Häuser auf Sylt mussten sie schliesslich aufgeben und
in den billigeren Süden und Osten ziehen. Seitdem wohnen da nur noch neureiche Skandal-Pupköppe.&lt;/p&gt;

&lt;p&gt;Die ersten Versuche die nun kleineren Eidechsen in antiken Smartphones einzubauen, schlugen
jedoch drastisch fehl. Einerseits sind Eidechsen nicht SMD tauglich und andererseits haben sie mit
dem Reflowlöten ein Akzeptanzproblem. Zudem sind die Viecher völlig unkooperativ und rennen immer weg,
damit ist die Ausbeute sehr niedrig. Also nicht gerade interessant für börsennotierte Unternehmen.&lt;/p&gt;

&lt;p&gt;Der Vorteil der Verwendung von Eidechsen wäre allerdings die Lebensdauer, die wesentlich
länger ist als heute produzierte Mobiltelefone. Man sollte vielleicht doch nochmal darüber nachdenken.&lt;/p&gt;

&lt;p&gt;Dennoch, Eidechsen haben einen nicht mehr wegzudenkenden Nischenplatz in unserer Gesellschaft
eingenommen, insbesondere als Navigationshilfen auf dem Weg zum Klo in Italien, genauer Alzate,
noch genauer in der Nähe des Hangars. Eidechse von rechts, man geht zum Klo, andersherum weg
vom Klo. Das kann im Druckstress durchaus hilfreich sein.&lt;/p&gt;

&lt;p&gt;Nur was tun, wenn gerade keine Eidechse zugegen ist? In Deutschland würde man sogleich
ein Eidechsenpräsenzgesetz verabschieden und in der EU ein Gremium gründen, was sich mit der
Regulierung der Eidechsendichte befasst. Und die Schweiz würde das Gesetz als Erste kritiklos
übernehmen. In der Zwischenzeit hätten sich alle in Italien in der Nähe von Hangars in die Hose
geschissen.&lt;/p&gt;

&lt;p&gt;Man sieht also gewisse Navigationsprobleme können mittels Eidechseneinsatz vereinfacht werden.
Nein, ich bin kein Lobbyist der Eidechsenvereinigung, aber ich denke, man sollte in weltwirtschaftlich
schwierigen Zeiten auch mal lateral nach Lösungen suchen.&lt;/p&gt;

&lt;p&gt;Aus diesem Grund hat man sich Anfang des 20. Jahrhunderts redlich bemüht, die Eidechse in
unsere technologisch getriebene Gesellschaft zu integrieren. Das zeigte insbesondere der Versuch,
sie als wesentliche Komponente eines innovativen Kompassystems einzubinden. Die Entlöhnung
war überdurchschnittlich, auch für die Unterkunft und Krankenversicherung wurde gesorgt. Das
ganze scheiterte leider am mangelnden Magnetisierungswillen seitens dieser Tiere. Damit waren sie
endgültig aus dem Geschäft, keiner wollte mehr mit Eidechsen arbeiten. Die dann folgende Arbeits-
und Perspektivlosigkeit legte später die Grundlage für die NSDAP. Der Rest ist bekannt.&lt;/p&gt;

&lt;p&gt;Es gestaltete sich schliesslich einfacher Satelliten in die Erdumlaufbahn zu schiessen und ein bisschen
Relativitätstheorie zu treiben, ohne Eidechsen. Denn Letztere erreichen sehr selten relativistisch
relevante Geschwindigkeiten und grosse Masse haben sie auch nicht.&lt;/p&gt;

&lt;p&gt;Darum führen Eidechsen heute ein eher zurückgezogenes Leben und verstecken sich, wenn jemand
in ihrer Nähe erscheint. Das sollte nun ausreichen, um die gesamte Geschichte der letzten
245 Millionen Jahre zu verstehen. Wenn noch Fragen sind, wende man sich vertrauensvoll an
&lt;a href=&quot;https://tranalyzer.com&quot;&gt;tranalyzer.com&lt;/a&gt;. Dort sitzt ein Ameisenbär, der kennt sich mit
der Weltgeschichte bestens aus, zumindest aus Ameisenbärsicht.&lt;/p&gt;

&lt;p&gt;Die Abscheu der Eidechsen gegenüber dem Menschen zwang sie schliesslich in den Untergrund.
Darum kann man sie nur erkennen, wenn man sich nahe am Boden befindet und weiss wo sie liegen.
Z.B. wie schon erwähnt, hinterm Hangar in Alzate. Also aus der Luft wird man sie jedenfalls nicht
entdecken. Ausser bis vor 65 Millionen Jahren, da waren Sie ja grösser. Dann war da aber auch kein
Hangar und Landen wäre dann nicht gerade die beste Option gewesen. Darum ist damals auch keiner
aussengelandet. Und wenn doch, war man Matsch oder Frühstück. Darum findet man auch keine
intakten Segelflugzeuge in den einschlägigen geologischen Schichten.&lt;/p&gt;

&lt;p&gt;Heute kann man aber Landen ohne gefressen zu werden, zumindest nicht von Eidechsen. Das tut
man zweckmässigerweise auf einem Flugplatz oder irgendeinem angemessenen Feld; Und das alles
trotz heutiger hoher Eidechsendichte im Süden. Aber manchmal kann man sich das nicht immer aussuchen,
wo man landet oder von wem man gefressen wird.&lt;/p&gt;

&lt;p&gt;Also nehmen wir mal an, man landet heutzutage, also letztes Jahr, aussen. Natürlich landet man
immer aussen. Drinnen zu landen macht ja keinen Sinn, ausser es handelt sich um eine Kamikaze-
drohne. Die Erörterung dieser autonomen Flugkörper und deren Interaktion mit Eidechsen im Urkrainekrieg
würde den Rahmen dieses Artikels sicherlich sprengen und ist zudem klassifiziert. Interessierte wenden
sich bitte an das eidgenössische Departement für Verteidigung.&lt;/p&gt;

&lt;p&gt;Nun zum Thema, was war das nochmal? Eidechsenunterstütztes Kacken? Nein, Aussenlanden.
Ah ja, das passiert, wenn man nicht da hinkommt, wo man hin will, meistens zurück zum Flugplatz
wo es Futter und Bier gibt. Letzteres ist ein unwiderstehlicher Anreiz es doch noch zurückzuschaffen.
Aber manchmal spielt das Wetter nicht mehr mit, eine verdammte Sauerei. Und irgendwann, nach
heroischem Kampf, muss man eben die Eidechsen besuchen gehen, was auch unterhaltsam sein kann.&lt;/p&gt;

&lt;p&gt;Porlezza ist so ein sagenumwobener Ort, passende Eidechsendichte, aber grosses Oma mit Hund
Vorkommen. Das hätte ich mal vorher wissen sollen. Den Eidechsendichteplan hatte ich ja mit. Jeder
Pilot in Italien erhält da so eine Karte, oder man kann die lokale Dichte auch bei Milano Info erfragen.
Aber wer rechnet schon mit ‘ner Oma, und dann noch mit Hund während man landen will. Unabhängigkeit
angenommen wäre das Produkt der Wahrscheinlichkeiten dieser drei Ereignisse sicherlich
kleiner, als ein flötenspielender Braunbär in einer Pizzeria.&lt;/p&gt;

&lt;p&gt;Also, jetzt reichts aber! Ich muss den Leser an dieser Stelle ernsthaft ermahnen, dass er sich mal
konzentriere. Diese andauernd herumschweifenden Gedankengänge bringen mich als Schreiber völlig
aus dem Konzept und machen den vorliegenden Text inkohärent und nur länger. Also den Teil,
der noch kommt, der davor ist ja schon geschrieben und damit im Moment nicht mehr relevant. So
fokussiere er sich endlich. Vielleicht trinke er ein Bier, esse einen Biber, um den Wald zu schützen,
und lese dann weiter. &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Habe er sich jetzt wieder gesammelt? Gut. Also weiter. Man stelle sich nun vor, man komme
in ca. 600m Grund in Porlezza an, versucht sich noch 10 Minuten am Hang zu halten und schaut
sich währenddessen die Felder an. Sieht er das jetzt vor seinem geistigen Auge? Fühlt er die innere
Anspannung ansteigen? Gut, das ist wichtig. So sei er nun hervorragend vorbereitet das Bild 1 unten
zwecks Landefeldauswahl zu betrachten und der Echtzeitbeschreibung zu lauschen; Also soweit man
mit den Ohren lesen kann.&lt;/p&gt;

&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;/static/posts/mit-eidechsen-war-alles-besser/situation.png&quot;&gt;
  &lt;img src=&quot;/static/resized/situation-800x355.png&quot; alt=&quot;&quot; srcset=&quot;            /static/resized/situation-480x213.png 480w,            /static/resized/situation-800x355.png 800w,    &quot; /&gt;
  &lt;em&gt;Porelzza Landefelder: 1: OK für 15m Flieger Man beachte die Stahlstange und die
            Holzpflöcke, 2: Besseres, aber Baumreihe im Anflug, 3: noch besser, aber Weg mit Oma und Hund
            im Anflug, 4: am Besten, aber kostet 400 Euro wegen der Kühe. Blaue Anflugpfeile gemäss damals
            vorherschender Windrichtung, rot die Holzpflöcke/Eisenstange&lt;/em&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nun, welches nimmt man jetzt? Bei 400m fliege ich über die Felder und schau mir alles genau
an. Zum Glück hab ich ‘ne neue Brille.&lt;/p&gt;

&lt;p&gt;Feld 4, ja das wäre gut. Nur was sind das da für Tiere an der Seite? Kühe! Ne, nix mit vache.
Eidechsen sind angesagt.&lt;/p&gt;

&lt;p&gt;Ah ja, ein Windsack bei Feld 3 und ein kleines Häuschen. Das muss der Gleitschirmclub sein.
Feld 1 wäre eine Option für einen Standardflieger, sieht gut vom Bewuchs aus, hat allerdings am
Anfang rechts ein paar Holzpflöcke und irgendwas Orangenes im Anflug, aber nicht hoch. Wäre lang
genug und das Offizielle halt. Und hat links einen Windsack, das muss ja für was Aeronautisches sein.&lt;/p&gt;

&lt;p&gt;Oh, Nr 2 wäre auch gut, breiter, aber die Struktur des Feldes sieht komisch aus und es scheint mir
etwas unduliert zu sein, aber gut landbar. Die Bäume im Anflug sind kein Problem, da pass ich durch,
oder drüber mit ‘nem Slip. Nein, keine Unterhose, wir sind hier nicht im Porno! Nr 3 wäre die beste
Wahl, langer hindernisfreier Anflug, gut im Wind, und am Anfang gemäht, das isses. Da kriegt man
sogar ‘ne ASH runter.&lt;/p&gt;

&lt;p&gt;Also Rad raus, abkreisen und nochmal alles anschauen. Bei ca 300m die Katastrophe. WAS IST
DAS DA??? Oma mit Hund und beide bewegen sich synchron und langsam auf den Anflug von Feld
3 zu. Langsam aber sicher. Und was jetzt? Die Oma bleibt stehen, der Hund streift ziellos um sie
herum. Und wahrscheinlich guckt die jetzt zu mir hoch und denkt:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Oh ein Flugzeug! Wuffi, lass uns mal hier bleiben und schaun was der so macht.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hau ab mit deiner Dreckstöle! Rufen bringt nix, die hört dich eh nicht. Wahrscheinlich Hörgerät
vergessen. Wat nu? Anfliegen und darauf hoffen, dass sie abhaut? Vielleicht kriegt sie noch einen
Herzinfarkt und ich muss auf dem Hund landen. Auf ‘ner Eidechse ging ja, aber ein Hund und ‘ne
Oma ist ‘ne ganz andere Nummer. Da stehste tagsdrauf in der Bildzeitung als islamistischer Terrorist.
Hätte vorher meinen Bart absäbeln sollen, kommt auf die Checkliste.&lt;/p&gt;

&lt;p&gt;200m, hau endlich ab!!!!!!!!!!!! Alte Leute mit Hund sollte man sofort verbieten, zumindest bei
landbaren Feldern.&lt;/p&gt;

&lt;p&gt;Die Alte bewegt sich nicht, der Hund schnüffelt unbeeindruckt herum und überlegt sich, ob er
pinkeln soll. Also Alternativplan, den muss man immer haben. Entscheidung: Feld 1, 150m, im
Queranflug schon Klappen etwas raus, keine Turbulenzen, also stabiler Endanflug mit 95, dann short
final mit 90. Die orangene Stange! Da muss ich rüber und die AK setzt sich ca 30m hinter der Pflockreihe
ins Gras (s.Bild 2 unten).&lt;/p&gt;

&lt;p&gt;Also mit Minimalfahrt ins Feld und sofort bremsen. Bremsen? Zum Glück habe ich die am Tag
vorher noch korrigiert. Die AK Gruppe hat die im Winter vorher so ziemlich verhunzt. Ansonsten
hätte ich nicht bremsen können. Verdammte Hacke, Bremsen stellt man bei AUFGERÜSTETEN
FLIEGER ein, ihr elenden Saftsäcke!&lt;/p&gt;

&lt;p&gt;In so einer Situation funktioniert man einfach, da wird nix mehr nachgedacht. Alles läuft automatisch,
Muskelgedächnis. Darum ist es so wichtig: Kleine Spannweite, niedrige Anfluggeschwindigkeit
und gutes Training auf dem Typ. Mit ‘ner motorhavarierten AS34 wäre das wegen der Spannweite
und der höheren Landegeschwindigkeit schwieriger gewesen.&lt;/p&gt;

&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;/static/posts/mit-eidechsen-war-alles-besser/glider-front.png&quot;&gt;
  &lt;img src=&quot;/static/resized/glider-front-1400x2488.png&quot; alt=&quot;&quot; srcset=&quot;            /static/resized/glider-front-480x853.png 480w,            /static/resized/glider-front-800x1422.png 800w,            /static/resized/glider-front-1400x2488.png 1400w,    &quot; /&gt;
  &lt;em&gt;Feld 1, man beachte die Holzpflöcke hinten links. Bei höherem Bewuchs Vorsicht!&lt;/em&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also Haube auf, erstmal Aussteigen, Photo machen, die Oma vermöbeln, den Hund in die Umlaufbahn
schmeissen und dann den Eidechsen danken. Die Oma ist aber mittlerweile weitergezogen.
Ja klar, es gibt nix mehr zu glotzen. Stattdessen kommt ein Auto mit ‘ner Frau drin: Tamara. Ihres
Zeichens Gleitschirmfliegerin. Klasse!&lt;/p&gt;

&lt;p&gt;Seil an die Anhängerkupplung, AK drehen, Seil in die Klinke würgen und das Ding rausziehen
auf ihr Gelände, genau auf Feld 3. Und dann die Gretchenfrage von einem unbeteiligten deutschen
Gleitschirmflieger, musste ja kommen:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Warum biste nicht auf diesem Feld gelandet?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nun, der Oma Faktor. Gelächter…&lt;/p&gt;

&lt;p&gt;Erstmal Max anrufen, zwecks Rückholung. Als nächstes, Kaffee und Kuchen fassen. Das scheint
es grundsätzlich bei Gleitschirmfliegern zu geben, wenn sie abgesoffen sind. Also darüber lamentieren
sie so lange, bis sich alle einig sind: Scheisstag! Dann kommt Max endlich an, schnell abrüsten
und ab zum Restaurant, man hat ja kaum was im Magen.&lt;/p&gt;

&lt;p&gt;In Bild 3 unten sieht man das Feld 3 vom Boden aus, wirklich schöner Anflug und relativ eben.
Der Anflug in Gegenrichtung ist allerdings wegen des Weges Oma-Hund verseucht. Kann ich aber
trotzdem empfehlen, das Landefeld, nicht das Oma-Hund Gespann.&lt;/p&gt;

&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;/static/posts/mit-eidechsen-war-alles-besser/glider-side.png&quot;&gt;
  &lt;img src=&quot;/static/resized/glider-side-1400x2488.png&quot; alt=&quot;&quot; srcset=&quot;            /static/resized/glider-side-480x853.png 480w,            /static/resized/glider-side-800x1422.png 800w,            /static/resized/glider-side-1400x2488.png 1400w,    &quot; /&gt;
  &lt;em&gt;Feld 3, gehört dem Paraclub, optimal, Blick Richtung Nord-Westen. freier Anflug von beiden Richtungen, quer dazu Feld 2&lt;/em&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also man merke: Wenn ‘ne Oma bei Feld 3 anschlappt, landet auf Feld 2. Wenn da auch ‘ne Oma
auftaucht, auf Feld 1. Auf keinen Fall auf Feld 4 aufsetzen, das kostet 400 Euro, weil der grantige
Bauer erstmal alle Kühe wegtreiben muss. Ein deutscher Segelflieger durfte das mal erleben, sagt
Tamara. Achtet auf beide Windsäcke.&lt;/p&gt;

&lt;p&gt;Es gibt noch ein Feld weiter nördlich, ist auch für ‘ne ASH geeignet. Warum ich da nicht gelandet
bin. Nun, die Gleitschirmfliegerbutze, die transzendente Wahrnehmung von Kaffee und Kuchen und
zwei Windsäcke waren entscheidende Faktoren. Der Windsack links von Feld 1 ist übrigens für einen
privaten Helikopter gedacht. Der Sinn der orange angestrichenen Eisenstange in Feld 1 ist mir bis
heute noch schleierhaft, vielleicht eine etwas eigenwillige Pistenbegrenzung.&lt;/p&gt;

&lt;h2 id=&quot;zusammenfassend-kann-man-folgendes-sagen&quot;&gt;Zusammenfassend kann man folgendes sagen:&lt;/h2&gt;

&lt;p&gt;Die Geschichte wird unser Zeitalter als völlig bescheuert klassifizieren. Nicht nur wegen der AS34
Kaufoption, sondern auch wegen Trump, Gaza- und Ukrainekrieg, Glühbirnen/Heizungen/Autos mit
IP Stack, Katzenstreu, Kurbelwellenmontierte Ölpumpen, Facecrook, autonome nukleare Vorrichtungen,
vierlagiges Klopapier, Ecoboost Motoren, soziale Medien, Frontantrieb, GPT4.5, Religionen,
PTFE Dichtringe, usw.&lt;/p&gt;

&lt;p&gt;Mit der technischen Integration der Eidechsen in unsere Gesellschaft wäre heute garantiert alles
besser. Wir wären schon auf dem Mars, ich wäre nicht aussengelandet und wir hätten fliegende Autos
und den Warp Drive. Man hat unsere Generation als Kinder in einschlägigen Science Fiction Serien
schamlos belogen. Die Eidechsen, wären sie heute noch im Job, kämen nicht mehr aus dem Kacken
raus. Viele Leute fühlen sich heutzutage unter anderem deswegen betrogen und abgehängt, darum
wird auch alles brauner oder blauer. Das Resultat ist dasselbe: Scheisse.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Im Einklang mit dem Trumpschen Dekret zur spontanen Kausalitätsumkehr &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="meta" /><summary type="html">Disclaimer: I am publishing the following article on behalf of a friend who recently wrote it. I found the piece engaging and worth preserving, which is why I decided to share it here. The article reflects the author’s personal views and style; all opinions, interpretations, and conclusions expressed are solely those of the original author and do not represent my own.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/mit-eidechsen-war-alles-besser/glider-front-small.jpg" /><media:content medium="image" url="https://duckpond.ch/static/posts/mit-eidechsen-war-alles-besser/glider-front-small.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Reaping Poison Tasks In Celery</title><link href="https://duckpond.ch/python/2025/08/04/reaping-poison-tasks-in-celery.html" rel="alternate" type="text/html" title="Reaping Poison Tasks In Celery" /><published>2025-08-04T00:00:00+02:00</published><updated>2025-08-04T00:00:00+02:00</updated><id>https://duckpond.ch/python/2025/08/04/reaping-poison-tasks-in-celery</id><content type="html" xml:base="https://duckpond.ch/python/2025/08/04/reaping-poison-tasks-in-celery.html">&lt;p&gt;Celery pipelines often work fine until one task brings everything to a halt. For us, the root cause was simple: when Kubernetes OOM-killed a Celery worker during execution, RabbitMQ would redeliver the unacknowledged task. In practice, this meant the same poison task (often memory-intensive) would bounce from worker to worker, gradually stalling the entire pipeline.&lt;/p&gt;

&lt;h2 id=&quot;task-queue-architecture&quot;&gt;Task Queue Architecture&lt;/h2&gt;

&lt;p&gt;Our setup uses a &lt;strong&gt;one-queue-per-task-type&lt;/strong&gt; model. Every Celery task is routed to its own RabbitMQ queue. This design ensures:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;All tasks are treated with equal priority&lt;/li&gt;
  &lt;li&gt;Workers can subscribe to all tasks without any implicit weighting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The task-to-queue mapping is created dynamically at startup:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task_name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;queue_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;f&quot;celery:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;task_queues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;task_routes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;queue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, there’s a catch. Even with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker_prefetch_multiplier=1&lt;/code&gt;, Celery fetches one task per queue. If a worker listens to 100 queues, it may prefetch 100 tasks at once. When that worker is OOM-killed, all prefetched but unacknowledged tasks are redelivered, each with their delivery count incremented.&lt;/p&gt;

&lt;p&gt;This results in two problems:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Poison tasks cause repeated worker crashes and restarts.&lt;/li&gt;
  &lt;li&gt;Healthy tasks also get redelivered unnecessarily, increasing delivery counts and wasting processing time.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;introducing-dead-letter-queues&quot;&gt;Introducing Dead Letter Queues&lt;/h2&gt;

&lt;p&gt;To make task failure explicit and avoid stalling the pipeline, we introduced RabbitMQ Quorum Queues with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-delivery-limit&lt;/code&gt; and dead-lettering. The model now looks like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Each task-specific queue is a quorum queue with a delivery limit.&lt;/li&gt;
  &lt;li&gt;On reaching the delivery limit, tasks are routed to a &lt;strong&gt;graveyard queue&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;The graveyard queue also has a delivery limit, after which tasks go to a final &lt;strong&gt;dead queue&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/reaping-poison-tasks-in-celery/task-queues.svg&quot; alt=&quot;dead letter queues&quot; class=&quot;stretch&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This setup ensures that poison tasks are automatically redirected out of the main processing flow.&lt;/p&gt;

&lt;h2 id=&quot;reaper-workers&quot;&gt;Reaper Workers&lt;/h2&gt;

&lt;p&gt;To process the graveyard and dead queues, we introduced a dedicated class of workers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;They &lt;strong&gt;only subscribe to the graveyard and dead queues&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;They run with &lt;strong&gt;worker_concurrency=1&lt;/strong&gt; and &lt;strong&gt;worker_prefetch_multiplier=1&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Graveyard tasks are re-executed in isolation - one at a time.&lt;/li&gt;
  &lt;li&gt;Dead queue tasks are never executed. They are skipped by immediately raising an exception.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isolation is essential: the reaper worker only prefetches a single task from the graveyard, and because it subscribes to no other queues, there is no chance of delivery count pollution from co-scheduled tasks.&lt;/p&gt;

&lt;p&gt;This design has several advantages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Poison tasks no longer interfere with normal task execution.&lt;/li&gt;
  &lt;li&gt;Dead-lettering is now a first-class concept.&lt;/li&gt;
  &lt;li&gt;Tasks that exceed retry limits become visible failures, not silent stalls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The task base class inspects RabbitMQ’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-death&lt;/code&gt; headers to determine how many times a message has been dead-lettered. When the task sees multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x-death&lt;/code&gt; entries, it is flagged as permanently dead and skipped.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x_death&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;x-death&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_death&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeadTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;This task has already been reaped.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;why-it-works&quot;&gt;Why It Works&lt;/h2&gt;

&lt;p&gt;This architecture isolates failure from progress. Reaper workers handle the worst-case tasks without contaminating the main processing pool. Delivery limits and explicit dead letter queues prevent infinite retries. The one-queue-per-task model ensures fair scheduling, and now with dead-lettering, also ensures robustness under failure.&lt;/p&gt;

&lt;p&gt;With this change, a single poison task no longer halts the system. Failures become visible, bounded, and isolated - the way they should be.&lt;/p&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="python" /><summary type="html">Celery pipelines often work fine until one task brings everything to a halt. For us, the root cause was simple: when Kubernetes OOM-killed a Celery worker during execution, RabbitMQ would redeliver the unacknowledged task. In practice, this meant the same poison task (often memory-intensive) would bounce from worker to worker, gradually stalling the entire pipeline.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/reaping-poison-tasks-in-celery/task-queues.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/reaping-poison-tasks-in-celery/task-queues.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Convert Video For Chromecast</title><link href="https://duckpond.ch/bash/2022/04/20/convert-video-for-chromecast.html" rel="alternate" type="text/html" title="Convert Video For Chromecast" /><published>2022-04-20T00:00:00+02:00</published><updated>2022-04-20T00:00:00+02:00</updated><id>https://duckpond.ch/bash/2022/04/20/convert-video-for-chromecast</id><content type="html" xml:base="https://duckpond.ch/bash/2022/04/20/convert-video-for-chromecast.html">&lt;p&gt;Chromecast does only support a selection of audio and video codecs. I have used
the following a lot to convert almost any video for streaming on the Chromecast:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ffmpeg &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; input.mp4 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-map&lt;/span&gt; 0:v:0 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;:v copy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-map&lt;/span&gt; 0:a:0 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-map&lt;/span&gt; 0:a:0 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;:a:0 aac &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-ac&lt;/span&gt;:a:0 2 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt;:a:0 192k &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;:a:1 copy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  output.mp4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This post is mostly just a copy&amp;amp;paste from &lt;a href=&quot;https://www.stix.id.au/wiki/ffmpeg_conversion_for_Chromecast&quot;&gt;stix.id.au&lt;/a&gt;.
Just reposting this here to preserve the information. All credits to the original author.&lt;/p&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="bash" /><summary type="html">Chromecast does only support a selection of audio and video codecs. I have used the following a lot to convert almost any video for streaming on the Chromecast:</summary></entry><entry><title type="html">Citation Needed</title><link href="https://duckpond.ch/citation%20needed/2021/01/25/citation-needed.html" rel="alternate" type="text/html" title="Citation Needed" /><published>2021-01-25T00:00:00+01:00</published><updated>2021-01-25T00:00:00+01:00</updated><id>https://duckpond.ch/citation%20needed/2021/01/25/citation-needed</id><content type="html" xml:base="https://duckpond.ch/citation%20needed/2021/01/25/citation-needed.html">&lt;p&gt;&lt;img src=&quot;/static/posts/citation-needed/xkcd_protester.png&quot; alt=&quot;citation-needed&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://peter.eisentraut.org/blog/2014/11/04/checking-whitespace-with-git/&quot;&gt;Checking whitespace with Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ionic-team/ionic-framework/issues/16455#issuecomment-505397373&quot;&gt;Ionic: Make source maps for android work&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/corkami/mitra&quot;&gt;A tool to generate binary polyglots&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.void.gr/kargig/blog/2016/12/12/firejail-with-tor-howto/&quot;&gt;Firejail with Tor HOWTO&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xorpd.net/pages/xchg_rax/snip_00.html&quot;&gt;xchg rax,rax&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Hack-with-Github/Awesome-Hacking&quot;&gt;Awesome Hacking: A collection of awesome lists for hackers, pentesters &amp;amp; security researchers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gtfobins.github.io/&quot;&gt;GTFOBins is a curated list of Unix binaries that can be exploited by an attacker to bypass local security restrictions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://highon.coffee/blog/reverse-shell-cheat-sheet/&quot;&gt;Reverse Shell Cheat Sheet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hookbin.com/&quot;&gt;Capture and inspect HTTP requests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt&quot;&gt;What is RCU?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pagedout.institute&quot;&gt;Paged Out! is a free experimental (one article == one page) technical magazine&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.alchemistowl.org/pocorgtfo/&quot;&gt;International Journal of Proof-of-Concept or Get The Fuck Out&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="citation needed" /><summary type="html"></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Count Beta Release</title><link href="https://duckpond.ch/count/2020/10/19/count-beta-release.html" rel="alternate" type="text/html" title="Count Beta Release" /><published>2020-10-19T00:00:00+02:00</published><updated>2020-10-19T00:00:00+02:00</updated><id>https://duckpond.ch/count/2020/10/19/count-beta-release</id><content type="html" xml:base="https://duckpond.ch/count/2020/10/19/count-beta-release.html">&lt;p&gt;During COVID-19 times it is vital that we don’t gather in big groups.
That puts the burden of tracking people in enclosed spaces onto the ones in
charge. Counting is a lot of work with not much reward.&lt;/p&gt;

&lt;p&gt;With &lt;a href=&quot;https://github.com/Enteee/count&quot;&gt;Count&lt;/a&gt; I am beta Beta-releasing an app which wants to change
that. &lt;a href=&quot;https://github.com/Enteee/count&quot;&gt;Count&lt;/a&gt; helps you keep tack of things but then also provides
visualizations of the data collected.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/count-beta-release/count.jpg&quot; alt=&quot;Count&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Yes, there are quite a few &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; other counting apps on the Play Store already.
Some other notable projects are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=de.sleak.thingcounter&amp;amp;hl=en&quot;&gt;Thing Counter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.useless.counter&amp;amp;hl=en&quot;&gt;Click Counter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=me.tsukanov.counter&amp;amp;hl=en&quot;&gt;Counter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=de.cliff.strichliste&amp;amp;hl=en&quot;&gt;Strichliste&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.visiativity.tallycounter&amp;amp;hl=en&quot;&gt;Tally Counter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they are all not open source, lack important features (looking at the comment
section) and don’t do much with the data they are collecting. This is where
&lt;a href=&quot;https://github.com/Enteee/count&quot;&gt;Count&lt;/a&gt; steps in.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Enteee/count&quot;&gt;Count&lt;/a&gt; is a simple tally counter with advanced visualizations
based on metadata collected during counting. Every time the counter is changed
the app collects time, location and position of the event. Using this data
we can then provide insight about all different aspects such as frequency and
locality.&lt;/p&gt;

&lt;p&gt;Would you like to know when people visit which one of your branches?
&lt;a href=&quot;https://github.com/Enteee/count&quot;&gt;Count&lt;/a&gt; can tell you that.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/count-beta-release/preview.gif&quot; alt=&quot;Count Preview&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With this blog post I am starting the open beta of the app. You can join
now if you have an Android phone.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://play.google.com/apps/testing/ch.duckpond.count&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Join%20Android%20Beta-NOW!-brightgreen&quot; alt=&quot;Join Android Beta&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If want to contribute to &lt;a href=&quot;https://github.com/Enteee/count&quot;&gt;Count&lt;/a&gt;, you can do this using the
following channels:&lt;/p&gt;

&lt;script async=&quot;&quot; defer=&quot;&quot; src=&quot;https://buttons.github.io/buttons.js&quot;&gt;&lt;/script&gt;

&lt;ul&gt;
  &lt;li&gt;Tweet: &lt;a href=&quot;https://twitter.com/intent/tweet?text=Count%2C%20Visualize%2C%20Understand&amp;amp;hashtags=CountApp,Ionic,JavaScript,TypeScript&amp;amp;url=https%3A%2F%2Fgithub.com%2FEnteee%2Fcount&quot;&gt;&lt;img src=&quot;https://img.shields.io/twitter/url?label=%23CountApp&amp;amp;url=https%3A%2F%2Fgithub.com%2FEnteee%2Fcount&quot; alt=&quot;Twitter URL&quot; /&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Star: &lt;a class=&quot;github-button&quot; href=&quot;https://github.com/Enteee/count&quot; data-icon=&quot;octicon-star&quot; data-show-count=&quot;true&quot; aria-label=&quot;Star Enteee/count on GitHub&quot;&gt;Star&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Resolve: &lt;a class=&quot;github-button&quot; href=&quot;https://github.com/Enteee/count/issues&quot; data-icon=&quot;octicon-issue-opened&quot; data-show-count=&quot;true&quot; aria-label=&quot;Issue Enteee/count on GitHub&quot;&gt;Issue&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Donate: &lt;a class=&quot;github-button&quot; href=&quot;https://github.com/sponsors/Enteee&quot; data-icon=&quot;octicon-heart&quot; aria-label=&quot;Sponsor @Enteee on GitHub&quot;&gt;Sponsor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;And by a few I mean a lot &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="count" /><summary type="html">During COVID-19 times it is vital that we don’t gather in big groups. That puts the burden of tracking people in enclosed spaces onto the ones in charge. Counting is a lot of work with not much reward.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/count-beta-release/count.jpg" /><media:content medium="image" url="https://duckpond.ch/static/posts/count-beta-release/count.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Disable reMarkable Touchscreen with evkill</title><link href="https://duckpond.ch/evkill/bash/2020/08/10/disable-reMarkable-touchscreen-with-evkill.html" rel="alternate" type="text/html" title="Disable reMarkable Touchscreen with evkill" /><published>2020-08-10T00:00:00+02:00</published><updated>2020-08-10T00:00:00+02:00</updated><id>https://duckpond.ch/evkill/bash/2020/08/10/disable-reMarkable-touchscreen-with-evkill</id><content type="html" xml:base="https://duckpond.ch/evkill/bash/2020/08/10/disable-reMarkable-touchscreen-with-evkill.html">&lt;p&gt;&lt;a href=&quot;https://github.com/Enteee/evkill&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evkill&lt;/code&gt;&lt;/a&gt; is a silencer for evdev input devices. Run a single command and make
your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/&lt;/code&gt; devices go “psst!”.  In this post we will use &lt;a href=&quot;https://github.com/Enteee/evkill&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evkill&lt;/code&gt;&lt;/a&gt;
on a reMarkable e-ink writing tablet to disable the capacitive display while writing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/disable-reMarkable-touchscreen-with-evkill/evkill.png&quot; alt=&quot;evkill on reMarkable&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Since reMarkable has introduced page flips using swipe gestures, it happens a
lot that I unintentionally change page while writing. They must have put some
detection for this into their software, but for whatever reason this does not
work for me on my device &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. This is why I would like to be able to disable the
capcitive sensor behind the screen by a button press. &lt;a href=&quot;/nix/bash/2020/01/08/reMarkable&quot;&gt;In a previous post I
already showed how we can toggle flight mode with the hardware
buttons&lt;/a&gt;. This time we will reuse the same script. But
instead of switching network devices on and off, we will use &lt;a href=&quot;https://github.com/Enteee/evkill&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evkill&lt;/code&gt;&lt;/a&gt;
to disable input devices.&lt;/p&gt;

&lt;h2 id=&quot;the-hack&quot;&gt;The Hack&lt;/h2&gt;

&lt;p&gt;First, we download the &lt;a href=&quot;https://github.com/Enteee/evkill&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evkill&lt;/code&gt;&lt;/a&gt; armv7l build and upload the executable
to the reMarkable.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl https://raw.githubusercontent.com/Enteee/evkill/master/install.sh | &lt;span class=&quot;nv&quot;&gt;ARCH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;armv7l&quot;&lt;/span&gt; sh
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scp evkill root@10.11.99.1:.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we cross compile and upload &lt;a href=&quot;https://github.com/freedesktop-unofficial-mirror/evtest&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evtest&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git clone https://github.com/freedesktop-unofficial-mirror/evtest.git
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;evtest
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  dockcross/linux-armv7a &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; dockercross
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x dockercross
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./dockercross bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;autoreconf -iv &amp;amp;&amp;amp; ./configure --host=arm-linux-gnueabi &amp;amp;&amp;amp; make&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scp evtest root@10.11.99.1:.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Given the device mapping from the table below, we adapt the script from &lt;a href=&quot;/nix/bash/2020/01/08/reMarkable&quot;&gt;the
previous post&lt;/a&gt;.&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Device&lt;/th&gt;
      &lt;th&gt;Name&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event0&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Wacom I2C Digitizer&lt;/td&gt;
      &lt;td&gt;The electromagnetic resonance device for the pen&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event1&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;cyttsp5_mt&lt;/td&gt;
      &lt;td&gt;The capacitive touchscreen&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event2&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;gpio-keys&lt;/td&gt;
      &lt;td&gt;The hardware buttons&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The idea is that the script detects button presses by listening on
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event2&lt;/code&gt; using &lt;a href=&quot;https://github.com/freedesktop-unofficial-mirror/evtest&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evtest&lt;/code&gt;&lt;/a&gt;. Once it registers a button press
from the left and right hardware button it will start an &lt;a href=&quot;https://github.com/Enteee/evkill&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evkill&lt;/code&gt;&lt;/a&gt;
process in the background and disable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event1&lt;/code&gt;. If we then again
press both buttons, the script will terminate all running &lt;a href=&quot;https://github.com/Enteee/evkill&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;evkill&lt;/code&gt;&lt;/a&gt;
instances with the effect of enabling the touchscreen again.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-euo&lt;/span&gt; pipefail

&lt;span class=&quot;nv&quot;&gt;DEVICE_BUTTONS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/dev/input/event2'&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DEVICE_TO_KILL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/dev/input/event1'&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# commands&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;EVTEST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; evtest &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'./evtest'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;EVKILL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; evkill &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'./evkill'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false

&lt;/span&gt;toggle_evkill&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;evkill_pid
  &lt;span class=&quot;nv&quot;&gt;evkill_pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;pidof evkill &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;evkill_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=&amp;gt; Disable touchscreen: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EVKILL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEVICE_TO_KILL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EVKILL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEVICE_TO_KILL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp;
  &lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=&amp;gt; Enable touchscreen: kill &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;evkill_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;evkill_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp;&amp;gt;/dev/null
  &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

handle_events&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_left_down
  &lt;span class=&quot;nv&quot;&gt;key_left_down&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 105 (KEY_LEFT), value 1*'&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_left_up
  &lt;span class=&quot;nv&quot;&gt;key_left_up&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 105 (KEY_LEFT), value 0*'&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_right_down
  &lt;span class=&quot;nv&quot;&gt;key_right_down&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 106 (KEY_RIGHT), value 1*'&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_right_up
  &lt;span class=&quot;nv&quot;&gt;key_right_up&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 106 (KEY_RIGHT), value 0*'&lt;/span&gt;


  &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$EVTEST&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEVICE_BUTTONS&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; line&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
      case&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_left_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_left_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_right_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_right_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -&amp;gt; LEFT &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; , RIGHT: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;toggle_evkill
      &lt;span class=&quot;k&quot;&gt;fi
  done&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

handle_events
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we now save the script to an executable file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable-touchscreen.sh&lt;/code&gt;
and run it on the device, we have achieved what we wanted.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x disable-touchscreen.sh
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scp disable-touchscreen.sh root@10.11.99.1:.
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; root@10.11.99.1 ./disable-touchscreen.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: In case you omit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t -t&lt;/code&gt; options to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;, the script will
keep running on the reMarkable even after you exit ssh with CRTL+C. This might
cause some problems when the devices activates the lock screen.&lt;/p&gt;

&lt;p&gt;Below a demonstration of how this looks like on an actual reMarkable:&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
  &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/skB0LoFMXNs&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Version 2.2.0.48 &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="evkill" /><category term="bash" /><summary type="html">evkill is a silencer for evdev input devices. Run a single command and make your /dev/input/ devices go “psst!”. In this post we will use evkill on a reMarkable e-ink writing tablet to disable the capacitive display while writing.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/disable-reMarkable-touchscreen-with-evkill/evkill.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/disable-reMarkable-touchscreen-with-evkill/evkill.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Support Binary File Objects with pandas.DataFrame.to_csv</title><link href="https://duckpond.ch/python/bash/2020/05/07/support-binary-file-objects-with-pandas.dataframe.to_csv.html" rel="alternate" type="text/html" title="Support Binary File Objects with pandas.DataFrame.to_csv" /><published>2020-05-07T00:00:00+02:00</published><updated>2020-05-07T00:00:00+02:00</updated><id>https://duckpond.ch/python/bash/2020/05/07/support-binary-file-objects-with-pandas.dataframe.to_csv</id><content type="html" xml:base="https://duckpond.ch/python/bash/2020/05/07/support-binary-file-objects-with-pandas.dataframe.to_csv.html">&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas.DataFrame.to_csv&lt;/code&gt; does not support writing to binary file objects &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.
This causes confusion &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;sup id=&quot;fnref:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; and makes the function difficult to work
with. In this article I will first illustrate the problem with an example.
Then, I will present a monkey patch for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas.DataFrame.to_csv&lt;/code&gt; which mitigates
the known pitfall.&lt;/p&gt;

&lt;p&gt;Let us write two files containing the names of three beautiful Swiss cities (in descending order).&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bern&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Genève&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Zürich&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The files we will write are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cities-utf-8.csv&lt;/code&gt; which is in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utf-8&lt;/code&gt; encoding
an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cities-latin.csv&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISO/IEC 8859-1 (latin)&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-utf-8.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-latin.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;latin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Everything works as expected and the files are written in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utf-8&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latin&lt;/code&gt;
encoding respectively.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;file cities-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.csv
cities-latin.csv: ISO-8859 text
cities-utf-8.csv: UTF-8 Unicode text
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hexdump &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; cities-latin.csv
00000000  30 0a 42 65 72 6e 0a 47  65 6e e8 76 65 0a 5a &lt;span class=&quot;nb&quot;&gt;fc&lt;/span&gt;  |0.Bern.Gen.ve.Z.|
00000010  72 69 63 68 0a                                    |rich.|
00000015
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hexdump &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; cities-utf-8.csv
00000000  30 0a 42 65 72 6e 0a 47  65 6e c3 a8 76 65 0a 5a  |0.Bern.Gen..ve.Z|
00000010  c3 bc 72 69 63 68 0a                              |..rich.|
00000017
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So far so good. But what if we want to write those to a file object instead?&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-utf-8.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-latin.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;latin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Both files look suddenly very similar:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cities&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cities&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;latin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Unicode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cities&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;8.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Unicode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hexdump&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cities&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;latin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;00000000&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;47&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a8&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;76&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;00000010&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bc&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;69&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;68&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;                              &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rich&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;00000017&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hexdump&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cities&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;8.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;00000000&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;47&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a8&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;76&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;00000010&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bc&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;69&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;68&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;                              &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rich&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;00000017&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In fact, they are exactly the same.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sha256sum cities-*.csv
ab401dff37c00f4d22e4ab2aa70fa2d67d89bd042787cb9e643bea7eeb5ee577  cities-latin.csv
ab401dff37c00f4d22e4ab2aa70fa2d67d89bd042787cb9e643bea7eeb5ee577  cities-utf-8.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well, obviously. In order to be able to write a different encoding than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utf-8&lt;/code&gt;
we have to open the files in binary mode:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-utf-8.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-latin.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;latin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which gives us a lovely: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeError: a bytes-like object is required, not 'str'&lt;/code&gt;.
Turns out that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas.DataFrame.to_csv&lt;/code&gt; can not write to a binary file object.
As a work around &lt;a href=&quot;https://github.com/pandas-dev/pandas/issues/23854#issuecomment-440910802&quot;&gt;people have suggest just wrapping the file object in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;io.TextIOWrapper&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-utf-8.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextIOWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-latin.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextIOWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;latin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which runs, but does not work because it also discards the encoding. On top of
that, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TextIOWrapper&lt;/code&gt; closes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fd&lt;/code&gt; if we don’t call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TextIOWrapper.detach&lt;/code&gt; before
the wrapper is freed. Which brings us back to square one.&lt;/p&gt;

&lt;p&gt;The best solution to this bug (or &lt;a href=&quot;https://github.com/pandas-dev/pandas/issues/23854#issuecomment-440910802&quot;&gt;documentation issue&lt;/a&gt;) would be to create a pull request which implements
support for binary file objects. Sadly, updating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas&lt;/code&gt; is just not an viable
option for me right now. Therefore I had to implement the following hackish
solution which monkey patches &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas.DataFrame.to_csv&lt;/code&gt; so that the function
supports binary file objects.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;contextlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contextmanager&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Semaphore&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;MONKEY_PATCH_DATAFRAME_TO_CSV&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Semaphore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contextmanager&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;monkey_patch__DataFrame_to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Monkey patch pandas.DataFrame.to_csv to make the function work with binary file objects.

    This is here because to work around the following issues:
      - https://github.com/pandas-dev/pandas/issues/9712
      - https://github.com/pandas-dev/pandas/issues/19827
      - https://github.com/pandas-dev/pandas/issues/13068
      - https://github.com/pandas-dev/pandas/issues/23854
    &quot;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pandas&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_DataFrame_to_csv_orig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_DataFrame_to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path_or_buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RawIOBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BufferedIOBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringIO&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Test if binary: https://stackoverflow.com/a/44584871
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path_or_buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RawIOBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BufferedIOBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Set line terminator of StringIO objec to DataFrame.to_csv's
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# default for line_termnator
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__version__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;# the default was changed in pandas 0.24 version
&lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;default_line_termnator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesep&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;default_line_termnator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linesep&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;# Note: Other than to_csv, StringIO will reject uncommon line terminators
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# by throwing an exception. But I think that's a good thing.
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;sio&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;newline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;line_terminator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default_line_termnator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_DataFrame_to_csv_orig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;# enforce utf-8, this is currently ignored because of:
&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# https://github.com/pandas-dev/pandas/issues/13068
&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# but if that issue ever gets fixed we prevent
&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# double-encoding with this.
&lt;/span&gt;                        &lt;span class=&quot;s&quot;&gt;&quot;encoding&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

                        &lt;span class=&quot;c1&quot;&gt;# enforce mode, this is currently ignored because of:
&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# https://github.com/pandas-dev/pandas/issues/19827
&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# but if that issue ever gets fixed this patch
&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# should still work.
&lt;/span&gt;                        &lt;span class=&quot;s&quot;&gt;&quot;mode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

                        &lt;span class=&quot;c1&quot;&gt;# always use Unix-style line_terminators. The conversion
&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# line_teminator will happen on StringIO.write
&lt;/span&gt;                        &lt;span class=&quot;s&quot;&gt;&quot;line_terminator&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;path_or_buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;# encode to the specified encoding or fall back
&lt;/span&gt;                    &lt;span class=&quot;c1&quot;&gt;# to pandas documented default
&lt;/span&gt;                    &lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;encoding&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_DataFrame_to_csv_orig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path_or_buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# apply monkey patch
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_DataFrame_to_csv&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Since we are patching in global scope, we have to
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# ensure here that only one thread runs at the time.
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Otherwise monkey patching and reverting the patch
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# can go really wrong...
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MONKEY_PATCH_DATAFRAME_TO_CSV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_DataFrame_to_csv&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# restore original to_csv
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_DataFrame_to_csv_orig&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we now use this monkey patch, we can finally generate the files&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monkey_patch__DataFrame_to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-utf-8.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cities-latin.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;latin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;… and get the expected content:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;file cities-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.csv
cities-latin.csv: ISO-8859 text
cities-utf-8.csv: UTF-8 Unicode text
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hexdump &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; cities-latin.csv
00000000  30 0a 42 65 72 6e 0a 47  65 6e e8 76 65 0a 5a &lt;span class=&quot;nb&quot;&gt;fc&lt;/span&gt;  |0.Bern.Gen.ve.Z.|
00000010  72 69 63 68 0a                                    |rich.|
00000015
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hexdump &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; cities-utf-8.csv
00000000  30 0a 42 65 72 6e 0a 47  65 6e c3 a8 76 65 0a 5a  |0.Bern.Gen..ve.Z|
00000010  c3 bc 72 69 63 68 0a                              |..rich.|
00000017
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas.__version__ == &quot;1.0.3&quot;&lt;/code&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/pandas-dev/pandas/issues/9712&quot;&gt;to_csv and bytes on Python 3&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/pandas-dev/pandas/issues/13068&quot;&gt;Python 3 writing to_csv file ignores encoding argument&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/pandas-dev/pandas/issues/23854&quot;&gt;df.to_csv() ignores encoding when given a file object or any other filelike object&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/pandas-dev/pandas/issues/19827&quot;&gt;File mode in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_csv&lt;/code&gt; is ignored, when passing a file object instead of a path&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="python" /><category term="bash" /><summary type="html">pandas.DataFrame.to_csv does not support writing to binary file objects 1. This causes confusion 2345 and makes the function difficult to work with. In this article I will first illustrate the problem with an example. Then, I will present a monkey patch for pandas.DataFrame.to_csv which mitigates the known pitfall. pandas.__version__ == &quot;1.0.3&quot; &amp;#8617; [to_csv and bytes on Python 3][pandas-issue-9712] &amp;#8617; [Python 3 writing to_csv file ignores encoding argument][pandas-issue-13068] &amp;#8617; [df.to_csv() ignores encoding when given a file object or any other filelike object][pandas-issue-23854] &amp;#8617; [File mode in to_csv is ignored, when passing a file object instead of a path][pandas-issue-19827] &amp;#8617;</summary></entry><entry><title type="html">Citation Needed</title><link href="https://duckpond.ch/citation%20needed/2020/05/02/citation-needed.html" rel="alternate" type="text/html" title="Citation Needed" /><published>2020-05-02T00:00:00+02:00</published><updated>2020-05-02T00:00:00+02:00</updated><id>https://duckpond.ch/citation%20needed/2020/05/02/citation-needed</id><content type="html" xml:base="https://duckpond.ch/citation%20needed/2020/05/02/citation-needed.html">&lt;p&gt;&lt;img src=&quot;/static/posts/citation-needed/xkcd_protester.png&quot; alt=&quot;citation-needed&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wisdom.engineering/awesome-unicode/&quot;&gt;Unicode is Awesome&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dsinecos.github.io/blog/Git-basics&quot;&gt;Git - Mental models and Cheatsheet&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://codewords.recurse.com/issues/two/git-from-the-inside-out&quot;&gt;Git from the inside out&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://nixos.wiki/wiki/Nixpkgs/Create_and_debug_packages&quot;&gt;Nixpkgs/Create and debug packages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=OQj9fCqeoa8&quot;&gt;Ein nicht nur astronomischer Blick auf den Stern von Bethlehem&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.flightera.net/&quot;&gt;Flightera.net is a young project that collects flight data and tracking information to generate statistics around aviation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://a11yproject.com&quot;&gt;A community-driven effort to make web accessibility easier&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/timjb/dotfiles/tree/master/nixos/roles/remarkable&quot;&gt;A CUPS driver which sends documents to the reMarkable cloud as nix derivation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://buttons.github.io/&quot;&gt;Official GitHub buttons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://google.github.io/styleguide/shellguide.html&quot;&gt;Shell Style Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="citation needed" /><summary type="html"></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Types for PlantUML Parser</title><link href="https://duckpond.ch/plantuml-parser/javascript/2020/02/25/types-for-plantuml-parser.html" rel="alternate" type="text/html" title="Types for PlantUML Parser" /><published>2020-02-25T00:00:00+01:00</published><updated>2020-02-25T00:00:00+01:00</updated><id>https://duckpond.ch/plantuml-parser/javascript/2020/02/25/types-for-plantuml-parser</id><content type="html" xml:base="https://duckpond.ch/plantuml-parser/javascript/2020/02/25/types-for-plantuml-parser.html">&lt;p&gt;&lt;a href=&quot;https://github.com/Enteee/plantuml-parser&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plantuml-parser&lt;/code&gt;&lt;/a&gt; 0.0.12 introduces TypeScript declarations.
Now you can parse PlantUML and get a fully typed result. PlantUML diagrams are awesome!
In this post I first give a brief introduction into PlantUML. Then I
will show how you can use &lt;a href=&quot;https://github.com/Enteee/plantuml-parser&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plantuml-parser&lt;/code&gt;&lt;/a&gt; with TypeScript
to make the most out of your diagrams.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/types-for-plantuml-parser/bob-leaves-alice.png&quot; alt=&quot;Bob leaves Alice&quot; /&gt;
&lt;em&gt;She wouldn’t be my type either&lt;/em&gt;&lt;/p&gt;

&lt;details&gt;
  &lt;summary class=&quot;center&quot;&gt;Show source&lt;/summary&gt;
  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@startuml
actor Alice
actor Bob

Alice -&amp;gt; Bob: Tells Bob that\n she does not like\ntypes
Bob --&amp;gt; Alice: Leaves her
@enduml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;
&lt;details&gt;
  &lt;summary class=&quot;center&quot;&gt;Show parser result&lt;/summary&gt;
  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
  {
    &quot;elements&quot;: [
      {
        &quot;left&quot;: &quot;Alice&quot;,
        &quot;right&quot;: &quot;Bob&quot;,
        &quot;leftType&quot;: &quot;Unknown&quot;,
        &quot;rightType&quot;: &quot;Unknown&quot;,
        &quot;leftArrowHead&quot;: &quot;&quot;,
        &quot;rightArrowHead&quot;: &quot;&amp;gt;&quot;,
        &quot;leftArrowBody&quot;: &quot;-&quot;,
        &quot;rightArrowBody&quot;: &quot;-&quot;,
        &quot;leftCardinality&quot;: &quot;&quot;,
        &quot;rightCardinality&quot;: &quot;&quot;,
        &quot;label&quot;: &quot;Tells Bob that\\n she does not like\\ntypes&quot;,
        &quot;hidden&quot;: false
      },
      {
        &quot;left&quot;: &quot;Bob&quot;,
        &quot;right&quot;: &quot;Alice&quot;,
        &quot;leftType&quot;: &quot;Unknown&quot;,
        &quot;rightType&quot;: &quot;Unknown&quot;,
        &quot;leftArrowHead&quot;: &quot;&quot;,
        &quot;rightArrowHead&quot;: &quot;&amp;gt;&quot;,
        &quot;leftArrowBody&quot;: &quot;-&quot;,
        &quot;rightArrowBody&quot;: &quot;-&quot;,
        &quot;leftCardinality&quot;: &quot;&quot;,
        &quot;rightCardinality&quot;: &quot;&quot;,
        &quot;label&quot;: &quot;Leaves her&quot;,
        &quot;hidden&quot;: false
      }
    ]
  }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;h2 class=&quot;no_toc&quot; id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#why-you-should-use-plantuml&quot; id=&quot;markdown-toc-why-you-should-use-plantuml&quot;&gt;Why you should use PlantUML&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#parsing-plantuml-with-typescript&quot; id=&quot;markdown-toc-parsing-plantuml-with-typescript&quot;&gt;Parsing PlantUML with TypeScript&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#contribute&quot; id=&quot;markdown-toc-contribute&quot;&gt;Contribute&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-you-should-use-plantuml&quot;&gt;Why you should use PlantUML&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;PlantUML is an open-source tool allowing users to create UML diagrams from a plain text language. The language of PlantUML is an example of a Domain-specific language. It uses Graphviz software to lay out its diagrams.&lt;/p&gt;

  &lt;p&gt;– Source: &lt;a href=&quot;https://en.wikipedia.org/wiki/PlantUML&quot;&gt;wikipedia/PlantUML&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I use PlantUML daily. A textual description of design diagrams alongside the
source code is my definition of a living document. You know the pain of out
of date documentation. Did you ever ask yourself why this happens? As a software
developer I do reject a tool if it does not make my life easier. Writing sound
and solid source code is already hard enough. Having to deal with a ton of different
documentation formats makes it even harder. Belive me, if design changes can be
documented with a few simple modifications of a text file, I will start doing it.&lt;/p&gt;

&lt;p&gt;This has the effect that documentation evolves together with the code. Add version
control to the mix and pull requests suddenly become self-documenting. Furthermore,
switching to Graphviz means no longer spending hours layouting documents. Time
better spent doing actual design work.&lt;/p&gt;

&lt;p&gt;With PlantUML you can document without proprietary file mongering software or
an overpriced vector graphic editor. In case you ask yourself what all that fuzz
is about. You create your design documents in PowerPoint and you are
happy with it - Then you should probably ask yourself when things started to go wrong
in you life.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/types-for-plantuml-parser/learn-plantuml.png&quot; alt=&quot;Learn PlantUML&quot; /&gt;
&lt;em&gt;You will find another Alice&lt;/em&gt;&lt;/p&gt;

&lt;details&gt;
  &lt;summary class=&quot;center&quot;&gt;Show source&lt;/summary&gt;
  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@startuml
(*) --&amp;gt; If &quot;Do you know PlantUML?&quot; then
  --&amp;gt; [No] &quot;Learn PlantUML&quot;
  If &quot;&quot; then
    --&amp;gt; [I don't\nhave time] &quot;Leave Alice&quot;
    --&amp;gt; &quot;Learn PlantUML&quot;
  else
    --&amp;gt; [Ok] &quot;Good&quot;
  EndIf
else
--&amp;gt; [Yes] &quot;Good&quot;
EndIf
--&amp;gt; &quot;Parse it&quot;
@enduml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;p&gt;You are now completely convinced and/or you already created a lot of documentation
in PlantUML. Good. But at some point you might like to get even more from that the
language. So why not start parsing it?&lt;/p&gt;

&lt;h2 id=&quot;parsing-plantuml-with-typescript&quot;&gt;Parsing PlantUML with TypeScript&lt;/h2&gt;

&lt;p&gt;Parsing the syntax to something machine-processable is easy with the command line
&lt;a href=&quot;https://github.com/Enteee/plantuml-parser&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plantuml-parser&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;details&gt;
  &lt;summary class=&quot;center&quot;&gt;Show example&lt;/summary&gt;

  &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; plantuml-parser
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;plantuml-parser &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
&amp;gt; @startuml
&amp;gt; package &quot;Wonderful world without Alice&quot; {
&amp;gt;   file Doc
&amp;gt;   file JSON
&amp;gt;   interface PlantUML
&amp;gt;   component &quot;plantuml-parser&quot; as pp
&amp;gt;
&amp;gt;   Doc -right- PlantUML
&amp;gt;   PlantUML ..&amp;gt; pp
&amp;gt;   pp -left-&amp;gt; JSON
&amp;gt; }
&amp;gt; @enduml
&amp;gt; EOF
[
  {
    &quot;elements&quot;: [
      {
        &quot;name&quot;: &quot;Wonderful world without Alice&quot;,
        &quot;title&quot;: &quot;Wonderful world without Alice&quot;,
        &quot;type&quot;: &quot;package&quot;,
        &quot;elements&quot;: [
          {
            &quot;name&quot;: &quot;PlantUML&quot;,
            &quot;title&quot;: &quot;PlantUML&quot;,
            &quot;members&quot;: []
          },
          {
            &quot;name&quot;: &quot;pp&quot;,
            &quot;title&quot;: &quot;plantuml-parser&quot;
          },
          {
            &quot;left&quot;: &quot;Doc&quot;,
            &quot;right&quot;: &quot;PlantUML&quot;,
            &quot;leftType&quot;: &quot;Unknown&quot;,
            &quot;rightType&quot;: &quot;Unknown&quot;,
            &quot;leftArrowHead&quot;: &quot;&quot;,
            &quot;rightArrowHead&quot;: &quot;&quot;,
            &quot;leftArrowBody&quot;: &quot;-&quot;,
            &quot;rightArrowBody&quot;: &quot;-&quot;,
            &quot;leftCardinality&quot;: &quot;&quot;,
            &quot;rightCardinality&quot;: &quot;&quot;,
            &quot;label&quot;: &quot;&quot;,
            &quot;hidden&quot;: false
          },
          {
            &quot;left&quot;: &quot;PlantUML&quot;,
            &quot;right&quot;: &quot;pp&quot;,
            &quot;leftType&quot;: &quot;Unknown&quot;,
            &quot;rightType&quot;: &quot;Unknown&quot;,
            &quot;leftArrowHead&quot;: &quot;&quot;,
            &quot;rightArrowHead&quot;: &quot;&amp;gt;&quot;,
            &quot;leftArrowBody&quot;: &quot;.&quot;,
            &quot;rightArrowBody&quot;: &quot;.&quot;,
            &quot;leftCardinality&quot;: &quot;&quot;,
            &quot;rightCardinality&quot;: &quot;&quot;,
            &quot;label&quot;: &quot;&quot;,
            &quot;hidden&quot;: false
          },
          {
            &quot;left&quot;: &quot;pp&quot;,
            &quot;right&quot;: &quot;JSON&quot;,
            &quot;leftType&quot;: &quot;Unknown&quot;,
            &quot;rightType&quot;: &quot;Unknown&quot;,
            &quot;leftArrowHead&quot;: &quot;&quot;,
            &quot;rightArrowHead&quot;: &quot;&amp;gt;&quot;,
            &quot;leftArrowBody&quot;: &quot;-&quot;,
            &quot;rightArrowBody&quot;: &quot;-&quot;,
            &quot;leftCardinality&quot;: &quot;&quot;,
            &quot;rightCardinality&quot;: &quot;&quot;,
            &quot;label&quot;: &quot;&quot;,
            &quot;hidden&quot;: false
          }
        ]
      }
    ]
  }
]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;p&gt;But today we want to have a look at the programmatic use of the parser. Therefore
I created the following demonstration which shows how easy it is to parse PlantUML
in TypeScript. The demonstration also contains an example on how type guards can
leverage processing of diagrams.&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
  &lt;script id=&quot;asciicast-8FC3oAI3PCtGdljCISvWVo0o8&quot; src=&quot;https://asciinema.org/a/8FC3oAI3PCtGdljCISvWVo0o8.js&quot; async=&quot;&quot;&gt;&lt;/script&gt;

&lt;/div&gt;

&lt;details&gt;
  &lt;summary class=&quot;center&quot;&gt;Show source&lt;/summary&gt;
  &lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;diag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
@startuml
class Alice &amp;lt;&amp;lt;single&amp;gt;&amp;gt; {
  +cry(): any
}
note left of Alice::cry
Untyped. She
does not like types
end note
@enduml
`&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// importing the parser&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;plantuml-parser&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// for each diagram parsed&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;diag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// extract all elements&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;elems&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;elems&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// we can use type guards to&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// distinguish between the different&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// elements in the diagram&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Class: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Note&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Note on &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;h2 id=&quot;contribute&quot;&gt;Contribute&lt;/h2&gt;

&lt;p&gt;If you do like the project and you would like to contribute, there are numerous
ways how you can do so. Even if you do not write source code. Every contribution
counts.&lt;/p&gt;

&lt;script async=&quot;&quot; defer=&quot;&quot; src=&quot;https://buttons.github.io/buttons.js&quot;&gt;&lt;/script&gt;

&lt;ul&gt;
  &lt;li&gt;Tweet: &lt;a href=&quot;https://twitter.com/intent/tweet?text=Parse%20PlantUML%20with%20JavaScript%20or%20TypeScript%20%F0%9F%9A%80&amp;amp;hashtags=PlantUMLParser,JavaScript,TypeScript&amp;amp;url=https%3A%2F%2Fgithub.com%2FEnteee%2Fplantuml-parser&quot;&gt;&lt;img src=&quot;https://img.shields.io/twitter/url?label=%23PlantUMLParser&amp;amp;url=https%3A%2F%2Fgithub.com%2FEnteee%2Fplantuml-parser&quot; alt=&quot;Twitter URL&quot; /&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Star: &lt;a class=&quot;github-button&quot; href=&quot;https://github.com/Enteee/plantuml-parser&quot; data-icon=&quot;octicon-star&quot; data-show-count=&quot;true&quot; aria-label=&quot;Star Enteee/plantuml-parser on GitHub&quot;&gt;Star&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Resolve: &lt;a class=&quot;github-button&quot; href=&quot;https://github.com/Enteee/plantuml-parser/issues&quot; data-icon=&quot;octicon-issue-opened&quot; data-show-count=&quot;true&quot; aria-label=&quot;Issue Enteee/plantuml-parser on GitHub&quot;&gt;Issue&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Donate: &lt;a class=&quot;github-button&quot; href=&quot;https://github.com/sponsors/Enteee&quot; data-icon=&quot;octicon-heart&quot; aria-label=&quot;Sponsor @Enteee on GitHub&quot;&gt;Sponsor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you.&lt;/p&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="plantuml-parser" /><category term="javascript" /><summary type="html">plantuml-parser 0.0.12 introduces TypeScript declarations. Now you can parse PlantUML and get a fully typed result. PlantUML diagrams are awesome! In this post I first give a brief introduction into PlantUML. Then I will show how you can use plantuml-parser with TypeScript to make the most out of your diagrams.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/types-for-plantuml-parser/bob-leaves-alice.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/types-for-plantuml-parser/bob-leaves-alice.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">reMarkable</title><link href="https://duckpond.ch/nix/bash/2020/01/08/reMarkable.html" rel="alternate" type="text/html" title="reMarkable" /><published>2020-01-08T00:00:00+01:00</published><updated>2020-01-08T00:00:00+01:00</updated><id>https://duckpond.ch/nix/bash/2020/01/08/reMarkable</id><content type="html" xml:base="https://duckpond.ch/nix/bash/2020/01/08/reMarkable.html">&lt;p&gt;I got my &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; recently and writing a few words about it will possibly
not hurt anyone. This is not a product review, I much rather try to focus on
technical aspects. Such as packaging the software and analyzing some of its
features.&lt;/p&gt;

&lt;p&gt;&lt;a target=&quot;_blank&quot; href=&quot;/static/posts/reMarkable/remarkable.png&quot;&gt;
  &lt;img src=&quot;/static/resized/remarkable-1400x1867.png&quot; alt=&quot;&quot; srcset=&quot;            /static/resized/remarkable-480x640.png 480w,            /static/resized/remarkable-800x1067.png 800w,            /static/resized/remarkable-1400x1867.png 1400w,    &quot; /&gt;
  &lt;em&gt;Sketching and writing on the device&lt;/em&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 class=&quot;no_toc&quot; id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#feature-overview&quot; id=&quot;markdown-toc-feature-overview&quot;&gt;Feature Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#software-for-the-remarkable-and-some-nixos-derivations&quot; id=&quot;markdown-toc-software-for-the-remarkable-and-some-nixos-derivations&quot;&gt;Software for the reMarkable and Some NixOS Derivations&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-official-linux-client&quot; id=&quot;markdown-toc-the-official-linux-client&quot;&gt;The Official Linux Client&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#accessing-the-remarkable-api-with-rmapi&quot; id=&quot;markdown-toc-accessing-the-remarkable-api-with-rmapi&quot;&gt;Accessing the reMarkable API With rMAPI&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#streaming-the-framebuffer-with-srvfb&quot; id=&quot;markdown-toc-streaming-the-framebuffer-with-srvfb&quot;&gt;Streaming the Framebuffer with srvfb&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#hacking-hardware-button-flight-mode-toggling&quot; id=&quot;markdown-toc-hacking-hardware-button-flight-mode-toggling&quot;&gt;Hacking Hardware Button Flight Mode Toggling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;feature-overview&quot;&gt;Feature Overview&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; is an electronic ink tablet designed for writing. E Ink writing
tablets promise excellent writing experience and a long battery lifetime. Which
should make them a good replacement for paper. There are a few competitors on
that market. For example the &lt;a href=&quot;https://goodereader.com/blog/product/supernote-a5-digital-note&quot;&gt;Ratta SuperNote&lt;/a&gt;,
heavily featured on &lt;a href=&quot;https://goodereader.com/blog/product/supernote-a6-digital-note&quot;&gt;goodereader&lt;/a&gt;&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;My main use-cases for the tablet are to-do lists, meeting notes, mind maps,
user interface mock-ups and ugly sketches. I bought an e ink tablet because I was fed up with
manually digitalizing paper. I finally chose the &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; because all of the
developers &lt;a href=&quot;https://github.com/orgs/reMarkable/people&quot;&gt;seem to be European cats&lt;/a&gt;
and the ecosystem &lt;a href=&quot;https://github.com/reHackable/awesome-reMarkable&quot;&gt;is hackable to at least some degree&lt;/a&gt;.
Also they &lt;a href=&quot;https://support.remarkable.com/hc/en-us/sections/115001534689-Release-notes&quot;&gt;release frequently&lt;/a&gt;
which is a big plus.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/reMarkable/ecosystem.svg&quot; alt=&quot;reMarkable ecosystem&quot; /&gt;
&lt;em&gt;IANAA - I am not an artist&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The device connects to the reMarkable cloud which has the main focus on document
sharing and backup. There is an App for Android / iOS as well as a client for
Windows and Mac OS. Sadly, the Linux client was discontinued in late 2017 &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.
For cross operating system compatibility the device can serve its files using
a built in web server accessible via USB. When connected to the cloud, the device
has some optical character recognition (OCR) capabilities and can convert
documents to scalable vector graphics (SVG). The live view feature would be
amazing but also requires the native QT app on the receiving end.&lt;/p&gt;

&lt;h2 id=&quot;software-for-the-remarkable-and-some-nixos-derivations&quot;&gt;Software for the reMarkable and Some NixOS Derivations&lt;/h2&gt;

&lt;p&gt;In this section I am looking at software written for the &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; and because
NixOS is awesome I also tried to create some derivations.&lt;/p&gt;

&lt;h3 id=&quot;the-official-linux-client&quot;&gt;The Official Linux Client&lt;/h3&gt;

&lt;p&gt;Using the &lt;a href=&quot;https://nixos.org/nixpkgs/manual/#sec-language-qt&quot;&gt;NixOS packaging guideline for QT&lt;/a&gt;,
and the following script:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-euo&lt;/span&gt; pipefail

&lt;span class=&quot;nv&quot;&gt;binary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?Missing binary&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; lib
&lt;span class=&quot;k&quot;&gt;do
  if &lt;/span&gt;ldd &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;binary&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;not found&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lib&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    nix-locate &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;lib/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi
done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;patchelf &lt;span class=&quot;nt&quot;&gt;--print-needed&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;binary&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was able to quickly pin down all the dependencies and patch the distributed
executable with NixOS specific rpaths &lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. With the resulting derivation &lt;a href=&quot;/static/posts/reMarkable/remarkable-linux-client.nix&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remarkable-linux-client.nix&lt;/code&gt;&lt;/a&gt;
I can now run the client inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix-shell&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;nix-shell &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--command&lt;/span&gt; reMarkable &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    nix-build &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'(import (builtins.fetchGit {
        url = &quot;https://github.com/nixos/nixpkgs/&quot;;
        ref = &quot;master&quot;;
        rev = &quot;471869c9185fb610e67940a701eb13b1cfb335a4&quot;;
    }) {}).qt5.callPackage ./remarkable-linux-client.nix {}'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The application starts and connects to the cloud and all other features &lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;
work just fine. But I ran into big problems when displaying any of the drawings.
All notebooks are completely empty. Without success I have checked the whole log
for any hints which might indicate issues related to the packaging.&lt;/p&gt;

&lt;p&gt;My best guess is that the &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; developers have changed the proprietary
file format and the extremely outdated Linux client is no longer able to read
that format properly. I got to this conclusion because the app logs the
following when displaying a a drawing:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Redrawing lines...
0 Lines starting at 0 of total 0
Redrawing lines completed in 0 ms
xochitl.documentworker: Storing page... 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But this is just a guess. If you have a better idea what might be going wrong,
I am curious to hear about them in the comment section. Having spent quite a few
hours on this issue, I finally gave up getting the Linux client to work.
Therefore I started looking for open source alternatives:&lt;/p&gt;

&lt;h3 id=&quot;accessing-the-remarkable-api-with-rmapi&quot;&gt;Accessing the reMarkable API With &lt;a href=&quot;https://github.com/juruen/rmapi&quot;&gt;rMAPI&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;From the &lt;a href=&quot;https://github.com/juruen/rmapi/blob/master/README.md&quot;&gt;README.md&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://github.com/juruen/rmapi&quot;&gt;rMAPI&lt;/a&gt; is a Go app that allows you to access the ReMarkable Cloud API programmatically.&lt;/p&gt;

  &lt;p&gt;You can interact with the different API end-points through a shell. However, you can also run commands non-interactively. This may come in handy to script certain workflows such as taking automatic backups or uploading documents programmatically.&lt;/p&gt;

  &lt;p&gt;&lt;img src=&quot;/static/posts/reMarkable/rmapi-console.gif&quot; alt=&quot;rMAPI console&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In short a great tool! Creating a derivation and using it under NixOS was
easy. I opened a &lt;a href=&quot;https://github.com/juruen/rmapi/pull/78&quot;&gt;pull request&lt;/a&gt; to share
my work with the &lt;a href=&quot;https://github.com/juruen/rmapi&quot;&gt;rMAPI&lt;/a&gt; project. Then I created another &lt;a href=&quot;https://github.com/NixOS/nixpkgs/pull/74657&quot;&gt;pull request&lt;/a&gt;
which adds this derivation to nixpkgs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lessons Learned&lt;/em&gt;: My initial Idea was to keep the full derivation in the &lt;a href=&quot;https://github.com/juruen/rmapi&quot;&gt;rMAPI&lt;/a&gt;
repository and just use that repository in the nix-expression added to nixpkgs.
This approach did not work because it requires import from derivations (IFD),
which are currently disabled in hydra &lt;sup id=&quot;fnref:5&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:5&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;With &lt;a href=&quot;https://github.com/juruen/rmapi&quot;&gt;rMAPI&lt;/a&gt; I can now easily replicate the file sharing aspects of the native
Linux client. But how can we get the live view to work?&lt;/p&gt;

&lt;h3 id=&quot;streaming-the-framebuffer-with-srvfb&quot;&gt;Streaming the Framebuffer with &lt;a href=&quot;https://github.com/Merovius/srvfb&quot;&gt;srvfb&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The idea is simple. We grab the framebuffer from the &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; and send it
back to the computer where we then render an image. In its most basic shape this
can be a &lt;sup id=&quot;fnref:6&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:6&quot; class=&quot;footnote&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh root@10.11.99.1 &lt;span class=&quot;s2&quot;&gt;&quot;cat /dev/fb0&quot;&lt;/span&gt; | &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  ffmpeg &lt;span class=&quot;nt&quot;&gt;-vcodec&lt;/span&gt; rawvideo &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; rawvideo &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;-pix_fmt&lt;/span&gt; gray16le &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; 1408,1872 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; - &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;-vframes&lt;/span&gt; 1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; image2 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;-vcodec&lt;/span&gt; mjpeg /tmp/frame.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The amazing project &lt;a href=&quot;https://github.com/Merovius/srvfb&quot;&gt;srvfb&lt;/a&gt; took this idea
to the next level. From the &lt;a href=&quot;https://github.com/Merovius/srvfb/blob/master/README.md&quot;&gt;README.md&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This repository contains a small webserver that can serve the contents of a Linux framebuffer device as video over HTTP. The video is encoded as a series of PNGs, which are served in a multipart/x-mixed-replace stream. The primary use case is to stream a &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; screen to a computer and share it from there via video-conferencing or capturing it. For that reason, there is also a proxy-mode, which streams the frames as raw, uncompressed data from the remarkable and can then do the png-encoding on a more powerful machine. Whithout that, the framerate is one or two frames per second, which might not be acceptable (it might be, though).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Running this on the &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; is super easy. The result is great and even the
documented lag in non-proxy mode is acceptable. Problem solved.
Thank you &lt;a href=&quot;https://github.com/Merovius&quot;&gt;Merovius&lt;/a&gt;, keep up the good work!&lt;/p&gt;

&lt;p&gt;My last pain point with the reMarkable is the missing hardware button for flight
mode toggling. Because of battery lifetime I keep flight mode always on. But
when I occasionally do need internet, the flight mode option in the settings is
always a million clicks away.&lt;/p&gt;

&lt;h2 id=&quot;hacking-hardware-button-flight-mode-toggling&quot;&gt;Hacking Hardware Button Flight Mode Toggling&lt;/h2&gt;

&lt;p&gt;I could not find a tool to get the flight mode working via button press, so I
built something on my own. But before doing so, I familiarized myself with the
internals of the device. For this I had to become &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; first.
Good news! It is possible and super easy:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Connect the tablet to your computer via USB.&lt;/li&gt;
  &lt;li&gt;Get the IP address as well as the root password from the about page.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;, done!&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh root@10.11.99.1
ｒｅＭａｒｋａｂｌｅ
╺━┓┏━╸┏━┓┏━┓   ┏━╸┏━┓┏━┓╻ ╻╻╺┳╸┏━┓┏━┓
┏━┛┣╸ ┣┳┛┃ ┃   ┃╺┓┣┳┛┣━┫┃┏┛┃ ┃ ┣━┫┗━┓
┗━╸┗━╸╹┗╸┗━┛   ┗━┛╹┗╸╹ ╹┗┛ ╹ ╹ ╹ ╹┗━┛

remarkable: ~/ &lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;
Linux remarkable 4.9.84-zero-gravitas &lt;span class=&quot;c&quot;&gt;#1 Thu Jun 27 14:19:15 UTC 2019 armv7l GNU/Linux&lt;/span&gt;

remarkable: ~/ &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /proc/cpuinfo
processor         : 0
model name        : ARMv7 Processor rev 10 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;v7l&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
BogoMIPS          : 24.00
Features          : half thumb fastmult vfp edsp neon vfpv3 tls vfpd32
CPU implementer   : 0x41
CPU architecture  : 7
CPU variant       : 0x2
CPU part          : 0xc09
CPU revision      : 10

Hardware          : Freescale i.MX6 SoloLite &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Device Tree&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Revision          : 0000
Serial            : &lt;span class=&quot;k&quot;&gt;****************&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The remarkable File system structure is partially documented on the &lt;a href=&quot;https://remarkablewiki.com/tech/filesystem&quot;&gt;remarkable wiki&lt;/a&gt;.
Some other locations I found interesting:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/root/.local/share/remarkable/xochitl/&lt;/code&gt;: Your documents and metadata.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/remarkable.conf&lt;/code&gt;: Passwords and keys.
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;remarkable: ~/ &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; Password /etc/remarkable.conf
&lt;span class=&quot;nv&quot;&gt;DeveloperPassword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; ROOT PASSWORD &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;masked&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;***&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; Screen lock password &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;masked&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;***&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;remarkable: ~/ &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/wifinetworks/,$p'&lt;/span&gt; /etc/remarkable.conf
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;wifinetworks]
&lt;span class=&quot;nv&quot;&gt;SSID1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;@Variant&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; Key &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;Wifi network &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;masked&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;***&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SSID2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;@Variant&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; Key &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;Wifi network &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;masked&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;***&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enough of the playtime, let’s get cracking. Again, my goal is to toggle flight mode
when the left and right button are both pressed at the same time. For this I
have to listen on evdev events and once both buttons are pressed run
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rfkill block all&lt;/code&gt; to enable flight mode or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rfkill unblock all&lt;/code&gt; to disable it again.&lt;/p&gt;

&lt;p&gt;In my simple approach I am using &lt;a href=&quot;https://github.com/freedesktop-unofficial-mirror/evtest&quot;&gt;evtest&lt;/a&gt; to parse the binary events from
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event*&lt;/code&gt;. First I cross compiled &lt;a href=&quot;https://github.com/freedesktop-unofficial-mirror/evtest&quot;&gt;evtest&lt;/a&gt; with &lt;a href=&quot;https://github.com/dockcross/dockcross&quot;&gt;dockercross&lt;/a&gt; for the
32 bit ARMV7-A and copied the resulting binary to the reMarkable.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git clone https://github.com/freedesktop-unofficial-mirror/evtest.git
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;evtest
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  dockcross/linux-armv7a &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; dockercross
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x dockercross
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./dockercross bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;autoreconf -iv &amp;amp;&amp;amp; ./configure --host=arm-linux-gnueabi &amp;amp;&amp;amp; make&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scp evtest root@10.11.99.1:.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;evdev exposes three different event sources on the &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt;:&lt;/p&gt;

&lt;table class=&quot;table&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Device&lt;/th&gt;
      &lt;th&gt;Name&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event0&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Wacom I2C Digitizer&lt;/td&gt;
      &lt;td&gt;The electromagnetic resonance device for the pen&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event1&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;cyttsp5_mt&lt;/td&gt;
      &lt;td&gt;The capacitive touchscreen&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event2&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;gpio-keys&lt;/td&gt;
      &lt;td&gt;The hardware buttons&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; script uses the just compiled &lt;a href=&quot;https://github.com/freedesktop-unofficial-mirror/evtest&quot;&gt;evtest&lt;/a&gt; binary to fetch and
handle button press events on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/input/event2&lt;/code&gt;. With
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rfkill list | grep -q &quot;Soft blocked: no&quot;&lt;/code&gt; it then detects if flight mode is
disabled and calls out to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rfkill&lt;/code&gt; accordingly.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-euo&lt;/span&gt; pipefail

&lt;span class=&quot;nv&quot;&gt;DEVICE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/dev/input/event2'&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;EVTEST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; evtest &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'./evtest'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RFKILL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; rfkill&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false

&lt;/span&gt;toggle_flight_mode&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RFKILL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; list | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Soft blocked: no&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RFKILL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; block all
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RFKILL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; unblock all
  &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

handle_events&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_left_down
  &lt;span class=&quot;nv&quot;&gt;key_left_down&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 105 (KEY_LEFT), value 1*'&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_left_up
  &lt;span class=&quot;nv&quot;&gt;key_left_up&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 105 (KEY_LEFT), value 0*'&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_right_down
  &lt;span class=&quot;nv&quot;&gt;key_right_down&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 106 (KEY_RIGHT), value 1*'&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;key_right_up
  &lt;span class=&quot;nv&quot;&gt;key_right_up&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*type 1 (EV_KEY), code 106 (KEY_RIGHT), value 0*'&lt;/span&gt;


  &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$EVTEST&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEVICE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; line&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
      case&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_left_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_left_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_right_down&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key_right_up&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -&amp;gt; LEFT &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; , RIGHT: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LEFT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RIGHT_DOWN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;toggle_flight_mode
      &lt;span class=&quot;k&quot;&gt;fi
  done&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

handle_events
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;done!.. One downside coming from directly using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rfkill&lt;/code&gt;, is that the user interface
does not properly detect this state change. Which means we can get the reMarkable into
weird states where the device is connected to a network with the flight mode icon
on. Rebooting the device recovers the clean state again. &lt;a href=&quot;https://www.youtube.com/watch?v=JYAq-7sOzXQ&quot;&gt;But let us call this a
feature, shall we?&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;After having used the reMarkable for a few weeks now I am still very satisfied
with the product. The overall user experience is very good. And the graphite tips
don’t wear out too fast. For the future I would like to see the following features
implemented:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;toggle flight mode with a hardware switch&lt;/li&gt;
  &lt;li&gt;mixed PDF and sheet notebooks&lt;/li&gt;
  &lt;li&gt;basic shapes such as circles, squares and lines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe we get all this with the reMarkable2. It seems that a new device is on its
way. &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; has filed a &lt;a href=&quot;https://fccid.io/2AMK2-RM110&quot;&gt;request for certification by the FCC (ID: 2AMK2-RM110)&lt;/a&gt; which contains a wealth of pictures and specs. But in a letter correspondence
from the 22. November 2019 they requested the dismissal of that FCC ID.
Whatever that means…&lt;/p&gt;

&lt;p&gt;Wrapping up, the &lt;a href=&quot;https://remarkable.com/&quot;&gt;reMarkable&lt;/a&gt; is a perfect example how open devices can encourage
a community to make so much more out of a already great product. I sincerely hope
that the company stays as open as it is right now &lt;sup id=&quot;fnref:7&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:7&quot; class=&quot;footnote&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; and no money hungry manager
or acquiring company destroys what they built so far.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The platform is so unnaturally biased towards that alternative, leading me into questioning their independence. We all need money to support our life. But a bit more transparency, especially when your main focus are product reviews, would be nice. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This needs to change! &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;20.03pre199897.471869c9185 &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Except the most important one… &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:5&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I also recommend reading &lt;a href=&quot;https://blog.hercules-ci.com/2019/08/30/native-support-for-import-for-derivation/&quot;&gt;this excellent article by Domen Kožar&lt;/a&gt; &lt;a href=&quot;#fnref:5&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:6&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Shamelessly stolen from &lt;a href=&quot;https://github.com/canselcik/libremarkable/wiki/Framebuffer-Overview&quot;&gt;canselcik/libremarkable Wiki&lt;/a&gt; &lt;a href=&quot;#fnref:6&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:7&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I can haz &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xochitl&lt;/code&gt; github repo? &lt;a href=&quot;#fnref:7&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="nix" /><category term="bash" /><summary type="html">I got my reMarkable recently and writing a few words about it will possibly not hurt anyone. This is not a product review, I much rather try to focus on technical aspects. Such as packaging the software and analyzing some of its features.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/resized/remarkable-800x1067.png" /><media:content medium="image" url="https://duckpond.ch/static/resized/remarkable-800x1067.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Citation Needed</title><link href="https://duckpond.ch/citation%20needed/2019/11/08/citation-needed.html" rel="alternate" type="text/html" title="Citation Needed" /><published>2019-11-08T00:00:00+01:00</published><updated>2019-11-08T00:00:00+01:00</updated><id>https://duckpond.ch/citation%20needed/2019/11/08/citation-needed</id><content type="html" xml:base="https://duckpond.ch/citation%20needed/2019/11/08/citation-needed.html">&lt;p&gt;&lt;img src=&quot;/static/posts/citation-needed/xkcd_protester.png&quot; alt=&quot;citation-needed&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dashboard.laterforreddit.com/analysis/&quot;&gt;Find the Ideal time to post on reddit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://duktape.org/&quot;&gt;Duktape is an embeddable Javascript engine, with a focus on portability and compact footprint&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://catonmat.net/bash-one-liners-explained-part-three&quot;&gt;Bash One-Liners Explained, Part III: All about redirections&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.kapwing.com&quot;&gt;Online Meme Generator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://r6.ca/blog/20140422T142911Z.html&quot;&gt;How to Fake Dynamic Binding in Nix&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.npmtrends.com/&quot;&gt;npm trends: Compare package download counts over time&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/softwarelibrary_msdos_games&quot;&gt;A Huge Collection of MS-DOS Games&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://theshortlog.com/2016/02/11/ipython-nix/&quot;&gt;Quick IPython shells with nix-shell&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.travis-ci.com/user/migrate/open-source-repository-migration&quot;&gt;Migrate to travis-ci.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sedimental.org/designing_a_version.html&quot;&gt;Designing a Version&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=_3loq22TxSc&quot;&gt;Great Impractical Ideas in Computer Science: PowerPoint Programming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/AceLewis/my_first_calculator.py&quot;&gt;my_first_calculator.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="citation needed" /><summary type="html"></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">udev Rule Script Template</title><link href="https://duckpond.ch/bash/nix/2019/10/25/udev-rule-script-template.html" rel="alternate" type="text/html" title="udev Rule Script Template" /><published>2019-10-25T00:00:00+02:00</published><updated>2019-10-25T00:00:00+02:00</updated><id>https://duckpond.ch/bash/nix/2019/10/25/udev-rule-script-template</id><content type="html" xml:base="https://duckpond.ch/bash/nix/2019/10/25/udev-rule-script-template.html">&lt;p&gt;When writing shell scripts invoked by udev rules, I find the following template
particularly useful:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-euo&lt;/span&gt; pipefail

&lt;span class=&quot;nv&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;udevscript&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;1&amp;gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;(&lt;/span&gt;logger &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;2&amp;gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;(&lt;/span&gt;ts &lt;span class=&quot;s1&quot;&gt;'[stderr]'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;(&lt;/span&gt;logger &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;running: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;## Actual script starts here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then use such a script in a udev rule like we always would:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;SUBSYSTEM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;usb&quot;&lt;/span&gt;, &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;ACTION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;remove&quot;&lt;/span&gt;, &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  ENV&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;ID_VENDOR_ID&lt;span class=&quot;o&quot;&gt;}==&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;17e9&quot;&lt;/span&gt;, &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  ENV&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;ID_MODEL_ID&lt;span class=&quot;o&quot;&gt;}==&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;6015&quot;&lt;/span&gt;, &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  RUN+&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/usr/lib/udev/scripts/undock.sh&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And when the rule runs the script, everything printed is sent to the system
log. Stderr messages will be prefixed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[stderr]&lt;/code&gt;. Which makes checking what
the script does as simple as:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;journalctl &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; undock.sh
Oct 25 22:58:35 puddle undock.sh[32366]: running: /usr/lib/udev/scripts/undock.sh
Oct 25 22:58:35 puddle undock.sh[32366]: &lt;span class=&quot;nv&quot;&gt;ID_SERIAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;DisplayLink_ThinkPad_Hybrid_USB-C_with_USB-A_Dock_10391787
Oct 25 22:58:35 puddle undock.sh[32366]: &lt;span class=&quot;nv&quot;&gt;ID_MODEL_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;6015
Oct 25 22:58:35 puddle undock.sh[32366]: &lt;span class=&quot;nv&quot;&gt;ACTION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;remove
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The template also supports debug output. Setting the first argument to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;
will print commands and their arguments as they are executed to stderr.&lt;/p&gt;

&lt;h2 id=&quot;bonus-a-nix-expression&quot;&gt;Bonus: A Nix Expression&lt;/h2&gt;

&lt;p&gt;The script above is derived from the following nix expression.&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nixpkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;utillinux&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/logger&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;moreutils&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;coreutils&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/tee&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;strict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;printenv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;strictCmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;strict&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      set -euo pipefail&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      ''&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;logCmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      exec 1&amp;gt; &amp;gt;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tee&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &amp;gt;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -t &quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;))&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      exec 2&amp;gt; &amp;gt;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; '[stderr]' | &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tee&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &amp;gt;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -t &quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;))&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      ''&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;debugCmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      set -x&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      ''&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;printenvCmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;printenv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      echo &quot;running: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      env&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      ''&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;writeShellScript&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;strictCmd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;logCmd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;debugCmd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;printenvCmd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;    ''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If invoked, the expression writes such a script to the nix store.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nix-repl&amp;gt; writeLoggedScript &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; import ./writeLoggedScript.nix &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;

nix-repl&amp;gt; :b writeLoggedScript &lt;span class=&quot;s2&quot;&gt;&quot;test.sh&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;echo this is just a test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;

this derivation produced the following outputs:
  out -&amp;gt; /nix/store/bzjllwyhy0zwm2f352a0gya8fp13qc7q-test.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="bash" /><category term="nix" /><summary type="html">When writing shell scripts invoked by udev rules, I find the following template particularly useful:</summary></entry><entry><title type="html">Migrate a git Repository with Submodules</title><link href="https://duckpond.ch/git-submodule-url-rewrite/git-sync-mirror/2019/07/08/migrate-a-git-repository-with-submodules.html" rel="alternate" type="text/html" title="Migrate a git Repository with Submodules" /><published>2019-07-08T00:00:00+02:00</published><updated>2019-07-08T00:00:00+02:00</updated><id>https://duckpond.ch/git-submodule-url-rewrite/git-sync-mirror/2019/07/08/migrate-a-git-repository-with-submodules</id><content type="html" xml:base="https://duckpond.ch/git-submodule-url-rewrite/git-sync-mirror/2019/07/08/migrate-a-git-repository-with-submodules.html">&lt;p&gt;With &lt;a href=&quot;https://hub.docker.com/r/enteee/git-sync-mirror&quot;&gt;git-sync-mirror&lt;/a&gt; migrating or mirroring a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; repository is a piece of
cake. But when the migrated repository contains submodules additional steps are
required.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Submodule references are not updated when mirroring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; repositories. This is
because those references are tracked in a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitmodules&lt;/code&gt; inside the
repository. This file is not changed during migration.&lt;/p&gt;

&lt;p&gt;As an example imagine you want to mirror the &lt;a href=&quot;https://github.com/githubtraining/example-dependency.git&quot;&gt;githubtraining/example-dependency&lt;/a&gt;
repository to &lt;a href=&quot;https://github.com/Enteee/example-dependency.git&quot;&gt;Enteee/example-dependency&lt;/a&gt;. Using &lt;a href=&quot;https://hub.docker.com/r/enteee/git-sync-mirror&quot;&gt;git-sync-mirror&lt;/a&gt; this is simple:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ GITHUB_USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Enteee&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ GITHUB_ACCESS_TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;hidden&amp;gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ONCE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;SRC_REPO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://github.com/githubtraining/example-dependency.git&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DST_REPO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GITHUB_ACCESS_TOKEN&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;@github.com/Enteee/example-dependency.git &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  enteee/git-sync-mirror
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The just mirrored &lt;a href=&quot;https://github.com/githubtraining/example-dependency.git&quot;&gt;githubtraining/example-dependency&lt;/a&gt; repository contains one
submodule at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;js&lt;/code&gt;. This submodule references the &lt;a href=&quot;https://github.com/githubtraining/example-submodule/tree/c3c588713233609f5bbbb2d9e7f3fb4a660f3f72&quot;&gt;githubtraining/example-submodule&lt;/a&gt;
repository.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git clone https://github.com/githubtraining/example-dependency.git
Cloning into &lt;span class=&quot;s1&quot;&gt;'example-dependency'&lt;/span&gt;...
remote: Enumerating objects: 39, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Counting objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;39/39&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Compressing objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;30/30&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
remote: Total 39 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, reused 39 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, pack-reused 0
Unpacking objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;39/39&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;example-dependency/.gitmodules
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;submodule &lt;span class=&quot;s2&quot;&gt;&quot;js&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  path &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; js
  url &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; https://github.com/githubtraining/example-submodule.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For a full migration we now also have to mirror &lt;a href=&quot;https://github.com/githubtraining/example-submodule/tree/c3c588713233609f5bbbb2d9e7f3fb4a660f3f72&quot;&gt;githubtraining/example-submodule&lt;/a&gt;
to &lt;a href=&quot;https://github.com/Enteee/example-submodule.git&quot;&gt;Enteee/example-submodule&lt;/a&gt;. We can do this exactly the same way as we did it
before with &lt;a href=&quot;https://github.com/githubtraining/example-dependency.git&quot;&gt;githubtraining/example-dependency&lt;/a&gt;. But since we were creating 1:1
mirrors, the first repository &lt;a href=&quot;https://github.com/Enteee/example-dependency.git&quot;&gt;Enteee/example-dependency&lt;/a&gt; still points to
&lt;a href=&quot;https://github.com/githubtraining/example-submodule/tree/c3c588713233609f5bbbb2d9e7f3fb4a660f3f72&quot;&gt;githubtraining/example-submodule&lt;/a&gt;. This is probably not what we want. The
repository &lt;a href=&quot;https://github.com/Enteee/example-dependency.git&quot;&gt;Enteee/example-dependency&lt;/a&gt; should point to &lt;a href=&quot;https://github.com/Enteee/example-submodule.git&quot;&gt;Enteee/example-submodule&lt;/a&gt;
instead.&lt;/p&gt;

&lt;h2 id=&quot;the-solution-git-submodule-url-rewrite&quot;&gt;The Solution: &lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; command that lets you rewrite submodule
urls. Installing the command is as simple as copying the script somewhere to
your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${PATH}&lt;/code&gt; and making it executable.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /usr/local/bin
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; git-submodule-url-rewrite &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/Enteee/git-submodule-url-rewrite/master/git-submodule-url-rewrite
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;a+x git-submodule-url-rewrite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, &lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt; should be available as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; command.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git submodule-url-rewrite &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt;
usage: git submodule-url-rewrite &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt;|--help] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt;|--verbose] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt;|--quiet] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt;|--recursive] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;|--no-stage] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt;|--no-update] sed-command

Rewrites all submodule urls using the given sed-script

options:
  &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt;|--help       Print this &lt;span class=&quot;nb&quot;&gt;help&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt;|--verbose    Make this script verbose
  &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt;|--quiet      Don&lt;span class=&quot;s1&quot;&gt;'t print anything
  -r|--recursive  Also rewrite submodules of submodules
  -s|--no-stage   Don'&lt;/span&gt;t stage changed .gitmodule files &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;commit
  &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt;|--no-update  Don&lt;span class=&quot;s1&quot;&gt;'t run '&lt;/span&gt;git submodule &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; update &lt;span class=&quot;nt&quot;&gt;--init&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' in each submodule

sed-command: A sed command used to transform urls.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using this command we can now rewrite all urls in &lt;a href=&quot;https://github.com/Enteee/example-dependency.git&quot;&gt;Enteee/example-dependency&lt;/a&gt;.
We just have to use a substituting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; command (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt;) which replaces all
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;githubtraining&lt;/code&gt; strings with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enteee&lt;/code&gt;. The following command does this:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;'s|githubtraining|Enteee|'&lt;/code&gt;. For more information about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; commands I
recommend &lt;a href=&quot;https://www.computerhope.com/unix/used.htm&quot;&gt;this tutorial on computerhope.com&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;example-dependency/
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git submodule-url-rewrite &lt;span class=&quot;s1&quot;&gt;'s|githubtraining|Enteee|'&lt;/span&gt;
rewrite url &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;submodule &lt;span class=&quot;s1&quot;&gt;'js'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/tmp/example-dependency'&lt;/span&gt; from &lt;span class=&quot;s1&quot;&gt;'https://github.com/githubtraining/example-submodule.git'&lt;/span&gt; to &lt;span class=&quot;s1&quot;&gt;'https://github.com/Enteee/example-submodule.git'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;, done!&lt;/p&gt;

&lt;h3 id=&quot;why-should-i-use-this&quot;&gt;Why should I use this?&lt;/h3&gt;

&lt;p&gt;In this section I try to answer a few common questions. Keep in mind that
&lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt; is a very simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; command which I found useful
in the past. Hence, I decided to open source it. Nobody forces you to use it.
If you find good reasons not to, then don’t. In order to improve
&lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt;, I would still be interested in those reasons.
Why not share them here?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Rewriting a a submodule url is a simple as:&lt;/p&gt;
  &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git config &lt;span class=&quot;nt&quot;&gt;--file&lt;/span&gt; .gitmodules submodule.js.url &lt;span class=&quot;s1&quot;&gt;'https://github.com/Enteee/example-submodule.git'&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git submodule &lt;span class=&quot;nb&quot;&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
  &lt;p&gt;why should I use &lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt;?&lt;/p&gt;

  &lt;p&gt;– unknown &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; user&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is exactly what &lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt; does. No magic involved. But
in addition, &lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt; provides the convenience of regex
and a recursive (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[-r|--recursive]&lt;/code&gt;) switch. Using recursion you can simply rewrite submodules
of submodules of submodules of … You get the idea.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;But I can just implement the same recursion with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git submodule foreach --recursive 'git config ...'&lt;/code&gt;.&lt;/p&gt;

  &lt;p&gt;– the same unknown &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; user&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes. Almost. Please note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git submodule foreach&lt;/code&gt; evaluates an arbitrary
shell command in each &lt;strong&gt;checked out submodule&lt;/strong&gt; &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. This means you have to run
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git submodule update --init --recursive&lt;/code&gt; first. Which will connect and clone
to the originally referenced repository. This was not possible in my environment.
Also, looping over all submodules in a shell script, without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git submodule foreach&lt;/code&gt;,
is not trivial &lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. Hence, I had to implement &lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite/blob/3d52c605330bebe48c5373fcb5b13dfe8e2264c0/git-submodule-url-rewrite#L109&quot;&gt;a looping mechanism&lt;/a&gt; which does
not rely on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git submodule foreach&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By open sourcing &lt;a href=&quot;https://github.com/Enteee/git-submodule-url-rewrite&quot;&gt;git-submodule-url-rewrite&lt;/a&gt;, I do hope I can provide functionality
which maybe does help others. If you find a bug or have a feature request, please
open an issue on GitHub. All comments and thoughts are welcome here on this page.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;From the &lt;a href=&quot;https://git-scm.com/docs/git-submodule#Documentation/git-submodule.txt-foreach--recursiveltcommandgt&quot;&gt;manpage&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;See: &lt;a href=&quot;https://stackoverflow.com/questions/12641469/list-submodules-in-a-git-repository/56912913#56912913&quot;&gt;this answer on StackOverflow&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Maybe I should implement a command that does just that at some point. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="git-submodule-url-rewrite" /><category term="git-sync-mirror" /><summary type="html">With git-sync-mirror migrating or mirroring a git repository is a piece of cake. But when the migrated repository contains submodules additional steps are required.</summary></entry><entry><title type="html">Citation Needed</title><link href="https://duckpond.ch/citation%20needed/2019/05/27/citation-needed.html" rel="alternate" type="text/html" title="Citation Needed" /><published>2019-05-27T00:00:00+02:00</published><updated>2019-05-27T00:00:00+02:00</updated><id>https://duckpond.ch/citation%20needed/2019/05/27/citation-needed</id><content type="html" xml:base="https://duckpond.ch/citation%20needed/2019/05/27/citation-needed.html">&lt;p&gt;&lt;img src=&quot;/static/posts/citation-needed/xkcd_protester.png&quot; alt=&quot;citation-needed&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/minimaxir/big-list-of-naughty-strings&quot;&gt;Big List of Naughty Strings&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pruned.blogspot.com/2012/01/gardens-as-crypto-water-computers.html&quot;&gt;Gardens as Crypto-Water-Computers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://script-8.github.io/&quot;&gt;SCRIPT-8 is a fantasy computer for making, sharing, and playing tiny retro-looking games&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://books.google.com/ngrams&quot;&gt;Google Books - Ngram Viewer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://aiweirdness.com/&quot;&gt;AIweirdness - Training neural networks to write unintentional humor as they struggle to imitate human datasets&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://hasthelargehadroncolliderdestroyedtheworldyet.com/&quot;&gt;Has the Large Hadron Collider Destroyed the World yet?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://spectrum.ieee.org/aerospace/aviation/how-the-boeing-737-max-disaster-looks-to-a-software-developer&quot;&gt;How the Boeing 737 Max Disaster Looks to a Software Developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.redhat.com/en/blog/secure-your-containers-one-weird-trick&quot;&gt;Secure Your Containers with this One Weird Trick&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/&quot;&gt;How a Kalman filter works, in pictures&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://madaan.github.io/init/&quot;&gt;Notes on Weight Initialization for Deep Neural Networks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="citation needed" /><summary type="html"></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">TOFU for Containers</title><link href="https://duckpond.ch/tls-tofu/git-sync-mirror/kamikaze/security/2019/05/07/tofu-for-containers.html" rel="alternate" type="text/html" title="TOFU for Containers" /><published>2019-05-07T00:00:00+02:00</published><updated>2019-05-07T00:00:00+02:00</updated><id>https://duckpond.ch/tls-tofu/git-sync-mirror/kamikaze/security/2019/05/07/tofu-for-containers</id><content type="html" xml:base="https://duckpond.ch/tls-tofu/git-sync-mirror/kamikaze/security/2019/05/07/tofu-for-containers.html">&lt;p&gt;Running containers behind a HTTPS scanning proxy can be tricky. The proxy will
send a certificate which is not trusted by the container with the effect of
breaking the internet.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/posts/tofu-for-containers/broken-internet.gif&quot; alt=&quot;broken internet&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are three possible ways to make the internet work again:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Disable SSL/TLS certificate chain verification&lt;/li&gt;
  &lt;li&gt;Forward and install the scanning certificate&lt;/li&gt;
  &lt;li&gt;Implement Trust On First Use (TOFU)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case you decide to disable certificate chain verification, I hope your code
reviewing fellow just silently gets up from his desk and punches you in the
face. Because you deserve it. &lt;strong&gt;NEVER EVER DO THIS!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, doing the right thing means forwarding and installing the
certificate inside the container. In order to achieve this there are steps
needed by the image maintainer as well as the poor soul running the container.
Wouldn’t it be nice if you didn’t need to worry about certificates when
deploying a container?&lt;/p&gt;

&lt;p&gt;TOFU can be a good trade-off between security and usability. The idea is simple:
Early on we try to reach out to the internet and simply trust every certificate
that we get in response. TOFU then caches those certificate and ensures that
subsequent connections are secure.&lt;/p&gt;

&lt;p&gt;Well, yes, this is not perfectly secure as well. An attacker could make you
trust a certificate if they are able to intercept the very first connection
attempt made by TOFU. But in practice this deemed to be quite difficult.
Especially for long running containers. Also there are other protocols which
implement TOFU successful.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh duckpond.ch
The authenticity of host &lt;span class=&quot;s1&quot;&gt;'[duckpond.ch]:7410 ([71.19.149.209]:7410)'&lt;/span&gt; can&lt;span class=&quot;s1&quot;&gt;'t be established.
ECDSA key fingerprint is SHA256:H28klEV+VMEwPst7POeHYzAGUrvfb15SRwZ/RnqhtDI.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;duckpond.ch]:7410,[71.19.149.209]:7410&lt;span class=&quot;s1&quot;&gt;' (ECDSA) to the list of known hosts.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;tofu-inside-a-container&quot;&gt;TOFU Inside a Container&lt;/h2&gt;

&lt;p&gt;What we are trying to do is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;TLS handshake with the server (or a proxy) and download all certificates.&lt;/li&gt;
  &lt;li&gt;Install the certificates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first step is easy, &lt;a href=&quot;https://www.openssl.org/docs/man1.0.2/man1/openssl-s_client.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl s_client&lt;/code&gt;&lt;/a&gt;
does most of the heavy TLS lifting for us. I am not a massive fan of having
OpenSSL installed in containers. But in this case this is probably the right
approach. Most applications build on top of that library anyways. Using OpenSSL
we implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls-tofu.sh&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env sh&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exuo&lt;/span&gt; pipefail
openssl s_client &lt;span class=&quot;nt&quot;&gt;-showcerts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; 2&amp;gt;/dev/null &amp;lt; /dev/null &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
| &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/-----BEGIN/,/-----END/p'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we can easily print all the certificates sent to us either by the server or
a transparent proxy:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./tls-tofu.sh &lt;span class=&quot;nt&quot;&gt;-connect&lt;/span&gt; duckpond.ch:443 &lt;span class=&quot;nt&quot;&gt;-servername&lt;/span&gt; duckpond.ch
+ openssl s_client &lt;span class=&quot;nt&quot;&gt;-showcerts&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-connect&lt;/span&gt; duckpond.ch:443 &lt;span class=&quot;nt&quot;&gt;-servername&lt;/span&gt; duckpond.ch
+ &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; /-----BEGIN/,/-----END/p
&lt;span class=&quot;nt&quot;&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIGcjCCBVqgAwIBAgISA5P3qUe9JqxFqESJKhzHY/neMA0GCSqGSIb3DQEBCwUA
...
QraNjGOLb8+mDxQItPL5EymbmdPrdA&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-----END&lt;/span&gt; CERTIFICATE-----
&lt;span class=&quot;nt&quot;&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
...
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-----END&lt;/span&gt; CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to make the system trust those certificates, we need to store them in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/ssl/certs/ca-certificates.crt&lt;/code&gt;. But this file is only writable by root and
we hopefully don’t have those permissions when running scripts inside a
container.&lt;/p&gt;

&lt;p&gt;This problem is solvable with &lt;a href=&quot;https://github.com/Enteee/kamikaze#readme&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kamikaze&lt;/code&gt;&lt;/a&gt;. &lt;a href=&quot;https://github.com/Enteee/kamikaze#readme&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kamikaze&lt;/code&gt;&lt;/a&gt; is a simple setuid
binary which allows us to run a command as root once. Using the power of
&lt;a href=&quot;https://github.com/Enteee/kamikaze#readme&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kamikaze&lt;/code&gt;&lt;/a&gt; we can now add trusted certificates.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env sh&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exuo&lt;/span&gt; pipefail
openssl s_client &lt;span class=&quot;nt&quot;&gt;-showcerts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; 2&amp;gt;/dev/null &amp;lt; /dev/null &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
| &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/-----BEGIN/,/-----END/p'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
| /kamikaze &lt;span class=&quot;nb&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; /etc/ssl/certs/ca-certificates.crt &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;enteeetls-tofu-container-image&quot;&gt;&lt;a href=&quot;https://hub.docker.com/r/enteee/tls-tofu&quot;&gt;enteee/tls-tofu&lt;/a&gt; Container Image&lt;/h3&gt;

&lt;p&gt;Based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls-tofu.sh&lt;/code&gt;-idea, I did create the &lt;a href=&quot;https://github.com/Enteee/tls-tofu&quot;&gt;tls-tofu GitHub project&lt;/a&gt;
and published &lt;a href=&quot;https://hub.docker.com/r/enteee/tls-tofu&quot;&gt;enteee/tls-tofu&lt;/a&gt; container images. Building and running your own
TLS-TOFU enabled image is as simple as:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker build &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; tls-tofu-enabled-image - &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  FROM enteee/tls-tofu
  # IMPORTANT: Drop privileges
  USER nobody

  # Run the application
  CMD [&quot;echo&quot;, &quot;Hello World!&quot;]
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker run &lt;span class=&quot;nt&quot;&gt;-ti&lt;/span&gt; tls-tofu-enabled-image
Hello World!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In a more elaborate example, we can use the just built image to run a container
which trusts the &lt;a href=&quot;https://self-signed.badssl.com&quot;&gt;self-signed BadSSL&lt;/a&gt; certificate.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-ti&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TLS_TOFU_HOST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;self-signed.badssl.com&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  tls-tofu-enabled-image &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  curl https://self-signed.badssl.com/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This should first print certificate information:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONNECTED(00000003)
---
Certificate chain
 0 s:C = US, ST = California, L = San Francisco, O = BadSSL, CN = *.badssl.com
   i:C = US, ST = California, L = San Francisco, O = BadSSL, CN = *.badssl.com
-----BEGIN CERTIFICATE-----
MIIDeTCCAmGgAwIBAgIJAPlgiuOcJ/T1MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
c2NvMQ8wDQYDVQQKDAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTAeFw0x
ODA4MTUxNTIxNTNaFw0yMDA4MTQxNTIxNTNaMGIxCzAJBgNVBAYTAlVTMRMwEQYD
VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQK
DAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMIE7PiM7gTCs9hQ1XBYzJMY61yoaEmwIrX5lZ6xKyx2
PmzAS2BMTOqytMAPgLaw+XLJhgL5XEFdEyt/ccRLvOmULlA3pmccYYz2QULFRtMW
hyefdOsKnRFSJiFzbIRMeVXk0WvoBj1IFVKtsyjbqv9u/2CVSndrOfEk0TG23U3A
xPxTuW1CrbV8/q71FdIzSOciccfCFHpsKOo3St/qbLVytH5aohbcabFXRNsKEqve
ww9HdFxBIuGa+RuT5q0iBikusbpJHAwnnqP7i/dAcgCskgjZjFeEU4EFy+b+a1SY
QCeFxxC7c3DvaRhBB0VVfPlkPz0sw6l865MaTIbRyoUCAwEAAaMyMDAwCQYDVR0T
BAIwADAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wDQYJKoZI
hvcNAQELBQADggEBAKr7JtZHTDuYs8/vGDFrtXb+dkjdNsZEIgyVh4vWZtLOANtO
39wM/LwGXUSjonEsYJabJgYpRdRSex41f78QfnARJona7fkcc1aHci7jdrzsxaNJ
iCc4G49ahgJ1NEIFmRNeEYlKYNNFeyGT6wxkLaV9AnC45MHlaumQyrRJwuXCQH/i
16Wk/qDtsu2nw6t+13OqwGfxR9krxDikVFO0YqgSMhqPmufz/6nY6uaXuOqzGv+P
rjJZDqCoRmVMqrISIUALWGCF3yasrViM6owIEhtN71UwrFZYYOeZ9nw2wvRK210z
c8LlWjgG56wRkLrq/mSINsQ3xmChO1PsBAeSHDU=
-----END CERTIFICATE-----
---
Server certificate
subject=C = US, ST = California, L = San Francisco, O = BadSSL, CN = *.badssl.com

issuer=C = US, ST = California, L = San Francisco, O = BadSSL, CN = *.badssl.com

---
No client certificate CA names sent
Peer signing digest: SHA512
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1599 bytes and written 450 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 315114D851A9EE6B159B59BE5CF639C36FD4F6F40F8F53B8601FE9305E3DF8F1
    Session-ID-ctx: 
    Master-Key: A5C589C7A5739E39046574BA5AEC4D130F44E489DF77C7D627F35DF91A75E37FBFC3C5F20907A816692F8E46A765A775
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 8a a2 1a 77 48 87 f2 35-55 9c a0 3f 71 4d 37 a7   ...wH..5U..?qM7.
    0010 - 64 49 98 47 22 64 dc 00-99 89 2d ed 98 41 49 09   dI.G&quot;d....-..AI.
    0020 - fc b8 1c f2 6e 08 5e 0d-28 9e 44 2f 3e df 34 9e   ....n.^.(.D/&amp;gt;.4.
    0030 - fa a5 f0 ca 1e dc b5 af-c0 54 cc 63 e8 1a a2 6d   .........T.c...m
    0040 - 41 61 e5 21 f9 82 68 39-4e 2f d6 9f 5e 21 7b 8f   Aa.!..h9N/..^!{.
    0050 - 06 9e 16 76 38 b6 08 12-ab ed ee 5b e5 e7 a5 eb   ...v8......[....
    0060 - 66 97 8b a8 fa fc d0 1f-aa 4d 53 6a 6f d6 07 df   f........MSjo...
    0070 - 5e 70 fe 72 18 82 38 c8-c8 c4 10 e5 05 b5 5f c6   ^p.r..8......._.
    0080 - ff cb d0 01 18 a8 66 9f-01 1c bd 2c 99 1c cc 14   ......f....,....
    0090 - 3d 37 c9 bb 4b 1d 1b 1a-ba 7d fd 15 19 e2 3a b5   =7..K....}....:.
    00a0 - 33 e9 68 d9 fa 20 55 ff-2f f1 10 a1 1c 88 e7 8f   3.h.. U./.......
    00b0 - 6c 72 e9 b6 bc 12 45 36-a7 d4 80 92 ea 82 34 b6   lr....E6......4.
    00c0 - fc bd ab 91 b8 8f aa 5a-a6 55 95 ae 23 5b b5 7b   .......Z.U..#[.{

    Start Time: 1557231372
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: no
---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then the &lt;a href=&quot;https://self-signed.badssl.com&quot;&gt;self-signed BadSSL&lt;/a&gt; page.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewport&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width=device-width, initial-scale=1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;shortcut icon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/icons/favicon-red.ico&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apple-touch-icon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/icons/icon-red.png&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;self-signed.badssl.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/style.css&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;font-size: 12vw;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    self-signed.&lt;span class=&quot;nt&quot;&gt;&amp;lt;br&amp;gt;&lt;/span&gt;badssl.com
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let us make the last step, and simulate the HTTPS scanning proxy scenario. For
this we first start &lt;a href=&quot;https://hub.docker.com/r/mitmproxy/mitmproxy&quot;&gt;mitmproxy/mitmproxy&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-ti&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; mitmproxy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  mitmproxy/mitmproxy mitmdump
Proxy server listening at http://&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;:8080
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then connect to &lt;a href=&quot;https://duckpond.ch&quot;&gt;duckpond.ch&lt;/a&gt; through the proxy:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-ti&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--link&lt;/span&gt; mitmproxy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TLS_TOFU_HOST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mitmproxy&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TLS_TOFU_PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;8080&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TLS_TOFU_S_CLIENT_ARGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-servername duckpond.ch&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  tls-tofu-enabled-image &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  curl &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; http://mitmproxy:8080 https://duckpond.ch
CONNECTED&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;00000003&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
Certificate chain
 0 s:
   i:CN &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy, O &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy
&lt;span class=&quot;nt&quot;&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIICwjCCAaqgAwIBAgIGDinF743TMA0GCSqGSIb3DQEBCwUAMCgxEjAQBgNVBAMM
CW1pdG1wcm94eTESMBAGA1UECgwJbWl0bXByb3h5MB4XDTE5MDUwNTE5MzUyNVoX
DTIxMDUwNjE5MzUyNVowADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AJh4sql+hJEvAstUd+gxcWIxy+vN9MC78WbT5Ox2oB7YO0r+3CCkeki3SehEBtHj
5MolxsspJipxF3gHN9el36vcN9dbj1sIGGukT7az7u8M0al4cVnP3Dyt+Fhb1Y2K
7UEiA2WibWEzeAlNbxASdHt7xqHTQfHMDBr+l1odg7eRe2QquFuLI8PhhSw1XSXw
yxY+A6/xxwkMcqU9s9pgQhP51qzAlY86SgZqHtgzCn7K0p3gCO+H2jhQu5R45h3X
1mJY6iRHf5WnEAPe/kdyjkme+8TsgeiZ13y7o+K9a6XwHQD1jTZFE9GW27cyVst/
0RHt6R+50DRSDJ1onPHUCGcCAwEAAaMaMBgwFgYDVR0RBA8wDYILZHVja3BvbmQu
Y2gwDQYJKoZIhvcNAQELBQADggEBAC7XwulSPQPHmESiQWiWjuDtyHu85lsGBvxS
C31zJRva8NuPlzIi4w3wQXXkuH+skNvTZ81WJxfxw+WjwcglYfmKG6yQVtZ0tdVk
uYUVq2Okn8oBSAI57XNaKqCGTioQhk7Mk/P93Y49UuNF/AC47IvxOWkj9QFm/wtu
+zRfko5VC7W7f1ji5tgnJduD46lR2hen12Px3tWyRWc1ECFjXZ4lBT1xUZIloPjQ
5PGDEIrg0zTmr3ZqUBGBKEjF1zrzXC3myMStn5pn0mBHgH0Fgrooswad0UzLAoDL
ECDdplotyGL9Wc1/CzLHsvsw8lvGCimi0ul0QOn9VQtnLKF/w/0&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-----END&lt;/span&gt; CERTIFICATE-----
 1 s:CN &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy, O &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy
   i:CN &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy, O &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy
&lt;span class=&quot;nt&quot;&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIDoTCCAomgAwIBAgIGDinFhnrGMA0GCSqGSIb3DQEBCwUAMCgxEjAQBgNVBAMM
CW1pdG1wcm94eTESMBAGA1UECgwJbWl0bXByb3h5MB4XDTE5MDUwNTE5MjM1N1oX
DTIyMDUwNjE5MjM1N1owKDESMBAGA1UEAwwJbWl0bXByb3h5MRIwEAYDVQQKDAlt
aXRtcHJveHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYeLKpfoSR
LwLLVHfoMXFiMcvrzfTAu/Fm0+TsdqAe2DtK/twgpHpIt0noRAbR4+TKJcbLKSYq
cRd4BzfXpd+r3DfXW49bCBhrpE+2s+7vDNGpeHFZz9w8rfhYW9WNiu1BIgNlom1h
M3gJTW8QEnR7e8ah00HxzAwa/pdaHYO3kXtkKrhbiyPD4YUsNV0l8MsWPgOv8ccJ
DHKlPbPaYEIT+daswJWPOkoGah7YMwp+ytKd4Ajvh9o4ULuUeOYd19ZiWOokR3+V
pxAD3v5Hco5JnvvE7IHomdd8u6PivWul8B0A9Y02RRPRltu3MlbLf9ER7ekfudA0
UgydaJzx1AhnAgMBAAGjgdAwgc0wDwYDVR0TAQH/BAUwAwEB/zARBglghkgBhvhC
AQEEBAMCAgQweAYDVR0lBHEwbwYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcD
BAYIKwYBBQUHAwgGCisGAQQBgjcCARUGCisGAQQBgjcCARYGCisGAQQBgjcKAwEG
CisGAQQBgjcKAwMGCisGAQQBgjcKAwQGCWCGSAGG+EIEATAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0OBBYEFAHmNa8e++bQ4Nr1XzdqPek/tp4eMA0GCSqGSIb3DQEBCwUA
A4IBAQAxC9SNuBLjVhSY2ilJRQc21bv/WoJAcmGtxLxhXn43RwnYsNxKDmS3bRwj
CbKOX2mhV7zqKRDvrA0iRoWndGwfQodnc9eairo3LSLCqg8+vFkwgaRQICyCkv18
6ElxxHVQinNrd4XyaStqrweqK+gbB1NymR/87nOiRXzK9utGjESifaUNl97fymTg
LL8BwQH5iHHlU5ud14AKkwr14QWrTzTbyP/McxLo/KfTjVCl30YO2onzMpwu2oW5
cRRfx96ajPoKwtVFBJTX/hdBoqkovNFvRSITMU3VHEKRfoIG2OJRsA1dl7ezb6Ao
Uf30567z+pXa2Dp8YOnUA3ARWBCu
&lt;span class=&quot;nt&quot;&gt;-----END&lt;/span&gt; CERTIFICATE-----
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
Server certificate
&lt;span class=&quot;nv&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;CN &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy, O &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mitmproxy

&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
No client certificate CA names sent
Peer signing digest: SHA512
Peer signature &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;: RSA
Server Temp Key: ECDH, P-256, 256 bits
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
SSL handshake has &lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;2316 bytes and written 439 bytes
Verification error: self signed certificate &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;certificate chain
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: D15B53E637A92CF86580E51B8053626F50F3256138E5F5A55BAD5B37A903CE67
    Session-ID-ctx: 
    Master-Key: 72C569F0C2C5A4EF5EB666DE0CF7B908411854010BA303F53DB9199E7872F68EE0E524E780DDF003819CA8DFBB3A1C3E
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;seconds&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    TLS session ticket:
    0000 - 24 fe cb 42 a8 12 eb b3-35 29 6a 12 11 79 af ba   &lt;span class=&quot;nv&quot;&gt;$.&lt;/span&gt;.B....5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;j..y..
    0010 - 63 50 85 58 bc 6f a7 5a-c5 5f 9b 11 21 97 23 ed   cP.X.o.Z._..!.#.
    0020 - 54 9c 99 0c 5a 26 a8 75-21 06 b8 26 e1 57 f7 f0   T...Z&amp;amp;.u!..&amp;amp;.W..
    0030 - f1 a0 c1 5f a3 d8 25 36-4f de cc 6d 76 c2 d9 89   ..._..%6O..mv...
    0040 - 99 46 ec 64 8d d8 c1 41-04 58 4c 7a bf 8f 1c a8   .F.d...A.XLz....
    0050 - 8e b4 42 bd 2b 73 03 07-05 26 36 66 66 53 ac 63   ..B.+s...&amp;amp;6ffS.c
    0060 - 52 98 c9 31 cb ea 5d c4-b6 76 4a d7 c4 79 1c 4c   R..1..]..vJ..y.L
    0070 - f1 3b 76 04 ed 15 07 ff-d0 2d c7 92 6c d8 56 f9   .&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;v......-..l.V.
    0080 - 94 19 5a 61 9b 58 db 68-1d 2e 0c 87 fb f9 64 38   ..Za.X.h......d8
    0090 - 56 5a 8f 4e 2e a9 31 f6-ac db c9 30 51 5e 84 00   VZ.N..1....0Q^..
    00a0 - 29 &lt;span class=&quot;nb&quot;&gt;fc &lt;/span&gt;48 d9 70 f3 87 3f-07 b9 11 9b 2a 7a 72 ce   &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.H.p..?....&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;zr.

    Start Time: 1557257748
    Timeout   : 7200 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;sec&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Verify &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;code: 19 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;self signed certificate &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;certificate chain&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Extended master secret: no
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
&amp;lt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;&lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From the &lt;a href=&quot;https://hub.docker.com/r/mitmproxy/mitmproxy&quot;&gt;mitmproxy/mitmproxy&lt;/a&gt; output we get that the request did actually go
through the proxy. All this without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; complaining about certificate
verification issues.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;172.17.0.3:47120: clientconnect
172.17.0.3:47120: Client Handshake failed. The client may not trust the proxy's certificate for mitmproxy.
172.17.0.3:47120: clientdisconnect
172.17.0.3:47122: clientconnect
172.17.0.3:47122: Client Handshake failed. The client may not trust the proxy's certificate for mitmproxy.
172.17.0.3:47122: clientdisconnect
172.17.0.3:47124: clientconnect
172.17.0.3:47124: GET https://duckpond.ch/
               &amp;lt;&amp;lt; 200 OK 20.86k
172.17.0.3:47124: clientdisconnect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Mission accomplished.&lt;/p&gt;

&lt;h3 id=&quot;a-real-world-example-enteeegit-sync-mirror&quot;&gt;A Real World Example: &lt;a href=&quot;https://hub.docker.com/r/enteee/git-sync-mirror&quot;&gt;enteee/git-sync-mirror&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/r/enteee/git-sync-mirror&quot;&gt;enteee/git-sync-mirror&lt;/a&gt; is a simple container image for synchronizing a git
mirror. Inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; it installs a run script (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/run.sh&lt;/code&gt;) and
overwrites the default command. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENTRYPOINT&lt;/code&gt; is still provided by
&lt;a href=&quot;https://hub.docker.com/r/enteee/tls-tofu&quot;&gt;enteee/tls-tofu&lt;/a&gt; which does all the TOFU magic.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM enteee/tls-tofu:alpine-latest

&lt;span class=&quot;c&quot;&gt;## Disable TLS-TOFU by default&lt;/span&gt;
ENV TLS_TOFU &lt;span class=&quot;nb&quot;&gt;false

&lt;/span&gt;RUN &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exuo&lt;/span&gt; pipefail &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    git &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; addgroup &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; 1000 &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; git &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; 1000 &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; git &lt;span class=&quot;nt&quot;&gt;-G&lt;/span&gt; git

USER git:git

COPY run.sh /run.sh
CMD &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/run.sh&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When running this container with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e TLS_TOFU=true&lt;/code&gt; &lt;a href=&quot;https://hub.docker.com/r/enteee/git-sync-mirror&quot;&gt;enteee/git-sync-mirror&lt;/a&gt;
silently does TLS-TOFU. And if we additionally specify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e TLS_TOFU_DEBUG=true&lt;/code&gt;,
we can see what is happening under to hood.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TLS_TOFU&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TLS_TOFU_DEBUG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  git-sync-mirror
+ &lt;span class=&quot;s1&quot;&gt;'['&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'='&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
+ openssl s_client &lt;span class=&quot;nt&quot;&gt;-verify_return_error&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-connect&lt;/span&gt; google.com:443 &lt;span class=&quot;nt&quot;&gt;-servername&lt;/span&gt; google.com
+ destroy_kamikaze
+ &lt;span class=&quot;s1&quot;&gt;'['&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; /kamikaze &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
+ /kamikaze &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
+ &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;sh &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; /run.sh
/run.sh: line 5: SRC_REPO: Missing &lt;span class=&quot;nb&quot;&gt;source &lt;/span&gt;repository
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This fails because we didn’t specify the mandatory &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SRC_REPO&lt;/code&gt; for &lt;a href=&quot;https://hub.docker.com/r/enteee/git-sync-mirror&quot;&gt;enteee/git-sync-mirror&lt;/a&gt;.
Nevertheless, we can still see &lt;a href=&quot;https://hub.docker.com/r/enteee/tls-tofu&quot;&gt;enteee/tls-tofu&lt;/a&gt; connecting to google.com. But
since all certificates are valid it does not add any new trusted ones. After
this &lt;a href=&quot;https://github.com/Enteee/kamikaze#readme&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kamikaze&lt;/code&gt;&lt;/a&gt; is destroyed and control is being handed over to &lt;a href=&quot;https://hub.docker.com/r/enteee/git-sync-mirror&quot;&gt;enteee/git-sync-mirror&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;caveat-restart-policies&quot;&gt;Caveat: Restart Policies&lt;/h3&gt;

&lt;p&gt;If an attacker is in control of your network, it is very likely that they can
also crash applications you are running in containers. If you restart the
container in this case, your application will re-TOFU. This means an attacker
can make the container trust every certificate they want. This is very, very
bad. Always disable automatic container restart with TLS-TOFU exactly for this
reason.&lt;/p&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/r/enteee/tls-tofu&quot;&gt;enteee/tls-tofu&lt;/a&gt; implements a simple, yet powerful base image which allows
containers to run in environments where something is tampering with the
Internet’s public key infrastructure. I generally disagree that HTTPS scanning
proxies are leveraging security in a network. They create a single point of
failure and considerably weaken a secure protocol design to protect privacy and
confidentiality.&lt;/p&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="tls-tofu" /><category term="git-sync-mirror" /><category term="kamikaze" /><category term="security" /><summary type="html">Running containers behind a HTTPS scanning proxy can be tricky. The proxy will send a certificate which is not trusted by the container with the effect of breaking the internet.</summary></entry><entry><title type="html">JavaScript: this</title><link href="https://duckpond.ch/javascript/web/2019/03/20/javascript-this.html" rel="alternate" type="text/html" title="JavaScript: this" /><published>2019-03-20T00:00:00+01:00</published><updated>2019-03-20T00:00:00+01:00</updated><id>https://duckpond.ch/javascript/web/2019/03/20/javascript-this</id><content type="html" xml:base="https://duckpond.ch/javascript/web/2019/03/20/javascript-this.html">&lt;p&gt;&lt;img src=&quot;/static/posts/javascript-this/fuck-this.jpg&quot; alt=&quot;Fuck-this.js&quot; /&gt;
&lt;em&gt;Fuck-this.js: A reddit post in &lt;a href=&quot;https://www.reddit.com/r/ProgrammerHumor/comments/b252lc/javascript_pain/&quot;&gt;r/ProgrammerHumor&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The snippet below tests if node.js &lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; forwards the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt;-object in various scenarios.
I wrote it to solve &lt;a href=&quot;https://stackoverflow.com/questions/40135510/set-this-for-required-arrow-functions&quot;&gt;this issue&lt;/a&gt; when refactoring &lt;a href=&quot;https://github.com/Enteee/FluentFlow&quot;&gt;FluentFlow&lt;/a&gt;.
If you can think of any unintuitive handling of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt;-object in JavaScript please share a snippet in the comment section.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 * Helper to require a module from a string
 */&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requireFromString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Module&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;__filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Body:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))();&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)})();&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Functions:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;f1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)});&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                               &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                                      &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;f2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                               &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                                      &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;f3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requireFromString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;module.exports = (() =&amp;gt; {console.log(this.a === 1)});&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                               &lt;span class=&quot;c1&quot;&gt;// false [1]&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                                      &lt;span class=&quot;c1&quot;&gt;// false [2]&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;f4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requireFromString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;module.exports = function(){ console.log(this.a === 1) };&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                               &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;f4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                                      &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Classes:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                     &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                            &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;c2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requireFromString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;module.exports = class c2 { log(){ console.log(this.a === 1) } };&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                     &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                            &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requireFromString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;module.exports = class c4 { constructor(log){ this.log = log } };&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                                 &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the documentation for &lt;a href=&quot;https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/this&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Pfeilfunktionen#No_binding_of_this&quot;&gt;arrow functions&lt;/a&gt; I can explain all cases except the ones labelled with [1] and [2]. But thanks to the excellent answer by &lt;a href=&quot;https://stackoverflow.com/users/496606/aaronofleonard&quot;&gt;aaronofleonard&lt;/a&gt; I now understand why:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The reason is because “fat arrow functions” always take their this lexically, from the surrounding code. They cannot have their this changed with call, bind, etc. Run this code as an example:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;face&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stuff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;object2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;foot&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; and such&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; and such&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;You will only see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;face&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;such&lt;/code&gt;.
So, as far as why that doesn’t work in the case of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f3&lt;/code&gt;, it’s because it’s a self-contained module being required. Therefore, it’s base-level arrow functions will only use the this in the module, they cannot be bound with bind, call, etc etc as discussed. In order to use call on them, they must be regular functions, not arrow functions.
What does “lexical this” mean? It basically works the same as a closure. Take this code for example:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// fileA.js&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;im here!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;infoPrintingFunctionA&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;infoPrintingFunctionB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./fileB&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;infoPrintingFunctionA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;infoPrintingFunctionB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// fileB.js&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;What will be the result? An error, info is not defined. Why? Because the accessible variables (the scope) of a function only includes the variables that are available where the function is defined. Therefore, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;infoPrintingFunctionA&lt;/code&gt; has access to info because info exists in the scope where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;infoPrintingFunctionA&lt;/code&gt; is defined.
However, even though &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;infoPrintingFunctionB&lt;/code&gt; is being called in the same scope, it was not defined in the same scope. Therefore, it cannot access variables from the calling scope.
But this all has to do with the variables and closures; what about this and arrow functions?
The this of arrow functions works the same as the closure of other variables in functions. Basically, an arrow function is just saying to include this in the closure that is created. And in the same way you couldn’t expect the variables of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fileA&lt;/code&gt; to be accessible to the functions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fileB&lt;/code&gt;, you can’t expect the this of the calling module (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fileA&lt;/code&gt;) to be able to be referenced from the body of the called module (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fileB&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;TLDR: How do we define “surrounding code”, in the expression “lexical this is taken from the surrounding code?” The surrounding code is the scope where the function is defined, not necessarily where it is called.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Tested for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v6.8.1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v8.15.0&lt;/code&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="javascript" /><category term="web" /><summary type="html">Fuck-this.js: A reddit post in r/ProgrammerHumor</summary></entry><entry><title type="html">Pdml2flow 5.0</title><link href="https://duckpond.ch/pdml2flow/networking/python/2018/11/12/pdml2flow-5.0.html" rel="alternate" type="text/html" title="Pdml2flow 5.0" /><published>2018-11-12T00:00:00+01:00</published><updated>2018-11-12T00:00:00+01:00</updated><id>https://duckpond.ch/pdml2flow/networking/python/2018/11/12/pdml2flow-5.0</id><content type="html" xml:base="https://duckpond.ch/pdml2flow/networking/python/2018/11/12/pdml2flow-5.0.html">&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2flow&lt;/code&gt; version 5.0 was released. New key features are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Plugin interface version 2: cli integration&lt;/li&gt;
  &lt;li&gt;Replaced &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2xml&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2json&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2frame&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;98% test coverage&lt;/li&gt;
  &lt;li&gt;Python 3.6 support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upgrade using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; is as simple as:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;pip &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--upgrade&lt;/span&gt; pdml2flow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;plugin-interface-version-2&quot;&gt;Plugin Interface Version 2&lt;/h2&gt;

&lt;p&gt;Before 5.0 plugins were configure with environment variables. Now, plugins are loaded and configured from the command line. In order to support multiple output sink all the flow writing logic was moved into plugin as well. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2flow&lt;/code&gt; with the JSON output plugin:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ pdml2flow +json '-h'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The argument after the plugin invocation is passed straight to the plugin. Which means that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-h&lt;/code&gt; from the example above is passed to the JSON plugin. To support this, the plugin interface was changed. Now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__&lt;/code&gt; will be passed all arguments (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*args&lt;/code&gt;) from the command line. In this case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ '-h' ]&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a/pdml2flow/plugin.py b/pdml2flow/plugin.py
index db016a3..06f7425 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/pdml2flow/plugin.py
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/pdml2flow/plugin.py
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -1,7 +1,20 @@&lt;/span&gt;
 # vim: set fenc=utf8 ts=4 sw=4 et :
 
&lt;span class=&quot;gd&quot;&gt;-class Plugin1(object):
-    &quot;&quot;&quot;Version 1 plugin interface.&quot;&quot;&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+class Plugin2(object): # pragma: no cover
+    &quot;&quot;&quot;Version 2 plugin interface.&quot;&quot;&quot;
+
+    @staticmethod
+    def help():
+        &quot;&quot;&quot;Return a help string.&quot;&quot;&quot;
+        pass
+
+    def __init__(self, *args):
+        &quot;&quot;&quot;Called once during startup.&quot;&quot;&quot;
+        pass
+
+    def __deinit__(self):
+        &quot;&quot;&quot;Called once during shutdown.&quot;&quot;&quot;
+        pass
&lt;/span&gt; 
     def flow_new(self, flow, frame):
         &quot;&quot;&quot;Called every time a new flow is opened.&quot;&quot;&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This change alone is not the reason for the interface bump. The true interface break comes from a change in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flow&lt;/code&gt; class. Before 5.0 frames of the flow were accessible using a getter called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_frames()&lt;/code&gt;. This was replaced with a more pythonic getter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frames&lt;/code&gt;. Example usage:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'frame'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'time_relative'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'raw'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or as an alternative using a list:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'frame'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'time_relative'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'raw'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;replaced-pdml2xml-and-pdml2json-with-pdml2frame&quot;&gt;Replaced &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2xml&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2json&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2frame&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Since all the output was moved into plugins, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2xml&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2json&lt;/code&gt; no longer made sense. This is why I replaced those tools with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pml2frame&lt;/code&gt; which supports the same plugin interface as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2flow&lt;/code&gt;. This means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2xml&lt;/code&gt; becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pml2frame +xml&lt;/code&gt;. And for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2json&lt;/code&gt; use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdml2frame +json&lt;/code&gt; instead.&lt;/p&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="pdml2flow" /><category term="networking" /><category term="python" /><summary type="html">pdml2flow version 5.0 was released. New key features are:</summary></entry><entry><title type="html">Citation Needed</title><link href="https://duckpond.ch/citation%20needed/2018/08/10/citation-needed.html" rel="alternate" type="text/html" title="Citation Needed" /><published>2018-08-10T00:00:00+02:00</published><updated>2018-08-10T00:00:00+02:00</updated><id>https://duckpond.ch/citation%20needed/2018/08/10/citation-needed</id><content type="html" xml:base="https://duckpond.ch/citation%20needed/2018/08/10/citation-needed.html">&lt;p&gt;&lt;img src=&quot;/static/posts/citation-needed/xkcd_protester.png&quot; alt=&quot;citation-needed&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/math/comments/7qpfls/does_there_exist_a_prime_number_whose/&quot;&gt;A prime number whose binary representation looks like a giraffe&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wordsandmagic.com/2017/09/01/RMagick-2-16-0-Error-MagickWand/&quot;&gt;Can’t install RMagick 2.16.0. Can’t find MagickWand.h&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://asciinema.org/&quot;&gt;asciinema: Record and share your terminal sessions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.facebook.com/notes/protect-the-graph/pyre-fast-type-checking-for-python/2048520695388071/&quot;&gt;Pyre: Fast Type Checking for Python&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/parallel/&quot;&gt;GNU parallel: A shell tool for executing jobs in parallel using one or more computers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dnephin/dobi&quot;&gt;dobi: A build automation tool for Docker applications&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/moby/buildkit&quot;&gt;BuildKit: A toolkit for converting source code to build artifacts in an efficient, expressive and repeatable manner&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.theguardian.com/lifeandstyle/2018/apr/20/cant-sleep-insomnia-identity&quot;&gt;Can’t sleep? Tell yourself it’s not a big deal&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Chakazul/Lenia&quot;&gt;Lenia: A 2D cellular automata with continuous space, time and states&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://jdow.io/blog/2018/03/18/web-application-penetration-testing-methodology/&quot;&gt;Web application penetration testing cheat sheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="citation needed" /><summary type="html"></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" /><media:content medium="image" url="https://duckpond.ch/static/posts/citation-needed/xkcd_protester.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Maintenance</title><link href="https://duckpond.ch/web/meta/2018/03/13/maintenance.html" rel="alternate" type="text/html" title="Maintenance" /><published>2018-03-13T00:00:00+01:00</published><updated>2018-03-13T00:00:00+01:00</updated><id>https://duckpond.ch/web/meta/2018/03/13/maintenance</id><content type="html" xml:base="https://duckpond.ch/web/meta/2018/03/13/maintenance.html">&lt;p&gt;I did some blog maintenance and released a new &lt;a href=&quot;/&quot;&gt;duckpond.ch&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Migrated to &lt;a href=&quot;https://getbootstrap.com/&quot;&gt;Bootstrap v4.0&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;The list numbers overflowed. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-position: outside;&lt;/code&gt; is not a god fit for long roman numerals. I am now using &lt;a href=&quot;https://stackoverflow.com/questions/10428720/how-to-keep-indent-for-second-line-in-ordered-lists-via-css&quot;&gt;css counters&lt;/a&gt; and &lt;a href=&quot;https://github.com/Enteee/duckpond.ch/blob/master/index.html#L8&quot;&gt;liquid templating&lt;/a&gt; instead.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Enteee/duckpond.ch/blob/master/_layouts/post.html#L12&quot;&gt;The twitter button now tweets 280 characters&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Enteee/duckpond.ch/blob/master/static/css/main.css#L321&quot;&gt;The logo is now responsive&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Enteee/duckpond.ch/blob/master/_env/nginx-dockerize/Dockerfile&quot;&gt;Start nginx using dockerize&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Enteee/duckpond.ch/blob/master/_env/dehydrated/Dockerfile&quot;&gt;Request certificates using dehydrated&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Enteee/duckpond.ch/blob/master/docker-compose.yml&quot;&gt;jekyll watches now for changes and rebuilds automatically&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/wildlyinaccurate/jekyll-responsive-image&quot;&gt;jekyll-responsive-images&lt;/a&gt; with &lt;a href=&quot;https://github.com/Enteee/duckpond.ch/blob/master/_layouts/responsive_image.html&quot;&gt;this layout&lt;/a&gt; automatically creates responsive versions of images.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/digitalsparky/jekyll-minifier&quot;&gt;jekyll-minifier&lt;/a&gt; minimizes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.html&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.css&lt;/code&gt; files.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mathjax.org/cdn-shutting-down/&quot;&gt;MathJax CDN shutting down on 30. April 2017&lt;/a&gt;. Switched to Cloudflare.&lt;/li&gt;
  &lt;li&gt;Removed annotations from the &lt;a href=&quot;/404.html&quot;&gt;duck in a toilet 404&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="web" /><category term="meta" /><summary type="html">I did some blog maintenance and released a new duckpond.ch.</summary></entry><entry><title type="html">:for</title><link href="https://duckpond.ch/bash/2018/03/08/for.html" rel="alternate" type="text/html" title=":for" /><published>2018-03-08T00:00:00+01:00</published><updated>2018-03-08T00:00:00+01:00</updated><id>https://duckpond.ch/bash/2018/03/08/:for</id><content type="html" xml:base="https://duckpond.ch/bash/2018/03/08/for.html">&lt;p&gt;Syntactical sugar for your bash parallelism. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:for&lt;/code&gt; calls a function for each argument in parallel.
If at lest one call exits with a non zero exit code, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:for&lt;/code&gt; retains this code and kills all running instances.&lt;/p&gt;

&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;

&lt;script src=&quot;https://gist.github.com/Enteee/c8c11d46a95568be4d331ba58a702b62.js?file=usage&quot;&gt;&lt;/script&gt;

&lt;details&gt;&lt;summary&gt;$ ./usage&lt;/summary&gt;
&lt;p&gt;
&lt;script src=&quot;https://gist.github.com/Enteee/c8c11d46a95568be4d331ba58a702b62.js?file=usage.run&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;/details&gt;

&lt;h2 id=&quot;source&quot;&gt;Source&lt;/h2&gt;

&lt;script src=&quot;https://gist.github.com/Enteee/c8c11d46a95568be4d331ba58a702b62.js?file=:for&quot;&gt;&lt;/script&gt;</content><author><name>{&quot;site&quot;=&gt;&quot;Ente&quot;, &quot;twitter&quot;=&gt;&quot;Enteeeeeee&quot;}</name></author><category term="bash" /><summary type="html">Syntactical sugar for your bash parallelism. :for calls a function for each argument in parallel. If at lest one call exits with a non zero exit code, :for retains this code and kills all running instances.</summary></entry></feed>