<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>kilko.de - original posts</title>
  <subtitle>Posts, reviews, links, talks, and TILs by Kilian Koeltzsch (no imported social activity)</subtitle>
  <link rel="self" type="application/atom+xml" href="https://kilko.de/original.atom.xml"/>
  <link rel="alternate" type="text/html" href="https://kilko.de/feed/"/>
  <id>https://kilko.de/original.atom.xml</id>
  <updated>2026-07-01T00:00:00+00:00</updated>
  <author><name>Kilian Koeltzsch</name></author>
  <entry xml:lang="en">
    <title>Importing CSV data into SQLite</title>
    <published>2026-07-01T00:00:00+00:00</published>
    <updated>2026-07-01T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/sqlite-importing-csv-data/"/>
    <id>https://kilko.de/feed/til/sqlite-importing-csv-data/</id>
    <content type="html">&lt;p&gt;Creating a table from CSV data is very straightforward.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sqlite3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; file.sqlite&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sqlite&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .mode&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; csv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sqlite&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .import&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; data.csv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; tablename&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sqlite&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s it.&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Youtube RSS Feeds</title>
    <published>2026-03-23T00:00:00+00:00</published>
    <updated>2026-03-23T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/youtube-rss-feeds/"/>
    <id>https://kilko.de/feed/til/youtube-rss-feeds/</id>
    <content type="html">&lt;p&gt;Any channel can be subscribed to via RSS by using the following URL format:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;feeds&#x2F;videos.xml?channel_id=CHANNEL_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The easiest way to get this is to just let your RSS Reader subscribe to the channel&#x27;s main URL, it will usually figure out the feed location.&lt;&#x2F;p&gt;
&lt;p&gt;The default feed includes &lt;em&gt;all&lt;&#x2F;em&gt; videos however, this includes live videos and shorts, there&#x27;s a few different playlist feeds that can be used to filter these out, instead of using the &lt;code&gt;channel_id&lt;&#x2F;code&gt; parameter, use the &lt;code&gt;playlist_id&lt;&#x2F;code&gt; parameter like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;feeds&#x2F;videos.xml?playlist_id=PLAYLIST_ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The playlist ID is the same as the channel ID (which includes the &lt;code&gt;UC&lt;&#x2F;code&gt; prefix), but with a different prefix:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;All videos: &lt;code&gt;UU&lt;&#x2F;code&gt; + rest of channel ID&lt;&#x2F;li&gt;
&lt;li&gt;Standard videos: &lt;code&gt;UULF&lt;&#x2F;code&gt; + rest of channel ID&lt;&#x2F;li&gt;
&lt;li&gt;Shorts: &lt;code&gt;UUSH&lt;&#x2F;code&gt; + rest of channel ID&lt;&#x2F;li&gt;
&lt;li&gt;Livestreams: &lt;code&gt;UULV&lt;&#x2F;code&gt; + rest of channel ID&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For example, the feed URL for stuffmadehere&#x27;s channel would be transformed like this to get only standard videos:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;feeds&#x2F;videos.xml?channel_id=UCj1VqrHhDte54oLgPG4xpuQ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;feeds&#x2F;videos.xml?playlist_id=UULFj1VqrHhDte54oLgPG4xpuQ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Kudos to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FreshRSS&#x2F;Extensions&#x2F;issues&#x2F;234#issuecomment-2738509387&quot;&gt;github.com&#x2F;FreshRSS&#x2F;Extensions&#x2F;issues&#x2F;234#issuecomment-2738509387&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>GPTDash</title>
    <published>2025-12-29T00:00:00+00:00</published>
    <updated>2025-12-29T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/talks/gptdash-39c3/"/>
    <id>https://kilko.de/feed/talks/gptdash-39c3/</id>
    <content type="html">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.de&#x2F;@pxie&quot;&gt;Benny&lt;&#x2F;a&gt; and I have been playing our &quot;reverse turing test&quot; game — aka GPTDash — at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;datenspuren.de&#x2F;&quot;&gt;Datenspuren&lt;&#x2F;a&gt; for two years now, so we decided it&#x27;s time we took it to the big stage. We wanted to play it with like a hundred people, but didn&#x27;t expect to get the big stage™ at 39c3, hall 1 that seats like 3k people. At the prime time gameshow slot late on day 2. That was quite humbling, especially since the app wasn&#x27;t even close to ready at the time.&lt;&#x2F;p&gt;
&lt;p&gt;We had the foresight to test the hell out of the app, but were still met with a few stupid issues on-stage. We&#x27;ve been told it wasn&#x27;t half as bad watching it live, but oh wow did we have a hard time keeping everything running. The app was built to easily handle 3k concurrent websocket connections, the server was almost idling during the event, but at some point it still just decided to drop and no longer allow WS connections. HTTP worked fine. No clue what happened there, it &lt;em&gt;felt&lt;&#x2F;em&gt; like the network was just not allowing any WS packets to go through. I realize that makes no sense. I&#x27;d love to figure out what happened there some day.&lt;&#x2F;p&gt;
&lt;iframe class=&quot;video-embed&quot; src=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;39c3-gptdash-der-reverse-turing-test&#x2F;oembed&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;In hindsight it definitely was fun though. We saw around 2.5k people participating live at some point — it was open to the live stream viewers as well. I can&#x27;t remember there being a gameshow at Congress that&#x27;s directly interactive for the audience with a large crowd, maybe it was a first in that regard!&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Rotate videos</title>
    <published>2025-08-20T00:00:00+00:00</published>
    <updated>2025-08-20T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/rotate-videos/"/>
    <id>https://kilko.de/feed/til/rotate-videos/</id>
    <content type="html">&lt;p&gt;Rotate videos using ffmpeg by transposing them. The command to do so is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ffmpeg&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; input.mp4&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;vf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;transpose=1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; output.mp4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The possible rotations are the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0 = 90CounterCLockwise and Vertical Flip (default)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1 = 90Clockwise&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2 = 90CounterClockwise&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3 = 90Clockwise and Vertical Flip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Use &lt;code&gt;-vf &quot;transpose=2,transpose=2&quot;&lt;&#x2F;code&gt; for 180 degrees.&lt;&#x2F;p&gt;
&lt;p&gt;Credits go to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;9570992&#x2F;1843020&quot;&gt;stackoverflow.com&#x2F;a&#x2F;9570992&#x2F;1843020&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Keep subtitles while transcoding</title>
    <published>2025-08-20T00:00:00+00:00</published>
    <updated>2025-08-20T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/keep-subtiles-while-transcoding/"/>
    <id>https://kilko.de/feed/til/keep-subtiles-while-transcoding/</id>
    <content type="html">&lt;p&gt;To keep all existent subtitle tracks when transcoding media with ffmpeg, just pass the flag &lt;code&gt;-map 0:s -c copy&lt;&#x2F;code&gt;, e.g.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ffmpeg&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;path&#x2F;to&#x2F;media.{mkv,mp4}&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; 0:s&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; copy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
  </entry>
  <entry xml:lang="en">
    <title>Create video from image and audio</title>
    <published>2025-08-20T00:00:00+00:00</published>
    <updated>2025-08-20T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/create-video-from-image-and-audio/"/>
    <id>https://kilko.de/feed/til/create-video-from-image-and-audio/</id>
    <content type="html">&lt;p&gt;Technically something simple along the following lines would work.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ffmpeg&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; image.png&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; audio.mp3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; video.mp4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This does however create a file with just a single frame, which doesn&#x27;t really work on YouTube for example. The following creates a video with repeating frames.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ffmpeg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;loop&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;png&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; audio&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mp3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;acodec&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; copy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;shortest&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;vf&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; scale&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1280&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;720&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; video&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mp4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Credits go to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;superuser.com&#x2F;a&#x2F;1041820&#x2F;236423&quot;&gt;superuser.com&#x2F;a&#x2F;1041820&#x2F;236423&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Concatenate videos</title>
    <published>2025-08-20T00:00:00+00:00</published>
    <updated>2025-08-20T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/concatenate-videos/"/>
    <id>https://kilko.de/feed/til/concatenate-videos/</id>
    <content type="html">&lt;p&gt;Concating several video files with ffmpeg is very straightforward if they all share the same container and codec.
Add all input files to a text file listing their relative or absolute paths.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;file&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;file1.mp4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;file&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;file2.mp4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ffmpeg&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; concat&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;safe&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; input.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; copy&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; output.mp4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;-safe 0&lt;&#x2F;code&gt; can be omitted if the filepaths are relative.&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Change video container</title>
    <published>2025-08-20T00:00:00+00:00</published>
    <updated>2025-08-20T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/change-video-container/"/>
    <id>https://kilko.de/feed/til/change-video-container/</id>
    <content type="html">&lt;p&gt;Quickly change a file&#x27;s container using &lt;em&gt;magic&lt;&#x2F;em&gt; shell syntax.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ffmpeg&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; path&#x2F;to&#x2F;file.{webm,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mp4}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
  </entry>
  <entry xml:lang="en">
    <title>Decoding Unknown Data with Codable</title>
    <published>2021-09-01T00:00:00+00:00</published>
    <updated>2021-09-01T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/decoding-unknown-data-with-codable/"/>
    <id>https://kilko.de/blog/decoding-unknown-data-with-codable/</id>
    <content type="html">&lt;p&gt;A friend recently asked me how to decode the following JSON through Swift&#x27;s &lt;code&gt;Codable&lt;&#x2F;code&gt; type.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;foobar&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;option.extra&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;option.extra.more&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The problem is that only the first two keys (&lt;code&gt;a&lt;&#x2F;code&gt; and &lt;code&gt;b&lt;&#x2F;code&gt;) are guaranteed to exist, anything aside from that (here everything prefixed with &lt;code&gt;option&lt;&#x2F;code&gt;) is extra and unknown at compile-time. Unknown keys should not be discarded however, they should be decoded into a dictionary for later use, we want a type like this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Json&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; a:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; b:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; extraOptions:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s a bit unclear what type to use for &lt;code&gt;extraOptions&lt;&#x2F;code&gt;, since we don&#x27;t really know what the values are. They can be anything, as long as it&#x27;s decodable. The folks at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;flight.school&quot;&gt;Flight School&lt;&#x2F;a&gt; have a fantastic package for us to facilitate that conveniently called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;flight-school&#x2F;anycodable&quot;&gt;AnyCodable&lt;&#x2F;a&gt;, which provides type-erased wrappers for &lt;code&gt;Encodable&lt;&#x2F;code&gt;, &lt;code&gt;Decodable&lt;&#x2F;code&gt;, and &lt;code&gt;Codable&lt;&#x2F;code&gt; values. We can use that to represent the requirement of having &lt;em&gt;anything&lt;&#x2F;em&gt;, as long as it&#x27;s decodable, by defining the type as follows.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Json&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; a:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; b:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; extraOptions:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; AnyDecodable&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As it stands we&#x27;re getting the automatically synthesized implementation of &lt;code&gt;Json&lt;&#x2F;code&gt;&#x27;s initializer, which will try to find a dictionary called &lt;code&gt;extraOptions&lt;&#x2F;code&gt; in the source JSON, which obviously isn&#x27;t present. We&#x27;ll have to implement the initializer ourselves. Doing so will also require us to define a type conforming to &lt;code&gt;CodingKey&lt;&#x2F;code&gt;, which we can use to specify the encoded keys in our source data.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;extension&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Json&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; Decodable&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  private&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; enum&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; CodingKeys&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; CodingKey&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-enummember&quot;&gt; a&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-enummember&quot;&gt;b&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;from&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; decoder&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Decoder&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; throws&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; container &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; decoder.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;container&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;keyedBy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; CodingKeys.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; container.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;forKey&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; container.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;forKey&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extraOptions&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ve now run into the problem of somehow having to specify all keys in our &lt;code&gt;CodingKeys&lt;&#x2F;code&gt; enum, which obviously doesn&#x27;t help for unknown keys. What we can do though is to use the &lt;code&gt;CodingKey&lt;&#x2F;code&gt; protocol to create our own &lt;em&gt;dynamic&lt;&#x2F;em&gt; key handling type, which can handle any &lt;code&gt;String&lt;&#x2F;code&gt; as a key.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; DynamicCodingKeys&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; CodingKey&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; stringValue:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  init?&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; stringValue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; not used here, but a protocol requirement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  var&lt;&#x2F;span&gt;&lt;span&gt; intValue:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  init?&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;intValue&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While we could use this entirely instead of our previous implementation, I personally find it quite a bit cleaner to keep the existing &lt;code&gt;CodingKeys&lt;&#x2F;code&gt; for known keys, but cover everything else with the &lt;code&gt;DynamicCodingKeys&lt;&#x2F;code&gt;. Creating a new container from our decoder keyed with &lt;code&gt;DynamicCodingKeys&lt;&#x2F;code&gt; however will give us a container containing &lt;em&gt;all&lt;&#x2F;em&gt; keys, not just the extra ones, so we&#x27;ll have to filter out those we&#x27;ve already taken care of, leaving us with something like this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;extension&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Json&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; Decodable&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  private&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; enum&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; KnownCodingKeys&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; CodingKey&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;CaseIterable&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-enummember&quot;&gt; a&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-enummember&quot;&gt;b&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    static&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; doesNotContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Json.DynamicCodingKeys&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Bool&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;allCases&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;\.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;contains&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;key.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; DynamicCodingKeys&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; CodingKey&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span&gt; stringValue:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    init?&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;      self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; stringValue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; not used here, but a protocol requirement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span&gt; intValue:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;?&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    init?&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;intValue&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;from&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; decoder&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Decoder&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; throws&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; container &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; decoder.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;container&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;keyedBy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; KnownCodingKeys.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; container.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;forKey&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; container.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;forKey&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extraOptions&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; extraContainer &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; decoder.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;container&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;keyedBy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; DynamicCodingKeys.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; key &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; extraContainer.allKeys &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;where&lt;&#x2F;span&gt;&lt;span&gt; KnownCodingKeys.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;doesNotContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;key&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;      let&lt;&#x2F;span&gt;&lt;span&gt; decoded &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span&gt; extraContainer.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;AnyDecodable.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;forKey&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; DynamicCodingKey&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; key.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;      self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extraOptions&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;key.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; decoded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The static function &lt;code&gt;KnownCodingKeys.doesNotContain(_:)&lt;&#x2F;code&gt; is totally not necessary, but it makes the loop in the initializer nicer to read.&lt;&#x2F;p&gt;
&lt;p&gt;When trying to run this, we see that it works as intended!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; json &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;quot;a&amp;quot;: 1,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;quot;b&amp;quot;: &amp;quot;foobar&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;quot;option.extra&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;a&amp;quot;: 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;quot;option.extra.more&amp;quot;: 4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;using&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable&quot;&gt;utf8&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; decoded &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; JSONDecoder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Json.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; json&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;decoded&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;Json&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  a&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  b&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;foobar&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  extraOptions&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;option.extra&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; AnyDecodable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;option.extra.more&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; AnyDecodable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s it, cya for the next blog post in - &lt;em&gt;check&#x27;s watch&lt;&#x2F;em&gt; - three years 😅&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Simulating a scan</title>
    <published>2018-09-01T00:00:00+00:00</published>
    <updated>2018-09-01T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/feed/til/simulating-a-scan/"/>
    <id>https://kilko.de/feed/til/simulating-a-scan/</id>
    <content type="html">&lt;p&gt;Fake a printed, hand-signed and scanned document using imagemagick. Beat the bureucracy.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; convert&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; input.pdf&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;colorspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; gray&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; +clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;blur&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; 0x0.1&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \)&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; +swap&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;compose&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; divide&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;composite&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;linear-stretch&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; 5%x0%&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;rotate&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.5&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; scan.pdf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Documentation on using &lt;code&gt;-blur&lt;&#x2F;code&gt; can be found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.imagemagick.org&#x2F;Usage&#x2F;blur&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Credits go to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tex.stackexchange.com&#x2F;a&#x2F;94541&quot;&gt;tex.stackexchange.com&#x2F;a&#x2F;94541&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Server Setup with traefik and docker-compose</title>
    <published>2018-07-26T00:00:00+00:00</published>
    <updated>2018-07-26T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/server-setup/"/>
    <id>https://kilko.de/blog/server-setup/</id>
    <content type="html">&lt;p&gt;In the &lt;a href=&quot;&#x2F;paperless&quot;&gt;last post&lt;&#x2F;a&gt; I hinted towards how I used docker-compose to run all the services I need on my server. Since someone asked me to describe it in a little more detail I&#x27;ll do just that in this post. It would probably also make sense to document it a bit for myself as I know that I&#x27;m going to forget parts of it at some point 🙈&lt;&#x2F;p&gt;
