<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rohan Verma</title><link>https://rohanverma.net/</link><description>Recent content on Rohan Verma</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://rohanverma.net/index.xml" rel="self" type="application/rss+xml"/><item><title>Using Minio in Github actions to mock s3</title><link>https://rohanverma.net/blog/2021/02/09/minio-github-actions/</link><pubDate>Tue, 09 Feb 2021 19:48:00 +0530</pubDate><guid>https://rohanverma.net/blog/2021/02/09/minio-github-actions/</guid><description>&lt;p>I recently added Github Actions for &lt;a href="https://github.com/rhnvrm/simples3">simples3.&lt;/a> In a recent PR, a &lt;a href="https://github.com/jacksgt">contributor&lt;/a>
added support for custom endpoints. I was currently running &lt;code>go test&lt;/code> on the
library using a local &lt;code>.env&lt;/code> files which contains real AWS keys and an actual &lt;code>s3&lt;/code>
bucket. I realized that we can mock this using a &lt;a href="https://min.io/">minio&lt;/a> server as well.&lt;/p>
&lt;p>Coming from having used Gitlab pipelines, it seemed to be straightforward
to do. I would simply have to run a service and just set the appropriate
environment vars. Unfortunately, Github pipelines does not seem to support
container args for services. The conventional hack on most repos, is to build a
custom Dockerfile with the custom command, but it seemed unnecessary for just mocking minio for tests.&lt;/p>
&lt;p>So after reading a bit, I found out that you can actually just run &lt;code>docker run&lt;/code>
inside the job command as a step. Essentially, it boils down to just adding it
to your pipeline and setting up a few env vars. Minio accepts &lt;code>MINIO_ACCESS_KEY&lt;/code>
and &lt;code>MINIO_SECRET_KEY&lt;/code> in the docker env, which it uses to bootstrap the admin
credentials for the first time. These can be further used to create a mock
bucket using &lt;code>aws s3 mb&lt;/code> and provide the custom endpoint as a flag. Finally,
&lt;code>simples3&lt;/code> will use its own environment vars to interact with the mock minio, and
run the tests on it.&lt;/p>
&lt;p>Here is the full workflow for reference:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="k">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>Go&lt;span class="w"> &lt;/span>Test&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>master&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">pull_request&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>master&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>ubuntu-latest&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>- &lt;span class="k">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>actions/checkout@v2&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>- &lt;span class="k">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>Set&lt;span class="w"> &lt;/span>up&lt;span class="w"> &lt;/span>Go&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>actions/setup-go@v2&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">go-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">1.15&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>- &lt;span class="k">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>Setup&lt;span class="w"> &lt;/span>minio&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="sd">|
&lt;/span>&lt;span class="sd"> docker run -d -p 9000:9000 --name minio \&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>-e&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;MINIO_ACCESS_KEY=minioadmin&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>\&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>-e&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;MINIO_SECRET_KEY=minioadmin&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>\&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>-v&lt;span class="w"> &lt;/span>/tmp/data&lt;span class="p">:&lt;/span>/data&lt;span class="w"> &lt;/span>\&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>-v&lt;span class="w"> &lt;/span>/tmp/config&lt;span class="p">:&lt;/span>/root/.minio&lt;span class="w"> &lt;/span>\&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>minio/minio&lt;span class="w"> &lt;/span>server&lt;span class="w"> &lt;/span>/data&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>export&lt;span class="w"> &lt;/span>AWS_ACCESS_KEY_ID=minioadmin&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>export&lt;span class="w"> &lt;/span>AWS_SECRET_ACCESS_KEY=minioadmin&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>export&lt;span class="w"> &lt;/span>AWS_EC2_METADATA_DISABLED=&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>aws&lt;span class="w"> &lt;/span>--endpoint-url&lt;span class="w"> &lt;/span>http&lt;span class="p">:&lt;/span>//&lt;span class="m">127.0.0.1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">9000&lt;/span>/&lt;span class="w"> &lt;/span>s3&lt;span class="w"> &lt;/span>mb&lt;span class="w"> &lt;/span>s3&lt;span class="p">:&lt;/span>//testbucket&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>- &lt;span class="k">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>Run&lt;span class="w"> &lt;/span>Go&lt;span class="w"> &lt;/span>Test&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>go&lt;span class="w"> &lt;/span>test&lt;span class="w"> &lt;/span>-v&lt;span class="w"> &lt;/span>./...&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AWS_S3_BUCKET&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>testbucket&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AWS_S3_ACCESS_KEY&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>minioadmin&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AWS_S3_SECRET_KEY&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>minioadmin&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AWS_S3_ENDPOINT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>http&lt;span class="p">:&lt;/span>//&lt;span class="m">127.0.0.1&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">9000&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>The Ethics of Invention</title><link>https://rohanverma.net/blog/2020/12/20/ethics-of-invention/</link><pubDate>Sun, 20 Dec 2020 03:23:00 +0530</pubDate><guid>https://rohanverma.net/blog/2020/12/20/ethics-of-invention/</guid><description>&lt;figure>&lt;a href="https://rohanverma.net/ox-hugo/ethics_of_invention.jpg">
&lt;img src="https://rohanverma.net/ox-hugo/ethics_of_invention.jpg"/> &lt;/a>
&lt;/figure>
&lt;p>The Ethics of Invention by Sheila Jasanoff is not a book on ethics, but rather a
book about the complicated relationship between Technology, Law, and Policy. By
the title, one might think that this is yet another &amp;ldquo;Techlash&amp;rdquo; book written to
be read by Tech Luddites. Rather, the book argues for a middle ground between
uncontrolled enthusiasm for technology and the timeless, often understandable,
hatred for technological progress. The main focus of the book is to highlight
the tendency of humans to give or delegate power to technological systems, which
end up governing human behavior without even them noticing it happening under
their noses.&lt;/p>
&lt;p>Examples and case studies are abundant in this book. To explain the point, she
takes us through how traffic signals came to dictate the laws of the road.
&amp;ldquo;Inanimate lights backed by invisible experts and unseen electrical circuits
have stepped in to discipline behavior that was once risky&amp;rdquo;. No one questions
it, and we often just go about our day being governed by them and reshaping our
world over time. Although, it is left to the reader to ponder if it is truly
possible to build democratic consensus around these issues. She compares the
power given to technological systems with legal constitutions. We can comprehend
our delegation to the lawmakers but often the delegation to technological
systems does not compute.&lt;/p>
&lt;p>She highlights three major fallacies that we fall prey to while designing
policies and how the over/under reliance on these impacts people.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Technological determinism&lt;/strong>: Once a new technology is invented, it &amp;ldquo;possesses
an unstoppable momentum, reshaping society to its insatiable demands&amp;rdquo;. It is
a belief that innovation is always beneficial or good for society and should
be pushed as far as possible. She takes the example of refrigerants being
beneficial in the short term but causing Ozone Depletion in the long term. Or
how automobiles led to enormous progress for humanity in the short term but
probably caused so many unaccounted externalities in the long term.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Myth of technocracy&lt;/strong>: That &amp;ldquo;only those with specialist knowledge and skills
can manage and control technology&amp;rdquo;. There are two points she makes here. The
first being a critique of the technical risk assessment. She argues that
technology is value-laden from start to finish and the inventors desired end
clouds our judgment and often forces externalities to be classified as
&amp;ldquo;unintended&amp;rdquo;. To expand on this, she says that &amp;ldquo;Experts’ imaginations are
often circumscribed by the nature of their expertise.&amp;quot;. It is often our first
instinct to rely on experts but they often &amp;ldquo;overestimate the degree of
certainty behind their positions&amp;rdquo; on a matter and &amp;ldquo;blind themselves to
knowledge coming from outside their closed ranks.&amp;rdquo; She goes through the
example of the Challenger shuttle and the 2008 financial crisis.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Unintended consequences&lt;/strong>: &amp;ldquo;If technological mishaps, accidents, and disasters
seem unintended, it is because the process of designing technologies is
rarely exposed to full public view&amp;rdquo;. She asks if it is fair to use such a
fuzzy word like &amp;ldquo;unintended&amp;rdquo;, can there ever be &lt;em>intended&lt;/em> consequences of a
technology, and if so shouldn&amp;rsquo;t policy be designed to tackle these in the
first place? Another problem raised is that intention is fixed at a specific
moment in time, morally, which would not be static in the long term and is
bound to change and who is responsible to track these changes.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>The rest of the book looks at problems of &amp;ldquo;risks, inequality, and human dignity&amp;rdquo;
that need to be addressed if our society is to progress and responsibly grow
alongside our technological innovations. Ending with a bunch of questions,
do we exist to further technology or technology exists to further our goals and
ambitions. Do we rely on technology to solve climate change or do we accept and
deal with the fact that we have mismanaged it in the first place. We need to
dispel our thought process from the three fallacies and start agreeing that
&amp;ldquo;ethical analysis, political supervision and long impeded systematic thinking&amp;rdquo;
can be applied to technological innovations.&lt;/p>
&lt;p>She argues that we need to rethink tech-policy, where we first look at risk
assessment to anticipate and rather bring in values into our decision making at
an earlier stage. At the same time, she asks us to not rely on &amp;ldquo;Trickle down
innovation&amp;rdquo; and look at collective ownership and collective good rather than
treating all tech as extractive and solely for profiteering. Another caution is
to keep ethical discourse public and draw a balance between private discourse so
that it does not spawn further public alienation between views of experts hired
by private bodies and democratic institutions.&lt;/p></description></item><item><title>Does NPD Governance Framework give Government a backdoor into private companies</title><link>https://rohanverma.net/blog/2020/12/03/npd-backdoor-govt/</link><pubDate>Thu, 03 Dec 2020 04:34:00 +0530</pubDate><guid>https://rohanverma.net/blog/2020/12/03/npd-backdoor-govt/</guid><description>&lt;p>Ministry of Electronics and Information Technology (MEITY) had released a report
&lt;a href="https://static.mygov.in/rest/s3fs-public/mygov%5F159453381955063671.pdf">1&lt;/a> on the “Non-Personal Data Governance Framework” in July 2020 &lt;a href="https://www.mygov.in/task/share-your-inputs-draft-non-personal-data-governance-framework/">2&lt;/a>. The report
recommends the establishment of a Non-Personal Data Regulatory Authority with an
enabling role as well as enforcing role. &lt;a href="https://www.mygov.in/task/share-your-inputs-draft-non-personal-data-governance-framework/">2&lt;/a> Along with this, the report defines
Non Personal Data as: &lt;a href="https://www.medianama.com/2020/07/223-summary-non-personal-data-report-meity/">3&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>Non-personal data: When the data is not ‘personal data’ (as defined under the
PDP Bill), or the data is without any personally identifiable information (PII).
It could be:&lt;/p>
&lt;ul>
&lt;li>Data that never related to an identified or identifiable natural person, such
as data on weather conditions, data from sensors installed on industrial
machines, data from public infrastructures, etc., or&lt;/li>
&lt;li>Data that was initially personal data but was later made anonymous. “Data
which are aggregated and to which certain data transformation techniques are
applied, to the extent that individual specific events are no longer
identifiable, can be qualified as anonymous data”.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>Every tech company, which collects non-personal data would be obliged to
register itself as a &lt;strong>Data Business&lt;/strong>.&lt;/p>
&lt;p>Access to non-personal data may be requested for three purposes, one of those
being: &lt;a href="https://www.medianama.com/2020/07/223-summary-non-personal-data-report-meity/">3&lt;/a>&lt;/p>
&lt;blockquote>
&lt;ol>
&lt;li>&lt;strong>Sovereign purposes&lt;/strong> such as national security, law enforcement, legal or
regulatory purposes. These purposes could include mapping physical and cyber
security vulnerabilities, mapping crime and taking preventive measures, a
regulator wanting to stay abreast of developments in the sector, or national
security (via telecommunications metadata, geospatial or financial data,
etc.)&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;p>Putting two plus two together, it is easy to understand that this could mean
that the government could get private tech companies to anonymize and give data
collected of its users, that could be private in nature, and hand it to them.
Another failure here is that it is clearly understood by the tech community that
using metadata, it is often trivial to de-anonymize data. Now the scope and
urgency of the matter is not specified. Neither is any recommendation given by
the report to limit the usage of this under exceptional circumstances, or maybe
atleast only when warranted by say the judiciary. Even though the report has
mentioned the need of &amp;ldquo;adequate checks against abuse of power by government or
other representative agencies&amp;rdquo; there is little emphasis on what these will be.
This was even mentioned in the Medianama article on the concerns &lt;a href="https://www.medianama.com/2020/07/223-five-key-concerns-with-indias-non-personal-data-report/">6&lt;/a>.&lt;/p>
&lt;p>Here I would like to invoke the &lt;strong>Iron Law of Data Collection&lt;/strong>, by Jon Snader &lt;a href="https://irreal.org/blog/?p=9319">4&lt;/a>,
which states that no matter what the original rationale given for its collection
new uses will be found for it and will eventually be abused.&lt;/p>
&lt;blockquote>
&lt;p>DEA expresses an interest in providing its agents “ ‘unlimited access to patient
de-identified data’ on re/filled prescriptions, daily supply, payment type,
dosing information and gender, among other characteristics, until at least
2025.” &lt;a href="https://irreal.org/blog/?p=9321">5&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>He gives a concrete example of how even anonymized data collected for the
medical use is being appropriated by law enforcement and is going to be
essentially abused.&lt;/p>
&lt;p>Now the issue here is that, when unlimited and multi-genre data collected by
private entities, lands into the laps of the government; how creative will our
government be?&lt;/p></description></item><item><title>The x4ivygA51F Rabbit Hole</title><link>https://rohanverma.net/blog/2020/11/25/x4ivyga51f-rabbit-hole/</link><pubDate>Wed, 25 Nov 2020 23:25:00 +0530</pubDate><guid>https://rohanverma.net/blog/2020/11/25/x4ivyga51f-rabbit-hole/</guid><description>&lt;p>I recently read the blog about the &lt;a href="https://nordpass.com/most-common-passwords-list/">200 most common passwords of 2020&lt;/a>. It was
also featured in Jon Snader&amp;rsquo;s &lt;a href="https://irreal.org/blog/?p=9301">recent blog post&lt;/a>. When the original post was made,
I had glanced at the mysterious &lt;code>x4ivygA51F&lt;/code>, which appeared at the 148th spot.
But when I read the blog post again and searched for an update on it, no one had
been able to figure out what that meant.&lt;/p>
&lt;p>There were some guesses. Few folks on &lt;a href="https://www.reddit.com/r/sysadmin/comments/jxtnet/200%5Fworst%5Fpasswords%5Ffor%5F2020/">reddit&lt;/a> and &lt;a href="https://boards.4channel.org/g/thread/78809839/why-is-x4ivyga51f-the-148th-most-common-password">4chan&lt;/a>, guessed that it might be
password written in another language. This guess was based on a &lt;a href="https://gizmodo.com/why-ji32k7au4a83-is-a-remarkably-common-password-1833045282">gizmodo article&lt;/a>,
which mentions something similar. To confirm this hypothesis, I tried searching
for Chinese websites if I could find some mention of this. There was little to
no mention of this to be able to confirm this, at least on what is to be found
through the indexed web on Google or Bing.&lt;/p>
&lt;p>Although, one interesting finding here that I saw no one talk about was a
post on the forum of a website called 51haoyou. The post on this forum, which
talks about a breach by hackers on January 10, 2019 talks about this specific
password. In a footnote on &lt;a href="http://www.51haoyou.com/discuzx3.2/thread-4903.html">this post&lt;/a>, it mentions the following:&lt;/p>
&lt;blockquote>
&lt;p>Note: x4i***1F is the trial password originally displayed by discuz, and
x4ivygA51F is the result of my modification of the display program so that the
password is displayed completely. On 2019-10-28, I completed &amp;ldquo;The server-side
program is added to determine whether the user login password is encrypted on
the client side, otherwise the php program is interrupted&amp;rdquo;, &amp;ldquo;Improved the Discuz
mobile version, and also realized that the user password is encrypted on the
client side. &amp;ldquo;Retransmit&amp;rdquo; time.&lt;/p>
&lt;/blockquote>
&lt;p>I guess that it could mean that the Discuz forum software by default suggests
this password somewhere on the registration page. Or administratively this is
set to be the default password somewhere by this BB software. Using the Cangjie
input method, I found those keystrokes build and translate to &lt;a href="https://translate.google.com/?hl=en&amp;amp;tab=TT&amp;amp;authuser=0#view=home&amp;amp;op=translate&amp;amp;sl=zh-CN&amp;amp;tl=en&amp;amp;text=%E7%94%A0%E9%A1%8C1%E7%81%AB">&lt;code>Question 1&lt;/code>&lt;/a>.
Something similar does come up on the &lt;a href="http://www.51haoyou.com/discuzx3.2/member.php?mod=register">login page&lt;/a> this does come up in terms of
security question, although this could be entirely incorrect as I don&amp;rsquo;t know
Chinese or Cangjie. Another guess here is that this is the breach that led to
the inclusion of this password in the list itself.&lt;/p></description></item><item><title>Generating Go docs using gomarkdoc and pandoc</title><link>https://rohanverma.net/blog/2020/11/24/generating-go-documentation/</link><pubDate>Tue, 24 Nov 2020 02:19:00 +0530</pubDate><guid>https://rohanverma.net/blog/2020/11/24/generating-go-documentation/</guid><description>&lt;p>I recently had to distribute the documentation of an internal library to users.
Go developers are used to their editor to providing them with auto completion,
usually with the help of gopls. A lot of times it is necessary for users to be
able to browse a comprehensive list of methods and data types available for
reference. For open source projects, we rely on &lt;a href="https://godoc.org/">godoc.org&lt;/a> and &lt;a href="https://pkg.go.dev">pkg.go.dev&lt;/a>. But
unfortunately, it is not easy to host one for internal projects.&lt;/p>
&lt;p>After a bit of searching, I found &lt;a href="https://github.com/princjef/gomarkdoc">princejef/gomarkdoc&lt;/a>. This project is able to process packages and use the docs, to generate a markdown file. Using this I was able to produce a markdown of the packages I wished to share with the users.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="c1"># install as a cli-tool&lt;/span>
go get -u github.com/princjef/gomarkdoc/cmd/gomarkdoc
&lt;span class="c1"># generate the docs using specific packages&lt;/span>
gomarkdoc ./pkg/foo &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> ./pkg/bar &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> ./pkg/lorem/ipsum &amp;gt; docs/user-docs.md
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This was good enough, but a better way would be to distribute it as a single
&lt;code>html&lt;/code> file. &lt;a href="https://pandoc.org/">Pandoc&lt;/a> is a swiss army knife, which can convert one markup format to
another. Combining this into the pipeline, I was able to generate an &lt;code>html&lt;/code> file
which has the documentation, which is viewable in the browser and contains links
to the git repository as well.&lt;/p>
&lt;p>To make the web document look good, I found two interesting things.&lt;/p>
&lt;p>The first being, &lt;a href="https://github.com/oxalorg/sakura">sakura.css&lt;/a>, which is a &lt;code>classless&lt;/code> css stylesheet, which can be
applied to any &lt;code>html&lt;/code> file and make it look like a modern website.&lt;/p>
&lt;p>The second useful thing, are a few flags available in &lt;code>pandoc&lt;/code> which help with making the documentation &lt;code>html&lt;/code> file truly standalone.&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://pandoc.org/MANUAL.html#option--css">&amp;ndash;css&lt;/a>: This flag accepts a link to a &lt;code>CSS&lt;/code> stylesheet&lt;/li>
&lt;li>&lt;a href="https://pandoc.org/MANUAL.html#option--self-contained">&amp;ndash;self-contained&lt;/a>: This flag produces the &lt;code>html&lt;/code> file in a way, such that no external dependency is needed.&lt;/li>
&lt;/ol>
&lt;p>Using these flags, we can come up with the following script to generate the &lt;code>html&lt;/code> file from the markdown file.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-sh" data-lang="sh">pandoc docs/user-docs.md &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> --toc &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> --metadata &lt;span class="nv">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;My Package - User Docs&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> -c https://unpkg.com/sakura.css/css/sakura.css &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> --self-contained &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> -o docs/user-docs.html
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This results in well rendered documentation which is standalone and looks modern as well.&lt;/p>
&lt;figure>
&lt;img src="https://rohanverma.net/ox-hugo/docs-sample.png"/>
&lt;/figure></description></item><item><title>Use of Facial Recognition Tech by Law Enforcement</title><link>https://rohanverma.net/blog/2020/11/16/use-of-facial-recognition-police/</link><pubDate>Mon, 16 Nov 2020 03:59:00 +0530</pubDate><guid>https://rohanverma.net/blog/2020/11/16/use-of-facial-recognition-police/</guid><description>&lt;p>The use of Facial Recognition by law enforcement to identify offenders is one of
the cases where the regulation of the technology lags way behind, and there is
an urgent need to bring about policy to reduce potential pain in our society.
Apart from the serious problems that are inherent with the technology like
mis-identification and minimal mapping between the training sets and the actual
photos. According to a recent report in TechCrunch &lt;a href="https://techcrunch.com/2020/03/11/india-used-facial-recognition-tech-to-identify-1100-individuals-at-a-recent-riot/">1&lt;/a>, the Home Minister of
India admitted to using a data set trained on the Aadhaar Database to identify
individuals involved in a recent riot. This software was originally obtained in
the name of a good cause, to identify missing children, although according to
the report &lt;a href="https://techcrunch.com/2020/03/11/india-used-facial-recognition-tech-to-identify-1100-individuals-at-a-recent-riot/">1&lt;/a> the software failed to even distinguish between boys and girls.
Recent advances in AI have made certain model that perform recognition by
training on huge datasets, unfortunately they are considered to be black boxes.
Along with this, there is a question of how in line it is with the Privacy Laws
and the inherent questions of cyber-security. To address these issues, we should
open discourse on these topics with experts. Unlike the approach in the west &lt;a href="https://securitytoday.com/articles/2019/10/10/california-to-become-third-state-to-ban-facial-recognition-software-in-police-body-cameras.aspx">2&lt;/a>
to ban the tech, we should try to come up with a middle ground. Ideally, a
policy should be discussed to thoroughly document the process of how the model
was trained and the dataset that was used in the process. After the model is
trained and put into production, there should a mandatory requirement to
disclose the uses of the model along with the false-negatives in a proper
manner. Using this information, there should be regular reviews to revisit the
efficacy of the model. A mechanism should be devised to be able to appeal for
manual review by an expert in case a person who is identified by this system. A
person should also be able to opting out of being incriminated by the use of
this technology. Such mechanisms, in such a policy, would address the privacy
concerns as well.&lt;/p></description></item><item><title>Goodbye upi.link</title><link>https://rohanverma.net/blog/2020/02/24/goodbye-upi-link/</link><pubDate>Mon, 24 Feb 2020 12:38:24 +0000</pubDate><guid>https://rohanverma.net/blog/2020/02/24/goodbye-upi-link/</guid><description>&lt;p>Dear upi.link users&lt;/p>
&lt;p>Thanks for using upi.link and making it grow organically to over ~2k daily hits.&lt;/p>
&lt;p>Due to a takedown request for the domain upi.link from the NPCI I have had to
shutdown upi.link for now.&lt;/p>
&lt;p>I had created upi.link to solve my own problem. Till date, sharing a upi request
outside an App is not possible without an intermediary for free. After sharing a
prototype with people in my workplace, I found out that it was useful beyond
what I had imagined. I shared it with more
people and finally put it up online. It has not been my best work
but more or less was an experiment for me. Running upi.link with the least
amount of cost and infra setup was personal challenge for me. For over a year I
had run this without any extra cost to me except the domain name.&lt;/p>
&lt;p>Unfortunately, I was naive when I started building this to assume that the
NPCI is a government body and UPI is a platform. In reality, the NPCI is a
non-profit (not non-commercial) body and UPI is its product. Even though some&lt;br>
of the UPI spec is open, it can hardly be called an open product. NPCI owns
a trademark for UPI and also has a guideline for domains using the trademark.&lt;/p>
&lt;p>At the moment, I am working on other things and I don&amp;rsquo;t have the resources to
keep the upi.link domain online by either asking/paying NPCI for allowing use of
this domain nor do I have any bandwidth in further developing anything on top of
upi.link as of now.&lt;/p>
&lt;p>I would also like to add that being involved in the fintech space in India, I
have come to appreciate what UPI has provided all of us in the short term. I am
also aware of the rising amounts of scams happening with UPI which the
NPCI tries to prevent through partner apps like BHIM, GooglePay, PhonePe etc.
Also, the fact that it has successfully reached out and onboarded
so many people and organizations is a monumental feat in itself.&lt;/p>
&lt;p>Some people, had suggested open sourcing this project but unfortunately; some
decisions, I had taken to reduce the cost of hosting the website, hinder the scope
of opensourcing the project.&lt;/p>
&lt;p>Regards&lt;/p></description></item><item><title>Utopia for Realists</title><link>https://rohanverma.net/blog/2019/11/26/utopia-for-realists/</link><pubDate>Tue, 26 Nov 2019 15:30:03 +0530</pubDate><guid>https://rohanverma.net/blog/2019/11/26/utopia-for-realists/</guid><description>&lt;p>&lt;img src="https://rohanverma.net/blog/2019/utopia.png" alt="book">&lt;/p>
&lt;p>This book was a recent recommendation from &lt;a href="https://nadh.in">Dr Kailash Nadh&lt;/a>. A
small but heavy book, that is engaging and well researched. Rutger Bregman has
done a thorough job of making arguments for three major policy ideas whose time
has come in the land of plenty that we live in (as compared to a human living
a century ago).&lt;/p>
&lt;ul>
&lt;li>Universal Basic Income&lt;/li>
&lt;li>Reducing Working Hours&lt;/li>
&lt;li>Open Borders&lt;/li>
&lt;/ul>
&lt;p>Universal Basic Income or UBI, might sound ludicrous at the first glance, but
numerous studies and on-field experiments have time and again shown that
the way ahead for the welfare-state might be direct money transfer (not just
benefits). The basis of the argument for UBI stems from the dignity given
to recipients of the no-strings attached benefits leading to a more efficient
and effective utilization of the safety net.&lt;/p>
&lt;p>The second policy of reducing working hours eventually, stems from the current
state of &amp;ldquo;Bullshit Jobs&amp;rdquo; that produce nothing of value but just move value
around. He mentions Keynes prediction that soon we will have 15 hour work weeks
based on an interpolation of the work hours at the time of writing his study. He
critiques the measure of economic progress used in this era, the Gross Domestic
Product (GDP), a perfectly reasonable measure nonetheless created during wartime
when it was not unreasonable to borrow from the future. A quote from the book
on where all the time has gone when productivity has skyrocketed.&lt;/p>
&lt;blockquote>
&lt;p>It&amp;rsquo;s quite simple really. Time is money. Economic growth can yield either more
leisure or more consumption. From 1850 until 1980, we got both, but since then
it is mostly consumption that has increased. Even where real incomes have
stayed the same and inequality has exploded, the consumption craze has
continued, but on credit.&lt;/p>
&lt;/blockquote>
&lt;p>The only way to solve this would be to legally reduce the working hours and
make multiple people share the work and giving everyone more time for leisure.
He even goes on to mention how this could end up solving a lot issues plaguing
our society such as inequality between the sexes.&lt;/p>
&lt;p>Also, he suggests taxing ventures such as banking and hedge funds that move
value around and to rather give incentives such as salary hikes or grants to
people to take up careers that are useful for the society like research,
teaching, maintaining cities etc.&lt;/p>
&lt;p>His third idea, of open borders, is a critique of what he calls &amp;ldquo;apartheid on
a global scale&amp;rdquo; that has been committed by closing down borders. It is a fact
that there is a huge disparity in pay and lifestyle around the world. He
mentions that economists predict an approximate &amp;gt;2 fold increase in global
prosperity if the world were to open their borders today and allow free
movement of human capital.&lt;/p>
&lt;p>He ends it with a final note on ideas. How ideas like Neo-Liberalism changed and
mobilized the world at a time of crisis. It made me realize that the only way for realists to
achieve the utopia we crave for, we must discuss these ideas in the open. It
may be through talking
about these in your workplace, discussing with friends and family, or publicly
writing on them. Makes you wonder, maybe we are living in a revolutionary time.&lt;/p></description></item><item><title>Building Go Plugins inside Docker</title><link>https://rohanverma.net/blog/2019/11/15/building-go-plugins-using-docker/</link><pubDate>Sun, 24 Nov 2019 15:30:03 +0530</pubDate><guid>https://rohanverma.net/blog/2019/11/15/building-go-plugins-using-docker/</guid><description>&lt;p>Using Go plugins in your projects comes with a lot of caveats. As of writing,
there hasn&amp;rsquo;t been much development on the feature recently. The
&lt;a href="https://github.com/golang/go/commits/master/src/plugin/plugin.go">commit history&lt;/a>
shows us that the last commit happened nearly 2 years ago. On the gopher slack,
the sentiment, more or less, is that this is not a priority anymore. Along with
this, there are multiple issues that come up with maintaining projects that use
it:&lt;/p>
&lt;ul>
&lt;li>The go version for both host and plugin should match exactly&lt;/li>
&lt;li>External dependencies should match&lt;/li>
&lt;li>Host and plugin &lt;code>GOPATH&lt;/code> needs to exactly match while building&lt;/li>
&lt;li>Plugins cannot depend on interfaces or structs of the host&lt;/li>
&lt;/ul>
&lt;p>To learn more you can refer &lt;a href="https://github.com/golang/go/issues/20481#issuecomment-326832200">this issue&lt;/a> detailing some of the problems with
go plugins&lt;/p>
&lt;p>It works well if the projects bundles all the plugins in its own source tree and
both the host and plugins are built together at the same time. But, that limits
the scope of the project. Externally maintained plugins are impossible to
build independent of the host program.&lt;/p>
&lt;p>To solve this, we can use Docker to build both the host program, the bundled plugins,
and the custom plugin together. Then this image can be distributed instead
of distributing the host and loading the plugins separately in production.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-Dockerfile" data-lang="Dockerfile">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> golang:1.12-alpine AS builder&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apk update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apk add gcc libc-dev make git&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /myproject-plugin/&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># Clone and build myproject and myproject-plugin together&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># prevent version conflict for go plugins&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> git clone https://github.com/rhnvrm/myproject.git &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> mkdir -p myproject/bundled_plugins/myplugin&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># Load our custom plugin from disk&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> ./myplugin.go ./myproject/bundled_plugins/myplugin/myplugin.go&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># CGO_ENABLED=1 is required&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">CGO_ENABLED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="nv">GOOS&lt;/span>&lt;span class="o">=&lt;/span>linux
&lt;span class="c"># `make build` builds the host program and bundled plugins&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># `go build` our custom plugin &lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nb">cd&lt;/span> myproject &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> make deps &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> make build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> go build -ldflags&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;-s -w&amp;#34;&lt;/span> -buildmode&lt;span class="o">=&lt;/span>plugin -o myplugin.prov &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> bundled_plugins/myplugin/myplugin.go&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> alpine:latest AS deploy&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apk --no-cache add ca-certificates&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /myproject/&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># Copy the assets from the builder image&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --from&lt;span class="o">=&lt;/span>builder /myproject-plugin/myproject/myproject /myproject-plugin/myproject-plugin/myproject/bundled0.prov /myproject-plugin/myproject/bundled1.prov /myproject-plugin/myproject/bundled2.prov /myproject-plugin/myproject/myplugin.prov ./&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;./myproject&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--config&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;/etc/myproject/config.toml&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--prov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;bundled0.prov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--prov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;bundled1.prov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--prov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;bundled2.prov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--prov&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;myplugin.prov&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This image can then be pushed and used where it needs to be deployed.&lt;/p></description></item><item><title>Creating archive pages grouped by year in Hugo</title><link>https://rohanverma.net/blog/2019/11/15/archive-pages-group-by-year-hugo/</link><pubDate>Fri, 15 Nov 2019 15:30:03 +0530</pubDate><guid>https://rohanverma.net/blog/2019/11/15/archive-pages-group-by-year-hugo/</guid><description>&lt;p>I was on the hunt to find out how to organize my &lt;a href="https://rohanverma.net/archive">blog&lt;/a> and
&lt;a href="https://rohanverma.net/project">project&lt;/a> archive pages by year in Hugo. After being unable to find
any easy solutions I decided I would sit down and write the go template to
render these pages myself. The idea was simple, iterate over the list divided
by year into sub lists and render tables, but it turned out to be a bit tricky.&lt;/p>
&lt;p>&lt;img src="https://rohanverma.net/blog/2019/images/blog_archive.png" alt="image_blog_archive">&lt;/p>
&lt;p>Here is the template, hope it is useful for someone who wants to have a similar
page.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-html" data-lang="html">{{ define &amp;#34;title&amp;#34; -}}
Blog List | {{ .Site.Title }}
{{- end }}
{{ define &amp;#34;header&amp;#34; }}
{{ partial &amp;#34;masthead.html&amp;#34; . }}
{{ end }}
{{ define &amp;#34;main&amp;#34; }}
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ .Title }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
{{ $prev := 3000}}
{{range .Site.RegularPages}}
{{if .Date}}
{{if gt $prev (.Date.Format &amp;#34;2006&amp;#34;)}}
{{ if ne $prev 3000}}
&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">table&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
{{ end }}
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{ .Date.Format &amp;#34;2006&amp;#34; }}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h2&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">table&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;all-posts&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
{{end}}
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">tr&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">td&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{.Date.Format &amp;#34;02 Jan&amp;#34;}}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">td&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">td&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;no-underline&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{.Permalink}}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>{{.Title}}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">td&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">tr&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
{{ $prev = .Date.Format &amp;#34;2006&amp;#34;}}
{{end}}
{{end}}
&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">table&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">section&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
{{ end }}
{{ define &amp;#34;footer&amp;#34; }}
{{ partial &amp;#34;powered-by.html&amp;#34; . }}
{{ end }}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above template loops through all the blogs (having a date) and renders
multiple tables
which are grouped by the year. We render &lt;code>h2&lt;/code> tags with the year if the previous
year is greater than the current year. Followed
by a &lt;code>&amp;lt;table&amp;gt;&lt;/code> tag having each next blog rendered as a row in the table. The
previous seen date is updated for the next iteration. In each iteration, before
rendering the table tag we check if the previous date was greater than the date
or&lt;code>3000&lt;/code> (an arbitrarily high year) which we set as the previous seen year
before starting the loop, to decide if we want to close the table tag. In the
next iteration, the previous year will be the same as the current year, and so
we can continue to render rows.&lt;/p></description></item><item><title>Containerized development workflow using remote gopls server</title><link>https://rohanverma.net/blog/2019/11/05/containerized-dev-using-remote-gopls/</link><pubDate>Tue, 05 Nov 2019 15:30:03 +0530</pubDate><guid>https://rohanverma.net/blog/2019/11/05/containerized-dev-using-remote-gopls/</guid><description>&lt;p>If your CGO development toolchain depends on external dependencies such
as system libraries, or you want to develop on an older version of
go while having a different version on your host, you can use a docker container and mount the source from your host machine and build the project inside the
container. This can enable us to have a consistent development environment across
various developers and their host systems without having to modify system
libraries.&lt;/p>
&lt;p>This article is a follow up to my previous article on &lt;a href="https://rohanverma.net/blog/2019/08/04/docker-containers-to-build-archaic-projects/">using containers
to develop on archaic projects&lt;/a>
that require specific system libraries. Last time I discussed how to create a
container having all the dependencies and dev tools installed and use them to
operate on the host file system. Taking this a step forward,
the biggest roadblock that arises with this approach is that our host machine&lt;br>
now loses the ability to run gotools on our source code. That means your editor
cannot run any of the tools you depend on to write, lint, or format your go
source code.&lt;/p>
&lt;p>To overcome this, we can use the experimental &lt;a href="https://godoc.org/golang.org/x/tools/internal/lsp/cmd#Application">remote lsp server&lt;/a>
feature available in gopls. &lt;a href="https://github.com/golang/tools/tree/master/gopls">gopls&lt;/a> (pronounced: &amp;ldquo;go please&amp;rdquo;) is the official language server for the Go language.
With this mode, the gopls running on the host
forwards all the commands to the gopls in our container. This way the host system
does not need to have any of the dependencies installed and can then use the
container gopls server to modify the source in the editor of the host.&lt;/p>
&lt;p>Let us look at the Dockerfile for this system.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-Dockerfile" data-lang="Dockerfile">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> ubuntu:xenial AS builder&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> apt-get install -y gcc g++ libssl-dev cmake wget libpcre3 libpcre3-dev git&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /tmp&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># Install Go.&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> wget https://dl.google.com/go/go1.12.9.linux-amd64.tar.gz &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> tar -C /usr/local -xzf go1.12.9.linux-amd64.tar.gz&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="c"># Install Swig.&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> wget http://prdownloads.sourceforge.net/swig/swig-4.0.1.tar.gz &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> tar -zxvf swig-4.0.1.tar.gz &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span> &lt;span class="nb">cd&lt;/span> /tmp/swig-4.0.1 &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> ./configure &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> make &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> make install &lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">ARG&lt;/span> MY_USER_ID&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> MY_USER_ID &lt;span class="si">${&lt;/span>&lt;span class="nv">MY_USER_ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">ARG&lt;/span> MY_PWD&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> MY_PWD &lt;span class="si">${&lt;/span>&lt;span class="nv">MY_PWD&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> mkdir -p &lt;span class="nv">$MY_PWD&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> useradd -r -m -u &lt;span class="nv">$MY_USER_ID&lt;/span> myuser&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> groupmod -g &lt;span class="nv">$MY_USER_ID&lt;/span> myuser&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">USER&lt;/span>&lt;span class="s"> myuser&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/usr/local/go/bin:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">MY_PWD&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/go/bin:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">PATH&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nv">GOPROXY&lt;/span>&lt;span class="o">=&lt;/span>https://proxy.golang.org &lt;span class="nv">GO111MODULE&lt;/span>&lt;span class="o">=&lt;/span>on go get golang.org/x/tools/gopls@v0.2.0&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> $MY_PWD&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> /home/myuser/go/bin/gopls -listen&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;:7050&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Firstly, we install all the dependencies and then create
a user which will have access to the source code. We pass
the same user ID as our host user by using the MY_USER_ID
as the build argument.&lt;/p>
&lt;p>Then we have to replicate the
host path in the container and set that as our work directory. This is a quirk
of gopls, if there is a mismatch in the path, the host will communicate the
wrong path to the server running in the container and will be unable to find it
inside the container.&lt;/p>
&lt;p>Finally, we install gopls in the container and then start it in the container
with the &lt;code>-listen=&amp;quot;:7050&amp;quot;&lt;/code> flag.&lt;/p>
&lt;p>We will now build the image.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">mkdir -p go
docker build &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span>--build-arg &lt;span class="nv">MY_PWD&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$PWD&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span>--build-arg&lt;span class="o">=&lt;/span>&lt;span class="nv">MY_USER_ID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sb">`&lt;/span>id -u &lt;span class="nv">$USER&lt;/span>&lt;span class="sb">`&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span>-t myapp:latest .
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note, we also create a directory called &lt;code>go&lt;/code> which will be the mock GOPATH
inside the container.&lt;/p>
&lt;p>Now we can expose this port to our host machine.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">docker run -d --name &lt;span class="s2">&amp;#34;myapp&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span>-u &lt;span class="sb">`&lt;/span>id -u &lt;span class="nv">$USER&lt;/span>&lt;span class="sb">`&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span>-e &lt;span class="s2">&amp;#34;GOPATH=&lt;/span>&lt;span class="nv">$PWD&lt;/span>&lt;span class="s2">/go&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span>-p 7050:7050 &lt;span class="se">\
&lt;/span>&lt;span class="se">&lt;/span>-v &lt;span class="nv">$PWD&lt;/span>:&lt;span class="nv">$PWD&lt;/span> myapp:latest
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notice, that we also mount the host source in the container with the same path,
the reason is explained above.&lt;/p>
&lt;p>We must now add the following flag to your editor config along with any other
flags we use for the language server to connect to the remote gopls server.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">gopls -remote &lt;span class="s2">&amp;#34;localhost:7050&amp;#34;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>For VSCode, you can add the following to your &lt;code>settings.json&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;go.languageServerFlags&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="s2">&amp;#34;-remote=localhost:7050&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;-v&amp;#34;&lt;/span>
&lt;span class="p">],&lt;/span>
&lt;span class="nt">&amp;#34;go.useLanguageServer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;go.gopath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;go&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>After adding this flag, you will now be able to edit your source with all the
added benefits that come with a language server like auto completion and
auto formatting.&lt;/p>
&lt;p>Since this is an experimental feature, it might break (a lot). Do let me know
in the comments if this was helpful for you.&lt;/p></description></item><item><title>PaisaVasool - HackInOut 2019</title><link>https://rohanverma.net/blog/2019/10/25/paisavasool-hackinout-2019/</link><pubDate>Fri, 25 Oct 2019 15:30:03 +0530</pubDate><guid>https://rohanverma.net/blog/2019/10/25/paisavasool-hackinout-2019/</guid><description>&lt;p>Last weekend I attended a hackathon after a long time. HackInOut, which is
India&amp;rsquo;s biggest community hackathon, was happening very close to where I live. I
went with my office colleagues &lt;a href="https://vivekr.dev">Vivek&lt;/a> and &lt;a href="https://mrkaran.dev">Karan&lt;/a> and we had discussed a lot of ideas before reaching the venue,
but we were not sure what we will finally end up building.&lt;/p>
&lt;p>&lt;img src="https://rohanverma.net/images/hackinout2019.jpg" alt="hackinout2019">&lt;/p>
&lt;p>After discussing our ideas with Siddharth Shetty, an iSpirit fellow
and one of the mentors at the hackathon, we finally decided to build
PaisaVasool.&lt;/p>
&lt;p>&lt;img src="https://raw.githubusercontent.com/CapsLockHacks/paisa-vasool/master/assets/logo.png" alt="logo">&lt;/p>
&lt;h2 id="what-is-paisavasool">What is PaisaVasool&lt;/h2>
&lt;p>Split subscriptions and collect money automatically and instantly over UPI seamlessly.&lt;/p>
&lt;iframe width="100%" height="480" src="https://www.youtube.com/embed/QyaZFz8StoQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>&lt;/iframe>
&lt;h2 id="the-problem-it-solves">The problem it solves&lt;/h2>
&lt;p>Sharing subscriptions like Netflix friends is not seamless. Collect payments
online and reconciling them manually using Apps still requires us
to manually confirm payments, and APIs/SDKs by PSPs are expensive.&lt;/p>
&lt;h2 id="the-hack">The hack&lt;/h2>
&lt;p>We piggyback on paisa values in the UPI request amount as the transaction ID,
and reconcile the payment seamlessly and automatically into the subscriptions.&lt;/p>
&lt;h2 id="the-app">The app&lt;/h2>
&lt;p>To demonstrate the hack for this hackathon, we decided to concentrate our efforts
to Splitting Recurring Payments such as those for
Netflix/Amazon Prime/Hotstar/Spotify etc. Although, our
hack can be extended to any kind of collect requests.&lt;/p>
&lt;p>A user can create groups on our app and enter his friends contact details
and instantly get reconciliations from our app when he receives payments.&lt;/p>
&lt;h3 id="our-hack-allows-you-to-consolidate-your-upi-requests-for-free">Our hack allows you to consolidate your UPI requests for free!&lt;/h3>
&lt;ul>
&lt;li>Daily UPI requests are limited by PSP apps like Google Pay.
&lt;ul>
&lt;li>You can request upto 5 times on Google Pay a day&lt;/li>
&lt;li>New users can&amp;rsquo;t be requested more than once a day&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Payment API&amp;rsquo;s are expensive
&lt;ul>
&lt;li>Costs (2% per transaction) are added onto the consumers&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>You need to check your Bank transaction details and reconcile manually without a PSP&lt;/li>
&lt;li>Apps like Paisa Vasool are impossible on top of UPI as of today&lt;/li>
&lt;/ul>
&lt;h3 id="our-hack-allows-payers-to-pay-with-any-vpa">Our hack allows payers to pay with any VPA.&lt;/h3>
&lt;ul>
&lt;li>No need to ask your user to be on a specific UPI app&lt;/li>
&lt;li>No need for payee to pay from his own account (parents/friends can pay on behalf)&lt;/li>
&lt;/ul>
&lt;h3 id="instant-reconcilation">Instant reconcilation&lt;/h3>
&lt;ul>
&lt;li>We process the transacation as soon as we get the bank SMS&lt;/li>
&lt;li>Users don&amp;rsquo;t need to check their bank details&lt;/li>
&lt;/ul>
&lt;h2 id="challenges-we-ran-into">Challenges we ran into&lt;/h2>
&lt;p>The biggest challenge was to figure out a way to solve these issues:&lt;/p>
&lt;ul>
&lt;li>How to identify a transaction&lt;/li>
&lt;li>How to not need the payers VPA&lt;/li>
&lt;/ul>
&lt;p>We figured out that:&lt;/p>
&lt;ul>
&lt;li>The identifier can be embedded in the paisa value&lt;/li>
&lt;li>We can read SMS to get the amount and parse the transaction ID.&lt;/li>
&lt;/ul>
&lt;h3 id="detailed-technical-flow">Detailed technical flow&lt;/h3>
&lt;ul>
&lt;li>User installs our app and gives read permission for SMS to our app.
&lt;ul>
&lt;li>Our app is open source and only sends SMS with UPI VPAs to our server&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>They create a collect request in the UI with the amount and users, and create a group for recurring weekly/monthly transactions.
&lt;ul>
&lt;li>User adds his clients/friends to the group using mobile number and/or email
&lt;ul>
&lt;li>These are saved for the future&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The collect request is then processed by the server
&lt;ul>
&lt;li>UPI link (which can be embedded in SMS/Whatsapp/Telegram) is created using &lt;a href="https://upi.link/">https://upi.link/&lt;/a> API - It is sent to all the clients/friends with appropriate amount that has been hacked to include the transaction identifier in the amount paisa value.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The app waits for SMS from the bank of the User, which includes the amount (plus paisa value identifier) inside it.
&lt;ul>
&lt;li>On receiving the SMS, we change the state of the pending transaction to complete.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The user can check the state and see all his transactions from our app.&lt;/li>
&lt;/ul>
&lt;h2 id="future">Future&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://sahamati.org.in/">Sahamati framework&lt;/a> in the future will allow users to share their bank transaction ledger details with apps directly in a standardized format via Aggregators.&lt;/li>
&lt;li>UPI roadmap has split requests planned for the future.&lt;/li>
&lt;/ul></description></item><item><title>Facebook Birthday List to ICS file</title><link>https://rohanverma.net/blog/2019/10/10/facebook-birthday/</link><pubDate>Fri, 11 Oct 2019 01:03:03 +0530</pubDate><guid>https://rohanverma.net/blog/2019/10/10/facebook-birthday/</guid><description>&lt;p>I have recently stopped using facebook, but I really miss the convenience of
the birthday notifications. I tried to find the &lt;code>.ics&lt;/code> which you could
export from facebook events page to your calendar program
but it was nowhere to be found and I thought I would have to
script scraping it myself.&lt;/p>
&lt;p>A quick search on github later, I found that someone had already beat me to it.&lt;/p>
&lt;p>You can find the repository on &lt;a href="https://github.com/mobeigi/fb2cal">github&lt;/a> and follow the documentation, but I
have documented the steps I followed below:&lt;/p>
&lt;p>The first step is to fetch the repository from github and editing the config.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">git clone git@github.com:mobeigi/fb2cal.git
&lt;span class="nb">cd&lt;/span> fb2cal
cp config/config-template.ini config/config.ini
vim config/config.ini
&lt;/code>&lt;/pre>&lt;/div>&lt;p>I did not want to upload to google drive which is default so I edited the default config
to save the file locally. In case you face errors, you might need to
set logging to &lt;code>DEBUG&lt;/code> to figure out what went wrong.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="k">[AUTH]&lt;/span>
&lt;span class="na">fb_email&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">myemail@gmail.com&lt;/span>
&lt;span class="na">fb_pass&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">xxxxxxxxxxx&lt;/span>
&lt;span class="k">[DRIVE]&lt;/span>
&lt;span class="na">upload_to_drive&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">False&lt;/span>
&lt;span class="na">drive_file_id&lt;/span> &lt;span class="o">=&lt;/span>
&lt;span class="na">ics_file_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">birthdays.ics&lt;/span>
&lt;span class="k">[FILESYSTEM]&lt;/span>
&lt;span class="na">save_to_file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">True&lt;/span>
&lt;span class="na">ics_file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">./birthdays.ics&lt;/span>
&lt;span class="k">[LOGGING]&lt;/span>
&lt;span class="na">level&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">INFO&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we need to download the dependencies, I prefer to use &lt;code>pipenv&lt;/code> to automate
the virtual environment creation but you can install them using &lt;code>pip&lt;/code> as well.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">pipenv shell
pipenv install
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we need to run the script and wait for it to complete.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="nb">cd&lt;/span> src
python fb2cal.py
&lt;/code>&lt;/pre>&lt;/div>&lt;p>After it is complete you can view the &lt;code>.ics&lt;/code> file and import it into your
calendar app.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">cat birthdays.ics
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Afterwards, don&amp;rsquo;t forget to delete the &lt;code>config.ini&lt;/code> file
containing your facebook password in plaintext.&lt;/p></description></item><item><title>Using Docker containers for building Archaic Projects</title><link>https://rohanverma.net/blog/2019/08/04/docker-containers-to-build-archaic-projects/</link><pubDate>Sun, 04 Aug 2019 07:30:00 +0000</pubDate><guid>https://rohanverma.net/blog/2019/08/04/docker-containers-to-build-archaic-projects/</guid><description>&lt;p>Sometimes old projects and libraries require certain specific system library versions that are hard to reproduce, or cumbersome to replicate on our development environment. Docker can be used to simplify the build process of these projects and it is often a lot faster than starting a VM in your machine and using that to build these projects.&lt;/p>
&lt;p>I recently found out about such a project at my workplace. This was a C++ binary that was statically compiled, so there was no issue with distribution but the build process had become very complex over time. It seemed to only compile on dependencies that ship with Ubuntu 16.04 Xenial Xerxes. Currently, the project was using a vagrant box, and provisioning it with dependencies. Each time a new binary had to be complied, the VM had to be started, provisioned (install development tools) using a script, setup SSH, and then copy the source into the virtual machine. After that, the VM would be used to run the bash scripts which would build multiple binaries along with the required libraries. These final binaries were then again copied to the host machine using scp.&lt;/p>
&lt;p>The initial idea was to replicate the same thing in a Docker container. But can it be done in a better way?&lt;/p>
&lt;p>Yes there is a way. A cool thing about Docker is that we can share the filesystem between the host and the container using mounts. Yes, mounting is also possible using VMs but the sheer speed at which you can perform in-place operations using docker run and mounts as compared to a VM is ludicrous. Plus, you don&amp;rsquo;t need to have a VM always running. What we just need to do is to build a container having all the dependencies and dev tools installed and use the dev tools of the container to operate on the host file system.&lt;/p>
&lt;p>Let us dive in,&lt;/p>
&lt;p>First we will make a Dockerfile having all the required tools and libraries installed. We can save this file in our projects root directory.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-Dockerfile" data-lang="Dockerfile">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> ubuntu:xenial&lt;/span>&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get install -y gcc g++ libssl-dev cmake&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> mkdir -p /etc/myproject&lt;span class="err">
&lt;/span>&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /etc/myproject&lt;/span>&lt;span class="err">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notice above, that we have earmarked &lt;code>/etc/myproject&lt;/code> as the directory where we will mount our project source tree.&lt;/p>
&lt;p>The following sample makefile is used to build our project. Only two steps are involved with this file.&lt;/p>
&lt;ol>
&lt;li>&lt;code>make docker-image&lt;/code>&lt;/li>
&lt;li>&lt;code>make build&lt;/code>&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-Makefile" data-lang="Makefile">&lt;span class="nf">build&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="n">libs&lt;/span> &lt;span class="n">myproject&lt;/span> &lt;span class="c">## build all
&lt;/span>&lt;span class="c">&lt;/span>
&lt;span class="nf">docker-image&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="c">## build the docker image using the Dockerfile
&lt;/span>&lt;span class="c">&lt;/span> docker build -t myproject:latest .
&lt;span class="nf">libs&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="c">## build the libs
&lt;/span>&lt;span class="c">&lt;/span> docker run -it -v &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">PWD&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>:/etc/myproject myproject:latest /bin/bash -c &lt;span class="s1">&amp;#39;cd build &amp;amp;&amp;amp; chmod +x build-libs &amp;amp;&amp;amp; ./build-libs&amp;#39;&lt;/span>
&lt;span class="nf">myproject&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="c">## build myproject.bin
&lt;/span>&lt;span class="c">&lt;/span> docker run -it -v &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">PWD&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>:/etc/myproject myproject:latest /bin/bash -c &lt;span class="s1">&amp;#39;cd build &amp;amp;&amp;amp; chmod +x build-main &amp;amp;&amp;amp; ./build-main&amp;#39;&lt;/span>
&lt;span class="nf">help&lt;/span>&lt;span class="o">:&lt;/span>
@grep -E &lt;span class="s1">&amp;#39;^[a-zA-Z_-]+:.*?## .*$$&amp;#39;&lt;/span> &lt;span class="k">$(&lt;/span>MAKEFILE_LIST&lt;span class="k">)&lt;/span> &lt;span class="p">|&lt;/span> awk &lt;span class="s1">&amp;#39;BEGIN {FS = &amp;#34;:.*?## &amp;#34;}; {printf &amp;#34;\033[36m%-30s\033[0m %s\n&amp;#34;, $$1, $$2}&amp;#39;&lt;/span>
&lt;span class="nv">.DEFAULT_GOAL&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nb">help&lt;/span>
&lt;span class="nf">.PHONY&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="n">help&lt;/span> &lt;span class="n">build&lt;/span> &lt;span class="n">libs&lt;/span> &lt;span class="n">myproject&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let us look closely at each step required to finally produce the binary.&lt;/p>
&lt;p>First, we will run the command &lt;code>make docker-image&lt;/code> to create a local image from the docker file we hade written. This image has all the required dependencies.&lt;/p>
&lt;p>Now, the next step is to run the &lt;code>libs&lt;/code> and &lt;code>myproject&lt;/code> steps in order using &lt;code>make build&lt;/code>. In this step, we mount our hosts working directory to &lt;code>/etc/myproject&lt;/code> which we had mentioned above. After that we run a command that will run a script that runs &lt;code>g++&lt;/code> in our case to build the binary. And voila, it is done.&lt;/p></description></item><item><title>A review of Siempo Launcher for Android</title><link>https://rohanverma.net/blog/2019/03/17/a-review-of-the-siempo-launcher/</link><pubDate>Sun, 17 Mar 2019 20:13:13 +0000</pubDate><guid>https://rohanverma.net/blog/2019/03/17/a-review-of-the-siempo-launcher/</guid><description>&lt;h2 id="three-months-of-self-inflicted-digital-pain-and-how-it-changed-my-habits">Three months of self inflicted digital pain and how it changed my habits&lt;/h2>
&lt;p>Last December, I decided to start an experiment and adopt a new launcher called &lt;a href="http://www.getsiempo.com/">Siempo&lt;/a> apart from the OnePlus and Nova launcher that I’m used to since I started using Android. After three months, even though I have stopped using it, I have observed some changes and would like to share it with my readers who are also interested in digital well being. There has been a recent surge in people starting to restart blogging even though on closed platforms like Medium, but I guess it at least that is searchable outside from search engines, and hope to see more self hosted blogs following Hackernoon’s recent split. Apart from this, people like me who were active social media users who now realize the downright issues with platforms like Facebook, Instagram, YouTube and Snapchat and their algorithms, and are no longer interested in being driven by them throughout their day. In this article I talk about my experience with it and how it changed my usage habits.&lt;/p>
&lt;p>After surveying all the options, the only fully featured launcher (that was usable) I found was &lt;a href="http://www.getsiempo.com/">Siempo&lt;/a>. An other notable mention was the Minimal Launcher but it did not have a free dark mode or even proper app search, making it unusable apart from phone calls and messages. I did not want to go to the extreme with this experiment so Siempo seemed to be the best option out there for Android. A few notable features of this app based on my experience are mentioned below. But before that, I must mention what I guess mostly the ideas on which the app is based on.&lt;/p>
&lt;p>Tristan Harris, a Former Design Ethicist at Google had around 2-3 years ago started a movement called &lt;em>&lt;a href="http://humanetech.com/">Time Well Spent&lt;/a>&lt;/em> &lt;a href="http://humanetech.com/">now called&lt;/a> &lt;em>&lt;a href="http://humanetech.com/">Humane Tech&lt;/a>.&lt;/em> Nothing better to explain this than his TED Talk on “How a handful of tech companies control billions of minds every day”&lt;/p>
&lt;iframe width="100%" height="480" src="https://www.youtube.com/embed/C74amJRp730" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>&lt;/iframe>
&lt;p>A few notable things from the website are copied below for reference&lt;/p>
&lt;blockquote class="wp-block-quote">
&lt;p>
&lt;strong>Unfortunately, what&amp;#8217;s best for capturing our attention isn&amp;#8217;t best for our well-being:&lt;/strong>
&lt;/p>
&lt;p>
Snapchat &lt;strong>turns conversations into streaks&lt;/strong>, redefining how our children measure friendship.&lt;br />Instagram &lt;strong>glorifies the picture-perfect life&lt;/strong>, eroding our self worth.&lt;br />Facebook &lt;strong>segregates us into echo chambers&lt;/strong>, fragmenting our communities.&lt;br />YouTube &lt;strong>autoplays the next video within seconds&lt;/strong>, even if it eats into our sleep.
&lt;/p>
&lt;p>&lt;cite>&lt;a href="http://humanetech.com/problem">http://humanetech.com/problem&lt;/a>&lt;/cite>&lt;/p>
&lt;/blockquote>
&lt;h3 id="highlights-about-siempo">Highlights about Siempo&lt;/h3>
&lt;h4 id="nothing-on-the-screen-when-you-tap-that-power-button">Nothing on the screen when you tap that power button&lt;/h4>
&lt;p>There is nothing more that forms habits when you tap that power button and click that enticing app which you see that you had yourself originally placed on your home screen for convenience but now are more or less compelled to check since you have formed a habit to click it as soon as you press that physical button and the screen lights up. So this app displays nothing more than a blank screen with an optional text message which it calls an &lt;em>Intention&lt;/em> although I kept it as a blank screen as I find motivational quotes a bit gimmicky and silly but might be it useful for you if you feel it helps you stay motivated to use this launcher.&lt;/p>
&lt;h4 id="everything-is-monochrome-and-apps-have-no-logos">Everything is Monochrome and Apps have no Logos&lt;/h4>
&lt;p>Colorful app icons entice you to open them. When you swipe right in Siempo you will find your most useful apps utility apps as Icons (and not logos). But wait here is the catch, these are supposed to be set by you and are based on categories. You decide the app but not the categories that can be placed here, so you can have a food app but not “Uber Eats”, “Zomato” or “Swiggy”, similarly for for your travel app, you can select only one and the icon will be a generic cab icon.&lt;/p>
&lt;h4 id="the-essentials-which-don8217t-belong-to-a-category">The essentials which don’t belong to a category&lt;/h4>
&lt;p>There will be some apps which you use which don’t belong to a category from the first page, so you again swipe right and find the frequently used apps along with a search bar incase you forgot to add the app below. These are again not based on your usage analytics or recent usage, another tactic used to form habits but by you. But here is the catch with this page, no logos here as well for the pinned apps but no icon either, only the first letter of the app and the full name below it.&lt;/p>
&lt;h4 id="the-blacklisted-ones-social-media">The blacklisted ones (social media)&lt;/h4>
&lt;p>And finally the biggest offenders of the attention economy. These are not even spared with fixed placement, and are randomly shuffled each time you swipe three times after you press your physical power button and unlock your screen making you &lt;em>literally think twice before opening this app&lt;/em>.&lt;/p>
&lt;p>Apart from this, Siempo allows you to set a custom timer of a set duration after which you use these apps such as 15 minutes, all such apps cease to function and a full page block is shown asking you to reflect for one minute if you wish to continue using this app.&lt;/p>
&lt;h3 id="reflections-on-this-experiment">Reflections on this experiment&lt;/h3>
&lt;p>Three months down with this, I have to admit my overall phone usage drastically declined. I stopped playing PUBG, reduced Netflix/Prime Video, nearly have stopped using Facebook (the last time I checked it was like a month ago), minimized Instagram usage to only view direct messages. Apart from this, I felt more energized throughout the day as I tended to sleep earlier although I woke up late as well, I ended up having more time on my hands as I had more energy. I ended up reading on the Kindle app or watching single small 5-10 youtube curated videos through twitter or reddit instead of a few 30 minute videos suggested by Youtube. It definitely had changed my usage patterns but I will have to caution others about a few reasons mentioned below, and would heavily suggest self moderation instead of changing their launcher.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Absolute pain to use when you actually need your phone –&lt;/strong> There were times when I needed to use my phone and could not use it properly especially when with friends and family and there were times when I actually needed these apps when the block would impact my productivity.&lt;/li>
&lt;li>&lt;strong>I started to use YouTube and Twitter&lt;/strong> – These apps which I had marked as harmful, were now my most used apps even after going through trouble to use them. After the banner comes for 15 minutes of usage, I formed a habit of doing something else for a minute and reusing these two afterwards. My mind had adapted to the pain and eased it for these two. Earlier I used to use these less on my phone, but was using them more on my laptop back then so I guess it was just a move from not using the laptop for these apps.&lt;/li>
&lt;li>&lt;strong>App choices changed –&lt;/strong> This might be relevant to those who are price wary especially in the food/travel category. Before Siempo I certainly had a 50-50 split in terms of usage of certain apps as they offer equal number of discounts. For food, I switched between Swiggy and Zomato and Ola/Uber for cabs, it was again a 50-50 split as well. But since I had set Swiggy as my food app, and Ola as my cab app, I began to prefer convenience more for both of these after I switched back as I have now formed a deep association with these and the time I need their services I can only think of these two.&lt;/li>
&lt;/ol>
&lt;p>I hope, dear reader, I was able to help you with informing more about the digital well being space and I have motivated you to journey on your own quest and read more about it. Do comment about your experience with Siempo and similar apps along with what your thoughts are, would love to discuss this further.&lt;/p></description></item><item><title>upi.link - Progress Update 2</title><link>https://rohanverma.net/blog/2019/02/24/progress-update-2-upilink/</link><pubDate>Sun, 24 Feb 2019 12:38:24 +0000</pubDate><guid>https://rohanverma.net/blog/2019/02/24/progress-update-2-upilink/</guid><description>&lt;h2 id="highlights">Highlights&lt;/h2>
&lt;p>I wanted to finish the underlying infrastructural things
so that I can start to focus on the higher level
features.&lt;/p>
&lt;h2 id="features">Features&lt;/h2>
&lt;h3 id="implemented-user-profiles">Implemented User Profiles&lt;/h3>
&lt;p>Added support for &lt;a href="https://upi.link/profile">user profiles&lt;/a> this week.
Now, I can extend the user authentication table with profile data.
Since, I want to store minimal data about the users on my server,
I have used gravatar to load the profile
photo and as of now no other information other than email is stored.
Maybe, next week based on user feedback
I might save the preferred UPI VPA so that the user does not have to
type it every time.&lt;/p>
&lt;h3 id="track-generated-links">Track generated links&lt;/h3>
&lt;p>Now users can track the permanent links they have generated so that
they don&amp;rsquo;t have to create a new one every time.
This same feature will be used to display links on the public
profile which will be released next week. Also,
users will be able to edit and delete these as well soon so they
can be reused.&lt;/p>
&lt;h3 id="email-preview">Email preview&lt;/h3>
&lt;p>Now, you can preview the email that will be sent beside the
link generator on the &lt;a href="https://upi.link/send">send page&lt;/a> in
real time. I will add support to the profile page to send
emails directly from there to multiple comma separated emails.&lt;/p>
&lt;h3 id="front-page-design">Front page design&lt;/h3>
&lt;p>The home page has improved quite a lot from what we started,
now we have more details about the features and
a more clearer text explaining what upi.link aims to become.&lt;/p></description></item><item><title>upi.link - Progress Update 1</title><link>https://rohanverma.net/blog/2019/02/14/progress-update-1-upilink/</link><pubDate>Sun, 17 Feb 2019 12:38:24 +0000</pubDate><guid>https://rohanverma.net/blog/2019/02/14/progress-update-1-upilink/</guid><description>&lt;h2 id="what-am-i-building-in-a-sentence">What am I building, in a sentence?&lt;/h2>
&lt;p>🔗upi.link: A programmable shortlink generator based on UPI (universal bank2bank payments service in 🇮🇳) sharable via social media &amp;amp; chat.&lt;/p>
&lt;h2 id="what-did-i-complete-till-now">What did I complete till now?&lt;/h2>
&lt;h3 id="highlights">Highlights&lt;/h3>
&lt;h4 id="deployed-at-upilinkhttpsupilink">Deployed at &lt;a href="https://upi.link">upi.link&lt;/a>&lt;/h4>
&lt;p>I used AWS Free tier to deploy this website to reduce cost for hosting this. It uses AWS Lambda
Functions to generate shortlinks which
are stored inside Redis with a TTL of 3 Days. Authentication is
implemented using a postgres backend along with secure cookies for sessions.
I will port the lambda functions I wrote for
shortlink generation out of AWS Lambda
to my custom backend written in Golang as authentication. This is because
I have seen that the cold start for Lambdas is pretty slow for my use case and takes about
5-6 seconds which for the users seems like 10 seconds.&lt;/p>
&lt;p>The frontend is written using Nuxt.js which is a Universal app framework on top of
Vue and is similar to what Next.js is for React. This was a neccessity for this use case
as I need the links shared on social media as well as chat with
HTML OG (Open Graph) tags to be populated dynamically based on their content. The only other option
was to write a static website served through templating either in Go (Go templates)
or Python (Django/Flask using Jinja templates), but then I would not be able to utilize
the rapid development offered by Vue/Nuxt. Also, I can now separate my API and frontend, maybe
totally host the API proxied via &lt;a href="https://github.com/apex/up">Apex/Up&lt;/a>
and the frontend via
S3 buckets to reduce the cost to host the site.&lt;/p>
&lt;h4 id="created-a-roadmap">Created a roadmap&lt;/h4>
&lt;p>I shared this project with a few friends and groups and lots of feature requests
as well ideas propped up. To keep a track of all these I created a public
&lt;a href="//trello.com/b/VynO9sAk">Trello Board&lt;/a>. You can &lt;a href="mailto:rhnvrm+idev4op05mcu62vhb5ne@boards.trello.com">
email to the board &lt;/a> for adding more feature requests.&lt;/p>
&lt;h3 id="features">Features&lt;/h3>
&lt;h4 id="donation-campaigns">Donation Campaigns&lt;/h4>
&lt;p>One of the major features this week, I added a campaign collection feature.
The first campaign to be hosted is the &lt;a href="https://upi.link/c/BharatKeVeer">#BharatKeVeer&lt;/a>
campaign which was launched to collect funds for contributing to the
wives of the CRPF Soldiers who lost their lives during the Pulwama terrorist attack.&lt;/p>
&lt;h4 id="temp-link-generator">Temp Link Generator&lt;/h4>
&lt;p>I updated the temporary link generator on the home page. Now you don&amp;rsquo;t need to bother
with the Long Link and you can directly see the page.&lt;/p>
&lt;h4 id="whatsapptelegram-share-buttons">Whatsapp/Telegram Share buttons&lt;/h4>
&lt;p>Buttons for sharing on Whatsapp and Telegram were added below the generated shortlink&lt;/p>
&lt;h4 id="upi-app-compatible-qr-code">UPI App compatible QR Code&lt;/h4>
&lt;p>QR code with the embeded Deeplinking Intent is now displayed on the
redirect page for people who open the link on their laptops/desktops
so that they can simply scan the link from their phone.&lt;/p>
&lt;h4 id="emails-via-aws-ses">Emails via AWS SES&lt;/h4>
&lt;p>My SES request for sending emails was approved, I added the email button
behind authentication.&lt;/p>
&lt;h3 id="misc">📌Misc&lt;/h3>
&lt;h4 id="progress-blogs">Progress Blogs&lt;/h4>
&lt;p>Well, this blog, honestly, I wanted to have a blog where I could track the progress on this
project but I did not want to have another setup on say Ghost/Wordpress. So again Nuxt came
to my rescue and now I just have to add a page to my Nuxt folder and it handles everything
else such as routing etc.&lt;/p>
&lt;h4 id="recorded-demo-video-">Recorded demo video 📹&lt;/h4>
&lt;p>You can view this video on &lt;a href="https://youtu.be/USK4UOn1Xkw">youtube&lt;/a>&lt;/p>
&lt;iframe width="100%" height="480" src="https://www.youtube.com/embed/USK4UOn1Xkw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>&lt;/iframe></description></item><item><title>Presentation about upi.link at Barcamp Bangalore - Techlash</title><link>https://rohanverma.net/blog/2019/02/03/presentation-bcb-upilink/</link><pubDate>Sun, 03 Feb 2019 12:38:24 +0000</pubDate><guid>https://rohanverma.net/blog/2019/02/03/presentation-bcb-upilink/</guid><description>&lt;p>I recently got a chance to talk about &lt;a href="https://upi.link">upi.link&lt;/a>during
&lt;a href="https://barcampbangalore.com/bcb/bcb-spring-2019/bcb-spring-2019-techlash/building-a-mobile-app-neutral-upi-request-link-generator-at-https-upi-link">Techlash at Barcamp Bangalore&lt;/a>.
I got to tell people about having started this small project. Here is the link to my presentation:&lt;/p>
&lt;p>&lt;a href="https://upi.link/presentation/">Presentation&lt;/a>&lt;/p>
&lt;p>A small summary of &lt;a href="https://upi.link">upi.link&lt;/a>&lt;/p>
&lt;blockquote>
Introducing [upi.link](https://upi.link), which is is a Payment Service Provider (PSP) neutral
UPI Request Link generator built upon the UPI Deeplinking Spec, enabling SMEs and individual users
to collect payments directly by sharing links on chat apps or embedding on their own websites,
without having to rely on building their own mobile apps and calling intents, making manual
UPI requests on PSP apps, tying up with service aggregators that charge fees, or even
purchasing licenses to APIs when they are just getting started.
&lt;/blockquote></description></item><item><title>Setting SO_REUSEPORT and similar socket options in Go 1.11</title><link>https://rohanverma.net/blog/2019/01/08/setting-so_reuseport-and-similar-socket-options-in-go-1-11/</link><pubDate>Tue, 08 Jan 2019 12:38:24 +0000</pubDate><guid>https://rohanverma.net/blog/2019/01/08/setting-so_reuseport-and-similar-socket-options-in-go-1-11/</guid><description>&lt;p>Go recently introduced a heavily requested feature that allows programmers to set socket options before accepting and creating connections. You can find a mention of this in &lt;a href="https://golang.org/doc/go1.11#net">Go 1.11 Release Notes&lt;/a>. Although, not many have written on this and implementing this is a bit confusing due to a change in the way one has to implement this. So I decided to share this with others who might be interested in using this feature.&lt;/p>
&lt;p>By reading how support for this has been added, we can get an idea about how to go about implementing this. The release notes mention that a new &lt;a href="https://golang.org/pkg/net/#ListenConfig">ListenConfig&lt;/a> type has been added. This type contains a Control function which needs to be implemented. This Control function should contain code to set the Socket Option.&lt;/p>
&lt;p>Let us see how one would start a UDP reader that performs a callback on receiving a packet.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="kd">type&lt;/span> &lt;span class="nx">UDPOptions&lt;/span> &lt;span class="kd">struct&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">Address&lt;/span> &lt;span class="kt">string&lt;/span>
&lt;span class="nx">MinPacketLength&lt;/span> &lt;span class="kt">int&lt;/span>
&lt;span class="nx">MaxPacketLength&lt;/span> &lt;span class="kt">int&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="kd">func&lt;/span> &lt;span class="nf">StartUDPReader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">opt&lt;/span> &lt;span class="nx">UDPOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">callback&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">([]&lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">addr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">net&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ResolveUDPAddr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;udp&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Address&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sysLog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;address resoultion failed: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nx">conn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">net&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ListenUDP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;udp&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">addr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sysLog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;listen failed: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nx">packet&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nb">make&lt;/span>&lt;span class="p">([]&lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MaxPacketLength&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">for&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ReadFromUDP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">packet&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">n&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MinPacketLength&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">n&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">lt&lt;/span>&lt;span class="p">;=&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MaxPacketLength&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nf">callback&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">packet&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is how the reader would look after adding SO_REUSEPORT using the new way.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="kd">func&lt;/span> &lt;span class="nf">StartUDPReader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">opt&lt;/span> &lt;span class="nx">UDPOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">callback&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">([]&lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">lc&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">net&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ListenConfig&lt;/span>&lt;span class="p">{&lt;/span>
&lt;span class="nx">Control&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">network&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">address&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">c&lt;/span> &lt;span class="nx">syscall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">RawConn&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">error&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kd">var&lt;/span> &lt;span class="nx">opErr&lt;/span> &lt;span class="kt">error&lt;/span>
&lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Control&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">func&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fd&lt;/span> &lt;span class="kt">uintptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">opErr&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SetsockoptInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fd&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SOL_SOCKET&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">unix&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SO_REUSEPORT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">})&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nx">err&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nx">opErr&lt;/span>
&lt;span class="p">},&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nx">lp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">lc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ListenPacket&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Background&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="s">&amp;#34;udp&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Address&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sysLog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;dial failed: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nx">conn&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">lp&lt;/span>&lt;span class="p">.(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="nx">net&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">UDPConn&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="nx">err&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">ipv4&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">NewPacketConn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">conn&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nf">SetControlMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ipv4&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">FlagDst&lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="nx">ipv4&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">FlagInterface&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sysLog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Fatalf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;set control msg failed: %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nx">packet&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nb">make&lt;/span>&lt;span class="p">([]&lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MaxPacketLength&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">for&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ReadFromUDP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">packet&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">n&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MinPacketLength&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">n&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">lt&lt;/span>&lt;span class="p">;=&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MaxPacketLength&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nf">callback&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">packet&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Using this approach we can reuse the port and have zero downtime, between restarts by starting the new reader before stopping the currently running reader.&lt;/p></description></item><item><title>Streaming audio from Linux to Android using PulseAudio over LAN</title><link>https://rohanverma.net/blog/2018/11/19/streaming-audio-from-linux-to-android-using-pulseaudio-over-lan/</link><pubDate>Mon, 19 Nov 2018 19:50:35 +0000</pubDate><guid>https://rohanverma.net/blog/2018/11/19/streaming-audio-from-linux-to-android-using-pulseaudio-over-lan/</guid><description>&lt;p>Suppose you are in a situation where you want to watch a movie on your TV or monitor but don’t want to use speakers. Maybe you are looking to listen to an audio book stored on your laptop but don’t want to transfer it to your phone. Or you just want to buy an audio jack splitter. Look no further, PulseAudio to the rescue.&lt;/p>
&lt;p>PulseAudio provides streaming via SimpleProtocol on TCP via a simple command. All you need to do is find the source and start streaming it.&lt;/p>
&lt;p>You can find the source by running this command:&lt;/p>
&lt;p>&lt;code>pactl list | grep &amp;quot;Monitor Source&amp;quot;&lt;/code>&lt;/p>
&lt;p>After this, you can run:&lt;/p>
&lt;p>&lt;code>pactl load-module module-simple-protocol-tcp rate=48000 format=s16le channels=2 source=&amp;amp;lt;SOURCE&amp;gt; record=true port=&amp;amp;lt;PORT (eg 8000)&amp;gt;&lt;/code>&lt;/p>
&lt;p>Next, you will need to download PulseDroid, the apk can be found in the Github repository or you can use the following command to download it using wget:&lt;/p>
&lt;p>&lt;code>wget https://github.com/dront78/PulseDroid/raw/master/bin/PulseDroid.apk&lt;/code>&lt;/p>
&lt;p>Just enter the IP address of your machine (you can find it by running ifconfig) and the port you chose and press the Start button.&lt;/p></description></item></channel></rss>