Saptak's Blog

Anonymous Chat using OnionShare

Posted: 2021-02-26T15:22:50+05:30

Onionshare Chat Option screen

So the new OnionShare is out and it has a bunch of exciting new features and some improvements in the UI/UX designs of the tool. One of the main new features that I helped build was the anonymous chat feature in OnionShare. Just like the other modes (share, receive, and website), there is now a chat mode. So if you want to start a chat service, you just start the chat server, share the onion address of the server with people you want to chat with, everyone opens this onion address in Tor Browser and voila! You have an anonymous chat.

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

Adding CSP hashes for styles in Chromium

Posted: 2021-01-17T14:13:55+05:30

Content Security Policy (or CSP) is a way of avoiding certain types of website-related attacks like cross-site scripting and malicious data injections. It is a way by which website developers can tell the browser what content origins are approved so that everything else is blocked. One needs to add a Content-Security-Policy HTTP header mentioning the sources which they allow for loading scripts, styles, images, etc.

To read in detail about CSP, check Content Security Policy Level 3 working draft.

We are going to discuss here why sha256 hashes often don't let inline styles to not pass in chromium browsers. Chromium browser console complains about the style-src hashes mismatch even though it shows them to be the same. Why? And how to solve it?

TL;DR: If using <style>, use style-src. If using style="" attribute in HTML tag, use style-src-attr

Now, if you are interested in more information, let's dive a little deeper into what's going on.

Hashes to allow inline styles & scripts

The usual practice of having a tight, secure CSP is to not allow any inline style or inline scripts. This helps mitigate malicious scripts entered via data injection from getting executed.

When I say inline scripts, one might understand 2 different scenarios:

<!-- Scenario 1 -->
<script>alert('Hello world');</script>


<!-- Scenario 2 -->
<button onclick="alert('Hello world');">
    Click me!

Now, the easiest way to allow this would be to add unsafe-inline in script-src of the CSP. But then we are back to the problem of executing malicious scripts entered by data injection. There are two ways to still allow only these scripts to work: nonce and sha256 hashes. We are going to talk about sha256 hashes here.

The idea is to get the sha256 hash of the entire script and add it to the script-src. So in this case, it would be something like this:

script-src 'self' 'sha256-DUTqIDSUj1HagrQbSjhJtiykfXxVQ74BanobipgodCo='

You can get the hash from Also, chromium browsers will usually show the hash that should be added for a particular inline script.

The Problem

Now, all this sounds good, and in Firefox, just adding the above to your CSP will make both the scripts to work. However, in chromium, the above CSP will work only in Scenario 1 but not in Scenario 2. You can read more about the discussion here:

In JavaScript, I think in general scenario 1 will be much more encouraged than scenario 2. So scenario 2 might not be encountered that often. However, the situation changes, when it comes to styles (or CSS)

In case of inline styles, following are the scenarios:

<!-- scenario 1 -->
<style>p{color: blue;}</style>


<!-- scenario 2 -->
<p style="color: blue;">This is a text</p>

In CSS, the second scenario is much more common when someone does inline styles than scenario 1. But again, in this case, adding a sha256 hash to style-src won't execute the scenario 2 in chromium browsers.

This is because styles added in scenario 2 are part of the style attribute in the HTML tag which in CSP terms are essentially event handlers. According to w3c CSP draft, the hash in style-src allows the inline styles mentioned inside <style> tag to pass but doesn't allow event handlers (as is the case in scenario 2). There's more on this discussion here.

So it's a feature?

Yes, it is a feature. In chromium browsers, adding a hash to style-src only allows any inline style written inside the <style> tags to execute. This is by design. If you need to execute the inline styles present in style= attribute of HTML tags, you need to use another directive in CSP called style-src-attr. Similarly, script-src-attr should be used if you are doing JavaScript event handling in the HTML tag itself.

So, for example, if you want to only allow an inline CSS such as this:

<p style="color: blue;">This is a text</p>

all you need to do is put the sha256 hash in style-src-attr along with 'unsafe-hashes'. This will tell the browser to allow any inline style, with the hashes that you added in style-src-attr to be executed.