&lt;p&gt;The basic gist is that everything runs in a docker container and exposes only what&#x27;s necessary to the public web (and not directly by itself). Using docker to orchestrate everything has the benefit of not having to worry about dependencies or unsupported systems (to a certain degree at least) and leads to minimal overhead when trying out new stuff or updating existing services.&lt;&#x2F;p&gt;
&lt;p&gt;On a sidenote: I host my stuff on a VM provisioned from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hetzner.com&#x2F;cloud&quot;&gt;Hetzner&lt;&#x2F;a&gt;. I used to host all my stuff on Digital Ocean, but I recently ran into hardware limitations for the price class I was willing to pay and had a look around. Hetzner&#x27;s pricing is by far the best I could find, their servers are located in Germany (EU is a must for me) and their relatively new VM offering has a pretty site as well that looks just like a DO ripoff 😅&lt;&#x2F;p&gt;
&lt;h1 id=&quot;traefik&quot;&gt;traefik&lt;&#x2F;h1&gt;
&lt;p&gt;Holding most of it together is the fantastic &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;traefik.io&quot;&gt;traefik&lt;&#x2F;a&gt;, a simple reverse-proxy with everything built-in that I need here. It runs in it&#x27;s own docker container and comes with a minimal config that I&#x27;ll go into in a second. The reason I run it at all is because it comes with two amazing features that make it perfect for the job:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For one it connects directly to the docker socket on the host machine and therefore discovers new services instantly and by itself.&lt;&#x2F;li&gt;
&lt;li&gt;It automatically handles registering TLS certificates with Let&#x27;s Encrypt with literally &lt;em&gt;zero&lt;&#x2F;em&gt; overhead on my end.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;What we&#x27;re going to do is configure a docker network - let&#x27;s call it &lt;em&gt;web&lt;&#x2F;em&gt; - that every service will be placed into that&#x27;s actually connected to the internet. Since this network will be service agnostic we&#x27;ll create it as an external network directly with the docker cli.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docker&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; network&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; create&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; web&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can create a new docker-compose file for traefik that looks something like this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ervices&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;pp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;raefik&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;estart&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;orts&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;80:80&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;443:443&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;8080:8080&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;olumes&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;var&#x2F;run&#x2F;docker.sock:&#x2F;var&#x2F;run&#x2F;docker.sock:ro&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;traefik.toml:&#x2F;traefik.toml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;data&#x2F;traefik&#x2F;acme.json:&#x2F;acme.json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;etworks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; w&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;eb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;etworks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eb&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xternal&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This describes a new service based on the official traefik image, which is restarted automatically if it stops, exposes ports 80 (HTTP), 443 (HTTPS) and 8080 (dashboard) directly, has a few mounted volumes and is part of the &lt;em&gt;web&lt;&#x2F;em&gt; network. For the network to be picked up by docker-compose we need to explicitly list it in a networks section below.&lt;&#x2F;p&gt;
&lt;p&gt;Ports 80 and 443 are pretty self explanatory, but 8080 is where traefik hosts its own dashboard by default. You can also choose to go about that differently, but that&#x27;s the traefik default. I actually configured the dashboard to be shown on port 443 of a subdomain of my choosing behind a basic auth challenge, so that it&#x27;s not entirely open to the public, but you can do whatever you like.&lt;&#x2F;p&gt;
&lt;p&gt;By mounting &lt;code&gt;&#x2F;var&#x2F;run&#x2F;docker.sock:ro&lt;&#x2F;code&gt; into the container we give traefik access to information on all running containers on the host as I mentioned previously, read-only however. In &lt;code&gt;acme.json&lt;&#x2F;code&gt; traefik stores the Let&#x27;s Encrypt certificate date. We also mount an additional config file, &lt;code&gt;traefik.toml&lt;&#x2F;code&gt;, the contents of which are also relatively simple.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;debug&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;logLevel&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ERROR&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;defaultEntryPoints&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;http&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;https&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;web&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Port for the status&#x2F;dashboard page&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;address&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:8080&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    address&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:80&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;redirect&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        entryPoint&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;https&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;https&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    address&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:443&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;entryPoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;https&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;tls&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;retry&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unix:&#x2F;&#x2F;&#x2F;var&#x2F;run&#x2F;docker.sock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;domain&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;kilian.io&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;watch&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;exposedByDefault&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;acme&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;me@kilian.io&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;storage&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;acme.json&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;entryPoint&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;https&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;onHostRule&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;acme&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;httpChallenge&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    entryPoint&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;http&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s not a lot to say about this. We configure the log level, the default entry points and a redirect to always use TLS. Additionally we tell traefik about the base domain and also about how to handle TLS and and which challenge to use.&lt;&#x2F;p&gt;
&lt;p&gt;The only thing left to do now is to setup your domains DNS records to point to your server&#x27;s IP address and ideally set up a wildcard subdomain, e.g. &lt;code&gt;*.kilian.io&lt;&#x2F;code&gt; in my case to point to the server as well. This allows you to specify any necessary subdomains directly (blog.kilian.io runs on GitHub pages for example, not my server) and let everything else fall back to the server and therefore into traefik&#x27;s hands.&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t forget to boot up traefik via docker-compose from within it&#x27;s directory and we&#x27;re good to go ✨&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docker-compose&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; up&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;running-other-services&quot;&gt;Running Other Services&lt;&#x2F;h1&gt;
&lt;p&gt;As examples we&#x27;re going to configure two web-facing services. Instances of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitea.io&quot;&gt;gitea&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;huginn&#x2F;huginn&quot;&gt;huginn&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gitea&quot;&gt;Gitea&lt;&#x2F;h2&gt;
&lt;p&gt;Gitea is rather easy to set up, since it requires no other services besides itself. Let&#x27;s go straight for the docker-compose file.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ervices&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;pp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;itea&#x2F;gitea:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;estart&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nvironment&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; U&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;SER_UID=1000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; U&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;SER_GID=1000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;etworks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; w&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;eb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;olumes&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;data&#x2F;gitea:&#x2F;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;orts&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2221:22&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;abels&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.enable=true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.backend=gitea&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.docker.network=web&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.frontend.rule=Host:git.kilian.io&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.port=3000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;etworks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eb&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xternal&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All we need to do is declare two env vars, the network this container will be added to, a volume for persisting data beyond container restarts and rebuilds, specifically for gitea a custom SSH port and most importantly a few labels.&lt;&#x2F;p&gt;
&lt;p&gt;These labels are what traefik uses to understand how to channel traffic to this container. The last two are the most important ones. First we tell traefik which host header to look for in requests to the server (here: git.kilian.io) and then which port this service wants to map to port 80 &#x2F; 443 on that domain.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s it. A request to git.kilian.io (or directly to the server with a fitting host header field) will be forwarded to port 3000 of this service. All you have to do is start it up.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docker-compose&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; up&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;TLS is configured automatically by traefik on the first request (which might therefore take a second longer). You should be seeing a valid certificate if everything is set up correctly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;huginn&quot;&gt;Huginn&lt;&#x2F;h2&gt;
&lt;p&gt;Huginn is slightly more complex since we&#x27;re going to need two services. The application itself and a database.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ervices&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;pp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uginn&#x2F;huginn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;estart&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;etworks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; w&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;eb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;nternal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nvironment&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; P&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;OSTGRES_PORT_5432_TCP_ADDR=db&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; P&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;OSTGRES_PORT_5432_TCP_PORT=5432&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; H&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;UGINN_DATABASE_ADAPTER=postgresql&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; H&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;UGINN_DATABASE_USERNAME=huginn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; H&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;UGINN_DATABASE_PASSWORD=...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;IMEZONE=Berlin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;olumes&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;data:&#x2F;server&#x2F;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;abels&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.enable=true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.backend=huginn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.frontend.rule=Host:huginn.kilian.io&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.port=3000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;traefik.docker.network=web&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ostgres&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;estart&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;etworks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;nternal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nvironment&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; P&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;OSTGRES_USER=huginn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; P&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;OSTGRES_PASSWORD=...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;olumes&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;data&#x2F;huginn&#x2F;db:&#x2F;var&#x2F;lib&#x2F;postgresql&#x2F;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;etworks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nternal&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eb&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;xternal&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The key difference is that we obviously need to create both services, the main application and a postgres database. Since docker-compose doesn&#x27;t place both of these containers in a new network of their own by default &lt;strong&gt;if&lt;&#x2F;strong&gt; there&#x27;s already a network present (the &lt;em&gt;web&lt;&#x2F;em&gt; network), we also have to explicitly create a new network (I&#x27;m calling it &lt;em&gt;internal&lt;&#x2F;em&gt;) so that these services can connect to each other. Notice however how the database is &lt;em&gt;not&lt;&#x2F;em&gt; a part of the web network, there&#x27;s no reason for it to be accessible from the outside.&lt;&#x2F;p&gt;
&lt;p&gt;And that&#x27;s also already it for this case as well 👌&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;Running applications including any other services they might depend on behind a reverse-proxy taking care of TLS and only exposing necessary stuff to the web should no longer be an issue. The main complexity for setting up new services lies in... well... setting them up correctly. Most &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.12factor.net&#x2F;config&quot;&gt;well designed&lt;&#x2F;a&gt; applications allow this to be done by setting a few environment variables, making our setup via docker-compose extremely straight-forward and easy to replicate.&lt;&#x2F;p&gt;
&lt;p&gt;Persistence is easily solved via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;storage&#x2F;volumes&#x2F;&quot;&gt;docker volumes&lt;&#x2F;a&gt; or volume mounts. I prefer the latter, even though they lead to a few permission issues here and there and are not host-os-agnostic. I suggest having a look at volumes, there are quite a few advantages. It&#x27;s just a tiny bit more effort to backup and transfer your persisted data.&lt;&#x2F;p&gt;
&lt;p&gt;I also suggest storing your service&#x27;s docker-compose files and other configs (like the traefik.toml) in a git repo (obviously not public), which makes setup on a new machine even easier.&lt;&#x2F;p&gt;
&lt;p&gt;🖖&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Paperless</title>
    <published>2018-07-20T12:00:00+02:00</published>
    <updated>2018-07-20T12:00:00+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/paperless/"/>
    <id>https://kilko.de/blog/paperless/</id>
    <content type="html">&lt;p&gt;For the past few years I&#x27;ve been scanning all paper correspondence, be that something I receive via snail-mail, invoices, contracts or whatever else. This has been a huge improvement over not doing so. Before I had troubles finding documents when I needed them and usually had them lying around at home, not at hand when necessary.&lt;&#x2F;p&gt;
&lt;p&gt;Both of these issues are easily resolvable by having digital copies. They can be accessed remotely and are searchable. OCR software has gotten pretty fantastic as well, making it possible to search for certain keywords even in pretty bad scans.&lt;&#x2F;p&gt;
&lt;p&gt;Having used Evernote as a backing store for this database for years now, I&#x27;ve become rather dissatisfied with that platform. It&#x27;s slow, it&#x27;s primarily suited for different use cases and it&#x27;s gotten expensive as well. So I decided to migrate to something else, using the very cool software &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;danielquinn&#x2F;paperless&quot;&gt;paperless&lt;&#x2F;a&gt;. Here&#x27;s a short overview of my workflow using paperless.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h1&gt;
&lt;p&gt;I rent a VM to run most of the web-based application I use. Every application is managed via a docker-compose file. This makes it trivial to manage services with very different architectures, including paperless.&lt;&#x2F;p&gt;
&lt;p&gt;The docker-compose file looks something like the following. Please note that I did leave out a few details that are very specific to my setup, this won&#x27;t work &lt;em&gt;as is&lt;&#x2F;em&gt;. You&#x27;re at least going to have to manage exposing port 8000 of the server service.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ervices&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;erver&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;anielquinn&#x2F;paperless&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;estart&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;olumes&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;path&#x2F;to&#x2F;your&#x2F;data:&#x2F;usr&#x2F;src&#x2F;paperless&#x2F;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;path&#x2F;to&#x2F;your&#x2F;media:&#x2F;usr&#x2F;src&#x2F;paperless&#x2F;media&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;path&#x2F;to&#x2F;your&#x2F;consume:&#x2F;consume&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nvironment&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; P&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;APERLESS_OCR_LANGUAGES=deu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    c&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ommand&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;runserver&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;0.0.0.0:8000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  c&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;onsumer&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    i&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;anielquinn&#x2F;paperless&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;estart&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;epends_on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;erver&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;olumes&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;path&#x2F;to&#x2F;your&#x2F;data:&#x2F;usr&#x2F;src&#x2F;paperless&#x2F;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;path&#x2F;to&#x2F;your&#x2F;media:&#x2F;usr&#x2F;src&#x2F;paperless&#x2F;media&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;path&#x2F;to&#x2F;your&#x2F;consume:&#x2F;consume&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    c&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ommand&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;document_consumer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here I instantiate two services based on the same image, one running the paperless webserver, one running the paperless consumer service, which watches for new files in its consume directory. Paperless has three options of adding new files.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The consumer watches a directory and adds new files from there, removing them afterwards.&lt;&#x2F;li&gt;
&lt;li&gt;The consumer watches your mail account and adds files from specific emails.&lt;&#x2F;li&gt;
&lt;li&gt;New files are added via a POST request to the HTTP API.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m also not a huge fan of docker volumes, although they might have a few advantages. Mounting directories is easier to manage imho 🙈&lt;&#x2F;p&gt;
&lt;h1 id=&quot;workflow&quot;&gt;Workflow&lt;&#x2F;h1&gt;
&lt;p&gt;So now we have paperless running and keeping watch over the consume directory, automatically adding new files when you add them. At this point you should take a moment and get acquainted with paperless itself. If you&#x27;ve ever seen a Django admin interface, this will feel very familiar, because that&#x27;s exactly what it is. That does feel a bit hacky, but it seems to work good enough. You might also want to have a look at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thomasbrueggemann&#x2F;paperless-desktop&quot;&gt;this electron desktop app&lt;&#x2F;a&gt; which uses paperless&#x27; API to show all scanned documents in a prettier fashion. &lt;del&gt;Unfortunately it seems to only run on macOS at the moment&lt;&#x2F;del&gt; &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; and it is an alpha, but it&#x27;s definitely a great start! It also allows drag and drop of PDF files to upload them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;paperless.png&quot; alt=&quot;paperless desktop&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The current setup might now already work out great for you, depending on how you scan files and are able to upload files to the consume directory. I&#x27;m very used to using a scanning app on my mobile device though, specifically &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;scanbot.io&#x2F;en&#x2F;index.html&quot;&gt;Scanbot&lt;&#x2F;a&gt;. Scanbot has the great feature of letting you upload finished scans to a service of your choice, including FTP and WebDAV shares. This seemed like a great choice to push scans directly to my server. I don&#x27;t have an FTP server running (and don&#x27;t want to), but WebDAV is a core feature of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nextcloud.com&quot;&gt;Nextcloud&lt;&#x2F;a&gt;. And I do have a Nextcloud instance on my server as well.&lt;&#x2F;p&gt;
&lt;p&gt;What I ended up with is the Nextcloud docker container having the consume directory mounted. Then I enabled the fantastic Nextcloud app allowing external storage, added said directory directly to Nextcloud and then configured Scanbot to upload scans via WebDAV directly to the paperless consume directory. An added bonus is that the Nextcloud desktop client also syncs the consume directory to my computer, so I also have a directory there where I can drop PDF files to be added to paperless 👌&lt;&#x2F;p&gt;
&lt;p&gt;Everything now works rather seamlessly and I have better control of my files than with Evernote. The only thing missing would be a dedicated mobile app to search for scans. Maybe I&#x27;ll have a go at that 😊&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;I&#x27;ve been told that this is no longer the case. Thanks Julius! &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Chat Statistics</title>
    <published>2018-07-02T00:00:00+00:00</published>
    <updated>2018-07-02T00:00:00+00:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/chat-statistics/"/>
    <id>https://kilko.de/blog/chat-statistics/</id>
    <content type="html">&lt;p&gt;I recently stumbled across yet another &lt;em&gt;statistics from my chat with my girlfriend&lt;&#x2F;em&gt; post on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;reddit.com&#x2F;r&#x2F;dataisbeautiful&quot;&gt;&#x2F;r&#x2F;dataisbeautiful&lt;&#x2F;a&gt; so today I wanted to evaluate some simple data myself. Unfortunately I&#x27;m quite far from being an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lucaswoltmann.com&#x2F;forecasting&#x2F;2018&#x2F;02&#x2F;05&#x2F;Where-do-you-go-to.html&quot;&gt;actual datascientist&lt;&#x2F;a&gt; (below you&#x27;ll see some of the first matplotlib plots I&#x27;ve ever made), so I&#x27;m going to focus a bit more on data acquisition and some very simple findings and visualizations.&lt;&#x2F;p&gt;
