Planet dgplug

January 21, 2022

Darshna Das

Hana hana Linux and system startup!

In my last blog, I learnt about the history of Linux. How a community has a power to come together from diverse field and make different distibutions of Linux. In this blog I will jot down about the system setup of Linux. What happens after our laptop/computer is switched on. As said Hana hana – step-by-step!

After I switch on my laptop, the BIOS (Basic Input/Output System) initializes the hardware and tests the main memory, this is aka POST (Power on self test). The BIOS software is stored on the ROM chip of motherboard. After this the remaining steps are done by the OS. After the POST is completed the system control passes from BIOS to boot loader. The boot loader is presented on one of the hard disks of system. A number of boot loaders exist for Linux, among them the most common ones are GRUB (GRand Unified Boot loader), ISOLINUX and DAS U-Boot. When booting linux the boot loader is responsible for executing the kernel image and the initial RAM disk. The boot loader resides on the first sector of the hard disk aka Master Boot Record(MBR). The MBR is 512 bytes and at this stage the boot loader examines the partition table and finds a bootable partition. Once it finds a bootable partition it then searched foe the second stage boot loader, for eg: GRUB and then loads into RAM. The second stage boot loader resides under /boot. After this a splash screen is displayed and the user is given a choice to select which OS to boot. The next step is followed by the boot loader, as per the selected OS, it loads the kernel of the selected OS into RAM and passes control to it. Kernels are always compressed so it’s first job is to compress itself and then it will analyze the system hardware and check and initialize any hardware device driver built into kernel.


The initramfs filesystem image contains the programs and binary files which are needed to perform all the actions to mount the proper root filesystem, like providing kernel functionality for the needed filesystem and device drivers for mass storage controllers with facility called udev(user’s drive) which is responsible for locating which device is present, they need to operate and load it properly. The mount program instructs that OS that a filesystem is ready to use. If this is successful then initramfs is cleared from RAM, and the initramfs program is executed in the root file system(/sbin/init). init handles the overall pivoting of the final real root system.

Near the end of the boot process, init starts a number of text-mode login prompts. These enables one to give in username and password. However, if one is running a system with a graphical login interface, these steps cannot be seen then. The default command shell is bash (Bourne Again shell) but there are other advanced command shells. The boot loader downloads both the kernel and the inital-RAM based filesystem into the memory, so that the kernel can directly use it. When the kernel is loaded it immediately initializes computer memory and also configures all hardware attached to the system.

Kernel, init and Services.

Once the kernel has setup with it’s hardware and the mounted root filesystem, the kernel runs /sbin/init. This is now the initial services which then starts the other systems running. init is responsible for starting and shutting down the system cleanly. Another responsibility of init is to act as a manager for all non-kernel processes and upon the completion clean it and restart user login services when users log in and out. Dating back to traditional methods this same process has the system passing through a sequence of runlevels containing scripts that start and stop services.

Startup alternatives

SysVinit viewed things as a sequential process. One process needed to be completed before it could move onto the next process; thus startup did not take advantage of parallel processing that could be done on multiple processors or core. Over the time developers realised that they need methods with faster capabilities. The two main alternatives were developed:


  • Developed by Ubuntu and first included in 2006
  • Adopted in Fedora 9 (in 2008) and in RHEL 6 and its clones


  • Adopted by Fedora first (in 2011)
  • Adopted by RHEL 7 and SUSE
  • Replaced Upstart in Ubuntu 16.04

systemd features

Systems with systemd startup were faster than init method. This is due to it largely replaces a set of steps with parallelization techniques, which allows multiple services to initiate simultaneously. Complicated startup shell scripts are replaced my simple configuration files, which mentions whatneeds to be done before a system startup, how to execute, what conditions needs to be finished before the completion of the startup. What was before done by /sbin/init is now taken over by /lib/systemd/systemd. One of the basic most used systemd command is $systemctl. For an example:

  • Starting, stopping, restarting a service (using httpd, the Apache web server, as an example) on a currently running system:

$ sudo systemctl start|stop|restart httpd.service

Linux filesystem

There are different kind of filsystem used by linux:

  • Conventional disk filesystems: ext3, ext4, XFS, Btrfs, JFS, NTFS, vfat, exfat, etc.
  • Flash storage filesystems: ubifs, jffs2, yaffs, etc.
  • Database filesystems
  • Special purpose filesystems: procfs, sysfs, tmpfs, squashfs, debugfs, fuse, etc.

Let’s get the difference between partition and filesystems. A partition is a contiguous section of a disk and a filesystem is the method of storing or finding the files on a hard disk.

Partition as a container in which filesystem resides

Linux stores it’s files in a layout known as File System Hierarchy(FHS). It uses ‘/’ to separate paths and it does not have drive letter. Multiple drives and/or partitions are mounted as directories in the single filesystem. Removable media such as USB drives and CDs and DVDs will show up as mounted at /run/media/yourusername/disklabel for recent Linux systems,

All Linux filesystem names are case-sensitive, so /boot, /Boot, and /BOOT represent three different directories (or folders).

The partition layout needs to be decided at the time of installation

Like other operating systems, Linux distributions are provided on removable media such as USB drives and CDs or DVDs. Most Linux distributions also support booting a small image and downloading the rest of the system over the network. These small images are usable on media, or as network boot images, in which case it is possible to perform an install without using any local media.

by climoiselle at January 21, 2022 04:03 PM

Kushal Das

Using Python to access a Solid Pod

solid logo

From the project website:

Solid is a specification that lets people store their data securely in decentralized data stores called Pods. Pods are like secure personal web servers for your data.

We can host these Pods in personal servers or at any provider. Everything is tied up based on the user identity, called WebID. It is an HTTP URI described as RDF document.

You can decide who/what can access your data. Applications/humans can use Solid authentication and identify to the Pod server to access the data using open protocols.

How to get a Pod?

The website lists current vendors who provide Pod services. If you want to play around locally, you can run community server on your local system. Or just create an account at, and use that to learn more.

Using Python with Solid

We already have a solid-file Python module. You can install it via pip in a virtual environment.

$ python3 -m venv .venv
$ source .venv/bin/activate
$ python3 -m pip install solid-file

For the rest of the example code, I am going to use my Pod at the, feel free to replace the username/password and URL values in the code as required.

USERNAME = "kushaldas"
PASSWORD = "******************************************"

IDP = ''

from solid.auth import Auth
from solid.solid_api import SolidAPI

auth = Auth()
api = SolidAPI(auth)

Here we are importing the module, creating an Auth object and identify using username and password.

Then we will check if a folder exist or not (it does not exist yet), and create the folder in this case.

folder_url = f"{POD_ENDPOINT}/languages/"
if not api.item_exists(folder_url):

The output is <Response [201 Created]>.

Next, we create two text files.

data = io.BytesIO("I ❤️ 🦀".encode("utf-8"))
file_url = f"{folder_url}hello.txt"
print(api.put_file(file_url, data, 'text/plain'))
data = io.BytesIO(b"Already 10 years of SOPA blackout")
msg_url = f"{folder_url}message.txt"
print(api.put_file(msg_url, data, 'text/plain'))

We can then list all of the items under our subfolder.

folder_data = api.read_folder(folder_url)
files = "\n".join(list(map(lambda x:, folder_data.files)))
print(f'Files in the folder: \n{files}')

We can then try to read one of the files we just now created.

resp = api.get(file_url)


Files in the folder: 

Why am I looking at Solid?

Solid as the specification is evolving along with the community. One usecase for any government organization would be if the citizens can control the access to their own data, and people can authorize who gets to access their data. Solid can become answer to that question. The specification is loose enough to allow building things easily on top of it.

I would love to see Python as a major part of this ecosystem. The solid-file project maintainers are doing a great job. But, we need more related projects, including proper examples of various usecases. Maybe a server too.

January 21, 2022 07:42 AM

January 18, 2022

Darshna Das

Hana hana Linux learning – history.

I will be learning and writing about Linux learning. There might be few topics which might be repeated in my blog because I once started learning from this site 2 years ago. Before we proceed further, I would like to break down the word hana hana, this a korean word which means step by step.

Last 3 days I have finished two chapters, and I learnt about Linux history and terminologies. Jotting down my points below: As we know in 1991 Linus Torvalds created Linux kernel, and later Linux distributions were created by the community of developers. In 1998 major companies like IBM and Oracle announced their support for Linux as well. Linux borrows heavily from UNIX operating system the only difference is Linux is free to use. The files are stored in hierarchical filesystem with the top node of system root "/".

Linux makes it’s components available via files or objects that look like file. Processes, devices and network sockets are all represented by file like objects and can often be worked with using the same utilities for regular files. Another advantage of Linux i.e it can executed through multiple threads and these are known as daemons.


  • Kernel – The interface between hardware and application.
  • Distribution – Collection of softwares making up a Linux-based OS.
  • Boot loader – Boots the OS
  • Service – Program that runs as a background process.
  • Filesystem – Method for storing and organizing files.
  • Command line – Interface for trying commands on top of OS.
  • Shell – Command line interpreter that interprets the command line input and instructs the OS to perform any necessary tasks and commands.

A full Linux distribution consists of the kernel plus a number of other software tools for file, user management and software package management.

by climoiselle at January 18, 2022 03:43 PM

January 07, 2022

Kushal Das

Trouble with signing and notarization on macOS for Tumpa

This week I released the first version of Tumpa on Mac. Though the actual changes required for building the Mac app and dmg file were small, but I had to reap apart those few remaining hairs on my head to get it working on any other Mac (than the building box). It was the classic case of Works on my laptop.

The issue

Tumpa is a Python application which uses PySide2 and also Johnnycanencrypt which is written in Rust.

I tried both briefcase tool and manual calling to codesign and create-dmg tools to create the and the tumpa-0.1.3.dmg.

After creating the dmg file, I had to submit it for notarisation to Apple, following:

xcrun /Applications/ --notarize-app --primary-bundle-id "in.kushaldas.Tumpa" -u "" -p "@keychain:MYNOTARIZATION" -f macOS/tumpa-0.1.3.dmg

This worked successfully, after a few minutes I can see that the job passed. So, I can then staple the ticket on the dmg file.

xcrun stapler staple macOS/tumpa-0.1.3.dmg

I can install from the file, and run the application, sounds great.

But, whenever someone else tried to run the application after installing from dmg, it showed the following.

mac failure screenshot


It took me over 4 hours to keep trying all possible combinations, and finally I had to pass --options=runtime,library to the codesign tool, and that did the trick. Not being able to figure out how to get more logs on Mac was making my life difficult.

I had to patch briefcase to make sure I can keep using it (also created the upstream issue).

--- .venv/lib/python3.9/site-packages/briefcase/platforms/macOS/	2022-01-07 08:48:12.000000000 +0100
+++ /tmp/	2022-01-07 08:47:54.000000000 +0100
@@ -117,7 +117,7 @@
                     '--deep', str(path),
-                    '--options', 'runtime',
+                    '--options', 'runtime,library',

You can see my build script, which is based on input from Micah.

I want to thank all of my new friends inside of SUNET who were excellent helping hands to test the multiple builds of Tumpa. Later many folks from IRC also jumped in to help to test the tool.

January 07, 2022 09:10 AM

January 03, 2022

Nabarun Pal

What's the plan for 2022?

I want to spend more time in 2022 to “Do Less!”, take things as they come, take breaks and try to travel or work on mechanical keyboards during those breaks. The time off that I took in 2021 - first half of August and last half of December, gave me a lot of breathing space and time to rethink priorities especially the first one where I partially overcame burnout due to several factors (Thanks to VMware for the generous leaves!).

Last year, I started actively taking care of my health. I jogged for ~700kms in the last quarter (Oct-Dec) although I did not jog during the time I was travelling to Bangalore/Delhi/Kolkata. I want to continue the same trend and target atleast 3000kms of jogging and a 10km run in 2022.

2021 was also when I moved back to my hometown, Agartala, and that too after a span of 9 years. I spent a lot of time with close family members and friends from school. I plan to spend more time with people who I care about and who care about me, be it in Bangalore or Agartala.

January 03, 2022 04:02 PM

December 28, 2021

Bhavin Gandhi

Using GNU Emacs in Linux Foundation exam

I cleared the Certified Kubernetes Security Specialist (CKS) exam recently. It is an exam where one has to perform tasks on the clusters to solve the scenario based questions. In this post, I will be talking on how I used GNU Emacs in the exam environment. Though I’m referring to CKS here, this post should be helpful for other exams by Linux Foundation like CKA, CKAD, etc. The exam environment Before we get to Emacs related details, let’s understand the CKS exam environment.