So the CSP will have something like this:

style-src-attr 'unsafe-hashes' 'sha256-C8uD/9cXZAvqgnwxgdb67jgkSDq7f8xjP8F6lhY1Gtk='

And, that's it! That will do the trick in any chromium browser. The related code for chromium can be found here. According to, all chromium browsers above 75 supports this behaviour.

Even though firefox still doesn't have support for style-src-attr but it allows inline styles and scripts of all types to pass based on style-src and script-src hashes. So as long as the hash is mentioned in both style-src and style-src-attr, it should work in most of the browsers.

As for the explanation behind why 'unsafe-hashes', there is a pretty good explainer document written by andypaicu talking about exactly this.

Also, read more about style-src-attr in detail in the w3c draft to understand exactly what's happening and what kind of risk it may still pose.

PS: Inline JavaScript event handlers using script-src-attr can be very risky given an attacker can trigger a passing javascript from within an unrelated HTML tag.

Do not fear, Tumpa is here

Posted: 2021-01-03T18:18:08+05:30

Anyone who has ever created an OpenPGP key knows that it is a terrifying day in their life. Be it someone skilled in computers, or someone who just cares about their privacy, creating OpenPGP key is a fearsome incident. Add moving all subkeys properly to yubikey along with managing all passphrases, and the terror just increases manifold.

Well, do not fear, Tumpa is here.

Tumpa loves GUI

For most journalists, lawyers, activists, or anyone who wants to have secure communication, OpenPGP key is a great way to send and receive encrypted messages. But most people dread a black terminal (or command line) with some text menu. That's the only way to probably create OpenPGP keys and transfer them to a smartcard (e.g, Yubikey) till now. So, when Kushal showed me johnnycanencrypt, his python library for various OpenPGP key based operations, we had this idea that it would be simply amazing if we can provide a Graphical User Interface (GUI) for people to create keys and transfer their keys to yubikey.

Being a digital security trainer, I can vouch that most journalists, lawyers, activists and anyone who doesn't sit in front of a terminal all day would rather have a desktop application to click a few buttons, fill up a few forms, and get their result, rather than typing command after command in a black screen.

And that's exactly what Tumpa does!

Tumpa provides a simple form where you need to add your name, all emails that you want to associate with your OpenPGP key, a passphrase for your OpenPGP key, click on the big "Generate" button, and boom!

That's it!

You have your OpenPGP key with proper subkeys and everything!

Demo of generating a key and transferring to yubikey

Well, what about transferring the key to the smart card? Just plug your Yubikey, click on the big "Upload to SmartCard" button, add the necessary passphrases, and done!

You have your key transferred to a physical key!

Tumpa helps you stay sane

Usually, a training session to teach someone to create OpenPGP key properly and transferring everything properly to a smartcard like yubikey takes about 3-4 hours. And after such a session, usually, everyone loses a bit of their sanity in the process.

The first time I and Kushal got the first draft working and went through the entire flow, we were both positively surprised and probably laughing hysterically (thanks Anwesha for tolerating us for the last few days).

Tumpa optimistically reduces work which you would take hours, into a few minutes. And also lets everyone keep their sanity. Most of the operations that would need you to type a lot of commands and understand some command-line options, can be achieved by a few clicks.

You can download the .deb package from the release page.

Then, install using dpkg -i ./tumpa_0.1.0+buster+nmu1_all.deb, preferrably on an airgapped computer inside of Tails.

Tumpa is still a work in progress

Tumpa is at a very early stage of development. We have tried to make Tumpa feature complete to the most necessary ones and make the initial release. But there's still a lot of work left to be done.

We want to make Tumpa even easier to use for people who don't want to get into all the intricacies of OpenPGP key while giving more advanced options to the more experimental and curious users.

Right now, Tumpa uses Curve25519 to create keys and subkeys with an expiration date of 3 years. We want to give options to possibly select these based on a user's need in case they really care and want to change things are. There are many such customizations and also simplifications that we will slowly add in the next releases trying to improve the entire user experience even more.

Tumpa needs feedback