&lt;p&gt;Since I probably don&#x27;t have any chats quite as active as the one with my girlfriend that seemed to be the obvious choice to analyze as well. Unfortunately it&#x27;s not just &lt;em&gt;one&lt;&#x2F;em&gt; chat, but split across four different messaging platforms since the inception of our relationship ~2.5 years ago. We started out using &lt;em&gt;WhatsApp&lt;&#x2F;em&gt;, then switched to &lt;em&gt;Telegram&lt;&#x2F;em&gt;, then to &lt;em&gt;iMessage&lt;&#x2F;em&gt; and back to &lt;em&gt;Telegram&lt;&#x2F;em&gt; interspersing everything with a bit of random images and answers to those on &lt;em&gt;Instagram&lt;&#x2F;em&gt;. I&#x27;d love to turn this into a discussion on the pros and cons of various messaging platforms, but this is neiter the time nor the place. You can hit me up via Matrix (@kiliankoe:matrix.org) if you&#x27;re interested in going down that route with me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;accessing-the-data&quot;&gt;Accessing the Data&lt;&#x2F;h2&gt;
&lt;p&gt;First things first, we need to actually get our hands on the chat data. This used to be rather tricky depending on the platform, but recently you might&#x27;ve heard some stir about the GDPR. A big upside to that is that I can go around to everybody that stores data on me and my behalf and ask nicely for a machine-readable export of said data. Instagram and Telegram recently implemented export features (Telegram finally doing so just a few days ago, perfect timing). WhatsApp seems to have featured an export for single chats for quite some time, which is nice. iMessage on the other hand is a bit more tricky. The chat data wasn&#x27;t included in my downloadable copy from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;privacy.apple.com&quot;&gt;Apple&#x27;s privacy portal&lt;&#x2F;a&gt;, which I assume is due to the fact that all messages are end-to-end encrypted and only accessible from my local devices. Thankfully all message data is available in &lt;code&gt;~&#x2F;Library&#x2F;Messages&#x2F;chat.d&lt;&#x2F;code&gt; on my Mac, which is just another normal SQLite db.&lt;&#x2F;p&gt;
&lt;p&gt;Time to fire up a Jupyter notebook&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; and consolidate everything 🎉&lt;&#x2F;p&gt;
&lt;p&gt;I was expecting to run into a few (albeit minor) problems doing this, so it came as no surprise when WhatsApp included quite a bit of random unicode chars to stumble over in it&#x27;s exported CSV. Interestingly enough Instagram was the least problematic with a well-formatted JSON file, you just have to find the correct conversation. Same goes for Telegram, which throws &lt;em&gt;all&lt;&#x2F;em&gt; conversations into a single file, but it&#x27;s not like a 15MB file will stop us.  Interestingly enough Telegram also doesn&#x27;t use UTC timestamps for the message dates, but your local timezone, so watch out and adjust accordingly&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. &lt;del&gt;Timestamps are also an issue on the iMessage end, since they seem to be a unix timestamp accurate to nanoseconds but offset 31 years into the past. I have no clue why, but would love to know.&lt;&#x2F;del&gt;
iMessage uses NTP timestamps, thanks to Johannes K. for telling me about that! 😊&lt;&#x2F;p&gt;
&lt;p&gt;Having worked around all those issues I aggregated data on the message date, author, text, platform and type (message, attachment, sticker, voice&#x2F;video) and created one big CSV sorted chronologically containing the entirety of our digital conversation since we&#x27;ve met. 62k messages that fit nicely into 5.7MB.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-can-we-learn&quot;&gt;What Can We Learn&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m not all too interested in sharing exact data on our relationship, so please regard that as being a side effect. Instead I wasn&#x27;t able to find any fun writeups showing a bit of sample code as well, which is the main reason for this blog post. Unfortunately though the codesamples below are far from ideal, but the beauty of doing something like this in a Jupyter notebook is that the performance costs start to become negligible since you&#x27;re only running the current cell. Everything else that&#x27;s already been computed &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-3-1&quot;&gt;&lt;a href=&quot;#fn-3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; just stays in memory. A seriously awesome feature.&lt;&#x2F;p&gt;
&lt;p&gt;So, now that everything is cleaned up and structured nicely we can ask some questions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Who wrote the most messages in total?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;her&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; msgs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; if&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;author&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;her&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;me&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; msgs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; if&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;author&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;me&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;her:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;her&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;her&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;msgs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;me:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;me&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;me&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;msgs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; her: 35152 56.588161432090025&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; me: 26967 43.411838567909975&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Looks like my girlfriend takes the lead with an 8k message headstart, which accounts to 56% of all messages. Could that be due to me writing longer messages instead? 🤔&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;How&#x27;s the average count of words per message per person?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; re&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;wordcount_her&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; her&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    count&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;re&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;findall&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;\w&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    wordcount_her&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span&gt; count&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;her&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;words total:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; wordcount_her&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;avg words&#x2F;msg:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; wordcount_her&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;her&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;wordcount_me&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; me&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    count&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;re&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;findall&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;\w&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    wordcount_me&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span&gt; count&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;me&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;words total:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; wordcount_me&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;avg words&#x2F;msg:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; wordcount_me&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;me&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; her&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; words total: 216606&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; avg words&#x2F;msg: 6.16198224852071&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; me&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; words total: 198979&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; avg words&#x2F;msg: 7.378610894797345&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aha! It might not be a huge difference, but it looks like my messages are bit longer on average. That evens things out 😄&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What are our most used words?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; collections&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; defaultdict&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;all_words&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; defaultdict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; msgs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; word&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; re&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;findall&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;\w&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        all_words&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;word&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;lower&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; word&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; sorted&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;all_words&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; key&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;all_words&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;get&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; reverse&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    count&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; all_words&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;word&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;    print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;word&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;count&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; ich: 16674&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; das: 9469&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; nicht: 7650&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; und: 7323&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; ist: 7258&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; die: 6828&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; du: 5920&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; aber: 5831&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; auch: 5824&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; dann: 4805&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; so: 4707&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; noch: 4611&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; hab: 4445&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; der: 4328&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; d: 4285&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; ja: 4201&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; da: 3098&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; wir: 2969&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; in: 2930&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; mal: 2901&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not the most interesting of results to be honest. Interestingly enough though the distribution looks pretty much the same if you go by person, so I&#x27;m willing to believe that this might be a relatively normal distribution for most German speakers. The &quot;d&quot; by the way is probably part of &quot;:D&quot; which the regex just found as being it&#x27;s own word. Oh well ¯\_(ツ)_&#x2F;¯&lt;&#x2F;p&gt;
&lt;p&gt;A much better query here would probably be to see which words are specific (or at least &lt;em&gt;more&lt;&#x2F;em&gt; specific) to one person instead of the other. I&#x27;m not quite sure how to go about doing that though 🙈&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;How many messages are sent per day and how many days are there without any messages at all?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;all_days&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; msgs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    date&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;date&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;strftime&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;%Y-%m-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    all_days&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;date&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;all_days&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;current_day_count&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 997&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; days we&amp;#39;ve been together as of today&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;all_days&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span&gt; current_day_count&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;messages per day:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;msgs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;all_days&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 959&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 96.18856569709128&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; messages per day: 64.77476538060479&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Looks like there&#x27;s been 38 days without any messages, which comes to down to around 4% of all 997 days. On average there&#x27;s 64.8 messages a day over the entire timespan. Splitting this up into a running average would probably make a lot of sense.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;How many messages are sent per weekday?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This might actually be a good usecase to try and plot something instead. Visualizing these numbers helps put everything into a bit more perspective.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;day_of_week&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; defaultdict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; msgs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    day&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;date&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;strftime&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;%A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    day_of_week&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;day&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Monday&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Tuesday&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Wednesday&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Thursday&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Friday&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Saturday&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Sunday&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ys&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;day_of_week&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;d&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; xs&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;bar&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;xs&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ys&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Messages per Day of Week&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Message Count&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;xscale&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;show&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;chat-stats-weekday-distribution.png&quot; alt=&quot;weekday distribution&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Also not a surprise, since we usually spend a lot more time together on weekends making digital conversation obsolete.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What about hours of the day instead?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;hour_of_day&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; defaultdict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; msgs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    hour&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;date&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;strftime&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;%H&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    hour_of_day&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;hour&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; sorted&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;hour_of_day&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;keys&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ys&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;hour_of_day&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;h&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; h&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; xs&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;bar&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;xs&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ys&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Messages per Hour of Day&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Message Count&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;show&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;chat-stats-hour-distribution.png&quot; alt=&quot;hour distribution&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To be honest I&#x27;m quite surprised that there&#x27;s any messages at all between 3 and 6am 🙈 I&#x27;ve got no clue how that ever happenend 😄&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What about overall activity?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I had no clue how to go about plotting this via matplotlib, so I decided to export the data as a CSV instead of use Apple&#x27;s Numbers, please don&#x27;t judge me 😅&lt;&#x2F;p&gt;
&lt;p&gt;Different platforms have different colors and it&#x27;s overall grouped by week, not single days, which was far too noisy.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;messages_per_date&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; defaultdict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;whatsapp&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;instagram&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;telegram&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;imessage&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; msgs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    date&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;date&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;strftime&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;%Y-%U&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    platform&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;platform&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    messages_per_date&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;date&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;platform&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;with&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; open&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;activity_platforms_week.csv&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; file&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    w&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; csv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;writer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; delimiter&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;writerow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;date&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;whatsapp&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;instagram&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;telegram&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;imessage&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; week&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; sorted&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;messages_per_date&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        whatsapp&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; messages_per_date&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;week&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;whatsapp&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        instagram&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; messages_per_date&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;week&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;instagram&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        telegram&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; messages_per_date&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;week&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;telegram&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        imessage&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; messages_per_date&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;week&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;imessage&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;writerow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;week&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; whatsapp&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; instagram&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; telegram&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; imessage&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;chat-stats-activity-week.png&quot; alt=&quot;total activity grouped by platform and week&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Pulling some stats from chats can be a lot of fun! This is also just scratching the surface, there&#x27;s a lot left to explore. I didn&#x27;t try and pull any conclusions from the gathered information, but I know that a lot can be gathered from this, and we&#x27;re basically just accessing the metadata, which each platform has access to, no matter if the data is end-to-end encrypted&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-4-1&quot;&gt;&lt;a href=&quot;#fn-4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; or not.&lt;&#x2F;p&gt;
&lt;p&gt;It might also be super interesting to use sentiment analysis to gain more insight into the nature of messages, maybe not explicitly for these shown here, but other chats as well. Or maybe just something as simple as our most used emoji 😜&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;The new lab mode is super awesome by the way! 😍 &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;This is probably a major headache if using Telegram whilst traveling, since you basically lose all time information. No clue how one would handle that correctly 🤔 &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;Including reading in the CSV data which is the only thing here that actually takes a few seconds. &lt;a href=&quot;#fr-3-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;Which would be the case for iMessage and WhatsApp (allegedly). Instagram offers no such thing and Telegram&#x27;s secret chats are ridiculous, since they lock you to the client where the chat is initiated. &lt;a href=&quot;#fr-4-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Docker Swift Runtime</title>
    <published>2018-01-29T12:25:00+01:00</published>
    <updated>2018-01-29T12:25:00+01:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/docker-swift-runtime/"/>
    <id>https://kilko.de/blog/docker-swift-runtime/</id>
    <content type="html">&lt;p&gt;I recently wrote a small &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vapor.codes&quot;&gt;Vapor&lt;&#x2F;a&gt; web app in Swift which I then wanted to deploy on my server inside a docker container. Not having all too much experience with docker my simple Dockerfile looked something like this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; swift:4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;EXPOSE&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 8080&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; swift&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-configuration&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; release&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ENTRYPOINT&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.build&#x2F;release&#x2F;Run&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;CMD&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;serve&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--env=production&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Whilst this worked perfectly fine, the image size turned out to be a whopping 1.5GB. That didn&#x27;t quite feel right if for an app as small as mine.&lt;&#x2F;p&gt;
&lt;p&gt;The problem is of course that the &lt;code&gt;swift&lt;&#x2F;code&gt; image contains the entire development environment that we don&#x27;t actually need anymore after having built the app.&lt;&#x2F;p&gt;
&lt;p&gt;After trying to go the route of automating the build process by using a container dedicated to building the app then extracting everything I needed from that container and building a new one from that I saw that docker actually had this functionality built in. It&#x27;s called a multistage build and lets you use separate images to perform whatever it is you want to do. In this case use an image for compiling the app and a separate one to actually package up and run the app binary and configs.&lt;&#x2F;p&gt;
&lt;p&gt;The Dockerfile for that case doesn&#x27;t get a lot more complicated, we just set a second base image after we&#x27;re done with the first one and copy over the necessary files.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; swift:4&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mkdir&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;build&#x2F;lib&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cp&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;R&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;usr&#x2F;lib&#x2F;swift&#x2F;linux&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.so&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;build&#x2F;lib&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; swift&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; release&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; mv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;swift&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; release&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-show-bin-path&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; &#x2F;build&#x2F;bin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ubuntu:16.04&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; apt-get&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;q&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; update&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    apt-get&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;q&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; install&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    libatomic1&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    libbsd0&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    libcurl3&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    libicu55&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    libxml2&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; rm&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;var&#x2F;lib&#x2F;apt&#x2F;lists&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-from=build&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;build&#x2F;bin&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-from=build&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;build&#x2F;lib&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;usr&#x2F;lib&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-from=build&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;Config&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;Config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;EXPOSE&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 8080&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ENTRYPOINT[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&#x2F;release&#x2F;Run&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;CMD[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;serve&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--env=production&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To properly set up the second image to run our app we just have to install a few dependencies and copy over the contents of &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;swift&#x2F;...&lt;&#x2F;code&gt;. The rest is the same as before.&lt;&#x2F;p&gt;
&lt;p&gt;Since we now no longer store all dev dependencies in our image, we&#x27;ve just made our image ~1.3GB smaller (around 200MB), that&#x27;s quite the improvement!&lt;&#x2F;p&gt;
&lt;p&gt;To wrap things up even nicer I published the runtime image as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;r&#x2F;kiliankoe&#x2F;swift-runtime&#x2F;&quot;&gt;&lt;code&gt;kiliankoe&#x2F;swift-runtime&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; so that we can reduce our Dockerfile to the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; swift:4&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RUN&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; swift&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; release&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; mv&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;swift&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; release&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-show-bin-path&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; &#x2F;build&#x2F;bin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; kiliankoe&#x2F;swift-runtime:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-from=build&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;build&#x2F;bin&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-from=build&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;Config&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&#x2F;Config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;EXPOSE&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 8080&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ENTRYPOINT[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&#x2F;release&#x2F;Run&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;CMD[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;serve&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--env=production&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;My image above is still based on &lt;code&gt;ubuntu:16.04&lt;&#x2F;code&gt;, which definitely shouldn&#x27;t be a necessity and accounts for most of the remaining 200MB. It would be nice to have far slimmer images in the future, based on alpine for example, but for time being the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.swift.org&#x2F;pipermail&#x2F;swift-users&#x2F;Week-of-Mon-20151228&#x2F;000653.html&quot;&gt;efforts needed seem to be insurmountable&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Reverse Engineering iOS Apps</title>
    <published>2017-05-30T12:30:38+02:00</published>
    <updated>2017-05-30T12:30:38+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/reverse-engineering-ios/"/>
    <id>https://kilko.de/blog/reverse-engineering-ios/</id>
    <content type="html">&lt;iframe width=&quot;100%&quot; height=&quot;500&quot; src=&quot;https:&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;lArXWiVWImk?rel=0&amp;amp;showinfo=0&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;As promised in my &lt;a href=&quot;&#x2F;itunes-mitm&quot;&gt;last post&lt;&#x2F;a&gt; I wanted to write a post about a talk I recently held at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mobilecamp.de&quot;&gt;Mobile Camp 2017&lt;&#x2F;a&gt;. I also held the talk again at our local Cocoaheads Meetup on May 24th 2017, this time being recorded on video (Thanks, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;benchr&quot;&gt;Ben&lt;&#x2F;a&gt;!). But since the talk is in German, I promised to publish an English summary here as well, so here we go 😊&lt;&#x2F;p&gt;
&lt;p&gt;The slides are online as well and can be found on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.io&#x2F;reverse-ios&quot;&gt;GitHub Pages&lt;&#x2F;a&gt;. I&#x27;ve linked to most things as well, everything in orange is clickable 😉&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-reverse-engineering&quot;&gt;What is Reverse Engineering?&lt;&#x2F;h2&gt;
&lt;p&gt;According to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Reverse_engineering&quot;&gt;Wikipedia&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Reverse engineering, also called back engineering, is the processes of extracting knowledge or design information from anything man-made and re-producing it or re-producing anything based on the extracted information. The process often involves disassembling something (a mechanical device, electronic component, computer program, or biological, chemical, or organic matter) and analyzing its components and workings in detail.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That&#x27;s exactly what we&#x27;re going to do. We have an app that we didn&#x27;t write ourselves and want to find out what it&#x27;s &lt;em&gt;doing&lt;&#x2F;em&gt; under the hood. Maybe we&#x27;re interested in how a certain behavior is built. Or we want to have a look at how the API the app talks to works or what kind of data is being sent to it (perhaps a little too much?). Or we might even be interested in finding out why a certain bug may be occuring.&lt;&#x2F;p&gt;
&lt;p&gt;Whatever our intent, there&#x27;s a few things we can do to achieve this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;inside-an-app&quot;&gt;Inside an App&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s a few things to find in an app. Obviously we have the app itself, meaning some kind of executable and possibly a list of frameworks. Additionally there&#x27;s some metadata, which in the iOS world mostly comes down to the info.plist. I&#x27;d add NSUserDefaults to this as well, although they&#x27;re not really included within the app, but stored elsewhere. But especially on macOS this is highly interesting, as a lot of apps offer a lot more customization than seemingly possible via their GUI. And finally we also have assets, e.g. bundled images, videos, sounds, fonts, strings and whatever else.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-possible&quot;&gt;What&#x27;s Possible&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m going to split this up into two cases, one being while the app is running and the other while it isn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;If the app is running we can on one hand start monitoring the network and intercept everything the app sends and receives. This is where we can figure out how an API the app might be talking to is working. On the other hand we can try and inject code into the app and bend it to our will.&lt;&#x2F;p&gt;
&lt;p&gt;When not having a running app at our disposal we can open it up and see what kind of stuff is lying around in the bundle. We can also dump the binary to gain a lot more insight into what kind of code the app is actually running behind the scenes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;intercepting-network-calls&quot;&gt;Intercepting Network Calls&lt;&#x2F;h2&gt;
&lt;p&gt;For this we&#x27;re going to be using a fantastic tool called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.charlesproxy.com&quot;&gt;Charles&lt;&#x2F;a&gt;, but there are also &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mitmproxy.org&quot;&gt;alternatives&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;codewithchris.com&#x2F;tutorial-using-charles-proxy-with-your-ios-development-and-http-debugging&#x2F;&quot;&gt;plenty&lt;&#x2F;a&gt; of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.facebot.org&#x2F;charles-basics&#x2F;&quot;&gt;tutorials&lt;&#x2F;a&gt; for this online, so I&#x27;m not going to go through this step by step.&lt;&#x2F;p&gt;
&lt;p&gt;The idea is that we can use Charles to intercept anything an app sends to &lt;em&gt;the internet&lt;&#x2F;em&gt; and receives from said &lt;em&gt;internet&lt;&#x2F;em&gt;. Charles has a pretty nifty feature called breakpoints that let us stop on both events and modify the request and response contents inline.
It becomes a bit trickier if the app is using transport encryption, which any sane app should at the very least be doing. If there are no additional measures like certificate pinning at play though we can just tell Charles to use its SSL proxying mode to use our own SSL certificate instead of the original. That way we can just as well listen in on any encrypted requests.&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned though if the app pins to a specific certificate (meaning their app is expecting a specific one, not just &lt;em&gt;any&lt;&#x2F;em&gt; SSL certificate), we&#x27;re kinda stuck here. &lt;strong&gt;And cert pinning is definitely something any app handling sensitive information should be looking in to if not already doing. Man-in-the-middle attacks are commonplace and it&#x27;s always a good idea to protect your users. There&#x27;s more than your reputation at stake 😉&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bundle&quot;&gt;The Bundle&lt;&#x2F;h2&gt;
&lt;p&gt;Apps usually bundle more than just their executable. So why not have a look, even if it&#x27;s just to satisfy our curiosity.&lt;&#x2F;p&gt;
&lt;p&gt;Go ahead and download whatever app you&#x27;re interested in via iTunes. If you&#x27;re looking for a specific (older) version, check out my &lt;a href=&quot;&#x2F;itunes-mitm&quot;&gt;last post&lt;&#x2F;a&gt; on how to do just that. Right click the app in iTunes and select &quot;Show in Finder&quot;. Here we can just rename the .ipa file to end in .zip and unpack it. Then we can have a look inside \o&#x2F; (Right click an .app and select &quot;Show package contents&quot; to open it).&lt;&#x2F;p&gt;
&lt;p&gt;For most apps there&#x27;s not all too much we can discover here. You&#x27;ll most likely be seeing icons and maybe some other media files. Possibly fonts and sounds as well. Obviously depends on the app.&lt;&#x2F;p&gt;
&lt;p&gt;The two apps I looked into in my talk were both related to public transport in Dresden, &#x27;cause I love myself some good public transport data. The first one, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;itunes.apple.com&#x2F;us&#x2F;app&#x2F;fahrinfo-dresden&#x2F;id314790387?mt=8&quot;&gt;Fahrinfo Dresden&lt;&#x2F;a&gt;, bundles an SQLite db containing a list of all stops in Dresden (for autocompletion) including their coordinates. This was definitely a cool resource to stumble across (especially since these coordinates were somewhat hard to gather elsewhere).&lt;&#x2F;p&gt;
&lt;p&gt;The second app, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;itunes.apple.com&#x2F;us&#x2F;app&#x2F;dvb-mobil&#x2F;id1174905421?mt=8&quot;&gt;DVB mobil&lt;&#x2F;a&gt;, is the official app our public transport provider publishes. As you might be able to tell by using the app and the performance that shows, this app is not a native app, but although I&#x27;d love to very much, this isn&#x27;t the place to argue about that... The app is built with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ionicframework.com&quot;&gt;Ionic&lt;&#x2F;a&gt; (previously known as Cordova - more or less), which means the core logic and pretty much everything is written in JavaScript and not compiled into a native binary. Interestingly enough this means that we&#x27;re going to be running across the actual source of the app in the bundle. Unfortunately though the app doesn&#x27;t do all that much that&#x27;s very interesting to look at, but it&#x27;s definitely cool to poke around a bit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;code-injection&quot;&gt;Code Injection&lt;&#x2F;h2&gt;
&lt;p&gt;This was the coolest part of the talk in my mind at least. Unfortunately it requires your device to be jailbroken. But once you&#x27;ve stepped past that hurdle, the fun begins.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to be using a fantastic tool called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.cycript.org&quot;&gt;cycript&lt;&#x2F;a&gt; to basically give us a live REPL into an app of our choice. This is suprisingly easy to do and surprisingly powerful as well.&lt;&#x2F;p&gt;
&lt;p&gt;For the talk I ran an app called Jodel, which, if you&#x27;re unfamiliar with it, is basically a German equivalent of YikYak. I ran the app on my jailbroken iPhone 4 (running a rather old iOS 7.1.2, but that shouldn&#x27;t be an issue here) and to then get up and running we just have to SSH into our iPhone and run the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cycript&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Jodel&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;-p&lt;&#x2F;code&gt; flag tells cycript to look through all running processes and find the one with the given name. Cycript will now present us with a prompt where we can use its mix of JS and Objective-C to do whatever we want.&lt;&#x2F;p&gt;
&lt;p&gt;One way of getting started is having a look at the current recursive description of the view hierarchy for example.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; UIApp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;keyWindow&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;recursiveDescription&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cycript has a pretty nice way of accessing anything you know a the memory address of.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; table&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Instance&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0xDEADBEEF&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; use an actual memory address, duh :P&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I used this to get a handle to the &lt;code&gt;JDLLoadingTableView&lt;&#x2F;code&gt; I found in the recursive description above, which is the main table view showing all &quot;jodels&quot;. Then I wanted to find the top-most cell and see if I can change its displayed text and vote count.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; idx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;NSIndexPath&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; indexPathForRow&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; inSection&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cell&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;table&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cellForRowAtIndexPath&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cell&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;hidden&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; YES&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; this worked, so we have a reference to the correct cell&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cell&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;hidden&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; NO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cell&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;recursiveDescription&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; we see there&amp;#39;s a `contentLabel`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cell&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;contentLabel&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;foobar&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; yay, it works \o&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cell&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;voteCountLabel&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;text&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;156&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; works as well :)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Obviously this only changes the display of things though. Once we send an upvote or trigger a refresh in any other way, our changes are obviously going to be reset. For trying to get around that it would make sense to see if we can change things directly in the models the app stores internally, but that&#x27;s something for another rainy day 😊&lt;&#x2F;p&gt;
&lt;p&gt;Another way of accessing any running instance of whatever class in cycript is the &lt;code&gt;choose&lt;&#x2F;code&gt; function. You just provide it with a name and it hands you all instances of that class it currently finds. And since Jodel uses Fabric, we can just do the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; crashlytics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; choose&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;CLSAnalyticsController&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;crash&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fabricAPIKey&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s also worth noting at this point that cycript is a fantastic way of getting around that pesky certificate pinning mentioned previously, since we can just go ahead and disable that if it&#x27;s getting in our way. It&#x27;s especially easy if the app is using a framework like &lt;code&gt;AFNetworking&lt;&#x2F;code&gt;, where we can just set the &lt;code&gt;SSLPinningMode&lt;&#x2F;code&gt; to &lt;code&gt;None&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re a developer you might want to think about this. Anything you do on a user&#x27;s device is something the user can influence. If your app is talking directly to several third-party services and authenticating itself against them, the user of your app will probably be able to find out your API keys (or similar). It might be worth putting something like that behind your own API, so that you can play somewhat of a gatekeeper for most stuff at least. Also please make sure that you&#x27;re not throwing around sensitive information everywhere. Anything your app can access is something cycript (or similar and&#x2F;or more malicious tools) can access. Anything that&#x27;s just lying around unencrypted and way beyond its lifetime is something that will be read by someone unauthorized. It&#x27;s a &lt;em&gt;terrible&lt;&#x2F;em&gt; idea to store passwords in NSUserDefaults for example. But it&#x27;s maybe not quite as obvious that things like encryption keys should not be stored in memory indefinitely either, but just when you need them. It&#x27;s worth talking to someone with a bit more experience regarding this stuff if you want to take it seriously.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dumping-the-binary&quot;&gt;Dumping the Binary&lt;&#x2F;h2&gt;
&lt;p&gt;To find out more of what kind of data an app stores and what it&#x27;s &lt;em&gt;really&lt;&#x2F;em&gt; doing we&#x27;re going to have to decrypt the actual executable, arguably the most &lt;em&gt;involved&lt;&#x2F;em&gt; part of this post, but it&#x27;s actually rather straight-forward in acomplishing. The &quot;hard&quot; part lies in interpreting the results.&lt;&#x2F;p&gt;
&lt;p&gt;First off we&#x27;re going to want to find the location of the app on our device so that we can tell dumpdecrypted where to look. I&#x27;m using a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;conradev&#x2F;dumpdecrypted&quot;&gt;fork of dumpdecrypted&lt;&#x2F;a&gt; which also dumps all frameworks, which can come in handy.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; find&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;var&#x2F;mobile&#x2F;Applications&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Jodel&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;var&#x2F;mobile&#x2F;Applications&#x2F;[...]&#x2F;Jodel.app&#x2F;Jodel&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; [...] is a placeholder for the app&amp;#39;s ID :P&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After doing so we&#x27;ll see a Jodel.encrypted file lying around. This we can now pull over to our Mac and throw into a tool like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hopperapp.com&quot;&gt;Hopper&lt;&#x2F;a&gt; (there&#x27;s a free trial, which is rather neat) where can look at pretty much everything and try to see what happens where and how by looking at the generated pseudo-code (awesome feature!) or we can dump the encrypted file using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nygard&#x2F;class-dump&quot;&gt;class-dump&lt;&#x2F;a&gt; into a header listing everything that exists. This is useful if you already have a faint idea of what you&#x27;re looking for. In my case this was to find out some stuff about how Jodel generates the HMAC it uses to &quot;sign&quot; every request making it harder to tamper with their API 😉 Props to them for doing it this way by the way!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; class-dump&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Jodel.decrypted&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Jodel.h&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By the way, there&#x27;s the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nst&#x2F;iOS-Runtime-Headers&quot;&gt;iOS-Runtime-Headers&lt;&#x2F;a&gt; repo on GitHub which lists header info on all (including private) frameworks in iOS. It&#x27;s regularly updated when new versions of iOS emerge. Apple has followed the practice of adding frameworks as private variants before publicizing them in future versions, so it&#x27;s neat to see hints to some upcoming stuff before it actually gets announced at WWDC 😊&lt;&#x2F;p&gt;
&lt;p&gt;Soooo, that more or less brings us to the end of the talk. Thanks for being there, having a look at the video recording or reading this post. You&#x27;re awesome! I hope that at least some of the information gathered here was of use to you, I definitely learned some stuff when putting it all together. Cheers!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;&#x2F;h2&gt;
&lt;p&gt;If your code is running on somebody else&#x27;s device, they can do whatever they want with it. And there&#x27;s not really anything you can do to stop that. Make sure to think about this when building your app, but don&#x27;t worry about it all too much, it&#x27;s normal 😜&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Downloading older iOS app versions with iTunes</title>
    <published>2017-05-10T15:04:18+02:00</published>
    <updated>2017-05-10T15:04:18+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/itunes-mitm/"/>
    <id>https://kilko.de/blog/itunes-mitm/</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt; (2017-09-23):
Apple recently removed the App Store entirely from iTunes (a welcome move), which kinda broke the entire workflow here. It is however still possible to download IPA files to your Mac through the use of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;itunes.apple.com&#x2F;de&#x2F;app&#x2F;apple-configurator-2&#x2F;id1037126344?l=en&amp;amp;mt=12&quot;&gt;Apple Configurator 2&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Buy and download the app via your iOS device&lt;&#x2F;li&gt;
&lt;li&gt;Open Apple Configurator and login under &quot;Account&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Connect your device, then go to &quot;Add&quot; &amp;gt; &quot;Application&quot; and choose the app in question from your purchase history&lt;&#x2F;li&gt;
&lt;li&gt;A dialog will open asking you if you want to overwrite the app or continue in another fashion. As long as this dialog is open, the IPA file will be cached in &lt;code&gt;~&#x2F;Library&#x2F;Group Containers&#x2F;K36BKF7T3D.group.com.apple.configurator&#x2F;Library&#x2F;Caches&#x2F;Assets&#x2F;TemporaryItems&#x2F;MobileApps&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This process at least helps with downloading current IPA files. The act of downloading older versions is left as an exercise to the reader. I have to add that I haven&#x27;t checked if it&#x27;s still possible, I&#x27;m just guessing that it doesn&#x27;t differ too much from the process outlined below.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I recently held a talk about reverse engineering iOS apps at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mobilecamp.de&quot;&gt;Mobile Camp 2017&lt;&#x2F;a&gt;. There will be another post about said talk soon, since I&#x27;ll be holding it again at our local Cocoaheads meetup in a few days, this time with a video recording 😊.&lt;&#x2F;p&gt;
&lt;p&gt;One of my topics was taking a look into an app&#x27;s bundle, for which I had an app in mind that bundled an interesting SQLite database&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. I remembered stumbling across this a little while ago and thought it&#x27;d be something nice and small to show. On redownloading the current version of the app I noticed that the SQLite db was still there, but now encrypted 😿. Not wanting to look around for something else to demo I chose to force iTunes to just download an older version of the app instead. Doing so turned out to be rather easy if you&#x27;re at least somewhat familiar with &lt;em&gt;man-in-the-middle&lt;&#x2F;em&gt; attacking yourself, something I also went over in my talk.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m using a tool called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.charlesproxy.com&quot;&gt;Charles&lt;&#x2F;a&gt; for this. I can also recommend the very popular &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mitmproxy.org&quot;&gt;mitmproxy&lt;&#x2F;a&gt;. The idea is the same for both of them, but I&#x27;ll reference Charles here.&lt;&#x2F;p&gt;
&lt;p&gt;Run your tool of choice and start iTunes. Search for the app you want and click download. Since you don&#x27;t want the current version you can cancel the download right after that. Now check Charles for a request to &lt;code&gt;*-buy.itunes.apple.com&lt;&#x2F;code&gt;. Since this request is TLS encrypted we&#x27;re going to enable Charles&#x27; SSL Proxying (right-click the request and do so). Let iTunes re-send the request by starting the download again (we still just need it to start, not actually finish downloading). Charles should now show the request in its unencrypted form.&lt;&#x2F;p&gt;
&lt;p&gt;If you view the server&#x27;s response you&#x27;ll see a plist file (Apple&#x27;s XML based &quot;property list&quot; file format). You&#x27;re looking for a section that looks like the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;softwareVersionExternalIdentifiers&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;813164182&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;813305098&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;814184616&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;814214406&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;814369260&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;814850256&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;817722935&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;integer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a list of all versions of the app (or more precisely their IDs), starting with the oldest. Obviously your IDs will most likely differ.&lt;&#x2F;p&gt;
&lt;!--Unfortunately these version IDs aren&#x27;t directly convertible to the version string used by the app. You&#x27;re going to have to guess. For my case this was fine, since I just needed *any* old version. Otherwise you can of course just try a few times and see if you can find a specific one you&#x27;re looking for.--&gt;
&lt;p&gt;Unfortunately there&#x27;s no direct way to correlate all of these version identifiers with the app&#x27;s actual version strings. Apple&#x27;s servers only tell you the version string when requesting one of these specifically, but more on that in a pinch.&lt;&#x2F;p&gt;
&lt;p&gt;Go ahead and copy an ID. Now check the request tab of that same request. You should be able to find a section that specifies which version iTunes wants to download.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;appExtVrsId&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;822037669&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The easiest way in Charles is to now set a breakpoint on this request (right-click → Breakpoints). Restart the download in iTunes and Charles will pop up allowing you to edit the request iTunes sends to Apple before it goes through. This is where you can now insert your copied ID as the value for the just mentioned &lt;code&gt;appExtVrsId&lt;&#x2F;code&gt; field. After clicking &quot;Execute&quot; Charles will also stop the incoming response before allowing iTunes to read it. This includes a field named &lt;code&gt;bundleShortVersionString&lt;&#x2F;code&gt; which contains the human-readable ID. You can abort the response and try again if this wasn&#x27;t the app version you&#x27;re looking for.&lt;&#x2F;p&gt;
&lt;img style=&quot;display: block; margin: 0 auto;&quot; src=&quot;https:&#x2F;&#x2F;cloud.githubusercontent.com&#x2F;assets&#x2F;2625584&#x2F;25902476&#x2F;e85fca08-3599-11e7-9643-78d41095f4c1.png&quot; &#x2F;&gt;
&lt;p&gt;If it is a version that works for you, great! That&#x27;s it 🎉. iTunes is downloading the app as we speak and you can then do whatever you want with it. Be aware that any current versions of the app need to be removed from your device if you want to sync it to one.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;I&#x27;m referring to the app &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;itunes.apple.com&#x2F;us&#x2F;app&#x2F;fahrinfo-dresden&#x2F;id314790387?mt=8&quot;&gt;FahrInfo&lt;&#x2F;a&gt; for Dresden. The bundled SQLite db contains a list of all public transport stops including their coordinates, which was used by the app for autocompletion. And if you know me, you know that I&#x27;m a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kiliankoe&#x2F;vvo&quot;&gt;sucker&lt;&#x2F;a&gt; for local public transport data. &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Keeping Static Images out of your GitHub Repo</title>
    <published>2017-04-10T14:12:35+02:00</published>
    <updated>2017-04-10T14:12:35+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/github-images/"/>
    <id>https://kilko.de/blog/github-images/</id>
    <content type="html">&lt;p&gt;People love images &lt;em&gt;[citation needed]&lt;&#x2F;em&gt;. Or at least I know I do. And I&#x27;ve previously stated elsewhere that I hate downloading or installing software that doesn&#x27;t show me any obvious references to its visuals beforehand. I mean come on, it&#x27;s just a  screenshot, how hard can it be? GIFs or videos get bonus ✨ points. This obviously also goes for CLIs, which doesn&#x27;t mean you should skimp on man pages and&#x2F;or good &lt;code&gt;--help&lt;&#x2F;code&gt; documentation, but a link to a &quot;&lt;em&gt;video&lt;&#x2F;em&gt;&quot; powered by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;asciinema.org&quot;&gt;asciinema&lt;&#x2F;a&gt; speaks volumes compared to a textual description of usage patterns!&lt;&#x2F;p&gt;
&lt;p&gt;A lot of the tools we use and download nowadays are primarily hosted publicly on GitHub, which is why a good README has become the defacto standard homepage for your project. &lt;strong&gt;This&lt;&#x2F;strong&gt; is exactly where your screenshots, GIFs or videos should go. And it&#x27;s what most people do, which is awesome.&lt;&#x2F;p&gt;
&lt;p&gt;The problem with inline images and GIFs however is that they have to come from &lt;em&gt;somewhere&lt;&#x2F;em&gt; and that &lt;em&gt;somewhere&lt;&#x2F;em&gt; is usually from within the repo (a &lt;code&gt;.&#x2F;screenshots&lt;&#x2F;code&gt; dir for example). Especially for GIFs though this can quickly bloat your repo by several megabytes 😕&lt;&#x2F;p&gt;
&lt;p&gt;The solution is rather simple, just let GitHub host the static assets instead 🤷‍♀️ There&#x27;s no obvious direct upload form anywhere, but we can just abuse GitHub&#x27;s hosting of images for issues for this. The process is fairly simple:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Open a new issue in a repo of your choice (doesn&#x27;t matter since it won&#x27;t be part of the URL and we&#x27;re not going to actually open the issue).&lt;&#x2F;li&gt;
&lt;li&gt;Drag and drop the image into the issue body.&lt;&#x2F;li&gt;
&lt;li&gt;Your browser will upload the image to GitHub&#x27;s static hosting server and you&#x27;ll be provided with a public link similar to this one: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloud.githubusercontent.com&#x2F;assets&#x2F;2625584&#x2F;24861665&#x2F;d962d642-1df9-11e7-9bd5-cb4f02c5d0c1.png&quot;&gt;&lt;code&gt;https:&#x2F;&#x2F;cloud.githubusercontent.com&#x2F;assets&#x2F;2625584&#x2F;24861665&#x2F;d962d642-1df9-11e7-9bd5-cb4f02c5d0c1.png&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Copypasta that link into your project&#x27;s README and discard the issue 🙌&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This is no different than hosting the image somewhere else like imgur, but it just seems logical to let GitHub handle the public remote hosting for the repo including the static assets without forcing them on everybody wanting to clone the repo 👌&lt;&#x2F;p&gt;
&lt;p&gt;Also, I sincerely apologize for a post that&#x27;s several times longer than it needs to be for a topic so simple 😜&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Akronymisierbar</title>
    <published>2017-04-08T22:06:26+02:00</published>
    <updated>2017-04-08T22:06:26+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/akronymisierbar/"/>
    <id>https://kilko.de/blog/akronymisierbar/</id>
    <content type="html">&lt;p&gt;I&#x27;m a podcast addict. I have been for a few years now and I can&#x27;t seem to stop. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;podcastprofile.com&#x2F;kiliankoe&quot;&gt;Podcastprofile&lt;&#x2F;a&gt; has a not very current list of all the feeds I&#x27;m subscribed to.&lt;&#x2F;p&gt;
&lt;p&gt;With all the content I consume it only seems logical to produce something at some point as well, or at least try. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hoodie_de&quot;&gt;Hendrik&lt;&#x2F;a&gt; and me have had that plan for quite a while if I recall correctly, and two months ago we finally decided to do something about it. So we sat down and hit record. What came out of that is &lt;em&gt;Akronymisierbar&lt;&#x2F;em&gt; (German for &lt;em&gt;acronyminizable&lt;&#x2F;em&gt;, &#x27;cause all good podcast names are acronyminizable 😜), a podcast about everything that comes to our minds, from programming languages (there&#x27;s a lot about programming languages) to how frogs swallow by using their eyeballs to push food down, although it&#x27;s generally more technical as both of us have vested interests in all things computer science.&lt;&#x2F;p&gt;
&lt;p&gt;The fourth episode (#003, we&#x27;re zero-indexed) was uploaded yesterday as of this posting. You can listen to it right here if you want.&lt;&#x2F;p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;166&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; src=&quot;https:&#x2F;&#x2F;w.soundcloud.com&#x2F;player&#x2F;?url=https%3A&#x2F;&#x2F;api.soundcloud.com&#x2F;tracks&#x2F;316479289&amp;amp;color=ff5500&amp;amp;auto_play=false&amp;amp;hide_related=true&amp;amp;show_comments=false&amp;amp;show_user=true&amp;amp;show_reposts=false&quot;&gt;&lt;&#x2F;iframe&gt;
(We&#x27;re probably going to move away from SoundCloud for future episodes, but we&#x27;ll definitely publish a new last episode with a new feed link on SoundCloud when that happens.)
&lt;p&gt;It&#x27;s definitely quite fun and if you&#x27;re based in Dresden, we&#x27;ve got to have you on the show! 😊&lt;&#x2F;p&gt;
&lt;p&gt;Interested? We&#x27;re listed on iTunes, Fyyd, Bitlove and probably other podcast directories as well. Give us a listen! We&#x27;re also very much interested in feedback of any kind, preferably via Twitter &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;akronymisierbar&quot;&gt;@akronymisierbar&lt;&#x2F;a&gt; ✌️&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Automatically Increment Your Xcode Project&#x27;s Build Number</title>
    <published>2016-11-09T13:43:52+01:00</published>
    <updated>2016-11-09T13:43:52+01:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/xcode-buildnumber/"/>
    <id>https://kilko.de/blog/xcode-buildnumber/</id>
    <content type="html">&lt;p&gt;Those of us who use Xcode to build, run and deploy projects of ours have probably run into an issue where we&#x27;ve forgotten to increment the build number before trying to deploy a project.
In my case the problem is usually iTunes Connect, which doesn&#x27;t take your app&#x27;s version (or &lt;code&gt;CFBundleShortVersionString&lt;&#x2F;code&gt;) into account, but only the build number (or &lt;code&gt;CFBundleVersion&lt;&#x2F;code&gt;). The former is the version we usually set (according to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;semver.org&quot;&gt;semver&lt;&#x2F;a&gt; if you&#x27;re a good person, although it really doesn&#x27;t make much of a difference for user-facing versions) to identify the version internally.
If you fail to increment the latter before uploading, iTunes Connect doesn&#x27;t see this as a new version, which stinks.&lt;&#x2F;p&gt;
&lt;p&gt;A first idea might be having a build phase which just increments that number every time you hit build. The obvious downside being that you&#x27;re either going to be committing increments of this number and fixing conflicts on merges or running into outdated build numbers if you have multiple people deploying your app. That&#x27;s no fun.&lt;&#x2F;p&gt;
&lt;p&gt;The best solution I&#x27;ve come across is coupling your build number to the commit count of your project&#x27;s git repo. This leads to reproducible build numbers also allowing you to track the state of the app (presuming no uncommited changes were present during archival) based on the build. It doesn&#x27;t however solve the issue we had with automatically incremented build numbers before, so what if we also reset the build number back to a default value right after we finish compiling? That way our build gets its nice identifying build number and we have no build number changes to commit. Sweet.&lt;&#x2F;p&gt;
&lt;p&gt;All we need for that are two run script build phases. One before near the top setting our &lt;em&gt;generated&lt;&#x2F;em&gt; build number and one near the bottom resetting it (before and after &lt;code&gt;Copy Bundle Resources&lt;&#x2F;code&gt; should probably suffice).&lt;&#x2F;p&gt;
&lt;p&gt;For the first one, add the following.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;buildNumber&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rev-list&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; HEAD&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; wc&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; tr&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&#x2F;usr&#x2F;libexec&#x2F;PlistBuddy&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Set :CFBundleVersion &lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;buildNumber&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PROJECT_DIR&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;INFOPLIST_FILE&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we&#x27;re utilizing &lt;code&gt;git rev-list&lt;&#x2F;code&gt; to list out all parent commits of the current state, piping that through &lt;code&gt;wc&lt;&#x2F;code&gt; to count the number of lines and removing leading whitespace with &lt;code&gt;tr&lt;&#x2F;code&gt;. This results in our build number that we set using &lt;code&gt;plistbuddy&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The second script is rather similar.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;&#x2F;usr&#x2F;libexec&#x2F;PlistBuddy&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Set :CFBundleVersion DEV&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PROJECT_DIR&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;INFOPLIST_FILE&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Same idea, just a hardcoded string &lt;code&gt;DEV&lt;&#x2F;code&gt; instead of a number of commits.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully this method will save you just that teeny amount of time updating your build number in the future 🙂&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Writing Custom Alfred Worklows</title>
    <published>2016-10-23T12:42:16+02:00</published>
    <updated>2016-10-23T12:42:16+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/alfred-workflows/"/>
    <id>https://kilko.de/blog/alfred-workflows/</id>
    <content type="html">&lt;p&gt;A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;BenchR&quot;&gt;friend of mine&lt;&#x2F;a&gt; has recently started automating some of his tasks using Alfred workflows, a feature of Alfred I&#x27;ve been using for years, but have never really thought about using for my own stuff before.&lt;&#x2F;p&gt;
&lt;p&gt;Although they can sometimes come across rather gimicky, Alfred workflows are actually quite powerful. There&#x27;s a huge amount of possibilities built right in, but in this post I really only want to focus on a short introduction to so called &lt;em&gt;script filters&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Please be aware that workflows are a paid &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.alfredapp.com&#x2F;powerpack&#x2F;&quot;&gt;powerpack&lt;&#x2F;a&gt; feature of Alfred. I can definitely recommend the upgrade though!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-a-script-filter&quot;&gt;What&#x27;s a &lt;em&gt;Script Filter&lt;&#x2F;em&gt;?&lt;&#x2F;h2&gt;
&lt;p&gt;On creating a new blank workflow you&#x27;re presented with an empty page. Here you can add a bunch of stuff, but the most straight-forward thing (in my opinion at least) is a script filter.&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;&#x2F;img&#x2F;alfredworkflows_1.png&quot; alt=&quot;&quot;&#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Script filters are input actions, meaning they can be run on certain keywords and with certain arguments. The argument can then be passed on to a given script (hence the name), which does some 🎩🐰✨ and returns some items for Alfred to display.&lt;&#x2F;p&gt;
&lt;p&gt;Alfred has a list of supported default &quot;languages&quot;, including bash or zsh, php, ruby, python, perl and applescript. Or you can just choose &quot;External script&quot; and go with whatever you want.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Caveat&lt;&#x2F;strong&gt;: If you&#x27;re looking to distribute your workflow to others, it might make sense to go with something that they can run natively without having to install additional dependencies. My first workflow was written in Node.js, which unfortunately doesn&#x27;t come preinstalled on macOS. And then one of the users had an older version running into all kinds of additional errors. On the other hand, if you&#x27;re writing a wrapper for npm tooling, node will probably be on your user&#x27;s machines and the language is their tool of choice anyways. It&#x27;s always a question of your target audience.&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s also noteworthy here is the &quot;Run Behaviour&quot;. Alfred defaults to running one instance of your script after each other. That means that if an instance is already running (maybe being rather slow due to a network call or something else), any further user input will only be processed after the first script has finished and a new instance can be started.
My experience has been that this can easily be changed to terminate the previous instance if the users adds additional input for most cases, making things a lot snappier.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-started-with-a-script-filter&quot;&gt;Getting Started with a Script Filter&lt;&#x2F;h2&gt;
&lt;p&gt;A user&#x27;s input is handed over to your script as argv by default, making it trivial to access this with your language of choosing.&lt;&#x2F;p&gt;
&lt;p&gt;To show a list of items in Alfred once you&#x27;re done you have to do nothing more than output some JSON (or XML) to STDOUT.
Alfred has &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.alfredapp.com&#x2F;help&#x2F;workflows&#x2F;inputs&#x2F;script-filter&#x2F;json&#x2F;&quot;&gt;pretty good documentation&lt;&#x2F;a&gt; on how your outputted data has to be formatted for Alfred to pick it up.&lt;&#x2F;p&gt;
&lt;p&gt;The basic idea is having a list of items with their respective fields. Most of them are pretty self-explanatory or well explained in the docs linked above.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;items&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;An item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;subtitle&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Some description &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;arg&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;an argument&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;icon&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;path&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;link to an image&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Setting an argument is especially helpful for other actions, as this is what is passed on to them. You can also specify different arguments for modifier keys, so a different actions will be passed on if the user holds ⌘ for example.&lt;&#x2F;p&gt;
&lt;p&gt;Alfred also does a lot for you out-of-the-box if you want to list files from your harddrive.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-a-helper-library&quot;&gt;Using a Helper Library&lt;&#x2F;h2&gt;
&lt;p&gt;You&#x27;ll make your life a lot easier using one of the plentiful alfred-workflow helper libraries out there, surely also for your language of choice.&lt;&#x2F;p&gt;
&lt;p&gt;Two I&#x27;d like to highlight are &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sindresorhus&#x2F;alfy&quot;&gt;Alfy&lt;&#x2F;a&gt; by Sindre Sorhus (Javascript) and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BenchR267&#x2F;goalfred&quot;&gt;goalfred&lt;&#x2F;a&gt; by Benjamin Herzog (golang).
Both have a fantastic API making your life a lot easier by just passing along the items and setting appropriate attributes.&lt;&#x2F;p&gt;
&lt;p&gt;Something specific to goalfred are the so called complex arguments. It turns out to be rather tricky if you want to pass along specific variables to further actions. Ben and I figured that out together as I was trying to build a workflow showing departures in our local public transport network (DVB in Dresden, Germany).&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;iframe src=&#x27;https:&#x2F;&#x2F;gfycat.com&#x2F;ifr&#x2F;CluelessSaneAtlasmoth&#x27; frameborder=&#x27;0&#x27; scrolling=&#x27;no&#x27; width=&#x27;640&#x27; height=&#x27;377&#x27; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;When hitting enter on an item I wanted to schedule a notification to be displayed several minutes before the departure. My first implementation included a small Swift app using system APIs to schedule that as it turns out you can&#x27;t schedule future notifications via applescript 😕
We later figured out how to implement the same functionality directly within the workflow getting rid of the rather cumbersome and awkward first implementation. Ben went ahead and built that directly into goalfred 🙂 A big recommendation goes out to that library.&lt;&#x2F;p&gt;
&lt;p&gt;Using that we can easily throw together a small (albeit useless) workflow script in go greeting the name the user enters or no one if no input is given.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;github.com&#x2F;BenchR267&#x2F;goalfred&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Deals with the normalizing issue below.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    query&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; goalfred&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NormalizedArguments&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; goalfred&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; query&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        goalfred&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;goalfred&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Item&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            Title&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello world!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        goalfred&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;goalfred&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Item&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            Title&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; 👋&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; query&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I would usually compile this running &lt;code&gt;go build &amp;lt;file&amp;gt;.go&lt;&#x2F;code&gt; and setting Alfred to run the binary as an external script, but you can also use a bash script filter to have Alfred run &lt;code&gt;go run &amp;lt;file&amp;gt;.go&lt;&#x2F;code&gt; or the binary, whatever you prefer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problems&quot;&gt;Problems&lt;&#x2F;h2&gt;
&lt;p&gt;A pain point specific to non-ascii-compatible languages is Alfred&#x27;s handling of special characters. It uses NSTask internally, which results in umlauts for example being split up into their parts before being passed on, meaning &lt;code&gt;ü&lt;&#x2F;code&gt; becomes &lt;code&gt;¨u&lt;&#x2F;code&gt;. &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.alfredforum.com&#x2F;topic&#x2F;2015-encoding-issue&#x2F;&quot;&gt;Here&lt;&#x2F;a&gt;&#x27;s some more info on that if you&#x27;re interested and some help on how to re-normalize the user&#x27;s input.&lt;&#x2F;p&gt;
&lt;p&gt;Something essential I haven&#x27;t found a good solution for are workflow updates. Some people publish their workflows using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.packal.org&quot;&gt;Packal&lt;&#x2F;a&gt;, which provides some help with that. There&#x27;s also a python lib with some magic.
The best solution I&#x27;ve seen this far is how Alfy recommends publishing workflows on npm and linking them to the correct directory on a post-install step. Still not perfect, but definitely interesting.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;If you want to check out the source to some workflows, I&#x27;ve published three of mine on GitHub: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kiliankoe&#x2F;alfred_dvb&quot;&gt;alfred dvb&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kiliankoe&#x2F;alfred_mensa&quot;&gt;alfred mensa&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kiliankoe&#x2F;alfred_n26&quot;&gt;alfred n26&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Got any additional input or cool workflow ideas? Hit me up on Twitter 🙃&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>WWDC 16</title>
    <published>2016-07-02T02:28:11+02:00</published>
    <updated>2016-07-02T02:28:11+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/wwdc-16/"/>
    <id>https://kilko.de/blog/wwdc-16/</id>
    <content type="html">&lt;p&gt;Had a fantastic time about two weeks ago at dubdubdeesee in San Francisco. First time at the West Coast and I&#x27;m completely blown away by the amount of amazing people and great time we had. This post is going to be a quick recap of my experiences along the way.&lt;&#x2F;p&gt;
&lt;p&gt;After not expecting to receive the scholarship by Apple at all, I unfortunately did not seem to qualify for Apple&#x27;s Travel Assistance, which meant I had to gather around ~1.5k€ for an unexpected and very sudden trip to San Francisco. Everything worked out though and I can&#x27;t say I&#x27;d regret even a moment of it all!&lt;&#x2F;p&gt;
&lt;p&gt;My flight went on the Saturday before and because I booked the cheapest flight possible at such a late time I only had a 10-hour layover in Munich 😅 Wasn&#x27;t quite as bad though, my parents live close by and we went for coffee and lunch in the city.&lt;&#x2F;p&gt;
&lt;p&gt;Arriving in San Francisco at 6 pm local time I was picked up by two friends, Ben and Micha, who also came to town for WWDC from Dresden. They had far better flights though without layovers like mine, so they left later, but arrived earlier 😄&lt;&#x2F;p&gt;
&lt;p&gt;We three spent the next day exploring the most known parts of the city. Had a look at Golden Gate from pretty much every angle, went to Fisherman&#x27;s Wharf and Coit Tower&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; to name a few.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;wwdc_16_1.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Ending the day we headed over to Twin Peaks for the sunset, where the view of the city was amazing! Only downside: It was freakin&#x27; freezing! We all had no idea that California even got this cold... No wonder Apple hands out jackets to WWDC attendees.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;wwdc_16_2.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I spent most of Sunday at the scholarship orientation that Apple had planned. There were a few interesting talks, lots of time to socialize with other scholars and some Apple employees and for those interested some dedicated selfie time with Tim Cook. I chose to skip that, the crowd was far too intimidating.&lt;&#x2F;p&gt;
&lt;p&gt;The best part was definitely meeting the other scholars and chatting about their backgrounds, projects and other shared interests. The apps some people built for the scholarship were seriously mind-blowing! I definitely had a great time there!&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a pic Apple photographers apparently took of Maria, Gustavo and me taking a selfie 😁 The image is straight out of the current version of the official WWDC app. The other pictures in the galleries there are also worth a look!&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;&#x2F;img&#x2F;wwdc_16_3.jpg&quot; alt=&quot;&quot;&#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Monday was keynote day. Woke up and walked to Bill Graham with Micha at around 5 am. We didn&#x27;t want to get up too early since the venue was far bigger than Moscone West the previous years. We were confident that 5 was early enough to get fairly good seats.
Turns out though that some Apple employees did their best to round up all scholars though and we got in a bit early and had fantastic seats right up in the in the first three rows at the front left and right.
Micha also had a great seat though, Apple&#x27;s decision to go with Bill Graham seemed like a very wise choice, it&#x27;s a huge place.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;wwdc_16_4.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t have a lot to say about the Keynote or Platforms State of the Union though. Their content is pretty known around the globe by now 😉 I&#x27;m just extremely excited to be using the announced features in my OS&#x27; as soon as possible and can&#x27;t wait to build stuff against the new APIs.&lt;&#x2F;p&gt;
&lt;p&gt;The rest of the week at Moscone West was just as great. We definitely had a blast in the sessions and just as much in the labs talking to some Apple engineers about the frameworks we&#x27;re using and some best practices. Just the atmosphere of having hundreds of developers around me interested in the same topics and building similar things was fantastic! There&#x27;s not much similar to how much that gets you motivated! 🤗&lt;&#x2F;p&gt;
&lt;p&gt;Our evenings were mostly spent at parties hosted by the great people at Twitter, Microsoft&#x2F;Xamarin, Mapbox, Lyft and Realm amongst a few others. I especially enjoyed the 1.0 launch party @ Realm HQ! Didn&#x27;t stay all too long though, since we at least wanted to check out the WWDC bash at Bill Graham as well. Didn&#x27;t expect such a big concert and I loved the food there!&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a few of us at Realm HQ. From right to left: Bilal, Fabian, Ben and me.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;wwdc_16_5.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We (Ben, Micha, Fabian and I) took off on Friday afternoon driving a few miles south visiting Facebook HQ (with a great tour from Max!&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;). Somewhat blown away by their campus to be honest! I was expecting a bigger building but not around 20 of them with all kinds of activities strewn throughout. As much as I despise the platform itself, it seems like an awesome place to work!&lt;&#x2F;p&gt;
&lt;p&gt;Also went by Apple HQ @ 1 Infinite Loop and Google HQ. Unfortunately no tours at both of them, but it was fun walking by nonetheless. Maybe next time? 😁&lt;&#x2F;p&gt;
&lt;p&gt;My last day in the area (Saturday - the others were staying until Tuesday) we (this time it was Ben, Micha, Matt, Max (not the one from FB) and me) spent in Yosemite, a seriously beautiful national park! Great weather and gorgeous views! Had a bit of bad luck though, because Obama and family were also here hiking, so there were a lot of people and we didn&#x27;t quite get to see everything we came for. Nevertheless definitely worth the 3-hour car ride though!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;wwdc_16_6.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And that&#x27;s pretty much it for my rather short overview of the week. As said before, we had a great time and I&#x27;d like nothing more than to be right back here in a year. Let&#x27;s see if I can make that work!&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;We didn&#x27;t want to go up though, as we had no intention of waiting there for roughly 45 minutes. The entire &lt;em&gt;culture of waiting&lt;&#x2F;em&gt; in the US is an aspect that really bothers me by the way. Enter a restaurant and be told that your table will be ready in half an hour? Wat? Not to mention more famous highlights like the DMV 😄 &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;And another guy whose name I unfortunately did not catch. We had a great time with you guys though! Maybe we&#x27;ll catch up at 33c3 end of the year? &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>On Quitting Facebook</title>
    <published>2016-06-30T02:24:08+02:00</published>
    <updated>2016-06-30T02:24:08+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/on-quitting-facebook/"/>
    <id>https://kilko.de/blog/on-quitting-facebook/</id>
    <content type="html">&lt;p&gt;I’ve been seriously disliking the idea of maintaining a Facebook profile for quite a while now. It adds close to nothing to my life. After having loaded the page it seems I am always being presented with the same content over and over. The newsfeed is completely and utterly useless.&lt;&#x2F;p&gt;
&lt;p&gt;A few months back I chose to delete my personal timeline. Facebook started to show me content from a few years back (from a much younger self) and I was presented with things that I really did not wish to display publicly online. It was nothing bad or deeply embarrassing, but I urge you to scroll all the way to the bottom of your FB profile and just start reading upwards. You&#x27;ll get what I mean 😉&lt;&#x2F;p&gt;
&lt;p&gt;I couldn&#x27;t and still can&#x27;t quite delete the entire profile though. I like the idea of having a social hub out there, a place where someone can enter my name into a search box and then contact me (via Messenger&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;#fn-1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;). And even though I maintain contact with most people via E-Mail and Twitter, there&#x27;s loads of others out there using FB and nothing else&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;#fn-2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. And I kinda want to be able to stay in contact with them as well.
In practice my &lt;em&gt;current&lt;&#x2F;em&gt; conversations in there are strewn across the last few months. One from March, one from April, two from June.
So it actually turns out that I have moved most of my conversations away from Messenger, that&#x27;s nice.&lt;&#x2F;p&gt;
&lt;p&gt;Another aspect I thought I&#x27;d miss out on when completely leaving the platform are events. A lot of my contacts plan parties and other events this way. Up until recently I&#x27;ve been using OS X&#x27;s FB integration (disgusting btw) to throw those into my calendar automatically. Which works to a certain degree. You can&#x27;t edit or delete events (or invitations), as this apparently is a read-only feed from FB. And when some contacts (you know who you are! 👀) started planning events spanning several months and thus completely spamming my entire calendar, I threw that out.
I have to say... I&#x27;m not entirely sure what I&#x27;ve missed in the months since, but I don&#x27;t think it has been all too much. Every now and then I receive a &quot;&lt;em&gt;Did you see the FB event and invitation?&lt;&#x2F;em&gt;&quot; message, which of course is a little more work on the side of the event organizers (which I regret), but I can&#x27;t say that it hasn&#x27;t been working out.&lt;&#x2F;p&gt;
&lt;p&gt;Summing up, neither Messenger nor events have been particularly good reasons to keep the dreaded profile around.&lt;&#x2F;p&gt;
&lt;p&gt;There is however one thing I just can&#x27;t seem to migrate well. And that&#x27;s FB groups.
For obvious reasons these are &lt;strong&gt;incredibly&lt;&#x2F;strong&gt; popular in my social circles. The ones I check regularly&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-3-1&quot;&gt;&lt;a href=&quot;#fn-3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; are a group related to my studies at my university (with tons of inside info, which kinda sucks to miss out on), our local Cocoaheads chapter (Would really welcome to see this someplace else! 😁) and a few others.&lt;&#x2F;p&gt;
&lt;p&gt;After having had this post as a draft for more than a year now, I&#x27;ve still gotten no closer to deleting my profile than deleting timeline contents and disabling posts there by others. But at least my FB usage has come down to a minimum, which is at least a small success. Maybe I will be able to get rid off it entirely at some point in the near future.&lt;&#x2F;p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;I want to give a quick shoutout to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;messenger.com&quot;&gt;messenger.com&lt;&#x2F;a&gt;, a site built by Facebook that contains only the messenger and nothing else. It&#x27;s actually quite nice! &lt;a href=&quot;#fr-1-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;Came into contact with this again after attending WWDC as a scholarship recipient. Most of the other scholars I talked to were expecting Messenger as a default way of communication. &lt;a href=&quot;#fr-2-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;I can recommend the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;itunes.apple.com&#x2F;app&#x2F;facebook-groups&#x2F;id931735837&quot;&gt;Groups&lt;&#x2F;a&gt; app for iOS at least. Works rather well. And also has well designed UI gestures. &lt;a href=&quot;#fr-3-1&quot;&gt;↩&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;section&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>WWDC 16 Scholarship</title>
    <published>2016-05-18T02:21:05+02:00</published>
    <updated>2016-05-18T02:21:05+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/wwdc-16-scholarship/"/>
    <id>https://kilko.de/blog/wwdc-16-scholarship/</id>
    <content type="html">&lt;p&gt;Apple was gracious enough to grant me a WWDC scholarship this year. I’m still blown away!&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;&#x2F;img&#x2F;wwdcscholarship.png&quot; alt=&quot;print(&amp;quot;Hello, Scholar!&amp;quot;)&quot;&#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The next challenge is obviously getting to SF. Unfortunately it’s not exactly cheap traveling all the way from Germany, but I’ll find a way. Definitely couldn’t justify missing this one-of-a-kind event.&lt;&#x2F;p&gt;
&lt;p&gt;Stay posted for more, especially once it’s June 😁&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Cheap DynDNS Alternative</title>
    <published>2016-03-07T02:19:48+02:00</published>
    <updated>2016-03-07T02:19:48+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/cheap-dyndns-alternative/"/>
    <id>https://kilko.de/blog/cheap-dyndns-alternative/</id>
    <content type="html">&lt;p&gt;Until recently I had the pleasure of living with a static IP. This made accessing my Raspberry PI from outside my home rather easy. I could just open the port on my router and hardcode the IP anywhere.&lt;&#x2F;p&gt;
&lt;p&gt;After moving though easy access like that unfortunately fell away. I looked into some services like DynDNS but found them cumbersome and hard to work with. A simple solution to this “problem” presented itself via CloudFlare. Why not just get my current IP every $TIME_INTERVAL or so and send that to CloudFlare via their API updating an existing DNS record.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s a simple ruby script for just that.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;! &#x2F;usr&#x2F;bin&#x2F;env ruby&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;net&#x2F;http&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;cloudflare&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Email used for login&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_EMAIL&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ENV&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;CLOUDFLARE_EMAIL&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; API Key, can be found here =&amp;gt; https:&#x2F;&#x2F;www.cloudflare.com&#x2F;a&#x2F;account&#x2F;my-account&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_API_KEY&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ENV&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;CLOUDFLARE_API_KEY&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The zone (aka domain) this is for, e.g. &amp;#39;example.com&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_ZONE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ENV&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;CLOUDFLARE_ZONE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The specific record name to edit, e.g. &amp;#39;record.example.com&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_RECORD_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; ENV&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;CLOUDFLARE_RECORD_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; get_ip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  Net&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;HTTP&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;URI&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;http:&#x2F;&#x2F;canihazip.com&#x2F;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; update_cloudflare&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;ip&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;  cf&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; CloudFlare&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;connection&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_API_KEY&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; CLOUDFLARE_EMAIL&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  begin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    rec_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    records&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; cf&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rec_load_all&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_ZONE&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    records&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;response&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;recs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;objs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;each&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; do&lt;&#x2F;span&gt;&lt;span&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;record&lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;      if&lt;&#x2F;span&gt;&lt;span&gt; record&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; CLOUDFLARE_RECORD_NAME&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        rec_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; record&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;rec_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        break&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;      end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Sending &amp;#39;1&amp;#39; for the TTL = &amp;#39;automatic&amp;#39; meaning approx. 300 (5 minutes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cf&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;rec_edit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_ZONE&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; rec_id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; CLOUDFLARE_RECORD_NAME&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ip&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  rescue&lt;&#x2F;span&gt;&lt;span&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;    puts&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;    puts&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Successfully updated A record for &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt;CLOUDFLARE_RECORD_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; to &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ip&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;update_cloudflare&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;get_ip&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The script relies on two things to work correctly:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure to set the record to be updated beforehand, this script will look at all records and update the IP of the correct one.&lt;&#x2F;li&gt;
&lt;li&gt;You need to pass a few environment variables as specified at the top of the script, namely &lt;code&gt;CLOUDFLARE_EMAIL&lt;&#x2F;code&gt;, &lt;code&gt;CLOUDFLARE_API_KEY&lt;&#x2F;code&gt;, &lt;code&gt;CLOUDFLARE_ZONE&lt;&#x2F;code&gt; and &lt;code&gt;CLOUDFLARE_RECORD_NAME&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now just periodically run it via cron or however you like 🙂&lt;&#x2F;p&gt;
&lt;p&gt;Setting up the record beforehand also gives you the freedom of specifying if you want CloudFlare tunneling for the record or not. Whatever you set will not be changed by this script.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE&lt;&#x2F;strong&gt;: Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;stefanmajewsky&quot;&gt;@stefanmajewsky&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jasontbradshaw&#x2F;gandi-dyndns&quot;&gt;here&lt;&#x2F;a&gt;’s a Python script for doing the same directly via Gandi.net. Awesome!&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Firewatch</title>
    <published>2016-02-13T02:17:52+02:00</published>
    <updated>2016-02-13T02:17:52+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/firewatch/"/>
    <id>https://kilko.de/blog/firewatch/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;firewatch_1.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Finished it two days ago and was left in some emotional turmoil. I believed the ending to be spoiled for me after accidentally having read a sentence on Reddit, but it turns out that was bullshit. Unfortunately that made it even worse.&lt;&#x2F;p&gt;
&lt;p&gt;It’s a gorgeous game, albeit relatively short (~4 hours). The gameplay mechanics work great (a bit unexpected at times) and the story is definitely captivating. Can’t say too much about it though without giving stuff away.
Definitely recommended! It’s &lt;strong&gt;not&lt;&#x2F;strong&gt; a horror game either, just thrilling at times and beautiful at others. Give it a shot! 😊&lt;&#x2F;p&gt;
&lt;p&gt;It’s available for Mac, Windows and PS4. Grab it on Steam &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;383870&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;firewatch_2.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Black Mirror</title>
    <published>2016-01-13T02:16:54+02:00</published>
    <updated>2016-01-13T02:16:54+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/blackmirror/"/>
    <id>https://kilko.de/blog/blackmirror/</id>
    <content type="html">&lt;p&gt;Blindly following a recommendation on Reddit I just finished watching the first season of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.imdb.com&#x2F;title&#x2F;tt2085059&quot;&gt;Black Mirror&lt;&#x2F;a&gt;. Not quite sure what I was expecting, only had the SciFi label in my head. Didn’t quite find that, more of a feeling of deep disgust with the different shown realities. Interesting nonetheless. And definitely a recommendation!&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>32c3</title>
    <published>2016-01-01T02:14:31+02:00</published>
    <updated>2016-01-01T02:14:31+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/32c3/"/>
    <id>https://kilko.de/blog/32c3/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;32c3_1.jpg&quot; alt=&quot;CCH outside&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That was seriously awesome! My first congress and I couldn’t be happier with it. Definitely coming back next year, for sure!&lt;&#x2F;p&gt;
&lt;p&gt;There’s so much I could write about here, but I’m going to keep it to a minimum.
I kinda regret watching quite a few talks live and not using the time at CCH to have a look at most assemblies and workshops. Will improve on that @ 33c3. All talks have since been published online at &lt;a href=&quot;https:&#x2F;&#x2F;kilko.de&#x2F;blog&#x2F;32c3&#x2F;media.ccc.de&quot;&gt;https:&#x2F;&#x2F;media.ccc.de&lt;&#x2F;a&gt;. Have a look, and come next year! 😊&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;32c3_2.jpg&quot; alt=&quot;the lounge&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>HeartBattery</title>
    <published>2015-11-20T02:07:50+02:00</published>
    <updated>2015-11-20T02:07:50+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/heartbattery/"/>
    <id>https://kilko.de/blog/heartbattery/</id>
    <content type="html">&lt;p&gt;A friend recently had the fun idea to write a small script for his machine running Arch displaying his current battery state as a series of hearts instead of the traditional boring percentage. He did so via a short python script and the magic of Font Awesome to integrate with Conky.&lt;&#x2F;p&gt;
&lt;p&gt;I have to admit that writing a quick script in your language of choice makes customization like this a lot easier than on OS X, but I liked the idea and wanted to follow along with something native as well. So today I wrote a little app in Swift titled HeartBattery.&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;&#x2F;img&#x2F;heartbattery.gif&quot; alt=&quot;&quot;&#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It doesn’t really do a whole lot besides pulling the state of your battery every five minutes and telling you a few things about it. The hardest part was actually getting the data and generating the image. Getting the data unfortunately isn’t as easy as reading it from a file somewhere like others systems do it, but you have to interface with some rather confusing C APIs. Not really wanting to do that I eventually stumbled across &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;beltex&#x2F;SystemKit&quot;&gt;SystemKit&lt;&#x2F;a&gt;, a Swift lib that abstracts all that away and offers an API that couldn’t be much simpler. It’s from the same person behind &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;beltex&#x2F;dshb&quot;&gt;dshb&lt;&#x2F;a&gt;, that amazingly pretty ncurses powered system monitor for OS X.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately it doesn’t seem possible to set a custom font for a status item and multiple images are also not an option (afaik), so I had to resort to compositing a few icons together on every refresh of the app to generate a single wide image for the menubar representing the current battery state. Really doesn’t feel like the cleanest option, but another thing learned can’t be bad either I guess.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to have a look you can find the source and download link on the GitHub repo &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kiliankoe&#x2F;HeartBattery&quot;&gt;here&lt;&#x2F;a&gt;. And as usual, if you have any ideas, suggestions or think my code can be improved upon and want to tell me how to pull it off, why not head straight for the issue&#x2F;pull request 😊&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;&#x2F;img&#x2F;heartbattery.png&quot; alt=&quot;&quot;&#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now all that’s missing is a bling sound whenever you lose a heart 😄&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>ParkenDD</title>
    <published>2015-02-26T02:01:49+02:00</published>
    <updated>2015-02-26T02:01:49+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/parkendd/"/>
    <id>https://kilko.de/blog/parkendd/</id>
    <content type="html">&lt;p&gt;A few months ago &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;codefor.de&quot;&gt;Code for Germany&lt;&#x2F;a&gt; launched. An &lt;em&gt;OK Lab&lt;&#x2F;em&gt; (&lt;em&gt;Open Knowledge Lab&lt;&#x2F;em&gt;) was founded in Dresden soon after, although similar projects (&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;offenesdresden.de&quot;&gt;offenesdresden.de&lt;&#x2F;a&gt;) were already in place. Everything merged and Dresden has had quite an active community interested in bringing Open Data to the next level. It&#x27;s awesome.&lt;&#x2F;p&gt;
&lt;p&gt;My first real contribution to this comes in form of an iPhone app using a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;RobTranquillo&#x2F;parkplaetzeDD&quot;&gt;PHP scraper backend&lt;&#x2F;a&gt; scraping the data at &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.dresden.de&#x2F;freie-parkplaetze&#x2F;&quot;&gt;dresden.de&#x2F;freie-parplaetze&lt;&#x2F;a&gt;. An Android application for this purpose came to life fairly quickly (find it &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;jkliemann.de&#x2F;parkendd&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;), so a clone for iOS just had to follow!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;parkendd.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It shows you whether a public parking lot is closed or not, how many spots are available and where it is on a map.
It&#x27;s currently waiting for review and will hopefully be on the App Store within a few days. Definitely fun to build and I learned quite a bit more about developing for iOS. Future projects will be even better and more fun to build ;)&lt;&#x2F;p&gt;
&lt;p&gt;The source for this application is available on Github. See https:&#x2F;&#x2F;github.com&#x2F;kiliankoe&#x2F;parkendd.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: Apple has just approved the first version of the app. It&#x27;s available here: https:&#x2F;&#x2F;itunes.apple.com&#x2F;de&#x2F;app&#x2F;parkendd&#x2F;id957165041&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Migrating to kilian.io</title>
    <published>2015-01-05T01:59:59+02:00</published>
    <updated>2015-01-05T01:59:59+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/kilian-io/"/>
    <id>https://kilko.de/blog/kilian-io/</id>
    <content type="html">&lt;p&gt;I spent most of today moving around my e-mail &lt;em&gt;infrastructure&lt;&#x2F;em&gt;. The way I had it configured would probably bring tears to most people&#x27;s eyes, so I&#x27;ll spare you the gruesome details. Let&#x27;s just say that nothing is forwarding to Google Mail any longer and I&#x27;ll be using a different address as my main contact address from here on out. Namely &lt;strong&gt;me@kilian.io&lt;&#x2F;strong&gt;. The domain kilian.io has been pointing to my server at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;uberspace.de&quot;&gt;Uberspace&lt;&#x2F;a&gt; for a little while now, but it hasn&#x27;t been much more than a little gimmick, just like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;kilian.sexy&quot;&gt;kilian.sexy&lt;&#x2F;a&gt; ;)
I&#x27;ve been starting to enjoy it a lot more than koeltzs.ch though, as it carries more personality, more &lt;em&gt;me&lt;&#x2F;em&gt; than just my last name. And since kilian@kilian.io just looks ridiculous I&#x27;m choosing to go with the very informal me@kilian.io instead.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;em&gt;old&lt;&#x2F;em&gt; domain koeltzs.ch isn&#x27;t going anywhere any time soon. I&#x27;ll probably be renewing it as well for the years to come, mails and redirects will still work. Transitioning basically my life&#x27;s worth of emails is no easy task either, but consider my Google Mail address officially deprecated from here on out.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve also used this chance to deactivate the catchall for all my domains. Mail bounces aren&#x27;t necessarily a bad thing and this should also greatly help reduce the amount of spam that hits my mailserver day and night.&lt;&#x2F;p&gt;
&lt;p&gt;My PGP key has also been updated with the new address and I&#x27;ll soon be transitioning to me@kilian.io for some of my xmpp&#x2F;jabber conversations as well. Even though my site already states this, that particular change will still take a bit.&lt;&#x2F;p&gt;
&lt;p&gt;I really do have to endorse &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fastmail.com&quot;&gt;Fastmail.com&lt;&#x2F;a&gt; somewhere throughout this post. Hosting my own mail server is something I definitely &lt;strong&gt;do not&lt;&#x2F;strong&gt; want to do be doing.  I have a tendency not to trust software that I maintain myself (I know that sounds sad) and even more so when I don&#x27;t understand it all. So even though I&#x27;d be able to keep it running, I&#x27;m not sure it would be doing so safely or for a very long time. I&#x27;m trusting this one with people that make this their living. The recommendation for Fastmail comes from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;marco.org&quot;&gt;Marco Arment&lt;&#x2F;a&gt;. They&#x27;ve been around for a little while and are doing some &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;blog.fastmail.com&#x2F;2014&#x2F;12&#x2F;23&#x2F;jmap-a-better-way-to-email&#x2F;&quot;&gt;really cool things&lt;&#x2F;a&gt; as well, they definitely know what they&#x27;re doing and are doing it well. And I enjoy paying for something of use to me, highlighting the fact that I&#x27;m probably not the product here.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m definitely going to run into a few gotchas with this move, I&#x27;m sure of that. But I have to be doing this someday. So &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;longestjokeintheworld.com&quot;&gt;better nate than lever&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s actually been a lot of fun and the only thing to change now is the domain for this blog. Unfortunately I can&#x27;t run this under two domains simultaneously, so I&#x27;m going to be making this switch Monday evening (as in today, depending on how current this is when you read it). So if you&#x27;re one of the few people I know to be subscribed to the feed, please update it to check blog.kilian.io from now on. Thanks :)&lt;&#x2F;p&gt;
&lt;p&gt;Edit: Done :)&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Pycharm and Java 8</title>
    <published>2014-12-19T01:57:50+02:00</published>
    <updated>2014-12-19T01:57:50+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/pycharm-and-java8/"/>
    <id>https://kilko.de/blog/pycharm-and-java8/</id>
    <content type="html">&lt;p&gt;There&#x27;s been a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;youtrack.jetbrains.com&#x2F;issue&#x2F;IDEA-131632&quot;&gt;bug&lt;&#x2F;a&gt; with current versions of Jetbrains products (not just Pycharm, but also IntelliJ IDEA, Android Studio, AppCode and surely the others as well) on OS X 10.10 Yosemite where the editor screen flickers uncontrollably when you scroll the view. It&#x27;s been making my workflow increasingly difficult and was generally unpleasing, but there is a way around this!&lt;&#x2F;p&gt;
&lt;p&gt;Kudos goes to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;justusadam.com&quot;&gt;Justus&lt;&#x2F;a&gt; for pointing this one out.&lt;&#x2F;p&gt;
&lt;p&gt;All you have to do is set your favorite Jetbrains IDE to use the JVM version 1.8 instead of the default 1.6. This is easily done on OS X by editing the &lt;code&gt;Info.plist&lt;&#x2F;code&gt; found at &lt;code&gt;&#x2F;Applications&#x2F;&amp;lt;PyCharm.app or something else&amp;gt;&#x2F;Contents&#x2F;Info.plist&lt;&#x2F;code&gt; like this.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;pycharm.png&quot; alt=&quot;screenshot&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Interestingly enough this will also make your Darcula theme a little darker (very pretty) and the font rendering gets a lot better as well. Perfect fix!&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Simple Robot</title>
    <published>2014-10-31T01:53:35+02:00</published>
    <updated>2014-10-31T01:53:35+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/simple-robot/"/>
    <id>https://kilko.de/blog/simple-robot/</id>
    <content type="html">&lt;p&gt;It&#x27;s been a while since I&#x27;ve touched Blender. A lot has changed, everything is awesome. There&#x27;s not all too much to say, but I&#x27;m a real sucker for pretty clay renders, so here&#x27;s a simple little robot.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;clay_posed_2400p.png&quot; alt=&quot;clay render&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note that this render is in 2400p, the native resolution of the new MacBook Pro. I didn&#x27;t want said device to go up in flames though, so a shoutout goes to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;render.st&quot;&gt;render.st&lt;&#x2F;a&gt; (and especially their signup bonus).&lt;&#x2F;p&gt;
&lt;iframe width=&quot;640&quot; height=&quot;480&quot; src=&quot;https:&#x2F;&#x2F;sketchfab.com&#x2F;models&#x2F;d560abac003249b293aed39a850dee72&#x2F;embed&quot; frameborder=&quot;0&quot; allowfullscreen mozallowfullscreen=&quot;true&quot; webkitallowfullscreen=&quot;true&quot; onmousewheel=&quot;&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p style=&quot;font-size: 13px; font-weight: normal; margin: 5px; color: #4A4A4A;&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;sketchfab.com&#x2F;models&#x2F;d560abac003249b293aed39a850dee72?utm_source=oembed&amp;utm_medium=embed&amp;utm_campaign=d560abac003249b293aed39a850dee72&quot; target=&quot;_blank&quot; style=&quot;font-weight: bold; color: #1CAAD9;&quot;&gt;Simple Robot&lt;&#x2F;a&gt;
    by &lt;a href=&quot;https:&#x2F;&#x2F;sketchfab.com&#x2F;kiliankoe?utm_source=oembed&amp;utm_medium=embed&amp;utm_campaign=d560abac003249b293aed39a850dee72&quot; target=&quot;_blank&quot; style=&quot;font-weight: bold; color: #1CAAD9;&quot;&gt;kiliankoe&lt;&#x2F;a&gt;
    on &lt;a href=&quot;https:&#x2F;&#x2F;sketchfab.com?utm_source=oembed&amp;utm_medium=embed&amp;utm_campaign=d560abac003249b293aed39a850dee72&quot; target=&quot;_blank&quot; style=&quot;font-weight: bold; color: #1CAAD9;&quot;&gt;Sketchfab&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Installing OS X applications through Homebrew</title>
    <published>2014-07-21T01:47:19+02:00</published>
    <updated>2014-07-21T01:47:19+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/brew-cask/"/>
    <id>https://kilko.de/blog/brew-cask/</id>
    <content type="html">&lt;p&gt;I have to admit to having been slightly jealous of a friend running Arch Linux on his laptop when he went ahead and installed &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.xmind.net&#x2F;de&#x2F;&quot;&gt;xmind&lt;&#x2F;a&gt; right through his package manager (or actually &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;yaourt&quot;&gt;yaourt&lt;&#x2F;a&gt;, but I won&#x27;t make the distinction here). Now don&#x27;t get me wrong, there&#x27;s nothing wrong with installing applications on OS X. It&#x27;s dead simple.&lt;&#x2F;p&gt;
&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;&#x2F;img&#x2F;brew-cask.png&quot; alt=&quot;drag&#x27;n drop&quot;&#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You download the disk image, double-click it to mount and drag the .app onto your linked Applications directory. But it&#x27;s so graphical... Any self-respecting &lt;em&gt;appleleptic&lt;&#x2F;em&gt; nowadays already has the fantastic &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;brew.sh&#x2F;&quot;&gt;Homebrew&lt;&#x2F;a&gt; package manager installed on their system anyways, which is awesome stuff, without question. And there is one package I have to admit to not having heard of before a few days ago, even though it has a rather big following which helps us out here.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;caskroom.io&#x2F;&quot;&gt;Homebrew Cask&lt;&#x2F;a&gt; (you just gotta love the naming scheme of things in the Homebrew universe: Brew, Cask, Formula, Tap, etc) and its sole purpose is allowing you to run commands like &lt;code&gt;brew cask install xmind&lt;&#x2F;code&gt;. It worries about where to get the latest version from, where to put it, and for a few selected applications also how to specifically uninstall them when you run &lt;code&gt;brew cask uninstall app-name&lt;&#x2F;code&gt;. It also doesn&#x27;t just do apps, but preference panes, quicklook plugins, services, widgets and a few more. It&#x27;s an allround powertool for installing stuff on your mac that homebrew itself doesn&#x27;t handle.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;so-how-do-you-get-it&quot;&gt;So how do you get it?&lt;&#x2F;h3&gt;
&lt;p&gt;Unfortunately cask isn&#x27;t included in main homebrew repo, but don&#x27;t despair, that just makes us run one more command.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew tap caskroom&#x2F;cask&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew install brew-cask&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note: You might find others online referencing to tap phinze&#x2F;cask instead, which isn&#x27;t wrong, it&#x27;s just old. Fortunately GitHub redirects phinze&#x2F;cask to caskroom&#x2F;cask, but this might stop working one day, so just go with caskroom&#x2F;cask and you won&#x27;t have to worry.&lt;&#x2F;p&gt;
&lt;p&gt;Cask is now installed on your system. It&#x27;s main working directory is &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;Library&#x2F;Taps&#x2F;caskroom&#x2F;homebrew-cask&lt;&#x2F;code&gt;. This is where the git repo resides, which is one of the most awesome facts about homebrew by the way. All of it being just a bunch of ruby scripts and a git repo to manage them. You don&#x27;t actually need to do anything in this directory though, unless you want to add your own casks, which we&#x27;ll go over how to do in a sec.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-cask&quot;&gt;Using cask&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask update&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is basically an alias to &lt;code&gt;git pull origin master&lt;&#x2F;code&gt; in the cask repo and updates your list of available casks to the current status. Running this often is key, as new casks are created and others are updated on a daily basis. Run this if you can&#x27;t find a cask or before installing one if you haven&#x27;t updated in a little why to ensure you get the newest version of an application.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask search app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Want to know if an app you&#x27;re looking to install is available through cask? Just run this. The naming strategy is pretty straight forward with everything being lowercase and dashes for spaces. Search also finds partial results, so for example this works:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;kilian@Apollo:~$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; brew&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cask&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; search&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; intellij&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Partial&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; matches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;intellij-idea&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;     intellij-idea-ce&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask install app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Installs an app. Simple. It also automatically runs through any zip files, disk images or packages. It&#x27;ll prompt you if an admin password is needed.
One thing to note is that applications are by default &lt;em&gt;not&lt;&#x2F;em&gt; installed to &lt;code&gt;&#x2F;Applications&lt;&#x2F;code&gt;, but to &lt;code&gt;&#x2F;opt&#x2F;homebrew-cask&#x2F;Caskroom&#x2F;app-name&#x2F;version&#x2F;app-name.app&lt;&#x2F;code&gt; and then symlinked to &lt;code&gt;~&#x2F;Applications&lt;&#x2F;code&gt;. This is no problem for your mac, but in case you&#x27;d like to link it to &lt;code&gt;&#x2F;Applications&lt;&#x2F;code&gt; instead, run the command like this &lt;code&gt;brew cask install app-name --appdir=&#x2F;Applications&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask uninstall app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This should be about as self-explanatory as it gets. As I mentioned already however this doesn&#x27;t just remove the application from your system, but for a few special apps that require their own uninstaller to be run, this is done instead.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lists all your installed casks. If you run it together with an application name it&#x27;ll tell you which files are installed and where they reside.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask cleanup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cleans up downloaded files which are usually not removed after installing an application. Might free you up quite a bit of space on your harddrive.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask home app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Takes you to the homepage of an application. Be it the developer&#x27;s site, a GitHub repo or whatnot.&lt;&#x2F;p&gt;
&lt;p&gt;And last but not least, an essential one if you&#x27;re an &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.alfredapp.com&#x2F;&quot;&gt;Alfred&lt;&#x2F;a&gt;-junkie like me:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask alfred link&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This adds the install directory to Alfred&#x27;s search path so you can find your installed applications with Alfred.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s actually all there is to using cask. It&#x27;s a fantastic little application and the list of applications it can install is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caskroom&#x2F;homebrew-cask&#x2F;tree&#x2F;master&#x2F;Casks&quot;&gt;pretty damn huge&lt;&#x2F;a&gt; (1.723 entries as of now).&lt;&#x2F;p&gt;
&lt;p&gt;There are however two other points I&#x27;d like to go over.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-brewfiles&quot;&gt;Using Brewfiles&lt;&#x2F;h3&gt;
&lt;p&gt;Homebrew includes the wonderful functionality of being able to run a number of commands from a file you specify. This is typically called a Brewfile, but you can name it whatever you want.&lt;&#x2F;p&gt;
&lt;p&gt;This of course finds it main purpose in installing a few packages together and &lt;em&gt;bundle&lt;&#x2F;em&gt; them together.&lt;&#x2F;p&gt;
&lt;p&gt;Using this is extremely simple, here&#x27;s a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kiliankoe&#x2F;dotfiles&#x2F;blob&#x2F;master&#x2F;Brewfile&quot;&gt;Brewfile&lt;&#x2F;a&gt; I set up so I can quickly install all the applications I use regularly on my system if I have to start from scratch (new machine, dead harddrive or ritual cleansing, who knows).&lt;&#x2F;p&gt;
&lt;p&gt;As you can see it just includes a command on each line without the &lt;code&gt;brew&lt;&#x2F;code&gt; command itself.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;install node&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;install ffmpeg&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# set up brew cask&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;tap caskroom&#x2F;cask&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;install brew-cask&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# cleanup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cask alfred link&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cleanup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cask cleanup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lines starting with a &lt;code&gt;#&lt;&#x2F;code&gt; are comments and will be ignored by homebrew.&lt;&#x2F;p&gt;
&lt;p&gt;Use the command &lt;code&gt;brew bundle filename&lt;&#x2F;code&gt; to let brew run through it. Already installed packages and applications will not be overwritten.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-your-own-casks&quot;&gt;Creating your own casks&lt;&#x2F;h3&gt;
&lt;p&gt;So you found something that&#x27;s not yet available through cask and want to become one of the 743 current contributors? That&#x27;s awesome! And it&#x27;s really quite simple as well. All you need is some basic knowledge how to use git and you&#x27;re set. Let&#x27;s take a look.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start by setting up your fork on GitHub. Visit the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caskroom&#x2F;homebrew-cask&quot;&gt;official repo&lt;&#x2F;a&gt; of cask on GitHub and click on the fork button in the upper right-hand corner. Wait a few seconds and GitHub will create a clone of the repo under your account. We&#x27;ll use this to change what we want and send a pull request back to main repo.&lt;&#x2F;p&gt;
&lt;p&gt;Now open up a terminal and navigate to the cask repo on your harddrive.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ cd &#x2F;usr&#x2F;local&#x2F;Library&#x2F;Taps&#x2F;caskroom&#x2F;homebrew-cask&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s branch off into a feature branch to add the new cask.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git branch add-app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git checkout add-app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can create the cask by running&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ brew cask create app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Please check the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caskroom&#x2F;homebrew-cask&#x2F;blob&#x2F;master&#x2F;CONTRIBUTING.md#cask-naming-details&quot;&gt;naming details&lt;&#x2F;a&gt; before naming your cask. The rest of the contributing guide contains all the following details as well, but I&#x27;m going to go through them anyways.&lt;&#x2F;p&gt;
&lt;p&gt;This should immediately open up your default editor where you can go ahead and fill out the details like version number, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;notepad2.blogspot.de&#x2F;2012&#x2F;07&#x2F;mac-os-x-how-to-generate-md5-sha1.html&quot;&gt;hash&lt;&#x2F;a&gt; of the download, link to the download, homepage and name of the to be linked file. After saving this you can already go ahead and try it out by installing it yourself. Use &lt;code&gt;brew cask edit app-name&lt;&#x2F;code&gt; to quickly jump back to editing the file again. If you&#x27;re sure everything works also run &lt;code&gt;brew cask audit app-name --download&lt;&#x2F;code&gt; for some additional checks.&lt;&#x2F;p&gt;
&lt;p&gt;Time to commit. Add your cask and commit it with a sensible message containing the name and version number.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git add Casks&#x2F;app-name.rb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git commit -m &amp;#39;Add app-name v0.1&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now let&#x27;s add our fork as a remote so we can push the changes to GitHub with one of the following two commands depending on if you interact with GitHub over SSH (second command) or not (first one).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git remote add fork https:&#x2F;&#x2F;github.com&#x2F;your-username&#x2F;homebrew-cask.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git remote add fork git@github.com:your-username&#x2F;homebrew-cask.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And push it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git push fork add-app-name -u&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can now visit the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caskroom&#x2F;homebrew-cask&quot;&gt;main cask repo&lt;&#x2F;a&gt; where a green button to submit your pull request should be greeting you. Click that, check the changed files just to be sure you did what you wanted and submit it. The repo is very active and presuming everything is awesome it shouldn&#x27;t be long for it to be merged.&lt;&#x2F;p&gt;
&lt;p&gt;Just don&#x27;t forget to switch back to the master branch in your local cask repo, otherwise you won&#x27;t be getting future updates ;)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git checkout master&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can also delete your local feature branch if everything is done.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git branch -d add-app-name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You&#x27;re now officially a contributer to cask, awesome stuff.&lt;&#x2F;p&gt;
&lt;p&gt;This also concludes this post. I hope it was interesting enough and you find cask to be just as useful as many others and I do.&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>This is getting ridiculous</title>
    <published>2014-04-21T01:43:52+02:00</published>
    <updated>2014-04-21T01:43:52+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/bike/"/>
    <id>https://kilko.de/blog/bike/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;bike.jpg&quot; alt=&quot;missing or broken bike parts&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;November 29th, 2012: Left a math lecture on a rainy day and wanted to get home as quick as possible. But it looked like I would be carrying my bike home seeing how some douche had stolen my front wheel on campus. Seeing how I moved to Dresden with &lt;em&gt;quick release skewers&lt;&#x2F;em&gt; (if I can trust dict.leo.org) however, this happening was just a matter of time.
Instead of directly buying a new wheel (and new brakes, thank you, asshole! Oh, and a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.pitlock.de&#x2F;&quot;&gt;Pitlock&lt;&#x2F;a&gt; system) I went with a completely new (as in old) and cheap bicycle. Fast forward to January and a few stitches on my face, and I wanted to go back to riding &lt;em&gt;my&lt;&#x2F;em&gt; bike.
&lt;strong&gt;Total cost: ~200 €&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;December 16th, 2013: Right in front of my window, where my bike is usually locked up, some asshat stole my handlebar. At least he&#x2F;she had the decency of not cutting anything off, but taking their time to nicely remove my brakes and gear levers without damaging anything. It still took me a while to fix this, seeing how winter came. And I had bought the wrong handlebars as a replacement as well. But I finally fixed it... on April 16th.
&lt;strong&gt;Total cost: ~30 €&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;April 21st, 2014: I was able to ride my bike to university once. ONCE! Just once, before wanting to do the same again today. But no, my rear gear shift was completely missing and the chain was cut. This is just perfect. The bike was again locked up right outside my window, but I noticed nothing. I love the people around here...
&lt;strong&gt;Total cost this time: 200 €... Well crap&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Update January 2015: One more theft in the meantime and brakes missing tonight. Both times from inside the locked bicycle room in my basement. Yay (┛ò__ó)┛彡┻━┻&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Valentina Kalashnikova - Last Dangerous Racoons</title>
    <published>2013-11-07T20:53:48+02:00</published>
    <updated>2013-11-07T20:53:48+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/valentina-kalashnikova/"/>
    <id>https://kilko.de/blog/valentina-kalashnikova/</id>
    <content type="html">&lt;p&gt;Just got back from an awesome gig from a small local band a friend of mine plays in. They&#x27;re called the &lt;strong&gt;Last Dangerous Racoons&lt;&#x2F;strong&gt; and this title of theirs is definitely worth a listen!&lt;&#x2F;p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;166&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; src=&quot;https:&#x2F;&#x2F;w.soundcloud.com&#x2F;player&#x2F;?url=https%3A&#x2F;&#x2F;api.soundcloud.com&#x2F;tracks&#x2F;43418321&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Also check out their &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lastdangerousracoons.de&quot;&gt;site&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lastdangerousracoons.bandcamp.com&#x2F;&quot;&gt;bandcamp&lt;&#x2F;a&gt; if you&#x27;re interested.&lt;&#x2F;p&gt;
</content>
  </entry>
  <entry xml:lang="en">
    <title>Launching Alfred with Caps Lock on OS X</title>
    <published>2013-11-05T20:50:33+02:00</published>
    <updated>2013-11-05T20:50:33+02:00</updated>
    <author><name>Kilian Koeltzsch</name></author>
    <link rel="alternate" type="text/html" href="https://kilko.de/blog/alfred-caps-lock/"/>
    <id>https://kilko.de/blog/alfred-caps-lock/</id>
    <content type="html">&lt;p&gt;In great contrast to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.alfredapp.com&#x2F;&quot;&gt;Alfred&lt;&#x2F;a&gt; being one of those apps I love more than anything on OS X, the caps lock key is something utterly useless nowadays (or ever for that matter). And if you do happen to find yourself in an argument on the internet, even &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=xznylVUyND0&quot;&gt;Homer Simpson agrees&lt;&#x2F;a&gt; that you put a lot more weight into your statement if you take your time to press the shift key. The already mentioned Alfred however is so immensely useful, that even two keys for its hotkey (the default &lt;code&gt;alt&lt;&#x2F;code&gt; + &lt;code&gt;space&lt;&#x2F;code&gt;) is too much. So why not combine the two and have a dedicated &lt;em&gt;launch everything and search for stuff anywhere&lt;&#x2F;em&gt; key on your keyboard?&lt;&#x2F;p&gt;
&lt;p&gt;So let&#x27;s go ahead and set that up.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-install-seil&quot;&gt;Step 1 - Install Seil&lt;&#x2F;h3&gt;
&lt;p&gt;The very cool Takayama Fumihiko (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;tekezo&quot;&gt;@tekezo&lt;&#x2F;a&gt;) wrote a great little tool dedicated to changing the behavior of the caps lock key (and some others for that matter) on Macs. Check out his site and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pqrs.org&#x2F;osx&#x2F;karabiner&#x2F;seil.html.en&quot;&gt;download it&lt;&#x2F;a&gt;. And yes, this app works just fine with OS X 10.9 Mavericks and should also already support 10.10 Yosemite. Note: Before a few versions ago Seil was known as PCKeyboardHack.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-2-getting-the-relevant-key-code&quot;&gt;Step 2 - Getting the relevant key code&lt;&#x2F;h3&gt;
&lt;p&gt;You could waste some time looking up the key code for the caps lock key, or you could download this nifty &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;manytricks.com&#x2F;keycodes&#x2F;&quot;&gt;little app&lt;&#x2F;a&gt; to check it quickly.&lt;&#x2F;p&gt;
&lt;p&gt;For me it was &lt;strong&gt;103&lt;&#x2F;strong&gt;, but I don&#x27;t want to guarantee that it&#x27;ll be the same for you.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-mapping-the-functionality&quot;&gt;Step 3 - Mapping the functionality&lt;&#x2F;h3&gt;
&lt;p&gt;Unfortunately the caps lock key is a bit special. It isn&#x27;t really registered by the system as a normal key like pretty much anything else, so we&#x27;re going to have to map it to something. The F-keys on your keyboard are pretty useful for that, so just choose one of those, as long as you&#x27;re not using it for anything else. It doesn&#x27;t matter if it&#x27;s a media key, if no function on your machine is set to happen when you press &lt;code&gt;fn&lt;&#x2F;code&gt; + &lt;code&gt;FX&lt;&#x2F;code&gt; (I&#x27;m using &lt;code&gt;F11&lt;&#x2F;code&gt;), this would work.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re not sure about this, check your System Preferences &amp;gt; Keyboard &amp;gt; Keyboard Shortcuts. Any functionality would be listed there.&lt;&#x2F;p&gt;
&lt;p&gt;Open Key Codes, hold &lt;code&gt;fn&lt;&#x2F;code&gt; if necessary and press your key. Aside from all the other information that should now be displayed, remember the key code.&lt;&#x2F;p&gt;
&lt;p&gt;Now open up PCKeyboardHack, check the &lt;em&gt;Change Caps Lock&lt;&#x2F;em&gt; option and set it to the key code we just looked up.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-4-alfred&quot;&gt;Step 4 - Alfred&lt;&#x2F;h3&gt;
&lt;p&gt;All that&#x27;s left is changing Alfred&#x27;s preferences. In the General tab, you&#x27;ll see the Alfred Hotkey option. Set this to the F-key you chose earlier, remember to hold &lt;code&gt;fn&lt;&#x2F;code&gt; if you&#x27;re on an Apple keyboard.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-5-disable-caps-lock&quot;&gt;Step 5 - Disable caps lock&lt;&#x2F;h3&gt;
&lt;p&gt;This step really isn&#x27;t necessary for this to already be working, try it out!&lt;&#x2F;p&gt;
&lt;p&gt;Something that seemed to be bothering me, is that Macs have a built-in feature to protect you from accidentally activating caps lock in every day use. This is a short delay when activating it (not deactivating, just activating), so when you press the key fast enough, nothing will happen. The only way to get around this is disabling caps lock itself, but that&#x27;s not really a problem since we&#x27;re not using it for its actual function anyways.&lt;&#x2F;p&gt;
&lt;p&gt;The option for this is in the System Preferences &amp;gt; Keyboard &amp;gt; Modifier Keys. Set caps lock to &lt;em&gt;No Action&lt;&#x2F;em&gt; here. That should do the trick. And now have fun!&lt;&#x2F;p&gt;
&lt;p&gt;By the way:
the famous &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Chromebook&quot;&gt;CR-48 Chromebooks&lt;&#x2F;a&gt; also had a dedicated search key instead of caps lock. I was jealous from the start, but this fixed it :)&lt;&#x2F;p&gt;
</content>
  </entry>
</feed>