by Bhavin Gandhi ( at December 28, 2021 05:57 PM

Jason Braganza

Raspberry Pi 4 Not Accessible After a Reboot

Update: 28/12/2021
The solution? Move off the 64 bit Raspberry Pi OS and install Ubuntu. Everything below is now just for history and context :)

Every time I update my Pi, whether it be an kernel update or due to something goofy I do, it refuses to be accessible via ssh after it boots back up.
And everytime I bring it back to my desk, it just works.
And when I put it back into its niche, by the door, it continues to work.
But inevitably, sooner or later, comes a reboot, and we’re back to being inaccessible.
No ssh, no plex, no network.

The other Pi, my old 3 series, keeps chugging along like a champ in the meanwhile.
This was driving me up the wall.
So I got to thinking, and I realised, I was running the 64 bit edition of the Raspberry Pi OS.
And then I got to thinking some more, hmm, this is not a mature os. What if, it might be buggy? Or what if, something I do, is tripping it up the wrong way?
Logically then, that meant the network stack, or the network interface, or the port itself.
Because everytime I brought the Pi in, to where my desktop is, I connect it to a switch (that connects to my upstream router).
And then when I take it back, it changes back to connecting to the router directly.

So the next time it borked, I put the Pi off, then turned the router off and on and then finally, put the Pi back on.
And that did the trick.
I tried it again this morning, when the Pi went offline, after another update. The heuristic still holds!
I’m sure, I could go digging further to find the root cause, (dhcp release renew not happening correctly?), this just lets me get on with my life and I’m happy :)

P.S. Subscribe to my mailing list!
Forward these posts and letters to your friends and get them to subscribe!
P.P.S. Feed my insatiable reading habit.

by Mario Jason Braganza at December 28, 2021 08:30 AM

December 26, 2021

Jason Braganza

On Reading

This post was first sent to my newsletter on December 19th, 2021.
You really ought to subscribe :)

Picture of a child holding out a scarf to a woman image, courtesy The Toymaker’s Journal

Lots of links in this letter, because there won’t be a work letter later, this month :)
This one is all about, drum roll … Reading :)

You know the drill. Click the headers to wander off to the original articles.

Books I Read Last Month

  • So Good They Can’t Ignore you, is a book I strangely keep coming back to.
    Lots to learn about how to build a sustainable career
  • Needle Points, You need to do the work required to hold a fair opinion. Tablet’s long form piece helped me understand vaccine hesitancy.
  • I finally got tired of waiting for the old man to finish his series, and started reading everything around it, to immerse myself in The World of Ice & Fire This was almost as fun as discovering and reading A Game of Thrones in the late 90s. Only for completionists though. It’ll feel like a slog for anyone who is not a fan. If he still keeps dilly dallying, I might have to go watch the TV series next.
  • And Discipline Equals Freedom, just for that semi regular kick in the pants.

My dirty little reading secret. Folks ask me how I read so much.
The answer is when I read for pleasure, I read what I like, when I like, and don’t bother completing it if I don’t and just dropping it, never to return to it or maybe coming back to it some other time.
Also I read a
lot more than I list here. I pick up ten, drop seven or eight, guiltily enjoy two to three, and probably feel comfortable listing a couple.

You’ve read the classic works of stoicism. Now what? The Daily Stoic helps with, Assembling A "Bible" of Stoicism: What To Read After The Romans

The world in which a man lives shapes itself chiefly by the way in which he looks at it, and so it proves different to different men; to one it is barren, dull, and superficial; to another rich, interesting, and full of meaning.
On hearing of the interesting events which have happened in the course of a man’s experience, many people will wish that similar things had happened in their lives too, completely forgetting that they should be envious rather of the mental aptitude which lent those events the significance they possess when he describes them …
All the pride and pleasure of the world, mirrored in the dull consciousness of a fool, are poor indeed compared with the imagination of Cervantes writing his Don Quixote in a miserable prison.

— Schopenhauer, The Wisdom of Life (1851)

P.S. And if you haven’t read the classic works and if you are new and want to explore the Stoic way of thinking about life, I heartily recommend Ryan Holiday’s trilogy for a gentle introduction. The Obstacle is the Way, Ego is the Enemy and Stillness is the key are short yet surprisingly deep and engaging books.

Ali Abdaal teaches us, The Art of Reading More Effectively and Efficiently

Level 4: Syntopical Reading
The final level of reading is about our understanding of a subject more generally. Whereas analytical reading focuses on our comprehension of a specific book, syntopical reading helps shape our opinion and increase our overall fluency of the wider topic through understanding how different books relate to one another. This may sound a little abstract, but bear with me.

“The benefits [of syntopical reading] are so great that it is well worth the trouble of learning how to do it”
— Mortimer Adler

The first step is to begin by deciding the subject we want to tackle (eg: productivity or habit-formation). We can then draw up a bibliography of books on the topic, and select just a handful of them that we believe to be most relevant.

Having compiled the list of books, we can begin reading syntopically. This means reading each of the books analytically and building mental connections between each of them. I try to define common subject keywords in my own words, identify and answer the most pressing questions that the books collectively address, and make an informed decision about the strengths of each author's argument.

“Creativity is just connecting things”
— Steve Jobs

Through syntopical reading we're connecting the best ideas on a subject, which acts as a powerful catalyst giving rise to creative solutions and real insight. It's truly game-changing (when we actually do it).

Shane Parrish distills Adler in How to Read a Book: The Ultimate Guide by Mortimer Adler

I bet you already know how to read a book. You were taught in elementary school.
But do you know how to read well?
There is a difference between reading for understanding and reading for information.
If you’re like most people, you probably haven’t given much thought to how you read. And how you read makes a massive difference to knowledge accumulation.
A lot of people confuse knowing the name of something with understanding. While great for exercising your memory, the regurgitation of facts without solid understanding and context gains you little in the real world.
A useful heuristic: Anything easily digested is reading for information.
Consider the newspaper, are you truly learning anything new? Do you consider the writer your superior when it comes to knowledge in the subject? Odds are probably not. That means you’re reading for information. It means you’re likely to parrot an opinion that isn’t yours as if you had done the work.
This is how most people read. But most people aren’t really learning anything new. It’s not going to give you an edge, make you better at your job, or allow you to avoid problems.

“Marking a book is literally an experience of your differences or agreements with the author. It is the highest respect you can pay him.”
— Edgar Allen Poe

Learning something insightful requires mental work. It’s uncomfortable. If it doesn’t hurt, you’re not learning. You need to find writers who are more knowledgeable on a particular subject than yourself. By narrowing the gap between the author and yourself, you get smarter.

Farnam Street has an awesome page, chockful of articles, that help us become better readers

You can’t get where you want to go if you’re not learning all the time. One of the best ways to learn is to read.

Reading habits don’t need to be complicated, you can start a simple 25 page a day habit right now. While it seems small the gains add up quickly.

Above all else remember that just because you’ve read something doesn’t mean you’ve done the work required to have an opinion.

and also

Quit Books

Bad books are a grind. Good books almost read themselves.

When you pick up a good book you feel it instantly. Not only are they well written and packed with ideas and insight, but they’re well organized. You want to read the next page.

Start books quickly but give them up easily. One of the biggest things that holds people back when reading is our desire to finish what we start. Good books finish themselves. You can’t put them down. Trying to finish a bad book, on the other hand, is like walking through the mud with a wheelbarrow full of bricks. Life is too short.

When it comes to reading, you don’t need to finish what you start.

Once you realize that you can quit bad books (or reading anything for that matter) without guilt, everything changes. Putting a bad book down creates the opportunity and space for a great book.

Skim a lot of books. Read a few. Immediately re-read the best ones twice.

That’s all for now, folks. See you in the new year!
But before I go, this beautiful Gaiman poem (the pic above) reminds me,

Sometimes it only takes a stranger, in a dark place,
to hold out a badly knitted scarf, to offer a kind word, to say
we have the right to be here, …

So, this Christmas …
I’m grateful, you’re a part of my life.
I’m grateful, you’ve made me a part of yours.
I’m grateful, you’ve given me the right to belong to your world!
I wish you be happy, I wish you love, and as ever I wish you warmth!
Merry Christmas!

P.S. Subscribe to my mailing list!
Forward these posts and letters to your friends and get them to subscribe!
P.P.S. Feed my insatiable reading habit.

by Mario Jason Braganza at December 26, 2021 12:15 AM

December 25, 2021

Sayan Chowdhury

Flatcar Container Linux on Raspberry Pi

Fresh raspberries from a Canadian Local Market.

Flatcar Container Linux recently announced the Stable support for ARM64.

Perfectly timed! I had a Raspberry Pi 4 lying around and had just ordered a few more to set up a home lab during the holidays. The newer Pis are yet to arrive, so better utilize the time writing a walkthrough on how to use Flatcar Container Linux on your Pis.

Hardware Requirements

  • Goes without saying, a Raspberry Pi 4
  • Form of storage, either USB and/or SD card. USB 3.0 drive recommended because of the much better performance for the price.
  • Display (via micro HDMI/HDMI/Serial Cables), keyboard

The UEFI firmware used in this guide is an UNOFFICIAL firmware. There is a possibility of damage caused due to the usage of this firmware. The author of this article would not be liable for any damage caused. Please follow this article at your own risk.

Update the EEPROM

The Raspberry PI 4 use an EEPROM to boot the system. Before proceeding ahead, it is recommended to update the EEPROM. Raspberry Pi OS automatically updates the bootloader on system boot. In case you are using Raspberry Pi OS already, then the bootloader may be already updated.

For manually updating the EEPROM, you can either use the Raspberry Pi Imager or the raspi-config. The former is the recommended method in the Raspberry Pi documentation.

We will also see later how the RPi4 UEFI firmware needs a recent version of EEPROM.

  • Install the Raspberry Pi Imager software. You can also look for the software in your distribution repository. Being a Fedora user I installed the software using dnf
dnf install rpi-imager
  • Launch Raspberry Pi Imager.
  • Select Misc utility images under Operating System.
  • Select Bootloader.
  • Select the boot-mode, SD, USB
  • Select the appropriate storage, SD or USB
  • Boot the Raspberry Pi with the new image and wait for at least 10 seconds.
  • The green activity LED will blink with a steady pattern and the HDMI display will be green on success.
  • Power off the Raspberry Pi and disconnect the storage.

Using the raspi-config

  • Update the rpi-eeprom package.
sudo apt update
sudo apt full-upgrade
sudo apt install rpi-eeprom
  • Run sudo raspi-config
  • Select Advanced Options.
  • Select Bootloader Version
  • Select Latest for latest Stable Bootloader release.
  • Reboot

Using the rpi-eeprom-update

  • Update the rpi-eeprom package.
sudo apt update
sudo apt full-upgrade
sudo apt install rpi-eeprom
  • Check if there are available updates.
sudo rpi-eeprom-update
  • Install the update
# The update is pulled from the `default` release channel.
# The other available channels are: latest and beta
# You can update the channel by updating the value of
# `FIRMWARE_RELEASE_STATUS` in the `/etc/default/rpi-eeprom-update`
# file. This is useful usually in case when you want
# features yet to be made available on the default channel.

# Install the update
sudo rpi-eeprom-update -a

# A reboot is needed to apply the update
# To cancel the update, you can use: sudo rpi-eeprom-update -r
sudo reboot

Installing Flatcar

Install flatcar-install script

Flatcar provides a simple installer script that helps install Flatcar Container Linux on the target disk. The script is available on Github, and the first step would be to install the script in the host system.

mkdir -p ~/.local/bin
# You may also add `PATH` export to your shell profile, i.e bashrc, zshrc etc.
export PATH=$PATH:$HOME/.local/bin

curl -LO
chmod +x flatcar-install
mv flatcar-install ~/.local/bin

Install Flatcar on the target device

Now that we have the flatcar-install installed in our host machine. We would go ahead and install the Flatcar Container Linux image on the target device. The target device could be a USB or SD Card. In my case, I reused the existing SD Card which I used in the previous steps. You can use a separate storage device as well.

The options that we will be using with the scripts are:

# -d DEVICE   Install Flatcar Container Linux to the given device.
# -C CHANNEL  Release channel to use
# -B BOARD    Flatcar Container Linux Board to use
# -o OEM      OEM type to install (e.g. ami), using flatcar_production_<OEM>_image.bin.bz2
# -i IGNITION Insert an Ignition config to be executed on boot.
  • The device would be the target device that you would like to use. You can use the lsblk command to find the appropriate disk. Here, I’m using /dev/sda which was in my case.
  • With the given values of channel and board, the script would download the image, verify it with gpg, and then copy it bit for bit to disk.
  • In our case, Flatcar does not yet ship Raspberry PI specific OEM images yet so the value will be an empty string ''.
  • Pass the Ignition file, config.json in my case, to provision the Pi during boot.
  "ignition": {
    "config": {},
    "security": {
      "tls": {}
    "timeouts": {},
    "version": "2.3.0"
  "networkd": {},
  "passwd": {
    "users": [
        "name": "core",
        "sshAuthorizedKeys": [
          <Insert your SSH Keys here>
  "storage": {
    "files": [
        "filesystem": "OEM",
        "path": "/grub.cfg",
        "append": true,
        "contents": {
          "source": "data:,set%20linux_console%3D%22console%3DttyAMA0%2C115200n8%20console%3Dtty1%22%0Aset%20linux_append%3D%22flatcar.autologin%20usbcore.autosuspend%3D-1%22%0A",
          "verification": {}
        "mode": 420
    "filesystems": [
        "mount": {
          "device": "/dev/disk/by-label/OEM",
          "format": "btrfs"
        "name": "OEM"
  "systemd": {}

Write away!

sudo flatcar-install -d /dev/sda -C stable -B arm64-usr -o '' -i config.json

If you already have the image downloaded you can use the -f param to specify the path of the local image file.

sudo flatcar-install -d /dev/sda -C stable -B arm64-usr -o '' -i config.json -f flatcar_production_image.bin.bz2

Raspberry Pi 4 UEFI Firmware

rpi-uefi community ships a SBBR-compliant(UEFI+ACPI), ArmServerReady ARM64 firmware for Raspberry Pi 4. We would be using the same to UEFI boot Flatcar.

v1.17 of the pftf/RPi4 introduced two major changes:

  • Firstly, it enabled firmware boot directly from the USB. This is particularly helpful if you are using the installation process using a USB device. To add a fun story, I dropped my Pi and broke the SD card slot. Until the Pi gets repaired, I’m making use of direct USB boot 😎
  • Secondly, support for directly placing the Pi boot files into the EFI System Partition (ESP). This feature was not implemented in the firmware, rather from the upstream firmware from Raspberry Pi Foundation. This is why it is recommended to update the Pi EEPROM at the very beginning.

Let’s move ahead with the final steps.

  • Place the UEFI firmware into the EFI System Partition.
efipartition=$(lsblk /dev/sda -oLABEL,PATH | awk '$1 == "EFI-SYSTEM" {print $2}')
mkdir /tmp/efipartition
sudo mount ${efipartition} /tmp/efipartition
pushd /tmp/efipartition
version=$(curl --silent "" | jq -r .tag_name)
sudo curl -LO${version}/RPi4_UEFI_Firmware_${version}.zip
sudo unzip RPi4_UEFI_Firmware_${version}.zip
sudo rm RPi4_UEFI_Firmware_${version}.zip
sudo umount /tmp/efipartition
  • Remove the USB/SD from the host device and attach it into the Raspberry Pi 4 and boot.

Voilà! In no time, your Raspberry Pi would boot and present you with a Flatcar Container Linux prompt.

Further Reading

Photo by Anto Meneghini

December 25, 2021 12:00 AM

November 29, 2021

Sandeep Choudhary

sh like infix syntax using Pipes(|) in Python

Today, we are going to see how we can use | operator in our python code to achieve clean code.

Here is the code where we have used map and filter for a specific operation.

In [1]: arr = [11, 12, 14, 15, 18]
In [2]: list(map(lambda x: x * 2, filter(lambda x: x%2 ==1, arr)))
Out[2]: [22, 30]

The same code with Pipes.

In [1]: from pipe import select, where
In [2]: arr = [11, 12, 14, 15, 18]
In [3]: list(arr | where (lambda x: x%2 ==1) | select(lambda x:x *2))
Out[3]: [22, 30]

Pipes passes the result of one function to another function, have inbuilt pipes method like select, where, tee, traverse.

Install Pipe

>> pip install pipe


Recursively unfold iterable:

In [12]: arr = [[1,2,3], [3,4,[56]]]
In [13]: list(arr | traverse)
Out[13]: [1, 2, 3, 3, 4, 56]


An alias for map().

In [1]: arr = [11, 12, 14, 15, 18]
In [2]: list(filter(lambda x: x%2 ==1, arr))
Out[2]: [11, 15]


Only yields the matching items of the given iterable:

In [1]: arr = [11, 12, 14, 15, 18]
In [2]: list(arr | where(lambda x: x % 2 == 0))
Out[2]: [12, 14, 18]


Like Python's built-in “sorted” primitive. Allows cmp (Python 2.x only), key, and reverse arguments. By default, sorts using the identity function as the key.

In [1]:  ''.join("python" | sort)
Out[1]:  'hnopty'


Like Python's built-in “reversed” primitive.

In [1]:  list([1, 2, 3] | reverse)
Out[1]:   [3, 2, 1]


Like Python's strip-method for str.

In [1]:  '  abc   ' | strip
Out[1]:  'abc'

That's all for today, In this blog you have seen how to install the Pipe and use the Pipe to write clean and short code using inbuilt pipes, you can check more over here


#100DaysToOffload #Python #DGPLUG

November 29, 2021 02:18 PM

November 28, 2021

Sandeep Choudhary

Profiling Django Application

My work required me to profile one of our Django applications, to help identify the point in the application which we can improve to reach our North Star. So, I thought it will be great to share my learning and tools, that I have used to get the job done.

What is Profiling?

Profiling is a measure of the time or memory consumption of the running program. This data further can be used for program optimization.

They are many tools/libraries out there which can be used to do the job. I have found these helpful.

Apache JMeter

It is open-source software, which is great to load and performance test web applications. It's easy to set up and can be configured for what we want in the result report.


Pyinstrument is a Python profiler that offers a Django middleware to record the profiling. The profiler generates profile data for every request. The PYINSTRUMENTPROFILEDIR contains a directory that stores the profile data, which is in HTML format. You can check how it works over here

Django Query Count

Django Query Count is a middleware that prints the number of database queries made during the request processing. There are two possible settings for this, which can be found here

Django Silk

Django Silk is middleware for intercepting Requests/Responses. We can profile a block of code or functions, either manually or dynamically. It also has a user interface for inspection and visualization

So, here are some of the tools which can be of great help in profiling your code and putting your effort in the right direction of optimization applications.


#100DaysToOffload #Python #DGPLUG

November 28, 2021 12:00 PM

November 26, 2021

Nabarun Pal

Update: Weekly Mentoring Sessions

I have had the pleasure to talk with 30+ folks and help them in their journey in the field of computer science and/or growing in their career with Open Source Software. It has been an honour that so many wanted to talk to me and get my views.

For the month of December, I am going to take a break from the mentoring sessions as I will travelling on most weekends and will be out for vacation in the later half of the month.

Fret not, I will try to make up for the lost time by doubling up my commitment for January 2022. But, in case you need to urgently talk with me, drop me a ping on hey [at] nabarun [dot] dev and I will try to schedule something which works for both of us.

Wish you all a very happy December! 🎉

PS: Stay tuned to the RSS feed! There are many articles which are languishing in my drafts, I may publish a few of them.

November 26, 2021 11:55 PM

November 16, 2021

Sayan Chowdhury

Hope - 30 Days of Read

The aim here is to read through the book and build the habit to spend some time to read through technical books everyday.

I’ll upload the notes from everyday, and after the finish of the chapter accumulate the notes into one.

November 16, 2021

pages: ~7


I read through the introduction section of the Linux Device Driver book.

  • Introduced on how device drivers are more about mechanism than policy.
    • Difference between mechanism and policy
  • Splitting the Kernel into process, memory, filesystems, network, and device control
  • Loadable modules, (insmod & rmmod)
  • Classes of Devices and modules (char, block and network)

November 16, 2021 12:00 AM

November 03, 2021

Priyanka Saggu

On Getting to Concious Competence, Cunningham's law & Bike-Shedding, Mistakes are unavoidable!

November 03, 2021

Some of the very important notes (for myself) from the early sections of the book, The Missing README, A Guide for the New Software Engineer by Chris Riccomini and Dmitriy Ryaboy.

Getting to Concious Competence

Martin M. Broadwell defines four stages of competence in Teaching for Learning:

  • unconcious imcompetence
  • concious incompetence
  • concious competence
  • unconcious competence.

Specifically, unconcious incompetence means you are unable to perform a task correctly and are unaware of the gap.

Concious incompetence means you are unable to perform a task correctly but are aware of the gap.

Concious competence means you are capable of performing a task with effort.

Finally, unconcious competence means you are capable of performing a task effortlessly.

All engineers start out conciously or unconciously incompetent. Even if you know everything about software engineering (an impossible task), you’re going to have to learn practical skills like those covered in this book. Your goal is to get to concious competence as quickly as possible.

Cunningham’s Law And Bike-Shedding

We advise you to document, conventions, onboarding procedures, and other oral traditions on your team. You will get a lot of comments and corrections. Do not take the comments Personally.

The point is not to write a perfect document but rather to write enough to trigger a discussion that flashes out the details. This is a variation of Cunningham’s law, which states that “the best way to get the right answer on the internt is not to ask a question; it’s to post the wrong answer.”

Be prepared for trivial discussions to become drawn out, a phenomenon called bike-shedding. Bike-shedding is an allegory by C. Northcote Parkinson, describing a committee assigned to review designs for a power plant. The committee approves the plans within minutes, as they are too complex to actually discuss. They then spend 45 minutes discussing the materials for the bike shed next to the plant. Bike-shedding comes up a lot in technical work.

Mistakes are unavoidable (Learn by Doing!)

At one of Chris’s first internships, he was working on a project with a senior engineer. Chris finished some changes and needed to get them deployed. The senior engineer showed him how to check code into the revision control system, CVS. Chris followed the instructions, blindly running through steps that involved branching, tagging, and merging. Afterward, he continued with the rest of his day and went home.

The next morning, Chris strolled in cheerfully and greeted everyone. They did their best to respond in kind, but they were low. When Chris asked what was up, they informed him that he had managed to corrupt the entire CVS repository. All of the company’s code had been lost. They had been up the entire night desperately trying to recover what they could and were eventually able to get most of the code back (except for Chris’s commits and a few others).

Chris was pretty shaken by the whole thing. His manager pulled him aside and told him not to worry: Chris had done the right thing working with the senior engineer.

Mistakes happen. Every engineer has some version of a story like this. Do your best, and try to understand what you’re doing, but know that these things happen.

November 03, 2021 12:00 AM

October 17, 2021

Priyanka Saggu

24, Happy Birthday!

October 17, 2021

Happy 24th Birthday, to me! 👧

I’m grateful for this beautiful life 🍀 , for my parents 👨‍👩‍👧‍👦 , and for every other thing that I’ve received as opportunities 🌟 & lessons 📝 & experiences 📈 from my life (so far), and the very kind & generous people 🧑‍🤝‍🧑 I’ve known.

Thank you so much, everyone. 🙂 🙏

October 17, 2021 12:00 AM

September 17, 2021

Robin Schubert

What it means being human (to me)


One of these posts; not technical, written with a hot head, controversial topics. Don't read if that already annoys you.

The argument

I've had a hot discussion yesterday, and I'm not quite happy how it went. It's not important how we came there but the core argument was about why I think that less to no money should be spend on military and arms, while the same money should be spent on peace studies and education of diplomats - which I refer to as people who try to understand a different culture and initiate an exchange of values and ethics - not eloquent deceitful but open and direct, fair and square.

My opposition's opinion was different, claiming that we would never live in peace without an army enforcing the peace. That's an opinion that I could argue about all day long, but then he said what really is upsetting me; It's human nature to have wars and to only care for oneself.

I strongly disagree.

As for myself, I don't want that. Does this make me not a human? When we hear, read or see in the news what war crimes are committed, how people are forced to live (if they may live), what people are capable of doing to each other, we call this inhuman. What do we actually mean when we say so? Are the people committing these crimes not human people, or is it the act that is inhuman?

Again, my opponent argues that you should not call someone a liar, but rather say you lied - so to condemn the act of lying but not the person. Well, do we do that in other situations? May we call someone a murderer or a thief when they kill people or steal things? Is it a matter of frequency how often I eat meat while still calling myself a vegetarian?

Being human

The definition of what is or is not human may differ vastly. However, it is schizophenic that if we agree something is inhuman, to not take the consequences; As a human being stop acting inhuman!

Like being a vegetarian or not being a liar and murderer, for me being human is a continuous process that demands continuous work on ourselves. I have to work on myself to act how I want a human to act. When I think that the way meat is "produced" today on earth is inhuman, then I will have to change my diet. I have a vote, I can choose what to buy (or not to buy) or what to write on my blog. I can show people that I disagree and I can sit together with them, discuss differences of opinions and find a rational consent, because that's what I think how a human would act.

On a fun side note, my opponent also argued that animals also fight themselves, so this is just natural. Well, be an animal then.

by Robin Schubert at September 17, 2021 12:00 AM

September 12, 2021

Sanyam Khurana

The Grit To Get Out

This is the first post on my blog in a while. I guess this is coming after almost 2 years 9 months. Yes, never wrote those end-of-year review posts too.

A lot has happened.

  • I left playing guitar at the end of 2018.
  • I didn't work on my book after …

by Sanyam Khurana at September 12, 2021 01:18 PM

August 22, 2021

Bhavin Gandhi

Building RepRap 3D printer

My younger brother goes by the name HemRobotics almost everywhere. I have been working with him on a 3D printer build. We completed the first build of our RepRap machine based on Prusa i3 printer. I will be talking about our experience and learnings from this project. Why did we build a 3D printer? It’s a hacker’s dream to have a 3D printer on their desk. I had seen 3D printers on television when I was in school.

by Bhavin Gandhi ( at August 22, 2021 12:05 PM

August 13, 2021

Anwesha Das

Finding and installing the security updates on Ubuntu

Keeping the system updated is a daily task of sys admins. To find out the available security updates on a Ubuntu machine use the commands here under. I have done this on a virtual machine Ubuntu 21.04.

apt update

This will update all the package information for the Debian repositories.

grep security /etc/apt/sources.list > /tmp/security.list

Getting the security repositories so we can only check in those. The apt software repositories are defined in the /etc/apt/sources.list file or in separate files under the /etc/apt/sources.list.d/ directory in
Ubuntu and all other Debian based distributions.

Finding and installing the security updates in Ubuntu

sudo apt list --upgradable -oDir::Etc::Sourcelist=/tmp/security.list
Listing... Done
libperl5.32/hirsute-security 5.32.1-3ubuntu2.1 amd64 [upgradable from: 5.32.1-3ubuntu2]
perl-base/hirsute-security 5.32.1-3ubuntu2.1 amd64 [upgradable from: 5.32.1-3ubuntu2]
perl-modules-5.32/hirsute-security 5.32.1-3ubuntu2.1 all [upgradable from: 5.32.1-3ubuntu2]
perl/hirsute-security 5.32.1-3ubuntu2.1 amd64 [upgradable from: 5.32.1-3ubuntu2]

Installing the security upgrades

The next step is to install the security upgrades.

sudo apt-get upgrade -oDir::Etc::Sourcelist=/tmp/security.list -s
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
  libperl5.32 perl perl-base perl-modules-5.32
4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Inst libperl5.32 [5.32.1-3ubuntu2] (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [amd64]) [perl:amd64 ]
Inst perl [5.32.1-3ubuntu2] (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [amd64]) []
Inst perl-base [5.32.1-3ubuntu2] (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [amd64]) []
Conf perl-base (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [amd64]) []
Inst perl-modules-5.32 [5.32.1-3ubuntu2] (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [all])
Conf libperl5.32 (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [amd64])
Conf perl (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [amd64])
Conf perl-modules-5.32 (5.32.1-3ubuntu2.1 Ubuntu:21.04/hirsute-security [all])

Stay safe and keep the system secured.

by Anwesha Das at August 13, 2021 02:01 PM

July 28, 2021

Anwesha Das

Upgrade CentOS 7 to CentOS 8 inplace

I wanted to upgrade one of my servers from CentOS 7 to CentOS 8. The following are the steps I followed :

Check the kernel version

Before we start the upgrade process, let us see the kernel version.

# uname -a
Linux centostest 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Installing basic packages

Now let us begin with installing the basic packages.

yum install epel-release -y

EPEL provides a set of additional packages for CentOS, RHEL from the Fedora sources.

yum install yum-utils rpmconf

yum-utils is a collection of utility tools and programs for managing yum repositories, installing different packages such as debug packages, source packages.

rpmconf searches for .rpmnew, .rpmsave, and .rpmorigfiles and asks the user what to do with them.

The following cammand will check the configuration files of all packages and ask users for input.

rpmconf -a

Install dnf package manager

dnf is the replacement for yum in the newer version of the OS.

yum install dnf

Remove yum

Remove yum, the default package manager for CentOS 7, and yum configuration file to avoid any conflict with dnf. Since for CentOS 8 dnf is the primary package manager.

dnf -y remove yum yum-metadata-parser

rm -Rf /etc/yum

Getting CentOS 8 release packages

We are all set to upgrade from CentOS 7 to CentOS 8, but we need to upgrade the system before that.

dnf upgrade

This will update packages to their latest versions that are both available and resolvable.
I have installed CentOS 8 release packages and also for EPEL.

dnf install{centos-linux-repos-8-2.el8.noarch.rpm,centos-linux-release-8.4-1.2105.el8.noarch.rpm,centos-gpg-keys-8-2.el8.noarch.rpm}
dnf -y upgrade
dnf clean all

Removed the old kernel core of CentOS 7 and conflicting packages along with it.

rpm -e `rpm -q kernel`
rpm -e --nodeps sysvinit-tools

CentOS 8 system upgrade

The following packages, dracut-network and rpmconf, conflicts upgrade process, therefore removed them.

dnf remove dracut-network rpmconf

With the following dnf command, we ask to download all the non deltarpms for CentOS 8.

dnf -y --releasever=8 --allowerasing --setopt=deltarpm=false distro-sync

Installing a new kernel

To get the new kernel for CentOS 8, run the following command.

dnf -y install kernel-core

The final step would be to install CenOS 8 minimal packages.

dnf -y groupupdate "Core" "Minimal Install"

After all done:

# cat /etc/os-release
NAME="CentOS Linux"
ID_LIKE="rhel fedora"
PRETTY_NAME="CentOS Linux 8"

Now run reboot to complete the upgrade.

Log back to the server, check the kernel version, and TaDa! We have CentOS 8 running on the system.

# uname -a
Linux centostest 4.18.0-305.10.2.el8_4.x86_64 #1 SMP Tue Jul 20 17:25:16 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Happy upgrading :)

by Anwesha Das at July 28, 2021 02:16 PM

June 24, 2021


Let's play with Traefik

I've been playing around with containers for a few years now. I find them very useful. If you host your own, like I do, you probably write a lot of nginx configurations, maybe apache.

If that's the case, then you have your own solution to get certificates. I'm also assuming that you are using let's encrypt with certbot or something.

Well, I didn't want to anymore. It was time to consolidate. Here comes Traefik.


So Traefik is

an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them.

Which made me realize, I still need nginx somewhere. We'll see when we get to it. Let's focus on Traefik.


If you run a lot of containers and manage them, then you probably use docker-compose.

I'm still using version 2.3, I know I am due to an upgrade but I'm working on it slowly. It's a bigger project… One step at a time.

Let's start from the top, literally.

version: '2.3'



Upgrading to version 3.x of docker-compose requires the creation of network to link containers together. It's worth investing into, this is not a docker-compose tutorial.

Then comes the service.

  container_name: traefik
  image: "traefik:latest"
  restart: unless-stopped
  mem_limit: 40m
  mem_reservation: 25m

and of course, who can forget the volume mounting.

  - "/var/run/docker.sock:/var/run/docker.sock:ro"


Now let's talk design to see how we're going to configuse this bad boy.

I want to Traefik to listen on ports 80 and 443 at a minimum to serve traffic. Let's do that.

  - --entrypoints.web.address=:80
  - --entrypoints.websecure.address=:443

and let's not forget to map them.

  - "80:80"
  - "443:443"

Next, we would like to redirect http to https always.

- --entrypoints.web.http.redirections.entryPoint.scheme=https

We are using docker, so let's configure that as the provider.

- --providers.docker

We can set the log level.

- --log.level=INFO

If you want a dashboard, you have to enable it.

- --api.dashboard=true

And finally, if you're using Prometheus to scrape metrics… You have to enable that too.

- --metrics.prometheus=true

Let's Encrypt

Let's talk TLS. You want to serve encrypted traffic to users. You will need an SSL Certificate.

Your best bet is open source. Who are we kidding, you'd want to go with let's encrypt.

Let's configure acme to do just that. Get us certificates. In this example, we are going to be using Cloudflare.

- --certificatesresolvers.cloudflareresolver.acme.dnschallenge.provider=cloudflare


Let's Encrypt have set limits on how many certificates you can request per certain amount of time. To test your certificate request and renewal processes, use their staging infrastructure. It is made for such purpose.

Then we mount it, for persistence.

- "./traefik/acme.json:/acme.json"

Let's not forget to add our Cloudflare API credentials as environment variables for Traefik to use.

  - CLOUDFLARE_API_KEY=<your-api-key-goes-here>


Now let's configure Traefik a bit more with a bit of labeling.

First, we specify the host Traefik should listen for to service the dashboard.

  - "traefik.http.routers.dashboard-api.rule=Host(``)"
  - "traefik.http.routers.dashboard-api.service=api@internal"

With a little bit of Traefik documentation searching and a lot of help from htpasswd, we can create a basicauth login to protect the dashboard from public use.

- "traefik.http.routers.dashboard-api.middlewares=dashboard-auth-user"
- "traefik.http.middlewares.dashboard-auth-user.basicauth.users=<user>:$$pws5$$rWsEfeUw9$$uV45uwsGeaPbu8RSexB9/"
- "traefik.http.routers.dashboard-api.tls.certresolver=cloudflareresolver"


I'm not going to go into details about the middleware flags configured here but you're welcome to check the Traefik middleware docs.

- "traefik.http.middlewares.frame-deny.headers.framedeny=true"
- "traefik.http.middlewares.browser-xss-filter.headers.browserxssfilter=true"
- "traefik.http.middlewares.ssl-redirect.headers.sslredirect=true"

Full Configuration

Let's put everything together now.

  container_name: traefik
  image: "traefik:latest"
  restart: unless-stopped
  mem_limit: 40m
  mem_reservation: 25m
    - "80:80"
    - "443:443"
    - --entrypoints.web.address=:80
    - --entrypoints.websecure.address=:443
    - --entrypoints.web.http.redirections.entryPoint.scheme=https
    - --providers.docker
    - --log.level=INFO
    - --api.dashboard=true
    - --metrics.prometheus=true
    - --certificatesresolvers.cloudflareresolver.acme.dnschallenge.provider=cloudflare
    - "/var/run/docker.sock:/var/run/docker.sock:ro"
    - "./traefik/acme.json:/acme.json"
    - CLOUDFLARE_API_KEY=<your-api-key-goes-here>
    - "traefik.http.routers.dashboard-api.rule=Host(``)"
    - "traefik.http.routers.dashboard-api.service=api@internal"
    - "traefik.http.routers.dashboard-api.middlewares=dashboard-auth-user"
    - "traefik.http.middlewares.dashboard-auth-user.basicauth.users=<user>:$$pws5$$rWsEfeUw9$$uV45uwsGeaPbu8RSexB9/"
    - "traefik.http.routers.dashboard-api.tls.certresolver=cloudflareresolver"
    - "traefik.http.middlewares.frame-deny.headers.framedeny=true"
    - "traefik.http.middlewares.browser-xss-filter.headers.browserxssfilter=true"
    - "traefik.http.middlewares.ssl-redirect.headers.sslredirect=true"


nginx pronounced

[engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev.

In this example, we're going to assume you have a static blog generated by a static blog generator of your choice and you would like to serve it for people to read it.

So let's do this quickly as there isn't much to tell except when it comes to labels.

  container_name: nginx
  image: nginxinc/nginx-unprivileged:alpine
  restart: unless-stopped
  mem_limit: 8m
  command: ["nginx", "-enable-prometheus-metrics", "-g", "daemon off;"]
    - "./blog/:/usr/share/nginx/html/blog:ro"
    - "./nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro"

We are mounting the blog directory from our host to /usr/share/nginx/html/blog as read-only into the nginx container. We are also providing nginx with a template configuration and passing the variables as environment variables as you noticed. It is also mounted as read-only. The configuration template looks like the following, if you're wondering.

server {

    listen       ${NGINX_BLOG_PORT};
    server_name  localhost;

    root   /usr/share/nginx/html/${NGINX_BLOG_HOST};

    location / {
	index  index.html;
	try_files $uri $uri/ =404;

Traefik configuration

So, Traefik configuration at this point is a little bit tricky for the first time.

First, we configure the host like we did before.

  - "``)"

We tell Traefik about our service and the port to loadbalance on.

- ""
- ""

We configure the middleware to use configuration defined in the Traefik middleware configuration section.

- ""
- ",browser-xss-filter,ssl-redirect"

Finally, we tell it about our resolver to generate an SSL Certificate.

- ""

Full Configuration

Let's put the nginx service together.

  container_name: nginx
  image: nginxinc/nginx-unprivileged:alpine
  restart: unless-stopped
  mem_limit: 8m
  command: ["nginx", "-enable-prometheus-metrics", "-g", "daemon off;"]
    - "./blog/:/usr/share/nginx/html/blog:ro"
    - "./nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro"
    - "``)"
    - ""
    - ""
    - ""
    - ",browser-xss-filter,ssl-redirect"
    - ""


It's finally time to put everything together !

version: '2.3'


    container_name: traefik
    image: "traefik:latest"
    restart: unless-stopped
    mem_limit: 40m
    mem_reservation: 25m
      - "80:80"
      - "443:443"
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --entrypoints.web.http.redirections.entryPoint.scheme=https
      - --providers.docker
      - --log.level=INFO
      - --api.dashboard=true
      - --metrics.prometheus=true
      - --certificatesresolvers.cloudflareresolver.acme.dnschallenge.provider=cloudflare
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik/acme.json:/acme.json"
      - CLOUDFLARE_API_KEY=<your-api-key-goes-here>
      - "traefik.http.routers.dashboard-api.rule=Host(``)"
      - "traefik.http.routers.dashboard-api.service=api@internal"
      - "traefik.http.routers.dashboard-api.middlewares=dashboard-auth-user"
      - "traefik.http.middlewares.dashboard-auth-user.basicauth.users=<user>:$$pws5$$rWsEfeUw9$$uV45uwsGeaPbu8RSexB9/"
      - "traefik.http.routers.dashboard-api.tls.certresolver=cloudflareresolver"
      - "traefik.http.middlewares.frame-deny.headers.framedeny=true"
      - "traefik.http.middlewares.browser-xss-filter.headers.browserxssfilter=true"
      - "traefik.http.middlewares.ssl-redirect.headers.sslredirect=true"

    container_name: nginx
    image: nginxinc/nginx-unprivileged:alpine
    restart: unless-stopped
    mem_limit: 8m
    command: ["nginx", "-enable-prometheus-metrics", "-g", "daemon off;"]
      - "./blog/:/usr/share/nginx/html/blog:ro"
      - "./nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro"
      - NGINX_BLOG_PORT=80
      - NGINX_BLOG_HOST=<>
      - "``)"
      - ""
      - ""
      - ""
      - ",browser-xss-filter,ssl-redirect"
      - ""

Now we're all set to save it in a docker-compose.yaml file and

docker-compose up -d

If everything is configured correctly, your blog should pop-up momentarily. Enjoy !

by Elia el Lazkani at June 24, 2021 08:30 PM

June 21, 2021


Playing with containers and Tor

As my followers well know, by now, I am a tinkerer at heart. Why do I do things ? No one knows ! I don't even know.

All I know, all I can tell you is that I like to see what can I do with the tools I have at hand. How can I bend them to my will. Why, you may ask. The answer is a bit complicated; part of who I am, part of what I do as a DevOps. End line is, this time I was curious.

I went down a road that taught me so much more about containers, docker, docker-compose and even Linux itself.

The question I had was simple, can I run a container only through Tor running in another container?


I usually like to start topics that I haven't mentioned before with definitions. In this case, what is Tor, you may ask ?

What is Tor?

Tor is free software and an open network that helps you defend against traffic analysis, a form of network surveillance that threatens personal freedom and privacy, confidential business activities and relationships, and state security.

Although that home page is obscure because it was replaced by the new design of the website. Although I love what Tor has done with all the services they offer, don't get me wrong. But giving so much importance on the browser only and leaving the rest for dead when it comes to website, I have to say, I'm a bit sad.

Anyway, let's share the love for Tor and thank them for the beautiful project they offered humanity.

Now that we thanked them, let's abuse it.

Tor in a container

The task I set to discover relied on Tor being containerized. The first thing I do is, simply, not re-invent the wheel. Let's find out if someone already took that task.

With a litte bit of search, I found the dperson/torproxy docker image. It isn't ideal but I believe it is written to be rebuilt.

Can we run it ?

docker run -it -p -d dperson/torproxy
curl -Lx http://localhost:8118

And this is definitely not your IP. Don't take my word for it! Go to in a browser and see for yourself.

Now that we know we can run Tor in a container effectively, let's kick it up a notch.


I will be testing and making changes as I go along. For this reason, it's a good idea to use docker-compose to do this.

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.

Now that we saw what the docker team has to say about docker-compose, let's go ahead and use it.

First, let's implement what we just ran ad-hoc in docker-compose.

version: '3.9'
    image: dperson/torproxy
    container_name: torproxy
    restart: unless-stopped

Air-gapped container

The next piece of the puzzle is to figure out if and how can we create an air-gapped container.

It turns out, we can create an internal network in docker that has no access to the internet.

First, the air-gapped container.

  image: ubuntu
  container_name: air-gapped
  restart: unless-stopped
    - bash
    - -c
    - sleep infinity
    - no-internet

Then comes the network.

    driver: bridge
    internal: true

Let's put it all together in a docker-compose.yaml file and run it.

docker-compose up -d

Keep that terminal open, and let's put the hypothesis to the test and see if rises up to be a theory.

docker exec air-gapped apt-get update


Err:1 focal InRelease
  Temporary failure resolving ''
Err:2 focal-security InRelease
  Temporary failure resolving ''
Err:3 focal-updates InRelease
  Temporary failure resolving ''
Err:4 focal-backports InRelease
  Temporary failure resolving ''
Reading package lists...
W: Failed to fetch  Temporary failure resolving ''
W: Failed to fetch  Temporary failure resolving ''
W: Failed to fetch  Temporary failure resolving ''
W: Failed to fetch  Temporary failure resolving ''
W: Some index files failed to download. They have been ignored, or old ones used instead.

looks like it's real peeps, hooray !

Putting everything together

Okay, now let's put everything together. The list of changes we need to make are minimal. First, I will list them, then I will simply write them out in docker-compose.

  • Create an internet network for the Tor container
  • Attach the internet network to the Tor container
  • Attach the no-internet network to the Tor container so that our air-gapped container can access it.

Let's get to work.

version: '3.9'

    image: dperson/torproxy
    container_name: torproxy
    restart: unless-stopped
      - no-internet
      - internet

    image: ubuntu
    container_name: air-gapped
    restart: unless-stopped
      - bash
      - -c
      - sleep infinity
      - no-internet

    driver: bridge
    internal: true
    driver: bridge
    internal: false

Run everything.

docker-compose up -d

Yes, this will run it in the background and there is no need for you to open another terminal. It's always good to know both ways. Anyway, let's test.

let's exec into the container.

docker exec -it air-gapped bash

Then we configure apt to use our torproxy service.

echo 'Acquire::http::Proxy "http://torproxy:8118/";' > /etc/apt/apt.conf.d/proxy
echo "export HTTP_PROXY=http://torproxy:8118/" >> ~/.bashrc
echo "export HTTPS_PROXY=http://torproxy:8118/" >> ~/.bashrc
export HTTP_PROXY=http://torproxy:8118/
export HTTPS_PROXY=http://torproxy:8118/
apt-get update
apt-get upgrade -y
DEBIAN_FRONTEND=noninteractive apt-get install -y curl

Harvesting the fruits of our labour

First, we always check if everything is set correctly.

While inside the container, we check the environment variables.

env | grep HTTP

You should see.


Then, we curl our IP.


And that is also not your IP.

It works !


Is it possible to route a container through another Tor container ?

The answer is obviously Yes and this is the way to do it. Enjoy.

by Elia el Lazkani at June 21, 2021 09:30 PM

April 19, 2021

Saptak Sengupta

Opting out of Google FLoC network

Recently, Google announced their new ad-tracking and surveillance tool called Federated Learning of Cohorts (FLoC). This is a new alternative to the third-party cookie tracking that is otherwise widely used for advertising business.

EFF has written more about the issues with using Google FLoC and also created a website where you can test if you are already a victim of their FLoC tests.

Google will track any user visiting your website even if it doesn't have Google analytics or any other services related to Google. One easy way for users visiting websites to opt out of this is to not use Google Chrome and use browsers like Firefox, etc. However, website maintainers can also help against this new tracking technology by opting out of the FLoC network.

Permissions-Policy Header

So the main way of opting out of this surveillance technology is to add a HTTP response header to their websites.

The HTTP response header is

Permissions-Policy: interest-cohort()

The FLoC technology uses interest-cohort to check for an allowlist. By default, everything is allowed as long as the user is visiting from a browser which supports InterestCohort API. However, by mentioning interest-cohort() in the Permissions-Policy header, the website is opting out from allowing any origin (including the current page) from being tracked using FLoC. Hence the FLoC feature is turned off for the website, even if the user is visiting your website from a Google Chrome browser.


To add the above header, go to your nginx configuration file and add the following inside the server block:

server {

    add_header Permissions-Policy "interest-cohort=()";

If you have different nginx confs for multiple websites running via nginx, you have to do the above in all the server blocks or nginx configuration files.

Then run nginx -t to test that everything is correct in your updated nginx configuration. Then, restart nginx by running the command service nginx restart (or any other command that you might use based on your OS to restart nginx)

If you are using any other web server or proxy server, you can check this link:

April 19, 2021 10:26 AM

April 03, 2021

Pradyun Gedam

OSS Work update #10

I’m trying to post these roughly once a month. Here’s the Feb post.

Work I did (1 Mar 2021 - 31 Mar 2021)


  • Started writing a PEP 517 build backend for Sphinx themes.
  • Started writing sphinx-basic-ng, an attempt at modernising the Sphinx theme ecosystem.
  • Released new versions of sphinx-autobuild.
  • More updates to Furo.
  • Made progress on installer’s implementation.
  • Made progress on pip’s documentation rewrite.
  • Started work on rewriting ScriptTest, to pay down pip’s technical debt.


  • Talking to relevant folks about toml on PyPI (and moving it to… a specific GitHub repository).
  • Finally sat down and did an “open source responsibility audit”.
  • My open social weekends is still a thing, and has been great so far!
  • Still collaborating on designing a lockfile format for Python.
  • Added to The Executable Book Project’s GitHub organisation! ^>^
  • Chatted with a few people about the state of Python and pip on Debian.

General notes

Looking back, I think I picked up a couple of new projects based on random brain waves I had! That’s perfectly timed, because I’ve decided to pivot away from my earlier approach of “yay, more responsibility!”.

What next?

This is getting increasingly harder to decide on, as my free time chunks are becoming smaller and I’m picking up bigger projects. :)


  • Sphinx Theme PEP 517 stuff: Make the initial release.
  • sphinx-basic-ng: Make the first usable release.
  • pip: Clear some of the backlog on the pull request front.
  • pip: More progress on the documentation rewrite.


  • Spend more time looking into the Python lockfile standardisation effort.
  • Write a blog post, on automated code formatting.
  • Find more speaking opportunities, to talk about things that aren’t Python packaging!

Other commitments

A full time job, that pays my bills. :)

April 03, 2021 12:00 AM

March 03, 2021

Robin Schubert

How to make your phone battery last for one week and longer with this simple trick

Sure, it sounds like a click-bait headline, but I'm serious. I've bought my Android phone, a Google Nexus X5, second-hand from ebay. It's a great device, popular with developers but it's not known for its long lasting battery. In fact, I had to charge my phone at least once a day, if not more often.

But not anymore. During the last three weeks I charged it twice, no more. I didn't just turn it off. What I did is simple: I stopped using it. I turned off the WiFi, the mobile data connection and as a result I don't have much reason to look at it very often. Sometimes not even once a day. I do get phone calls from time to time, and I even play some puzzle games while I brush my teeth, but that's it.

Why no smart phone?

I would say I was an average smart phone user. I do some of that social networking thingies, message with friends and family and if I don't know the answer to a random question I ask duckduckgo. I've not been a poweruser, I don't have a zillion followers on any platform, and I don't take pictures of me or my food or my cats (don't have any) all the time. But I did use my phone regularly to check my email, my calendar or via termux work remotely on several Linux servers.

I cannot give any reliable numbers about the amount of time I've spent staring on my smart phone, but I can tell that I took it everywhere. I took it to the toilet to read my mastodon timeline and I took it with me when I went to bed to read my RSS feeds. I had it lying next to me on the table during meals or at work, so I could peek at incoming messages.

After a couple of weeks without using my smart phone now, I can tell that I miss none of that. I integrated some things into a more or less regular schedule, like checking feeds and mastodon on the laptop only once a day before I go to bed. It saves a lot of time and I never had the feeling I would miss anything. However, I find much more time to read books or play some guitar now, which I enjoy very much.

I spending a huge amount of my day in front of a screen anyway, due to my job and hobbies, but I feel that dropping the phone is absolutely beneficial for my health and also I could be the better role model for my children.

How no smart phone?

That leaves us with the question: What apps/services to drop and how to replace those that I'm not willing to drop?

The obvious

The easiest part was to port the most phone actions to the laptop. I did read mails on the phone a lot, though I didn't reply very often. Sometimes I just flagged some as important and left the work for later. Doing email on my laptop only resulted in more consistency and structure for me, and additionally saved quite some time and stress in my off-work time.

Randomly browsing the web or looking up stuff on Wikipedia, just like reading feeds and timelines was also easy to replace and led to more structure and much less distraction. Interestingly, I don't think that I'm spending more time on the laptop now, but I never took any measure to verify that ;)

I used my phone as second factor for some two-factor authentication logins. For now I also replaced that with the OTP module of my password manager pass. It still is a valid second factor, as I need the laptop additionally to the password, however, I think I will replace that with a security key in future.

I still enjoy Spotify sometimes. To make that work without my phone, I installed spotifyd on my raspberry pi, which I use as media center. The daemon just serves as the player, while I'm controlling the playlist from my laptop (using a beautiful TUI written in Rust, called spt).

The messaging

I'm using Signal as my main messaging app, but also joined some Matrix rooms on different servers and even follow some Mattermost conversations. Since I never seriously used IRC on the phone, I instead tried to integrate the messaging to my favorite IRC client weechat, with quite decent success.

I'm running signald on my weechat server and fixed some on the signal-weechat plugin and message happily ever since. Signald is a java app that subscribes to my phone number and provides a socket that can be used to interact with the API. Is it as secure as the official Signal app? Nope. But that's not my threat model ;) It's way better than the crappy electron app, in my opinion.

After I configured that, I realized that there are also plugins available for matrix and Mattermost (the latter of which is in pre-alpha, but hey). Using weechat for all my messaging feels great and to me is a big improvement over the phone (and over web- and electron clients). I know that there may be security drawbacks, not in transportation but in the way data is stored on my server. Luckily I can address these issues and I can do that independent of any app.

The rest

I thought that I could just drop everything that I cannot port to laptop. However, I hardly found anything I had to give up. I still use the phone to take pictures sometimes (never took many) and use the audio recorder occasionally. I play less random games.

The only thing I miss is the calendar functionality. I do have an excellent calendar on the laptop, or course (khal). But sometimes I would like to quickly glance at my upcoming events or schedule an appointment without being on the laptop. I will need to think how to do that without going fully analog on this one again. I'm having an eye on the Mudita Pure which I think would support me in exactly the way I chose to take.

by Robin Schubert at March 03, 2021 12:00 AM

February 27, 2021

Pradyun Gedam

OSS Work update #9

Alrighty! Let’s start doing this again. The plan is to get back to doing these roughly once a month again.

Work I did (1 Jan 2021 - 26 Feb 2021)


  • Published the last version of pip that supports Python 2! 🎉
  • Published a few releases of Furo – a Sphinx theme I wrote.
  • Made some (unreleased) changes to sphinx-autobuild.
  • Made some more progress on installer – a reusable library for Python (wheel) package installation.
  • Rewrote and the generation pipeline for it.
  • Started a rewrite of pip’s documentation. I’d love to get some feedback on this.


  • I’m experimenting with a new thing: social weekends!
  • I presented 2 talks at FOSDEM: in the Python devroom and Open Source Design devroom. Shout-out to Bernard Tyers, for all the help and the bazillion reminders to make sure I do all the things on time. :)
  • Collaborating on designing a lockfile format for Python, that can hopefully be standardised for interoperability.

General notes

Onboarding in a new company, relocating internationally, settling into a new space has been… well, it’s all been a very interesting learning experience.

Given the fairly strict lockdown and the percentage of people wearing masks in my locality, I’ve spent a lots of time indoors. Looking forward to the social weekends experiment I’m doing.

What next?


  • pip: Work on the documentation rewrite, hopefully to get it ready in time for the next release.
  • pip: Clear some of the backlog on the pull request front.
  • pip: General discussions for new features and enhancements.
  • TOML: Work on writing that the compliance test suite.
  • TOML: Bring toml for Python back from the dead.
  • Furo: Make the first stable release.
  • Start work on the other Sphinx theme I have in mind.


  • Spend more time looking into the Python lockfile standardisation effort.
  • Catch up on the Python-on-Debian saga, and see how I can contribute constructively.

Other commitments

Oh, I have a full time job at Bloomberg now. :)

February 27, 2021 12:00 AM

February 26, 2021

Saptak Sengupta

Anonymous Chat using OnionShare

Let's dive in a little deeper into the feature.

Why do we need an anonymous chat?

A common question that we got during developing this feature is what's the use of an anonymous chat room since we already have end-to-end encrypted messaging apps. It leaves a lot fewer traces.

The way we achieve this is very simple. There is no form of storage whatsoever in OnionShare chat mode. The chat is not persistent. The chat server stores no information at all (not even the usernames of people chatting). So once the chat server is closed, and the Tor Browser tab with the chat client is closed, there is no data (or metadata) related to chat that remains, even in the person's system who started the server. Hence, it leaves much less trace compared to other chat applications.

A good example of the above as mentioned by Micah in his blog is:

If, for example, you send a message to a Signal group, a copy of your message ends up on each device (the devices, and computers if they set up Signal Desktop of each member of the group). Even if disappearing messages is turned on it’s hard to confirm all copies of the messages are actually deleted from all devices, and from any other places (like notifications databases) they may have been saved to. OnionShare chat rooms don’t store any messages anywhere, so the problem is reduced to a minimum.

Given that the OnionShare chat feature works over the onion network, so it also has the additional anonymity feature. Also, adding to the anonymity feature, OnionShare chat doesn't need any form of signing in. Hence, people chatting can stay anonymous, and everything happens inside the tor network. One can just start a chat server, share the link via some disposable way, and then wait for the other people to join while maintaining anonymity.

Because it's an onion service, there is no way for an attacker to eavesdrop on the messages. The closest they can get is if they run a malicious Tor rendezvous node that's chosen for the onion service, they'll be able to spy on encrypted onion traffic. So, there's no capturing ciphertext to decrypt later on.

So what happens under the hood?

The chat feature is dependent on flask-socketio and eventlet for the WebSocket server implementation, and client js for the frontend implementation of the chat client. So when a chat server is started, the WebSocket is started in a namespace "/chat". Whenever a new user joins the link, they are given a randomly generated username and they are added to the room "default". There is only one room, and the actual name of the room can be set from the OnionShare settings-related code, but it doesn't really impact anything in the implementation. Both the room name and the randomly generated username are stored in a flask session. But that information is also completely gone once the chat server is stopped. The room and username information are only there to emit the messages properly.

You can also change the randomly generated username to a username (or pseudo username) of your choice for that particular session.

There are two main types of messages:

  1. status messages - these are sent from the client to the server only when a new user joins or someone updates their username. The status message is then broadcasted to all the other connected clients, who will then see it as a form of a status message in the chat window.

Onionshare Chat window with status messages for user joining and changing username

  1. user messages - these are sent when a user sends a message. All messages are broadcasted, so in case you share the link to multiple users, there is no concept of private message and everyone connected to the room can view your messages. Hence, sharing the onion link securely is important.

Onionshare Chat window with status messages for user joining and changing username

All of these WebSocket communication happens over the Tor onion services. OnionShare in itself doesn't implement any encryption algorithm to the chat and heavily relies on the Tor onion service's encryptions for the same. The message from the client to the OnionShare server is E2EE as it goes via Tor's onion connection. Then the OnionShare server broadcasts the message to all the other clients connected to the chat room through their E2EE onion connection, over WebSockets.

So what now?

I feel, as of now, the OnionShare anonymous chat is great if you quickly want to have an anonymous, secure, non-persistent conversation with someone or a group of people. It is also great if a whistleblower wants to share some details over chat with a journalist and then remove all traces of that conversation completely. But I feel if someone needs to host a chat server for a long time where people can connect anonymously, this is probably not the best solution for that.

There are still some issues that we will hopefully improve in the next releases. Firstly, we need to try and make it a bit more asynchronous. Right now, if left inactive for a long time, the Tor connection over WebSocket is sometimes dropped which isn't a great user experience. We are working on improving that.

Also, we will improve the UI for the chat more to give a better user experience.

With the new tabs feature, one can have all different modes (even multiple servers of same mode) running at the same time. So you can have a chat server and share mode server running at the same time. All the modes are very independent of each other and hence don't affect one another in any way.

I hope you all enjoy the new chat feature and leave feedbacks/suggestions on how to improve it. You can also read more about this and other features at

February 26, 2021 09:52 AM

August 23, 2020

Abhilash Raj

Concurrency bugs in Go

I recently read this paper titled, Understanding Real-World Concurrency Bugs in Go (PDF), that studies concurrency bugs in Golang and comments on the new primitives for messages passing that the language is often known for.

I am not a very good Go programmer, so this was an informative lesson in various ways to achieve concurrency and synchronization between different threads of execution. It is also a good read for experienced Go developers as it points out some important gotchas to look out for when writing Go code. The fact that it uses real world examples from well known projects like Docker, Kubernetes, gRPC-Go, CockroachDB, BoltDB etc. makes it even more fun to read!

The authors analyzed a total of 171 concurrency bugs from several prominent Go open source projects and categorized them in two orthogonal dimensions, one each for the cause of the bug and the behavior. The cause is split between two major schools of concurrency

Along the cause dimension, we categorize bugs into those that are caused by misuse of shared memory and those caused by misuse of message passing

and the behavior dimension is similarly split into

we separate bugs into those that involve (any number of ) goroutines that cannot proceed (we call themblocking bugs) and those that do not involve any blocking (non-blocking bugs)

Interestingly, they chose the behavior to be blocking instead of deadlock since the former implies that atleast one thread of execution is blocked due to some concurrency bug, but the rest of them might continue execution, so it is not a deadlock situation.

Go has primitive shared memory protection mechanisms like Mutex, RWMutex etc. with a caveat

Write lock requests in Go have ahigher privilege than read lock requests.

as compared to pthread in C. Go also has a new primitive called sync.Once that can be used to guarantee that a function is executed only once. This can be useful in situations where some callable is shared across multiple threads of execution but it shouldn't be called more than once. Go also has sync.WaitGroups , which is similar to pthread_join to wait for various threads of executioun to finish executing.

Go also uses channels for the message passing between different threads of executions called Goroutunes. Channels can be buffered on un-buffered (default), the difference between them being that in a buffered channel the sender and receiver don't block on each other (until the buffered channel is full).

The study of the usage patterns of these concurrency primitives in various code bases along with the occurence of bugs in the codebase concluded that even though message passing was used at fewer places, it accounted for a larger number of bugs(58%).

Implication 1:With heavier usages of goroutines and newtypes of concurrency primitives, Go programs may potentiallyintroduce more concurrency bugs

Also, interesting to note is this observation in tha paper

Observation 5:All blocking bugs caused by message passing are related to Go’s new message passing semantics like channel. They can be difficult to detect especially when message passing operations are used together with other synchronization mechanisms

The authors also talk about various ways in which Go runtime can detect some of these concurrency bugs. Go runtime includes a deadlock detector which can detect when there are no goroutunes running in a thread, although, it cannot detect all the blocking bugs that authors found by manual inspection.

For shared memory bugs, Go also includes a data race detector which can be enbaled by adding -race option when building the program. It can find races in memory/data shared between multiple threads of execution and uses happened-before algorithm underneath to track objects and their lifecycle. Although, it can only detect a part of the bugs discovered by the authors, the patterns and classification in the paper can be leveraged to improve the detection and build more sophisticated checkers.

by Abhilash Raj at August 23, 2020 12:59 AM

August 08, 2020

Farhaan Bukhsh

Url Shortner in Golang

TLDR; Trying to learn new things I tried writing a URL shortner called shorty. This is a first draft and I am trying to approach it from first principle basis. Trying to break down everything to the simplest component.

I decided to write my own URL shortner and the reason for doing that was to dive a little more into golang and to learn more about systems. I have planned to not only document my learning but also find and point our different ways in which this application can be made scalable, resilient and robust.

A high level idea is to write a server which takes the big url and return me a short url for the same. I have one more requirement where I do want to provide a slug i.e a custom short url path for the same. So for some links like, I want to have a url like which is easy to remember and distribute.

The way I am thinking to implement this is by having two components, I want a CLI interface which talks to my Server. I don’t want a fancy UI for now because I want it to be exclusively be used through terminal. A Client-Server architecture, where my CLI client sends a request to the server with a URL and an optional slug. If a slug is present URL will have that slug in it and if it doesn’t it generates a random string and make the URL small. If you see from a higher level it’s not just a URL shortner but also a URL tagger.

The way a simple url shortner works:

Flow Diagram

A client makes a request to make a given URL short, server takes the URL and stores it to the database, server then generates a random string and maps the URL to the string and returns a URL like<randomstring>.

Now when a client requests to<randomstring>, it goest to the same server, it searches the original URL and redirects the request to a different website.

The slug implementation part is very straightforward, where given a word, I might have to search the database and if it is already present we raise an error but if it isn’t we add it in the database and return back the URL.

One optimization, since it’s just me who is going to use this, I can optimize my database to see if the long URL already exists and if it does then no need to create a new entry. But this should only happen in case of random string and not in case of slugs. Also this is a trade off between reducing the redundancy and latency of a request.

But when it comes to generating a random string, things get a tiny bit complicated. This generation of random strings, decides how many URLs you can store. There are various hashing algorithms that I can use to generate a string I can use md5, base10 or base64. I also need to make sure that it gives a unique hash and not repeated ones.

Unique hash can be maintained using a counter, the count either can be supplied from a different service which can help us to scale the system better or it can be internally generated, I have used database record number for the same.

If you look at this on a system design front. We are using the same Server to take the request and generate the URL and to redirect the request. This can be separated into two services where one service is required to generate the URL and the other just to redirect the URL. This way we increase the availability of the system. If one of the service goes down the other will still function.

The next step is to write and integrate a CLI system to talk to the server and fetch the URL. A client that can be used for an end user. I am also planning to integrate a caching mechanism in this but not something out of the shelf rather write a simple caching system with some cache eviction policy and use it.

Till then I will be waiting for the feedback. Happy Hacking.

I now have a Patreon open so that you folks can support me to do this stuff for longer time and sustain myself too. So feel free to subscribe to me and help me keeping doing this with added benefits.

by fardroid23 at August 08, 2020 01:49 PM

July 20, 2020

Farhaan Bukhsh

Link Tray

TLDR; Link Tray is a utility we recently wrote to curate links from different places and share it with your friends. The blogpost has technical details and probably some productivity tips.

Link Bubble got my total attention when I got to know about it, I felt it’s a very novel idea, it helps to save time and helps you to curate the websites you visited. So on the whole, and believe me I am downplaying it when I say Link Bubble does two things:

  1. Saves time by pre-opening the pages
  2. Helps you to keep a track of pages you want to visit

It’s a better tab management system, what I felt weird was building a whole browser to do that. Obviously, I am being extremely naive when I am saying it because I don’t know what it takes to build a utility like that.

Now, since they discontinued it for a while and I never got a chance to use it. I thought let me try building something very similar, but my use case was totally different. Generally when I go through blogs or articles, I open the links mentioned in a different tab to come back to them later. This has back bitten me a lot of time because I just get lost in so many links.

I thought if there is a utility which could just capture the links on the fly and then I could quickly go through them looking at the title, it might ease out my job. I bounced off the same idea across to Abhishek and we ended up prototyping LinkTray.

Our first design was highly inspired by facebook messenger but instead of chatheads we have links opened. If you think about it the idea feels very beautiful but the design is “highly” not scalable. For example if you have as many as 10 links opened we had trouble in finding our links of interest which was a beautiful design problems we faced.

We quickly went to the whiteboard and put up a list of requirements, first principles; The ask was simple:

  1. To share multiple links with multiple people with least transitions
  2. To be able to see what you are sharing
  3. To be able to curate links (add/remove/open links)

We took inspiration from an actual Drawer where we flick out a bunch of links and go through them. In a serendipitous moment the design came to us and that’s how link tray looks like the way it looks now.

Link Tray

Link Tray was a technical challenge as well. There is a plethora of things I learnt about the Android ecosystem and application development that I knew existed but never ventured into exploring it.

Link Tray is written in Java, and I was using a very loosely maintained library to get the overlay activity to work. Yes, the floating activity or application that we see is called an overlay activity, this allows the application to be opened over an already running application.

The library that I was using doesn’t have support for Android O and above. To figure that out it took me a few nights 😞 , also because I was hacking on the project during nights 😛 . After reading a lot of GitHub issues I figured out the problem and put in the support for the required operating system.

One of the really exciting features that I explored about Android is Services. I think I might have read most of the blogs out there and all the documentation available and I know that I still don't know enough. I was able to pick enough pointers to make my utility to work.

Just like Uncle Bob says make it work and then make it better. There was a persistent problem, the service needs to keep running in the background for it to work. This was not a functional issue but it was a performance issue for sure and our user of version 1.0 did have a problem with it. People got mislead because there was constant notification that LinkTray is running and it was annoying. This looked like a simple problem on the face but was a monster in the depth.

Architecture of Link Tray

The solution to the problem was simple stop the service when the tray is closed, and start the service when the link is shared back to link tray. Tried, the service did stop but when a new link was shared the application kept crashing. Later I figured out the bound service that is started by the library I am using is setting a bound flag to True but when they are trying to reset this flag , they were doing at the wrong place, this prompted me to write this StackOverflow answer to help people understand the lifecycle of service. Finally after a lot of logs and debugging session I found the issue and fixed it. It was one of the most exciting moment and it help me learn a lot of key concepts.

The other key learning, I got while developing Link Tray was about multi threading, what we are doing here is when a link is shared to link tray, we need the title of the page if it has and favicon for the website. Initially I was doing this on the main UI thread which is not only an anti-pattern but also a usability hazard. It was a network call which blocks the application till it was completed, I learnt how to make a network call on a different thread, and keep the application smooth.

Initially approach was to get a webview to work and we were literally opening the links in a browser and getting the title and favicon out, this was a very heavy process. Because we were literally spawning a browser to get information about links, in the initial design it made sense because we were giving an option to consume the links. Over time our design improved and we came to a point where we don’t give the option to consume but to curate. Hence we opted for web scraping, I used custom headers so that we don’t get caught by robot.txt. And after so much of effort it got to a place where it is stable and it is performing great.

It did take quite some time to reach a point where it is right now, it is full functional and stable. Do give it a go if you haven’t, you can shoot any queries to me.

Link to Link Tray:

Happy Hacking!

by fardroid23 at July 20, 2020 02:30 AM

June 07, 2020

Kuntal Majumder

Transitioning to Windows

So, recently I started using windows for work. Why? There are a couple of reasons, one that I needed to use MSVC, that is the Microsoft Visual C++ toolchain and the other being, I wasn’t quite comfortable to ifdef stuff for making it work on GCC aka, the GNU counterpart of MSVC.

June 07, 2020 02:38 PM

May 09, 2020

Kuntal Majumder

Krita Weekly #14

After an anxious month, I am writing a Krita Weekly again and probably this would be my last one too, though I hope not. Let’s start by talking about bugs. Unlike the trend going about the last couple of months, the numbers have taken a serious dip.

May 09, 2020 04:12 PM

May 06, 2020

April 11, 2020

Shakthi Kannan

Using Docker with Ansible

[Published in Open Source For You (OSFY) magazine, October 2017 edition.]

This article is the eighth in the DevOps series. In this issue, we shall learn to set up Docker in the host system and use it with Ansible.


Docker provides operating system level virtualisation in the form of containers. These containers allow you to run standalone applications in an isolated environment. The three important features of Docker containers are isolation, portability and repeatability. All along we have used Parabola GNU/Linux-libre as the host system, and executed Ansible scripts on target Virtual Machines (VM) such as CentOS and Ubuntu.

Docker containers are extremely lightweight and fast to launch. You can also specify the amount of resources that you need such as CPU, memory and network. The Docker technology was launched in 2013, and released under the Apache 2.0 license. It is implemented using the Go programming language. A number of frameworks have been built on top of Docker for managing these cluster of servers. The Apache Mesos project, Google’s Kubernetes, and the Docker Swarm project are popular examples. These are ideal for running stateless applications and help you to easily scale them horizontally.


The Ansible version used on the host system (Parabola GNU/Linux-libre x86_64) is Internet access should be available on the host system. The ansible/ folder contains the following file:



The following playbook is used to install Docker on the host system:

- name: Setup Docker
  hosts: localhost
  gather_facts: true
  become: true
  tags: [setup]

    - name: Update the software package repository
        update_cache: yes

    - name: Install dependencies
        name: "{{ item }}"
        state: latest
        - python2-docker
        - docker

    - service:
        name: docker
        state: started

    - name: Run the hello-world container
        name: hello-world
        image: library/hello-world

The Parabola package repository is updated before proceeding to install the dependencies. The python2-docker package is required for use with Ansible. Hence, it is installed along with the docker package. The Docker daemon service is then started and the library/hello-world container is fetched and executed. A sample invocation and execution of the above playbook is shown below:

$ ansible-playbook playbooks/configuration/docker.yml -K --tags=setup
SUDO password: 

PLAY [Setup Docker] *************************************************************

TASK [Gathering Facts] **********************************************************
ok: [localhost]

TASK [Update the software package repository] ***********************************
changed: [localhost]

TASK [Install dependencies] *****************************************************
ok: [localhost] => (item=python2-docker)
ok: [localhost] => (item=docker)

TASK [service] ******************************************************************
ok: [localhost]

TASK [Run the hello-world container] ********************************************
changed: [localhost]

PLAY RECAP **********************************************************************
localhost                  : ok=5    changed=2    unreachable=0    failed=0   

With verbose ’-v’ option to ansible-playbook, you will see an entry for LogPath, such as /var/lib/docker/containers//-json.log. In this log file you will see the output of the execution of the hello-world container. This output is the same when you run the container manually as shown below:

$ sudo docker run hello-world

Hello from Docker!

This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:

For more examples and ideas, visit:


A Deep Learning (DL) Docker project is available ( with support for frameworks, libraries and software tools. We can use Ansible to build the entire DL container from the source code of the tools. The base OS of the container is Ubuntu 14.04, and will include the following software packages:

  • Tensorflow
  • Caffe
  • Theano
  • Keras
  • Lasagne
  • Torch
  • iPython/Jupyter Notebook
  • Numpy
  • SciPy
  • Pandas
  • Scikit Learn
  • Matplotlib
  • OpenCV

The playbook to build the DL Docker image is given below:

- name: Build the dl-docker image
  hosts: localhost
  gather_facts: true
  become: true
  tags: [deep-learning]

    DL_BUILD_DIR: "/tmp/dl-docker"
    DL_DOCKER_NAME: "floydhub/dl-docker"

    - name: Download dl-docker
        dest: "{{ DL_BUILD_DIR }}"

    - name: Build image and with buildargs
         path: "{{ DL_BUILD_DIR }}"
         name: "{{ DL_DOCKER_NAME }}"
         dockerfile: Dockerfile.cpu
           tag: "{{ DL_DOCKER_NAME }}:cpu"

We first clone the Deep Learning docker project sources. The docker_image module in Ansible helps us to build, load and pull images. We then use the Dockerfile.cpu file to build a Docker image targeting the CPU. If you have a GPU in your system, you can use the Dockerfile.gpu file. The above playbook can be invoked using the following command:

$ ansible-playbook playbooks/configuration/docker.yml -K --tags=deep-learning

Depending on the CPU and RAM you have, it will take considerable amount of time to build the image with all the software. So be patient!

Jupyter Notebook

The built dl-docker image contains Jupyter notebook which can be launched when you start the container. An Ansible playbook for the same is provided below:

- name: Start Jupyter notebook
  hosts: localhost
  gather_facts: true
  become: true
  tags: [notebook]

    DL_DOCKER_NAME: "floydhub/dl-docker"

    - name: Run container for Jupyter notebook
        name: "dl-docker-notebook"
        image: "{{ DL_DOCKER_NAME }}:cpu"
        state: started
        command: sh

You can invoke the playbook using the following command:

$ ansible-playbook playbooks/configuration/docker.yml -K --tags=notebook

The Dockerfile already exposes the port 8888, and hence you do not need to specify the same in the above docker_container configuration. After you run the playbook, using the ‘docker ps’ command on the host system, you can obtain the container ID as indicated below:

$ sudo docker ps
CONTAINER ID        IMAGE                    COMMAND               CREATED             STATUS              PORTS                NAMES
a876ad5af751        floydhub/dl-docker:cpu   "sh"   11 minutes ago      Up 4 minutes        6006/tcp, 8888/tcp   dl-docker-notebook

You can now login to the running container using the following command:

$ sudo docker exec -it a876 /bin/bash

You can then run an ‘ifconfig’ command to find the local IP address (“” in this case), and then open in a browser on your host system to see the Jupyter Notebook. A screenshot is shown in Figure 1:

Jupyter Notebook


TensorBoard consists of a suite of visualization tools to understand the TensorFlow programs. It is installed and available inside the Docker container. After you login to the Docker container, at the root prompt, you can start Tensorboard by passing it a log directory as shown below:

# tensorboard --logdir=./log

You can then open in a browser on your host system to see the Tensorboard dashboard as shown in Figure 2:


Docker Image Facts

The docker_image_facts Ansible module provides useful information about a Docker image. We can use it to obtain the image facts for our dl-docker container as shown below:

- name: Get Docker image facts
  hosts: localhost
  gather_facts: true
  become: true
  tags: [facts]

    DL_DOCKER_NAME: "floydhub/dl-docker"

    - name: Get image facts
        name: "{{ DL_DOCKER_NAME }}:cpu"

The above playbook can be invoked as follows:

$ ANSIBLE_STDOUT_CALLBACK=json ansible-playbook playbooks/configuration/docker.yml -K --tags=facts 

The ANSIBLE_STDOUT_CALLBACK environment variable is set to ‘json’ to produce a JSON output for readability. Some important image facts from the invocation of the above playbook are shown below:

"Architecture": "amd64", 
"Author": "Sai Soundararaj <>", 

"Config": {

"Cmd": [

"Env": [

"ExposedPorts": {
   "6006/tcp": {}, 
   "8888/tcp": {}

"Created": "2016-06-13T18:13:17.247218209Z", 
"DockerVersion": "1.11.1", 

"Os": "linux", 

"task": { "name": "Get image facts" }

You are encouraged to read the ‘Getting Started with Docker’ user guide available at to know more about using Docker with Ansible.

April 11, 2020 06:30 PM

January 19, 2020

Rahul Jha

"isn't a title of this post" isn't a title of this post

[NOTE: This post originally appeared on, and has been posted here with due permission.]

In the early part of the last century, when David Hilbert was working on stricter formalization of geometry than Euclid, Georg Cantor had worked out a theory of different types of infinities, the theory of sets. This theory would soon unveil a series of confusing paradoxes, leading to a crisis in the Mathematics community  regarding the stability of the foundational principles of the math of that time.

Central to these paradoxes was the Russell’s paradox (or more generally, as we’d talk about later, the Epimenides Paradox). Let’s see what it is.

In those simpler times, you were allowed to define a set if you could describe it in English. And, owing to mathematicians’ predilection for self-reference, sets could contain other sets.

Russell then, came up with this:

\(R\)  is a set of all the sets which do not contain themselves.

The question was "Does \(R \) contain itself?" If it doesn’t, then according to the second half of the definition it should. But if it does, then it no longer meets the definition.

The same can symbolically be represented as:

Let \(R = \{ x \mid x \not \in x \} \), then \(R \in R \iff R \not \in R \)

Cue mind exploding.

“Grelling’s paradox” is a startling variant which uses adjectives instead of sets. If adjectives are divided into two classes, autological (self-descriptive) and heterological (non-self-descriptive), then, is ‘heterological’ heterological? Try it!

Epimenides Paradox

Or, the so-called Liar Paradox was another such paradox which shred apart whatever concept of ‘computability’ was, at that time - the notion that things could either be true or false.

Epimenides was a Cretan, who made one immortal statement:

“All Cretans are liars.”

If all Cretans are liars, and Epimenides was a Cretan, then he was lying when he said that “All Cretans are liars”. But wait, if he was lying then, how can we ‘prove’ that he wasn’t lying about lying? Ein?

This is what makes it a paradox: A statement so rudely violating the assumed dichotomy of statements into true and false, because if you tentatively think it’s true, it backfires on you and make you think that it is false. And a similar backfire occurs if you assume that the statement is false. Go ahead, try it!

If you look closely, there is one common culprit in all of these paradoxes, namely ‘self-reference’. Let’s look at it more closely.

Strange Loopiness

If self-reference, or what Douglas Hofstadter - whose prolific work on the subject matter has inspired this blog post - calls ‘Strange Loopiness’ was the source of all these paradoxes, it made perfect sense to just banish self-reference, or anything which allowed it to occur. Russell and Whitehead, two rebel mathematicians of the time, who subscribed to this point of view, set forward and undertook the mammoth exercise, namely “Principia Mathematica”, which we as we will see in a little while, was utterly demolished by Gödel’s findings.

The main thing which made it difficult to ban self-reference was that it was hard to pin point where exactly did the self-reference occur. It may as well be spread out over several steps, as in this ‘expanded’ version of Epimenides:

The next statement is a lie.

The previous statement is true.

Russell and Whitehead, in P.M. then, came up with a multi-hierarchy set theory to deal with this. The basic idea was that a set of the lowest ‘type’ could only contain ‘objects’ as members (not sets). A set of the next type could then only either contain objects, or sets of lower types. This, implicitly banished self-reference.

Since, all sets must have a type, a set ‘which contains all sets which are not members of themselves’ is not a set at all, and thus you can say that Russell’s paradox was dealt with.

Similarly, if an attempt is made towards applying the expanded Epimenides to this theory, it must fail as well, for the first sentence to make a reference to the second one, it has to be hierarchically above it - in which case, the second one can’t loop back to the first one.

Thirty one years after David Hilbert set before the academia to rigorously demonstrate that the system defined in Principia Mathematica was both consistent (contradiction-free) and complete (i.e. every true statement could be evaluated to true within the methods provided by P.M.), Gödel published his famous Incompleteness Theorem. By importing the Epimenides Paradox right into the heart of P.M., he proved that not just the axiomatic system developed by Russell and Whitehead, but none of the axiomatic systems whatsoever were complete without being inconsistent.

Clear enough, P.M. lost it’s charm in the realm of academics.

Before Gödel’s work too, P.M. wasn’t particularly loved as well.


It isn’t just limited to this blog post, but we humans, in general, have a diet for self-reference - and this quirky theory severely limits our ability to abstract away details - something which we love, not only as programmers, but as linguists too - so much so, that the preceding paragraph, “It isn’t … this blog … we humans …” would be doubly forbidden because the ‘right’ to mention ‘this blog post’ is limited only to something which is hierarchically above blog posts, ‘metablog-posts’. Secondly, me (presumably a human) belonging to the class ‘we’ can’t mention ‘we’ either.

Since, we humans, love self-reference so much, let’s discuss some ways in which it can be expressed in written form.

One way of making such a strange loop, and perhaps the ‘simplest’ is using the word ‘this’. Here:

  • This sentence is made up of eight words.
  • This sentence refers to itself, and is therefore useless.
  • This blog post is so good.
  • This sentence conveys you the meaning of ‘this’.
  • This sentence is a lie. (Epimenides Paradox)

Another amusing trick for creating a self-reference without using the word ‘this sentence’ is to quote the sentence inside itself.

Someone may come up with:

The sentence ‘The sentence contains five words’ contains five words.

But, such an attempt must fail, for to quote a finite sentence inside itself would mean that the sentence is smaller than itself. However, infinite sentences can be self-referenced this way.

The sentence
    "The sentence
        "The sentence
        is infinitely long"
    is infinitely long"
is infinitely long"

There’s a third method as well, which you already saw in the title - the Quine method. The term ‘Quine’ was coined by Douglas Hofstadter in his book “Gödel Escher, Bach” (which heavily inspires this blog post). When using this, the self-reference is ‘generated’ by describing a typographical entity, isomorphic to the quine sentence itself. This description is carried in two parts - one is a set of ‘instructions’ about how to ‘build’ the sentence, and the other, the ‘template’ contains information about the construction materials required.

The Quine version of Epimenides would be:

“yields falsehood when preceded by it’s quotation” yields falsehood when preceded by it’s quotation

Before going on with ‘quining’, let’s take a moment and realize how awfully powerful our cognitive capacities are, and what goes in our head when a cognitive payload full of self-references is delivered - in order to decipher it, we not only need to know the language, but also need to work out the referent of the phrase analogous to ‘this sentence’ in that language. This parsing depends on our complex, yet totally assimilated ability to handle the language.

The idea of referring to itself is quite mind-blowing, and we keep doing it all the time — perhaps, why it feels so ‘easy’ for us to do so. But, we aren’t born that way, we grow that way. This could better be realized by telling someone much younger “This sentence is wrong.”. They’d probably be confused - What sentence is wrong?. The reason why it’s so simple for self-reference to occur, and hence allow paradoxes, in our language, is well, our language. It allows our brain to do the heavy lifting of what the author is trying to get through us, without being verbose.

Back to Quines.

Reproducing itself

Now, that we are aware of how ‘quines’ can manifest as self-reference, it would be interesting to see how the same technique can be used by a computer program to ‘reproduce’ itself.

To make it further interesting, we shall choose the language most apt for the purpose - brainfuck:


Running that program above produces itself as the output. I agree, it isn’t the most descriptive program in the world, so written in Python below, is the nearest we can go to describe what’s happening inside those horrible chains of +’s and >’s:

THREE_QUOTES = '"' * 3

def eniuq(template): print(

eniuq("""THREE_QUOTES = '"' * 3

def eniuq(template): print(


The first line generates """ on the fly, which marks multiline strings in Python.

Next two lines define the eniuq function, which prints the argument template twice - once, plain and then surrounded with triple quotes.

The last 4 lines cleverly call this function so that the output of the program is the source code itself.

Since we are printing in an order opposite of quining, the name of the function is ‘quine’ reversed -> eniuq (name stolen from Hofstadter again)

Remember the discussion about how self-reference capitalizes on the processor? What if ‘quining’ was a built-in feature of the language, providing what we in programmer lingo call ‘syntactic sugar’?

Let’s assume that an asterisk, * in the brainfuck interpreter would copy the instructions before executing them, what would then be the output of the following program?


It’d be an asterisk again. You could make an argument that this is silly, and should be counted as ‘cheating’. But, it’s the same as relying on the processor, like using “this sentence” to refer to this sentence - you rely on your brain to do the inference for you.

What if eniuq was a builtin keyword in Python? A perfect self-rep was then just be a call away:


What if quine was a verb in the English language? We could reduce a lot of explicit cognitive processes required for inference. The Epimenides paradox would then be:

“yields falsehood if quined” yields falsehood if quined

Now, that we are talking about self-rep, here’s one last piece of entertainment for you.

The Tupper’s self-referential formula

This formula is defined through an inequality:

\({1 \over 2} < \left\lfloor \mathrm{mod}\left(\left\lfloor {y \over 17} \right\rfloor 2^{-17 \lfloor x \rfloor - \mathrm{mod}(\lfloor y\rfloor, 17)},2\right)\right\rfloor\)

If you take that absurd thing above, and move around in the cartesian plane for the coordinates \(0 \le x \le 106, k \le y \le k + 17\), where \(k\) is a 544 digit integer (just hold on with me here), color every pixel black for True, and white otherwise, you'd get:

This doesn't end here. If \(k\) is now replaced with another integer containing 291 digits, we get yours truly:

January 19, 2020 06:30 PM