We have started conducting user interviews. We would really love more people to do usability studies with a varied group of technologists, lawyers, journalists, activists, or anyone interested, to improve the UX manifold.

The UI, for now, is very simple and probably not the best. So we can definitely use any feedback or suggestions.

We are available on #tumpa channel on Freenode. Feel free to drop by with all your comments.

Also, read Kushal's release blog on Tumpa to know more about installation and packaging.

Handling nested serializer validations in Django Rest Framework

Posted: 2020-05-03T01:22:50+05:30

I understand that the title of this post is a little confusing. Recently, while working on the Projects API in Weblate, I came across an interesting issue. The Projects API in Weblate allowed you to get an attribute called source_language. Every project has only one source_language and in the API, it was a read-only property.

  "name": "master_locales",
  "slug": "master_locales",
  "web": "",
  "source_language": {
    "code": "en",
    "name": "English",
    "direction": "ltr",
    "web_url": "http:/",
    "url": ""
  "web_url": "",
  "url": "",
  "components_list_url": "",
  "repository_url": "",
  "statistics_url": "",
  "changes_list_url": "",
  "languages_url": ""

As you can see, unlike the other relational fields, it's not a HyperLinkedIdentityField. It uses the nested language serializer to show all the attributes of the source_language.

Now, previously, when a project was created via API, a default language was always assigned to the project and there was no way to define the source_language while creating the project via API.


Doing GET on Language Serializer when sending POST on Project Serializer

So we needed to add the feature to define the source_language of the project when we send a POST request to the Project API. And also edit the project via API to update the source_language. So, to use the same serializer, the request body for the POST request would look something like this:

  "name": "master_locales",
  "slug": "master_locales",
  "web": "",
  "source_language": {
    "code": "ru",
    "name": "Russian",
    "direction": "ltr",

Now, in general, we would have a python serializer like this:

class LanguageSerializer(serializers.ModelSerializer):
    web_url = AbsoluteURLField(source="get_absolute_url", read_only=True)
    class Meta:
        model = Language
        fields = ("code", "name", "direction", "web_url", "url")
        extra_kwargs = {
            "url": {"view_name": "api:language-detail", "lookup_field": "code"}

class ProjectSerializer(serializers.ModelSerializer):
    source_language = LanguageSerializer(required=False)
    # ...
    # Other parts of the serializer

The problem with having code like this is, when the ProjectSerializer gets a request like shown above and tries to validate the data in the request, it also validates the LanguageSerializer part. The LanguageSerializer part whenever it gets data, it will automatically try to validate the data. The code property of Language model has a unique constraint. So, when LanguageSerializer tries to validate

    "code": "ru",
    "name": "Russian",
    "direction": "ltr",

it will throw an error "This field must be unique" for code property in case a language with codename ru already exists in the database.


So there are few steps to get this done.

Remove validators from code field

extra_kwargs = {
    "url": {"view_name": "api:language-detail", "lookup_field": "code"},
    "code": {"validators": []},

Add "code": {"validators": []} to the extra_kwargs to remove the validator from the LanguageSerializer on every data request it receives.

Add manual validation for code field

Removing validator will also remove the validation while doing POST request. Now, the LanguageSerializer in Weblate specifically doesn't support POST, but in any case, you would manually need to add a validation function to the LanguageSerializer so if someone checks for validity before adding language, it throws an error. To do that, add a function validate_code like this:

def validate_code(self, value):
    check_query = Language.objects.filter(code=value)
    if check_query.exists() and not (
        isinstance(self.parent, ProjectSerializer)
        and self.field_name == "source_language"
        raise serializers.ValidationError(
            "Language with this Language code already exists."
    if not check_query.exists():
        raise serializers.ValidationError(
            "Language with this language code was not found."
    return value

Note: The name of the function must be validate_{field_name} when you are trying to validate a field based on how DRF handles validation.

Overwrite create() in ProjectSerializer

Finally, we would want to overwrite the create() function of ProjectSerializer to:

  • Validate source_language data using the above validation to check if the language with that code exists
  • Modify source_language key of the validated_data to have the Language model object rather than the dictionary, so it can be used to create a project with the foreign key.
  • Lastly, create a project with the new validata_data

The code would look something like this:

def create(self, validated_data):
    source_language_validated = validated_data.get("source_language")
    if source_language_validated:
        validated_data["source_language"] = Language.objects.get(
    project = Project.objects.create(**validated_data)
    return project

And now, if you create a project, using the source_language key, you can define the source language for the project while using the Project API. There might be several other ways to go about it. But this is one of the ways I found works.

Also, this feature is now live in Weblate 4.* versions which allows you to define the source_language via the API.

Creating Custom Whoosh Plugin

Posted: 2020-04-19T13:16:52+05:30

Recently, while trying to work on a query parser feature in Weblate, I came across this search engine library called Whoosh. It provides certain nice features like indexing of text, parsing of search queries, scoring algorithms, etc. One good thing about this library is most of these features are customizable and extensible.

Now, the feature I was trying to implement is an exact search query. An exact search query would behave in a way such that the backend would search for an exact match of any query text provided to it instead of the normal substring search. Whoosh provides a plugin for regex, which can be accessed via whoosh.qparser.RegexPlugin(). So we can technically go about writing a regex to do the exact match. But a regex search will have worse performance than a simple string comparison.

So, one of the ways of doing a new kind of query parsing is creating a custom whoosh plugin. And that's what this blog is going to be about.

Simple Whoosh Plugin

In some cases, you will probably not need a complicated plugin, but just want to extend the feature of an existing plugin to match a different kind of query. For example, let's say you want to extend the ability of SingleQuotePlugin to parse queries wrapped in either single-quotes or double-quotes.

class QuotePlugin(whoosh.qparser.SingleQuotePlugin):
    """Single and double quotes to specify a term."""
    expr = r"(^|(?<=\W))['\"](?P<text>.*?)['\"](?=\s|\]|[)}]|$)"

In the above example, QuotePlugin extends the already existing SingleQuotePlugin class. It just overrides the expression to parse the query. The expression, mentioned in the variable expr is usually a regex expression with ?P<text> part denoting the TermQuery. A TermQuery is the final term/terms searched for in the database. So in the above regex, we say to parse any query such that the TermQuery is wrapped in between single-quotes or double-quotes.

Query Class

A query class is the class, whose instance the final parsed term will be. Unless otherwise mentioned, it's usually <Term>. So if we want our plugin to parse the query and show it as an instance of a custom class, we need to define a custom query class.

class Exact(whoosh.query.Term):
    """Class for queries with exact operator."""


So, as you can say, we can just have a simple class just extending whoosh.query.Term so that while checking the parsed terms, we can get is as an instance of Exact. That will help us differentiate the query from a normal Term instance.

Custom Whoosh Plugin

After writing the query class, we will need to write the custom plugin class.

class ExactPlugin(whoosh.qparser.TaggingPlugin):
    """Exact match plugin with quotes to specify an exact term."""

    class ExactNode(whoosh.qparser.syntax.TextNode):
        qclass = Exact

        def r(self):
            return "Exact %r" % self.text

    expr = r"\=(^|(?<=\W))(['\"]?)(?P<text>.*?)\2(?=\s|\]|[)}]|$)"
    nodetype = ExactNode

In the above example, unlike the simple case, we extend TaggingPlugin instead of any other pre-defined plugin. Most of the pre-defined plugins in whoosh also extend TaggingPlugin. So it is a good fit as a parent class.

Then, we create a ExactNode class. This we will assign to the node type for the custom plugin. A node type class basically defines the query class to be used in this custom plugin, along with various representations and properties of the parsed node. qclass will have the query class created before to denote the Exact instance to the final parsed term.

Apart from that, we have the expr which contains the regex just like in the simple example to parse the query term.


After creating the custom plugin, you can:

  • add this plugin to the list of plugins defined in the whoosh query parser class
  • use the query class to make an isinstance() check when making database queries
  • check for the node type in the different nodes used by the parser

Configuring Jest with React and Babel

Posted: 2019-08-26T11:38:00+05:30
Jest is a really good frontend testing framework and works great with React and Babel out of the box, along with Enzyme for component testing. But, imports with React and Babel can often be filled with nasty imports. I wrote in a previous blog about how to make better more cleaner imports using some webpack tweaks.

But the problem appears when we try to write Jest and Enzyme tests with them. Because Babel can now longer understand and parse the imports. And without Babel parsing them and converting to ES5, jest cannot test the components. So we actually need a mix of Babel configuration and Jest configuration.

Note: This post assumes you already have jest, babel-jest and babel/plugin-transform-modules-commonjs packages installed using your favorite javascript package manager.

Basically, the workaround is first we need to resolve the cleaner imports into the absolute paths for the import using Jest configurations, and then use the Babel configurations to parse the rest code (without the modules) to ES5.

The configuration files look something like these:


module.exports = api => {
const isTest = api.env('test');
if (isTest) {
return {
presets: [
modules: false,
plugins: [
} else {
return {
presets: [
modules: false,


module.exports = {
moduleNameMapper: {
'^~/(.*)$': '<rootDir>/path/to/jsRoot/$1'

So let's go through the code a little.

In babel.config.js, we make a check to see if the code is right now in test environment. This is helpful because
  1. Jest sets the environment to "test" when running a test so it is easily identifiable
  2. It ensures that the test configuration don't mess up with the non test configurations in our Webpack config (or any other configuration you are using)
So in this case, I am returning the exact same Babel configuration that I need in my Webpack config in non-test environment.

In the test configuration for Babel, we are using a plugin "@babel/plugin-transform-modules-commonjs". This is needed to parse all the non component imports like React, etc. along with parsing the components from ES6 to ES5 after jest does the path resolution. So it helps to convert the modules from ES6 to ES5.

Now, let's see the jest.config.js. The jest configuration allows us to do something called moduleNameMapper. This is a very useful configuration in many different usecases. It basically allows us to convert the module names or paths we use for module import to something that jest understands (or in our case, something that the Babel plugin can parse). 

So, the left hand part of the attribute contains a regular expression which matches the pattern we are using for imports. Since our imports look something like '~/path/from/jsRoot/Component', so the regular expression to capture all such imports is '^~/(.*)$'. Now, to convert them to absolute paths, we need to append '<rootDir>/path/to/jsRoot/' in front of the component path.

And, voila! That should allow Jest to properly parse, convert to ES5 and then test.

The best part? We can use the cleaner imports even in the .test.js files and this configuration will work perfectly with that too.

Making cleaner imports with Webpack and Babel

Posted: 2019-08-10T12:30:00+05:30
You can bring in modules from different javascript file using require based javascript code, or normal Babel parse-able imports. But the code with these imports often become a little bad because of relative imports like:

import Component from '../../path/to/Component'

But a better, more cleaner way of writing ES6 imports is

import Component from '~/path/from/jsRoot/Component'

This hugely avoids the bad relative paths  for importing depending on where the component files are. Now, this is not parse-able by babel itself. But you can parse this by webpack itself using it's resolve attribute. So your webpack should have these two segments of code:

resolve: {
        alias: {
            '~': __dirname + '/path/to/jsRoot',
            modernizr$: path.resolve(__dirname, '.modernizrrc')
        extensions: ['.js', '.jsx'],
        modules: ['node_modules']


module: {
        rules: [
                test: /\.jsx?$/,
                use: [
                        loader: 'babel-loader',
                        query: {
                            presets: [
                                ['@babel/preset-env', { modules: false }]

The {modules: false} ensures that babel-preset-env doesn't handle the parsing of the module imports. You can check the following comment in a webpack issue to know more about this.

What's a ShadowDOM?

Posted: 2019-05-24T23:24:00+05:30
I first came to know about Shadow DOM from my curiosity about how browsers implement tags like <video> with all the controllers or <input> which changes based on the type attribute. We can never see any HTML code for these implementation and yet they are shown. How? That is when I stumbled upon this blog by Dimitri Glazkov which explains beautifully the concept of shadow DOM encapsulation used by browsers to implement such tags.

However, none of the browsers allowed developers to write their own custom shadow DOM (though google chrome had a v0 version implemented). I stumbled upon shadow DOM again while looking at an issue in jQuery to fix. So now, since 2018, most of the browsers have started supporting shadow DOM APIs and hence jQuery needed to implement support for that too.
So, what is this shadow DOM and why do we even use it?

What's a DOM?

W3C specification describes it as "a method of combining multiple DOM trees into one hierarchy and how these trees interact with each other within a document, thus enabling better composition of the DOM".

Now, to understand that, we need to understand what a DOM is. DOM or Document Object Model is a tree-like structure containing the different elements (or tags) and strings of text that are shown by the markup language (like HTML, XML, etc.).

So, let's say we have a HTML code something like this:

<!DOCTYPE html>
<meta charset="utf-8">
<h1>This is header</h1>
<p>This is a
<a href="">

So, visually you can show the DOM structure as something like:

Shadow DOM

Now, shadow DOM allows us to create separate hidden DOM trees that are attached to the elements of a regular DOM tree. This allows you to implement functional encapsulation, i.e. someone parsing the regular DOM tree and applying styling to the regular DOM tree doesn't know or affect the properties and functionalities of the shadow DOM tree. Hence you use the shadow DOM without knowing the intricate details of how the DOM is implemented. This is important, because this follows the basic ideas of Object Oriented Programming.

The shadow DOM tree starts with a shadow root and can then have any regular DOM element attached underneath it.

Let's see an example:

<!DOCTYPE html>
<meta charset="utf-8">
<div id="shadowHost">

const shadowHost = document.getElementById('shadowHost');
const shadowRoot = shadowHost.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<h1>Hello Shadow DOM</h1>';

So, this will create a shadow DOM. Visually you can represent this as:

So, as you can see, there are few different parts in a shadow DOM apart from it being just another DOM.
  • Shadow tree: The DOM tree inside the shadow DOM.
  • Shadow boundary: the place where the shadow DOM ends, and the regular DOM begins.
  • Shadow root: The root node of the shadow tree.
  • Shadow host: The regular DOM node that the shadow DOM is attached to.
  • Shadow child: The tree below the shadow root node.
The shadow DOM cannot be attached to few different elements as mentioned in the spec. Some of the reasons are:
  • The different form tags such as <input>, <textarea>, etc or any other html tag for which the browser implements its own Shadow DOM
  • Elements like <img> or <br> or <hr> which are usually self enclosing tags and don't usually contain a child node.
Also if you see in the code there is a "{mode: open}". The mode determines whether you can edit the style of the shadow DOM from outside the shadow DOM or not.

Why do we need Shadow DOM anyways?

There are few different scenarios where you might want to use shadow DOM. The most important functionality of shadow DOM is the implementation of the concept of encapsulation. So basically, someone using the shadow DOM host doesn't need to care the style and implementation of the DOM inside. So few use-cases would be following:
  • The browser implements Shadow DOM for various different tags such as <video>, <audio>, <input>, <select>, etc.
  • You can make your own custom Shadow DOM when you need to create an element that you want to not be modified by the styling of remaining DOM.
  • You can also use Shadow DOM, when you want to create a separation of a particular DOM element from the main DOM element.
So is Shadow DOM very shadow-ey? Well maybe, since it stays hidden from the main DOM traversal. But at the same time it is often very useful because of its encapsulation properties.

What to expect from GSoC?

Posted: 2019-03-10T16:06:00+05:30
If you were searching for a post about how to get selected in GSoC, and landed on this blog, you might be a little confused by the blog title. Because, well, this blog isn't about how to get selected in GSoC, or as many have sadly started saying, how to "crack" GSoC. There are plenty of blogs out there addressing that.

In this blog, I am going to write more about what you should expect from GSoC. And given that the applications for GSoC are about to start, it is high time you get your expectations right if you actually want to get the most out of it.
So let's get started.

1. Don't "Crack" GSoC

Unlike many other computer science and engineering programs, there is no pill that you take and you magically get selected overnight. Nor is there a particular curriculum or book that you can read over and over and practice and get selected.

Getting selected in GSoC is a gradual process that needs lots of patience and contribution and the slow but steady accrual of experience. So if you are actually reading this blog in an attempt to know how to get selected, you are kind of late in the process. Better late than never, though.

The only way you get selected in GSoC (at least in most organisations) is via Open Source Contributions. So now you might think, okay, open source contribution is the curriculum.

Yes and no.

If you consider open source as yet another chapter in your coursework, then getting started with open source contribution might be difficult.

There's a lot involved; from clean, readable coding to best practices to communication. It is an entirely new way of working (way of life?) that is going to last you forever and help you in the long run.

For many, like us (like me personally), it's more than even that. It's a belief, a principle, a movement. I am going to talk more about that in the points below.

So even though many blogs will tell you exactly how to pick an issue, and show many contributions and help you pick an easy organisation, I’d encourage you to enjoy the process and get involved wholeheartedly in it. Contribute to the project and organisation you feel excited about. Become a part of the organisation, get to know folks, learn as much as you can, expand your pool of knowledge. If after all that, the worst happens and you are not selected for GSoC, you can still keep contributing to a major open source project which is awesome!

2. Do it for Open Source, Not for Money

I know money is a really important part of life (and I am not denying that) and GSoC money is definitely tempting. So I am not complaining about the money being an intrinsic motivation. What I am trying to say is if you do GSoC only for the money, and stop contributing to Open Source after these 3 months, then the purpose of GSoC is lost.

GSoC, I believe, is meant to be a platform that helps you get started in your Open Source journey. It is that small little push that you need to start contributing to open source projects. Finding and contributing to an organisation all on your own might be a little difficult, GSoC provides you with a platform that helps you find them more easily and have a higher chance of starting your open source contribution in major organisation than you would normally do.

So use GSoC as a vehicle to begin your journey in the open source world. Once you start seeing it from that perspective, you will, hopefully appreciate the principles of open source and keep contributing to the open source world.

Open source projects appreciate great developers like you, so come be a part of it.

3. Take PR Reviews Positively

Now, if you have already grasped the previous points, you know GSoC is only the beginning. Apart from making all projects by you open source, a really important part of the journey is contributing to various wonderful open source projects, which is actually going to be most of your GSoC. And with contribution, comes pull requests (or patches in some cases).

Most times, you will receive plenty of comments and reviews on your pull requests. I have seen many folks get irritated. Many in face, feel that if you can't get a pull request merged without too many reviews, then that organisation is hard to contribute to, in GSoC. This causes many to try for organisations where pull requests get easily merged.

Don't be discouraged by the reviews.

Instead, use them as a learning opportunity. Most reviews are very constructive criticism that are will serve you well throughout your life. It will help you write code that is more readable, more efficient and code that works best in production both in terms of performance and maintainability. In GSoC you get to learn all this directly from upstream projects with super awesome developers and coders … and PR review is where you learn the most.

4. Collaborate, Don't Compete

Over the years, as both student and mentor in GSoC, I’ve seen participants duking it out for issues or work in organisations and projects. This is mainly because everyone has this feeling that if they solve more issues and bugs, they have a higher chance of getting selected or passing the evaluation. But at the same time, this often causes frustration if a PR is getting too many comments. Also, participants tend to start working on something different, leaving their previous work incomplete.

All these will actually just create a bad impression to the mentors and others in the organisation. It will deprive you of lots of peer learning opportunities because you will always be competing with everyone. So, try your best to collaborate with other participants and even the mentors and other contributors.

Collaboration is a central principle in the Open Source community at large. Collaborating with each other not only helps you learn a lot from your peers but also leads to a better, much cleaner project. Collaborate not only on code, but on shaping the best practices of a project, on blogs, on writing documentation and setting guidelines. You will also have a better overview of the entire project rather than just the small piece you work on.

Remember, GSoC is not a competition where you need to be the top scorer to win. Everyone is a winner if they contribute to the projects and help in growing the project. Believe me, most organisations will pass you even when you don't complete the entire proposal you made, if you made other quality contributions to the project and they feel that your work has helped in furthering development of the overall project.

5. Be Part of the Community

While being a part of GSoC, don't just code. Go, be a part of various open source communities. When you are selected for a particular organisation, be always active in their communication channels, be it IRC or slack or gitter or what have you. Help newcomers get started with the project, attend team meetings, make friends, and communicate with everyone. If possible, try to attend different meetups and conferences near your area.

These will help you network and make friends with a lot of people from different parts of the open source communities and you will get to learn even more. The best part about open source is that it allows you to grow beyond any boundaries and being part of different communities is one of the best ways to do this. Not only will you get to learn a lot code wise, but also about different aspects of life and technology and incidents that might help you shape your future.

And, most importantly, continue being involved in these communities even after GSoC ends. I have told plenty of folks, plenty of times before and I can't emphasise enough that GSoC is just the beginning of your journey. Your journey with open source coding and the communities, doesn't end after the 3 months of GSoC. It starts expanding. Yes, due to various circumstances, you might not always be able to actively contribute code to an open source project, but try to carve out time to help others in communities get started. Try to apply the lessons you learn in the communities, in your office work or university projects and when you do get time, contribute to the open source!

iTerm + zsh + tmux for your MacOS

Posted: 2018-10-11T20:34:00+05:30
Being a linux user for most part of my developer life, the most important thing of my life is the terminal (or shell or command line or console or whatever you want to call it) followed by text editor. Here I am going to talk mainly about the terminal (yes, I am just gonna call that throughout). So, when I started using a Macbook, the first question (apart from when to use CMD and CTRL) was which terminal and how to set it up with zsh and tmux. So I am gonna talk a little about that.

iTerm 2 for the win

The very first question was "Is the default terminal of Mac good?" Well, it's good enough, but most of the digging into led me to the same answer - Use iTerm2. So the very first step is to forget using the default terminal, and use iTerm2. The main reason is it comes with more customising options and better color support more than anything else.

You can download and unzip iTerm2 from here. All you need to do is unzip and put it into Applications folder and you are ready to use it.


I have been using Z Shell for quite some time and have been used to the commands and ways so much, it is a little difficult to use bash. I feel Z shell increases productivity a lot, but personal opinion. So, to install ZSH in your Mac, all you do is:

brew install zsh

Once you have it installed, you can run the command `zsh` to start the Z Shell.

Oh My Zsh

A big shoutout to Robby Russell for creating this gem. If you felt Zsh increases your productivity and takes your command line experience to  a whole new level, wait till you have used Oh My Zsh. It makes your life much more easier by showing a lot more information in your shell especially when you are using git. It can be used to customize and show a lot of information according to your will. The amount of already present templates is just huge.

Execute this in your terminal to get Oh-my-zsh:

sh -c "$(curl -fsSL"

Once you have installed, you can open the `~/.zshrc` file to add plugins like git, change themes and modify stuff. Here is a complete documentation and available template links.


Tmux is basically a terminal multiplexer allowing you to create and access different terminal sessions within a single window. It also allows you to split your window into different panes, hence allowing you to do a lot more in a single window. Just like zsh, tmux is also something I have been using for some time and helps me maximise my productivity. 

You can install tmux, just by doing:

brew install tmux

After that, you need to configure your tmux to make it comfortable for your key bindings. One good link to get started I guess is this.

Integrate ZSH and Tmux

Now, after all configuration, the biggest pain is you have to open the terminal, change to tmux and then start zsh and then do work. But what we want is iTerm default opens in Zsh. Now, I prefer that it doesn't open with tmux by default, because there are scenarios where I want to work outside of tmux sessions, so I leave tmux to be started by me. But then make tmux start with Zsh instead of bash.

So here is what you do.

1. Make zsh your default shell.
chsh -s `which zsh`

2. Make tmux open with zsh. To do this, put the below line in ~/.tmux.conf:
# set shell
set -g default-shell /bin/zsh

3. Kill all previously running tmux instances. 
killall tmux

4. Close iTerm2 and restart it.

Now, your terminal should start with zsh. When you enter into tmux, it should go into tmux session along with zsh instead of bash.

And Voila! There you have a nice productive terminal. Or so I feel.