A deus ex machina moment

Last Sunday, a bunch of us planned to cycle to Mysore from Bangalore, but most of the group developed cold feet, having never done over 50 km in a single ride and with Mysore 150 km away. Only three of us finally rode on Sunday morning. We decided to end the ride at Muthati, a village by the Cauvery near where it crosses into Tamil Nadu, about half-way to Mysore.

The ride turned into a rather memorable one for a number of reasons that are funny in retrospect. I’ve detailed it on Dailymile, but I failed to note the most surreal experience that day. It happened a little before 4 PM, when Zainab, Praneeth and I were sitting on the riverside at Muthati, waiting for the bus that may or may not give us a ride out of the valley:

Nikhil Kulkarni walked over and said hello and remarked about my forgotten wallet.

Step back and picture this: we had been up since 5 AM that morning. We had pedalled for six hours already. We were at the point of exhaustion – Zainab was complaining of a spinning head and had already taken a nap on the roadside before getting here. We had just eaten at a village shack that was nothing like the idyllic rural setting we’ve been brought up to expect – the only item on the menu at every shack was freshly killed chicken with rice and sambar. The ride downhill into the valley had been treacherous. The road was steep, curvy and broken. We were constantly in danger of going down a curve too fast (even with brakes) and bouncing off a pothole onto an oncoming bus. We made a curious sight in the village and everybody was whispering about us and our crazy ride from Bangalore.

We had felt relief at reaching the bottom of the valley. It was like summiting a mountain. You’ve done something hard and you are now alone on the top – and you still have to get down from there, arguably a harder task.

And then someone familiar shows up and already knows your story. A deus ex machina moment. What was Nikhil doing out here, 80+ km from Bangalore? How did he know about my wallet?

Turned out his wife and he had driven to Kanakapura and thought to extend their drive to Muthati, and of course he had been catching up with Twitter. I had not mentioned riding to Muthati, so meeting here was just a happy coincidence. Nikhil and I have often met at Barcamp, but I only knew him by face, first name and a company he’s long left. I didn’t know his Twitter id or that he followed me on Twitter.

It suddenly occurred to me to ask Nikhil and his wife for a ride. Could they? What car were they driving? Why yes, his wife said. With Nikhil and her in the front, the three of us could sit in the back. But what about the cycles? The car was a Santro, a hatchback. No chance it could take even one. Disappointed, we figured the bus was our only chance (unless we were willing to push our bikes up ~8 km of steep slope), so we left the riverside and rode back into the village.

That was when we found my tire was flat again. My options were to get it onto the bus and fix it when we were back on the road, or do it now before twilight. The bus, when it arrived, refused our bikes a ride. Maybe I could have sent it back in Nikhil’s car? We tried hiring a utility vehicle, but found we had just Rs 100 between the three of us. No one would take us to the road (20 km away) for that little, nor would they take us 40 km to the nearest ATM in Kanakapura.

Why hadn’t I just asked Nikhil for some money, promising to pay him back in Bangalore?

I scrambled for my phone. There was no network signal. I could perhaps find a spot with a weak signal, but would Nikhil’s phone be within network coverage? Then I realized I did not know his phone number, his Twitter id, or even his full name to do a (rather far-fetched) Google search to look him up.

The deus ex machina moment had come and passed. We were alone again. I fixed my flat and we got back on our bikes to ride uphill, reaching the main road some two hours later in complete darkness.

Redirecting www to non-www in Apache

I host many sites and generally prefer not having a www in the URL, to reduce clutter. I used to do this manually, adding a VirtualHost for each www domain to redirect to the non-www domain. This afternoon I thought I’d clean up and move them into a single VirtualHost, but finding a ready recipe proved surprisingly hard. Here’s my version:

<VirtualHost *:80>
  ServerName www.hasgeek.com
  ServerAlias www.cartonama.com
  # Add each of your www domains as a ServerAlias here

  RewriteEngine On
  RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
  RewriteRule (.*) http://%1$1 [R,L]
</VirtualHost>

You may want to use [R=301,L] to make that a 301 permanent redirect (supposedly good for SEO juice). I’ve found 301 redirects troublesome: they will stubbornly persist in browser caches, so I avoid them unless I’m absolutely sure it needs to be a 301.

Technology: outsource vs open source

I wrote this note a few years ago when my then employer — a tech company trying to get into retail — was considering outsourcing tech and laying off all tech employees. I didn’t like the idea, but as the manager it became my responsibility to give it a fair evaluation, so my team met several outsourcing providers over a few months. Every single one of them looked at our work and said they couldn’t build something like it in the same period with a similarly sized team. In other words, they were neither as fast nor as cheap as the internal team was. They also balked at the company’s condition: since we didn’t have any money, we’d pay them a share of our retail revenues rather than a fixed upfront fee.

The more I considered it, the more I was convinced it was all wrong, but the company’s management was insistent. People were leaving anyway: the rumours and the delayed salaries — thanks to the credit crunch of 2008 — were more than they could take. Eventually, I quit too.

I don’t think I ever sent this out. This was my private note to prepare for face-to-face meetings.

We are planning a model wherein we don’t pay the partner but give them a share of revenues. Why will they agree to this unless they have a stake in the company’s management? We’ve seen how [redacted] has imposed themselves on our ability to make independent decisions. What’s to say that the outsourcing partner won’t do the same, especially given they aren’t paid to take orders?

Software development structures are based on three variables: people, processes and machines. People are the hardest variable to control, so structures of scale tend to emphasise processes and machines (faster machines to compensate for more inefficient code). [Employer] has a known problem with tending to people. Rather than fixing this problem, we’re outsourcing it, even as management literature consistently emphasises the need to focus on people in an organisation that is establishing a [new] business (rather than scaling an established business).

Outsourcing case studies consistently point to how selecting members of an outsourced team is nearly as hard as hiring them oneself. If we expect rapid turnaround from a technology team, we’re not going to get it unless that team is staffed by the brightest minds around, using technology that we believe most appropriate for our required agility. This is antithetical to how outsourcing partners achieve their scale: by emphasising process over people.

There are advantages to outsourcing in terms of training and retrenchment costs, and of the individual affording focus on their technical career, but on the whole I remain unconvinced this model actually works.

An alternative that I find more agreeable is to liberate from company confines the technology rather than the individual. Most of what we do has a high degree of overlap with the rest of the world. If it didn’t, we wouldn’t survive as a business. We exist to gain from the small differences in practice. The technology we require, similarly, has wide application elsewhere. Technology, whether hardware or software, is affordable when mass produced. Mass production is the only model that allows high intellectual input while keeping the output affordable. Among the chief lessons from the open source movement is that software that serves as basic infrastructure gains higher quality at lower costs when liberated from the confines of an organisation.

This is the way our approach to outsourcing should work. For every requirement, adopt an existing piece of technology and add the necessary polish. For anything that doesn’t already exist, create it and give it a life beyond our own limited requirements.

Putting it to the test

I took a year off after quitting and spent much time mulling over what eventually became HasGeek. I had a bunch of ideas on how software ought to be built, and it was only fair that I do this at my own risk. My first project at HasGeek was DocType HTML5, for which I wanted to rethink how events are organized. Part of this was to make an interactive website that handled attendee management rather than doing it offline with spreadsheets and direct email and phone calls. As I wrote the code, I found myself having to make the choice I had outlined earlier: should I keep this code to myself, since this was the crux of a new business, or should I give it away and encourage others to adopt it and contribute?

I agonized over it for a few days, then decided the business hat I had worn for a couple months couldn’t override the developer hat that had considered this decision for a decade. I released all the code under a liberal BSD license. If you don’t know what a BSD license is, it essentially states that you can do anything you please with my code without informing me, just as long as you credit me as the original author.

You may imagine one of two things happened at this point:

  1. Developers jumped into the code and started contributing new features.
  2. Competitors stole my code and competed with me at far lower costs because they didn’t have to pay me for the code.

The reality? Nobody noticed. Not initially, at least. Then an event attendee chanced on the code and sent in a patch. He added an iCal entry so site visitors could save the event date to their calendars. A student copied my code to build a website for his college event. He put my fledgling company’s name in front of a new audience that I did not have the budget to address myself.

Two things did not happen:

  1. Nobody looked into my code to find a security flaw, to use it to break into my website and steal all my data.
  2. Nobody used my code to build a competing business.

Encouraged by this, I built my second project, the HasGeek Job Board, completely in public. The code has always been open. I was running a bigger risk this time. This wasn’t an event; anyone could put up a website with my code and outspend me on publicity. I was nervous and refrained from linking to the code from the website.

I put out an initial test release to get some feedback. What happened next blew me away. People started using it almost immediately, and even better, started reporting good results. The board got some 3500 unique visitors in the first month. Today it’s at over 11,000. Status report:

The product:

  1. Over 70,000 unique visitors in the past year. 11k a month and growing. 60%+ returning traffic. We haven’t spent a paisa on advertising it.
  2. Daily reports of people hired via the board. Thank you emails.
  3. Indirect revenue via our events, which was the goal in the first place.
  4. HR consultants and other parasites who find this a goldmine of companies that are hiring, but have lost out on the flip side: access to candidates looking for jobs. Candidates don’t need to talk to a consultant when they can apply to the company directly.

For the first few months, I regularly compared the board with others that targeted the same audience. They were tied to media properties with a bigger audience and I had no monopoly on good looks or usability, so I had reason to be nervous. They didn’t seem to be doing well. I saw announcements that they were going to relaunch soon and it would be fantastic. Nothing of the sort happened.

The code:

  1. Nobody stole our code and competed with us.

  2. The first person to contribute a patch got hired and became HasGeek’s first employee. HasGeek is now six people. If you’ve never seen us advertise on our own job board, that’s because we don’t have to. We simply hire volunteers who contribute to our code. It works great: we don’t need a ramp up period to get them familiar with our systems, and we don’t need to create fake scenarios for interviews. They are demonstrating capabilities with production code.

  3. Someone stole our UI and put it on top of their own code. The job board’s UI is a skeuomorph: it mimics the visual appearance of an older medium while neither reproducing its usability nor having the constraints that defined the original look. A sticky note gets curved because of the way paper bends. There is no reason to have curved shadows on a web page unless you are trying to evoke the emotions of working with paper. This rip-off site was a skeuomorph of our interface. It mimicked us but didn’t function the same way. I sent them a copyright violation notice because our code is open, but our identity is not. They didn’t respond, but it’s been a month and the site no longer works.

  4. Some folks wrote to ask if they could fork the code for their own websites. (You can! Just do it, don’t ask!) Their patches are making their way back upstream to our code. I’m glad because it means new functionality without having to invest in new code.

Going forward

After the job board, I made it a policy that all our code henceforth will be open source unless there’s a very good reason to keep it closed. We ask ourselves this question with every new project. Every one of the 30+ projects so far has remained open. The number of contributors is growing. We’re days away from releasing a project that is almost entirely built by volunteers.

We’ve gotten this far with zero equity financing, relying entirely on revenues and some debt to ease cashflow. We pay salaries. We even pay volunteers who commit to delivery. This money comes from our events, and the events are powered by our code: code which saves time and therefore finances the production of more code, rather than being spent on event logistics.

Will this last forever? I don’t know, but it certainly works right now. Nobody’s used our code to gain a cost advantage while competing with us. Releasing code has reduced our own cost of operations.

If you are struggling with the cost of producing code, or with hiring talent to produce it, ask yourself this: why is your code not open source?

(Comments are closed because this blog gets more spam than I can handle, but you can respond over at HackerStreet.)

Comments are disabled

I haven’t been blogging actively for years, but retired blogs don’t rest in peace. I no longer have the patience to delete a few hundred spam comments every week, so I’ve neutered the blog and disabled new comments permanently.

Automated spam scripts have been pounding my little VPS for years. The comments get flagged as spam, but removing them is a manual job. For the last couple months, the pounding increased to becoming a Denial of Service attack – my VPS would choke and freeze every few hours, necessitating a reboot. I hope this neutering will fix that.

If you want to write me, I’m @jackerhack on Twitter.

The perils of taking work home

Zainab and I moved into a new residence a month ago, but haven’t completely moved out of my parents’ house. Most of our stuff is still there, and we get all our mail there. We dropped in yesterday because my parents got us a cake for our anniversary. In the mail was a box of chocolates.

Me: “Google sent us some chocolates.”
Zainab: “What? How did they know?”

(Insert moments of staring at each other working out the privacy implications)

Me: “It’s not for us. It’s for HasGeek. And it’s new year’s greetings.”

Zainab and I have been together for six years and married three. HasGeek just turned a year old. HasGeek’s registered address is my parents’ residence. We’ve been having a good conversation with Google and other companies thanks to Droidcon, JSFoo and other events this year.

Unsubscribe story

Screenshot

Today I received an MSDN newsletter.

Screenshot

It was sent to an address I’ve used just once, to download the XP Embedded SDK back in 2006. I don’t remember asking for a newsletter, so my first instinct was to unsubscribe. Sure enough, there was a link in the footer.

Screenshot

But something was wrong. What happened to the + in my email address? I submitted anyway.

Screenshot

Which didn’t work, as expected. Trying again didn’t either.

Screenshot

So I ranted on Twitter and made fun of the error. Was it a JS error, or in the backend? Then I went poking around the URL and… wait, what’s this? Could it be? No! It’s my email address in some leet encoding scheme! And they turned my precious + into %20! Someone got mixed up between url_quote and url_quote_plus!

Screenshot

So I helpfully replaced the %20 with %2B.

Screenshot

Oh ho, that worked! Will it submit?

Screenshot

Hurrah!

Spam-proofing email addresses

I built a job board for HasGeek earlier this month. Announcement here. Many job boards track responses to an application by asking candidates to upload a resume. I didn’t like the UX of this approach, so I used a free-form text box where employers can indicate how they want candidates to apply. Most provide an email address.

The trouble with email addresses is that leaving one in the open inevitably attracts spam. Most of us are used to obscuring it as kiran(at)hasgeek(dot)in or similar, but this is bad for usability. I wanted email addresses to appear as proper links — clicking on one should open a mail client — and yet be hidden from spam bots. Someone had done a 1.5 year study on nine different obfuscation methods, with three that worked. I decided to apply two of them to the job board.

Consider this job listing. My email address appears at the bottom as a normal clickable link, but if you look at the page source, you’ll see this:

<a class="rot13" data-href="znvygb:xvena@unftrrx.va"><span class="y">kiran</span><span class="z">no</span><span class="y">&#64;hasg</span><span class="z">spam</span><span class="y">eek.in</span></a>

This code uses two of the methods: garbage text that is hidden via CSS, and a ROT13 encoded link that is decoded by JavaScript.

The CSS

This declaration is included in the site’s stylesheets:

/* Spam protection */
.z {display: none;}

There is no declaration for .y. That’s a dummy.

The JavaScript

This code (activated via jQuery) finds all ROT13 links and turns them into real links:

// ROT13 link handler
$(function() {
  $("a.rot13").each(function() {
    if ($(this).attr('data-href')) {
      var decoded = $(this).attr('data-href').replace(/[a-zA-Z]/g, function(c) {
        return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);
        });
      $(this).attr('href', decoded);
      $(this).removeAttr('data-href');
      $(this).removeClass('rot13');
    };
  });
});

The HTML uses data-href instead of href to avoid broken links when Javascript is disabled. data-href is a custom data attribute, a new feature in HTML5.

Backend

The job board is built with the Flask microframework and Jinja2 templates. Here’s the template snippet:

<div class="section">
  <h2>Apply for this position</h2>
  <p>{{ post.how_to_apply|scrubemail(('z', 'y')) }}</p>
</div>

scrubemail is a Jinja2 filter, defined thus:

from flask import Flask, Markup, escape
app = Flask(__name__)

@app.template_filter('scrubemail')
def scrubemail_filter(data, css_junk=''):
    return Markup(scrubemail(unicode(escape(data)), rot13=True, css_junk=css_junk))

The post.how_to_apply|scrubemail(('z', 'y')) declaration in Jinja2 translates to scrubemail_filter(post.how_to_apply, ('z', 'y')) in Python. Markup is a Flask extension to mark text as HTML-safe. Calling escape returns a Markup instance, so we convert it back to unicode before passing it to the actual scrubemail function, which isn’t Markup-aware.

And finally, the scrubemail function:

import re

EMAIL_RE = re.compile(r'\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b', re.I)

def scrubemail(data, rot13=False, css_junk=None):
    """
    Convert email addresses in text into HTML links,
    and optionally obfuscate them with ROT13 and empty CSS classes,
    to hide from spambots.

    >>> scrubemail(u"Send email to test@example.com and you are all set.")
    u'Send email to <a href="mailto:test@example.com">test@example.com</a> and you are all set.'
    >>> scrubemail(u"Send email to test@example.com and you are all set.", rot13=True)
    u'Send email to <a class="rot13" data-href="znvygb:grfg@rknzcyr.pbz">test@example.com</a> and you are all set.'
    >>> scrubemail(u"Send email to test@example.com and you are all set.", rot13=True, css_junk='z')
    u'Send email to <a class="rot13" data-href="znvygb:grfg@rknzcyr.pbz">test&#64;<span class="z">no</span>examp<span class="z">spam</span>le.com</a> and you are all set.'
    >>> scrubemail(u"Send email to test@example.com and you are all set.", rot13=False, css_junk=('z', 'y'))
    u'Send email to <span class="y">test&#64;</span><span class="z">no</span><span class="y">examp</span><span class="z">spam</span><span class="y">le.com</span> and you are all set.'
    """
    def convertemail(m):
        aclass = ' class="rot13"' if rot13 else ''
        email = m.group(0)
        link = 'mailto:' + email
        if rot13:
            link = link.decode('rot13')
        if css_junk and len(email)>3:
            third = int(len(email) / 3)
            parts = (email[:third], email[third:third*2], email[third*2:])
            if isinstance(css_junk, (tuple, list)):
                css_dirty, css_clean = css_junk
                email = '<span class="%s">%s</span><span class="%s">no</span>'\
                    '<span class="%s">%s</span><span class="%s">spam</span>'\
                    '<span class="%s">%s</span>' % (
                    css_clean, parts[0], css_dirty, css_clean, parts[1],
                    css_dirty, css_clean, parts[2])
            else:
                email = '%s<span class="%s">no</span>%s<span class="%s">spam</span>%s' % (
                    parts[0], css_junk, parts[1], css_junk, parts[2])
            email = email.replace('@', '&#64;')
        return '<a%s data-href="%s">%s</a>' % (aclass, link, email)
    data = EMAIL_RE.sub(convertemail, data)
    return data

How effective is this?

As you can see, extracting a real email address from the obfuscated version is trivial — if you know what you are looking for. It’ll take just five minutes to write a scraper that runs through the site extracting email addresses. This technique is useless on large sites that are targeted by spammers. For a small site like our job board, however, standard scrapers will fail to pick up anything.

Registering a Limited Liability Partnership

Last evening we received the final bit of paper required to function as a legal business. HasGeek is now a fully legit Limited Liability Partnership. The process took just under 110 days. I thought it would be instructive to lay down the timeline for those considering the startup route.

Phase 0: Decision

As a sole founder I could have settled for a sole proprietorship, but decided to go for an LLP because I did not see this as something that would work without partners, and switching in future would be a hassle. A sole proprietorship does not require registration to do business, but does need sales and/or service tax registrations to handle money above certain limits. Those registrations cannot be transferred when you incorporate.

HasGeek was built on the experience of running Barcamp Bangalore, where all of us were volunteers sparing a few hours over weekends. I learnt from there to regard volunteers as significant contributors, and wanted a legal structure that allowed rewarding them for their efforts. With an LLP, they can become partners.

NoDateDescriptionDays
1. Oct 18, 2010 Initial discussion with legal advisor on registering a startup -17
2. Oct 31, 2010 Decision to register an LLP -4

Further reading:
Quora: What are the pros and cons of a Limited Liability Partnership (LLP) registration over a Private Limited registration for Indian startups?

Phase 1: Partner Registration

The “Designated Partners” of an LLP need to be registered with the government before the organization can be. My wife Zainab Bawa stood in as the second partner with a small share.

NoDateDescriptionDays
3. Nov 4, 2010 Authorization to legal advisor to start the process. Primary motivation: have a bank account ready before DocType HTML5 in Chennai on November 27 0
4. Nov 9, 2010 First notice from llp.gov.in that I’ve registered for a Designated Partner Identification Number (DPIN) 5
5. Nov 10, 2010 First notice from llp.gov.in for Zainab’s DPIN registration 6
6. Nov 11, 2010 Both DPIN applications come back asking for us to sign across our photos. Zainab is away so we return the attested photos the following day 7
7. Nov 17, 2010 DPINs still not ready. Reason given: LLP department is upgrading to new e-form system 13
8. Nov 19, 2010 Zainab gets her DPIN. I don’t 15
9. Nov 24, 2010 My DPIN is rejected because my father’s name is written differently on my PAN card and driving license. They say it has to match the name on the driving license (id proof document) 20
10. Nov 26, 2010 My DPIN is assigned 22

Phase 2: Incorporation

An LLP is essentially a registered agreement between the partners. The agreement can pretty much say anything, even granting specific partners special powers. However, the agreement is not private. It has to be shared with the bank and possibly other service providers, and they must be notified of changes to the agreement, which includes addition and removal of partners. Each change requires re-registering the document with the government.

NoDateDescriptionDays
11. Nov 24, 2010 Legal advisor asks for a few paragraphs listing all business activities, to go into registration documents for the LLP. Requires me to imagine everything HasGeek may potentially do years from now, since amending in future can be a hassle 20
12. Nov 24, 2010 Legal advisor sends a draft of the partnership agreement 20
13. Nov 27, 2010 Event day: DocType HTML5 Chennai. Our lead sponsor agrees to pay in advance, but we can’t receive without being registered. We are way past the expected date for all this to be complete 23
14. Dec 4, 2010 Event day: DocType HTML5 Pune. More expenses with no inflow in sight. Audience turnout is twice what was expected, so our expenses double too. Zainab has been hospitalized a couple days prior and I coordinate the event from her bedside. I lose my voice thanks to the stress and the sore throat picked up in Chennai 30
15. Dec 6, 2010 I finally revise and send in the partnership agreement. My sore throat starts to lift and I can speak again 32
16. Dec 8, 2010 Minor amendments to partnership agreement. We submit signed and scanned copies for registration 34
17. Dec 15, 2010 HasGeek Media LLP is incorporated. llp.gov.in sends us an unsigned PDF of the certificate 41
18. Dec 28, 2010 After much pleading, a scanned copy of the signed certificate is finally emailed to us. A signed copy is necessary to apply for a PAN card and open a bank account 54

Phase 3: Permanent Account Number (PAN), the tax id

Despite having become a legal entity, the company cannot receive money (beyond a low limit) until it has a PAN card, or at least proof of having applied for one.

NoDateDescriptionDays
1. Dec 28, 2010 Legal advisor sends copy of PAN card and Service Tax applications, for us to print, sign, stamp, scan and submit 54
2. Dec 31, 2010 Zainab and I return from vacation two days ahead of schedule to submit the documents 57
3. Jan 9, 2011 NSDL sends email to acknowledge receipt of application for a PAN number 66
4. Jan 12, 2011 HasGeek’s PAN number is assigned. We get email notice 69
5. Jan 17, 2011 PAN card arrives in the mail 74

Phase 4: Opening a bank account

My legal advisor handled everything relating to incorporation and taxation, but opening a bank account fell to me. I figured I’d go with ICICI Bank since they have a branch across the road from home, and I already have a savings account there.

NoDateDescriptionDays
1. Dec 13, 2010 I approach ICICI Bank to open an account, but they can’t proceed until the company is incorporated 39
2. Dec 31, 2010 I submit all documents, including the necessary seals on the application form. My LLP agreement says I will be the sole authorized operator of the account, but the sales rep insists on Zainab’s photo and signature on the application form. He also wants Form 49, despite the company not yet having a PAN number. He refuses to accept Form 60. I tell him we have applied online and the acknowledgement does not say it is Form 49, but he wants a copy anyway. Says he will come home on Monday and pick it up 57
3. Jan 3, 2011 Sales rep fails to keep his appointment 60
4. Jan 4, 2011 Sales rep calls at 9 AM and says he will come by 11 AM. My house is 200 metres from the branch. He does not show up at 11. I call him, but he does not answer the phone. I call again at 12:30 and he’s confused about who I am and what appointment he had. Finally he remembers and says he had a meeting and could not come. I ask him when he’s coming. He says he’ll come at 3. At 4 he’s still not arrived and is not answering the phone again, so I go to the branch and insist on sitting there until the paperwork is processed. The sales rep is there but does not remember me at all. Another sales rep takes over the interaction. He says the account will take 3-4 working days, so it should be active on Monday 61
5. Jan 10, 2011 No news. I visit the branch around noon to check up. They check their systems and say there’s no update. They have no idea what the status is 67
6. Jan 12, 2011 I visit again to check status. They say the application came back the previous day with two issues, but the sales rep did not bother to call me. What are the issues? 1. One of the documents has my middle name (which I rarely use) and another does not. They want to confirm both are the same person. 2. Zainab’s name is listed in the account despite the LLP agreement saying I was the sole authorized operator. Now they want to take it out 69
7. Jan 20, 2011 After a few more trips to the branch to sign and re-sign various documents, the account is finally activated. The branch manager issues me a written apology for the delay, citing confusion over my name as the reason. This is not the first time my middle name has caused trouble. It takes another fortnight to get all the standard features enabled so I can start using the bank account 77
8. Jan 21, 2011 I make the initial deposit into the bank account and leave for DocType HTML5 in Hyderabad. More expenses, and we still can’t receive money because we need to charge for service tax but don’t have a service tax registration number 78

Phase 5: Service Tax and Tax-deduction Account Number (TAN)

We are a product company, but our product is not physical material. In tax-speak, that makes it a service business. Any business that grosses 10 lakhs (INR 1,000,000) in a year must collect service tax, for which it needs a service tax registration number. We were set to pass that figure in the first two months. Service tax registration requires proof of address in the name of the company, but the address on the incorporation certificate is not good enough. It has to be a bank statement or a phone bill. Since we had charged sponsors service tax, they could not pay up until we quoted the service tax number (or at least showed proof of application), but we could not apply for this number until the bank issued a statement for the account. I had expected the account to take 3-4 days, not 20.

A TAN number is required to deduct tax at source for consultants and employees.

NoDateDescriptionDays
1. Jan 24, 2011 I return from Hyderabad and get a statement from the bank, then pass this to legal advisor 81
2. Jan 25, 2011 Service tax application is submitted 82
3. Feb 5, 2011 Event day: DocType HTML5 Ahmedabad, which means more expenses without income 93
4. Feb 8, 2011 Service tax number is assigned 96
5. Feb 9, 2011 Tax-deduction Account Number (TAN) is assigned 97
6. Feb 11, 2011 The big payment cheque finally arrives! 99
7. Feb 19, 2011 Signed copy of service tax certificate arrives in the mail 107
8. Feb 21, 2011 Letter informing us of TAN allocation arrives in the mail 109

In 109 days, HasGeek became a registered business, crossed 10 lakhs in revenue, made over 1000 contacts in real life (with double that in the database), and will close the first financial year as a cashflow positive business. Personally, I went as far as 3.75 lakhs in debt, at a short term financing cost of about Rs 12,000. If I had known this would take 100+ days, I would have borrowed from friends and family or broken out savings. At pretty much every step in the process it looked like it would take at most another week to get everything in place. My credit card issuer decided this justified doubling my credit limit. They are now offering me 100% advance on a car loan.

My legal advisor was NovoJuris. I recommend them to anyone considering a startup. They took care of everything, letting me focus on my work. They went out of their way to prod government machinery into action, without which this would have taken much longer. However, the big lesson for me is to never let an external entity define my timelines. HasGeek was nearly killed before it was born because I allowed apathetic third parties (government and private sector bureaucracies) to control my cash flow. From now on, we will put things in place months before they are actually needed, with redundant options from other providers, whether for payment gateways or data cards.

Accounting for reimbursements

HasGeek’s legal registration is more or less complete, our bank account is operational, and the first of our sponsor cheques has cleared. It’s time to start reimbursing everyone who incurred an expense during our events.

I am therefore flummoxed to discover my accounting software has no apparent way to record a reimbursement.

Imagine this scenario: you run a startup, and your startup needs a VPS for the website, so you buy one. You can only pay for it with a credit card, but your company doesn’t have one. It has no credit history. You use your own credit card, but buy in the company’s name and then reimburse yourself from the company account. On company records, it was billed by the service provider, but paid out to you. Chances are your company did not even exist at the time you made the purchase, so you are reimbursing yourself long after the fact. Common enough? And yet, I can’t find a way to record this.

One way to get around this is to simply record it as paid out to the original service provider. Another is to treat each of these individuals as the service provider. But, Kesava Reddy, Kiran Jonnalagadda and Ravi Srinivasan do not provide air travel services. We merely booked tickets from Cleartrip using personal credit cards, on company business, and we need to be reimbursed individually.

Either I’m violating acceptable accounting standards by keeping such a record, or this is an oversight in the accounting software. I’m not sure which.

What software?

For many years I’ve kept personal accounts using GnuCash, which I absolutely loved, but is rather long in the tooth. GnuCash can only be used as an archaic cross-platform desktop app. There is no cloud version, no sync between instances, no multi-user ability. I thought I’d build my own web-based clone, designed specifically around reimbursements, but my needs are growing faster than my spare time. The prudent thing is to use something off the shelf, so when Zoho announced their new Zoho Books product, I jumped.

It’s been a great few days with Zoho Books, with their incredibly well thought out interface for small businesses. Books integrates features from their earlier CRM and invoicing products and has just the right amount of functionality to suit my needs. Except, I can’t record reimbursements.

Reimbursements wouldn’t have been a problem in GnuCash where everything is — in accounting parlance — a “manual journal”, a direct transaction between two ledgers. I could easily make three types of ledgers: an “asset” ledger for the company account, an “expense” ledger for servers, travel and others, and a “liability” ledger for each person who needs to be reimbursed. The original transaction would be between an expense and liability ledger, and the reimbursement between asset and liability. This way I record all the relevant facts — how much was spent on an expense, when, who paid for it, how much each had to be reimbursed, and when they were reimbursed.

Zoho Books and other accounting apps frown on manual entries, preferring that you make all ledger entries via the front-end tools of quotations, bills and invoices. I like this — it gives me a better sense of the company’s health — but there is no front-end tool for reimbursements.

What do I do?

Update: The folks at Zoho Books found me a workaround, by treating each reimbursable party as a credit card account. I’ve decided to stay with Zoho Books since their customer service makes it worthwhile.

Printing a ticket

I had a 5 PM bus out of Chennai, so I checked out of the hotel at 1, left my bags at the reception, and headed out to lunch with Billy. When we returned at 4 to take the luggage, I remembered I hadn’t yet printed my ticket, so asked the hotel manager if I could take a print.

“No problem,” he said, and handed me a business card. “Email it to me.”

Umm, from where? Maybe I could unpack my laptop, plugin a data card, go online, and send him the file. Or wait, the ticket was in Dropbox. I have a Dropbox app on my phone. Maybe I could email from the phone? Then I had to shake myself out of being a geek. This was going to take many precious minutes and I was in a rush. I simply asked him, “from where?”

He pointed at a computer meant for guests to check email. It was already powered on (minutes saved!) so I logged into Dropbox, opened the PDF file and hit Print. No printers configured. Then I saved it to Desktop, asked if he could open it over the network. Nope, he didn’t know what that was. Logged into Gmail, mailed him the file, logged out, and asked him to check.

“Not arrived,” he said.
“Refresh?”

He had Outlook on his desktop. I found his Refresh button and hit it, but no mail showed up. I was losing precious minutes, so I asked if I couldn’t just download it to his computer. He agreed. I logged into Dropbox again and located the file. It opened in Word and showed a lot of gibberish. Why was a PDF file opening in Word? I closed the window, downloaded it again, located it on the desktop, right clicked, and selected “Open With…”. The only available option was “Microsoft Word”. Didn’t he have Acrobat Reader? He had no idea what that was.

What kind of hotel has never had to deal with a PDF file before?

“What do I do now?”
“Email it to me.”
“But you don’t have Acrobat Reader.”
“Then you talk to my office manager.”

And he pointed me to a third computer with a chap sitting at it. I explained to him that I needed to print a PDF file and one computer didn’t have a printer while the other didn’t have a PDF reader. He said “no problem, email it to me.” I said the file was already on two computers, couldn’t he just take it off the LAN? “No, that’s not configured.”

Then he suggested I download it again to his computer. This time, finally, he had both a printer and a PDF reader.

Livestreaming events

We’re going to Chennai next week for DocType HTML5. We want to livestream the event for the folks who can’t make it. I see two ways to make this work:

  1. Use a computer with a webcam and broadcast with a service like Ustream, Justin.tv or Qik.
  2. Use a mobile phone with a 3G connection and a native app for one of these services.

If we use a laptop:

  1. All three services use Flash, which doesn’t work great on Linux, so the laptop must run Windows or Mac OS X. Built-in webcams on most laptops aren’t great either, so I need to get something like the Logitech Webcam Pro 9000 (Rs 4700).
  2. The microphone on most laptops isn’t great either. Need an external mic or line-in from the sound mixer.
  3. Need a dedicated laptop through the day, with Windows. Our conference router runs Linux and could have doubled for this duty, but the availability of Linux drivers for the webcam is unclear.
  4. A laptop is a heavy piece of equipment to lug around.

If we use a phone:

  1. Most phone cameras are crap. The Nokia N8 has a fantastic camera, but nobody makes Symbian apps anymore. There’s just Qik in the Ovi store. I tried Qik on my Android and the performance is crap, while Ustream works great.
  2. The only Android phones with HD cameras are above INR 25k (Samsung Galaxy S, HTC Desire HD, Sony Ericsson Xperia X10). We don’t have the budget to buy an expensive phone just to stream video. We’ll have to convince one of these companies to sponsor it.
  3. Phones are easy to carry and easy to lose. They won’t sit on a tripod, so an adapter is needed.

What do we do? Is there a webcam or phone you can recommend?

Update: I’ve settled on the Logitech HD Pro Webcam C910. Rs 5800 in Jayanagar, Bangalore, and works great with Ustream on our conference router running Ubuntu Maverick, with both BSNL 3G and Reliance NetConnect (both of which offer roughly 200 kBps on the uplink).

HasGeek

In August this year, I started working on a conference around HTML5. DocType HTML5 went live on October 9 with 200 participants, out of over 450 applicants. The event was the first coming out of a concept I had been toying with since the middle Barcamp Bangalore period in 2007. DocType HTML5 was an unexpected success. None of us thought it would go off as well as it did. Until two weeks before the event, we weren’t even sure it would happen.

The event helped shape the startup I had long wanted to do, to fix a personal problem: the lack of spaces in which to have in-depth technical conversations. HasGeek was named over a dinner conversation in one of the DocType HTML5 planning meetings, and our first act as a registered entity is to take DocType HTML5 across India. Registrations are now open for the Chennai (Nov 27) and Pune (Dec 4) editions, with upcoming editions in Hyderabad, Mumbai and Delhi scheduled for Jan and Feb 2011.

I expect that this event series will point to some aspect of the modern web that deserves a deep examination in the March-May 2011 period. Early feedback is pointing to the canvas tag.

Happier days

Happier Days
Children at Thiksey Monastery in Ladakh, July 2009

Checking my Ladakh pictures a year after the trip. Carefree times.

Optional unique fields with MongoDB

I’m trying out MongoDB for a new project and am having an interesting time wrapping my head around the way it works. I’ll post notes to this blog whenever there’s something particularly interesting. Today’s is one such.

MongoDB is a schema-free key:value store, where the value is a JSON document. It’s the leading contender in the NoSQL movement. JSON documents can be of arbitrary depth, just like XML, storing anything from a single snippet to an entire database. This flexibility makes me happy. Schemas that required tedious snowflake definitions in an RDBMS can be a single document in MongoDB.

Today’s eyebrow raiser, however, came from the way MongoDB handles indexing. Let me illustrate with a traditional SQL schema for user accounts. I want to handle three different scenarios for how user accounts come to exist. Users may sign up on the web, in which case they will have to pick a username. Users may be introduced via email (the way Posterous and TripIt work), in which case they have an email address but no username, or users may login the first time via OpenID, in which case neither username or email address is known at the time of account creation. An account may exist with any one of these three identifiers, but each is expected to be unique across the system. In SQLAlchemy:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Unicode, Integer
Base = declarative_base()

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    username = Column(Unicode(50), nullable=True, unique=True)
    email = Column(Unicode(50), nullable=True, unique=True)
    openid = Column(Unicode(250), nullable=True, unique=True)

I have three columns here, all of which are unique but optional. From SQLite’s documentation:

The UNIQUE constraint causes an unique index to be created on the specified columns. All NULL values are considered different from each other and from all other values for the purpose of determining uniqueness, hence a UNIQUE column may contain multiple entries with the value of NULL.

See? That’s straightforward. If the field contains a value, it has to be unique. If there’s no value (ie, null), that’s okay too. Job done. I figured it would work the same way with MongoDB, so I whipped this up with MongoEngine, a nice ORMish wrapper that allows defining basic validation on models:

from mongoengine import *

class User(Document):
    username = StringField(required=False, unique=True)
    email = StringField(required=False, unique=True)
    openid = StringField(required=False, unique=True)

This should work, right? It doesn’t. Behind the scenes, MongoEngine converts this model into a MongoDB index:

db.user.ensureIndex({username: 1}, {unique: true});
db.user.ensureIndex({email: 1}, {unique: true});
db.user.ensureIndex({openid: 1}, {unique: true});

MongoDB is schema-free, so these keys may or may not exist in the document, but here is what the documentation has to say about unique indexes:

When a document is saved to a collection with unique indexes, any missing indexed keys will be inserted with null values. Thus, it won’t be possible to insert multiple documents missing the same indexed key.

In MongoDB, null is also considered a unique value. Bummer. I finally came up with this solution:

from mongoengine import *

class User(Document):
    username = StringField(required=False)
    email = StringField(required=False)
    openid = StringField(required=False)

    meta = {
        'indexes': ['username', 'email', 'openid'],
        }

    def __repr__(self):
        return u"<User '%s'>" % (self.username or self.email or self.openid)

    def validate(self):
        super(User, self).validate()
        if not self.username and not self.email and not self.openid:
            raise ValidationError(u"One of 'username', 'email' or 'openid' must be provided.")

        # Check for uniqueness of username, email and openid.
        if self.username:
            existing = User.objects(username=self.username).first()
            if existing and existing.id != self.id:
                raise ValidationError(u"Username '%s' already in use." % self.username)
        if self.email:
            existing = User.objects(email=self.email).first()
            if existing and existing.id != self.id:
                raise ValidationError(u"Email '%s' already in use." % self.email)
        if self.openid:
            existing = User.objects(openid=self.openid).first()
            if existing and existing.id != self.id:
                raise ValidationError(u"OpenID '%s' already in use." % self.openid)

MongoEngine calls instance.validate() during a save(), so the constraints get applied. This isn’t particularly clean. The same check is repeated thrice, once for each field, and that’s up to three queries every time I want to save data. However, I don’t expect optional-but-unique to be a recurring pattern, and user accounts are infrequently edited, so this should be okay for now.

Fontconfig and Chromium

Thanks to this explanation from Evan Martin, I’ve finally figured out how to get fonts looking right in Chromium on Linux. The crux of the problem is that there are two font configuration systems. UI widgets read settings from one, the panel at System → Preferences → Appearance, while the browser area reads from fontconfig.

I prefer the appearance of full sub-pixel hinting on Ubuntu, but this setting, selected from the preferences panel, wasn’t being propagated to fontconfig, so web pages continued to have blurry text. The fix? Here is my new ~/.fonts.conf:

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
    <match target="font">
        <edit mode="assign" name="hintstyle">
            <const>hintfull</const>
        </edit>
        <edit mode="assign" name="rgba">
            <const>rgb</const>
        </edit>
    </match>
</fontconfig>

Evan proposes a test for what hintstyle fontconfig is using:

$ fc-match -v Arial | grep hintstyle
        hintstyle: 3(i)(w)

The hintstyle of “3” indicates full hinting. Ubuntu’s default, slight hinting, would be “1”.

Observations on what went wrong during the fire at Carlton Towers

Sashi, my very sensible manager who held us all together during the ordeal, consented to letting me share this email exchange. I’ve removed all name references for privacy:

---- Begin Message ----

From: Sashi
Subject: Lessons to learn - Carlton Towers Fire

Thanks to all the well wishers like you, we made it out of the building after staying in for 1 hr and 45 minutes approximately. We were blessed compared to some others who had terrible experience to report and some not there to report anymore.

I was overwhelmed with the number of people from [snip] that were there for me and was wondering if I did the right thing by calling [snip], creating panic and helplessness for you all watching from below. But our heartfelt thank you goes to each one of your wishes and prayers to give us strength to stay calm.

In fact I decided to call [snip] to draw upon the [snip] muscle power (to get some fire engines immediately), because even after 30-40 minutes after the smoke broke out there was total cluelessness with the ground personnel in the building and no sign of help in the vicinity. It would have helped if someone gave us a big picture on PA system of what was happening, some quick measure that have been taken, that help is on its way, and what we needed to do in the meantime.

Few other things that were very disturbing for me personally was.

  • Smoke alarms did not operate though I had personally tested the alarm in our reception when we moved in, 3 months ago. I could alert my team only after I sensed the burning smell. It was clearly the smell of electrical wire burning. When we suspected some mishap in our unit and peeped out to the reception area we started seeing smoke creeping in through the closed door gaps and it was totally dark outside our glass door. So by this time the entire corridor leading to any type of exit was engulfed with smoke. If the smoke alarms were battery operated they should have caught the smoke way ahead, I doubt if they are.

  • Exit doors did not open for they were locked and it had to be broken open. Goes to show lack of fire drill.

  • Help arrived to the building 60 minutes after the smoke broke. Today I hear the firemen reported in less than 30 minutes upon the request reaching them, which only goes to say there was a delay in reporting by at the least 20 minutes. The building admin was not even aware of the situation when I called her to ask for advice and thereafter did not pick up calls.

  • No announcement was made on what the situation was, neither was a request made on PA system for people to stay calm. Even public watching from outside did not have a word to say when victims in the building were yelling for help which increased the uncertainty in everyone’s mind. I am told there was some announcement, by people on the ground, which did not reach our ears in any case.

  • When help finally arrived, they had no access to many of the floors including the 5th floor we were in. The men who walked in to take us to safety eventually did not even a have a mask on. Such was the preparedness of fire men. They were not even apprised of the cause for fire (more of smoke). They seem to have come prepared to put down the fire but not so much to deal with smoke and putting off smoke.

  • They were ill equipped for the situation in many ways

  • The building structure by itself was not at all conducive for exit nor entry of service men in a fire/smoke situation. Upto 5th floor we have sliding windows which is easy to open and close, but to prevent mishap and security reasons quite a few 6th and 7th floor offices have grill structure behind the glass pane which makes it difficult for people to reach out for fresh air. Also the windows have to opened by lifting them (angle glass panes) which makes the operation and access very difficult (you would have noticed in some of the pictures).

  • There was total lack of leadership given the multi owner/tenant situation. It should have been possible to get the armed forces and HAL copters involved in the rescue operations with some hanging ladders and safety nets, given the proximity of both these locations. I do not know what it takes to make such a call and execute on it even as an exception. Such an attempt could have quite possibly saved all lives.

  • Most of us have experienced fire drills and I am not sure how often one is taught how to deal with smoke extensively, at the least I could not recall. We may have to use videos to demo some of these and with more frequency to increase the recall value

We were lucky in several counts.

  • We had an airtight door just after the conference room separating the main work area which prevented corridor smoke coming in.

  • We did not have centralized A/C therefore a/c ducts did not carry smoke in.

  • We had sliding windows which enabled us to maneuver the smoke better and stay in a safe and comfortable breathing zone.

  • We neither had visitors nor volunteers in [the day before] yesterday which would have increased our responsibility and therefore possible panic.

  • We had lot of people praying for us

More than everything we had a very small team 6 people in the office yesterday and the team stuck together in the decision to stay in and wait rather than taking a plunge into the smoke, even though it was attempted by two for a fraction of a second and then given up as a bad idea. One of them was busy twittering to keep himself calm. Here is the link of the same

http://www.india.com/news/india/blore-man-tweets-while-stuck-burning-carlton-towers_7243.

Thank you again for keeping us in your thoughts and being with us in person.

Hope we all learn from such instances (expensive learning) and have better safety measures designed in.

We are definitely happy to be alive.

Thanks,
Sashi

---- Quoted Message ----

From: [snip]
Sent: Wednesday, February 24, 2010 2:26 PM
Subject: Lessons to learn - Carlton Towers Fire

Hi Team, In the recent Carlton Tower fire incident at Bangalore, there was a rare opportunity to talk to an important person who was trapped in the said building. As a matter of fact I witnessed the complete incident. Information gathered/ a few lessons which could be grasped was:-

  • There was no credible evacuation drill practiced. This is true since people were actually trapped and could not evacuate due to heavy smoke.

  • Fire alarm was very feeble and was not audible. The facilities helpdesk / security help desk, whom occupants generally contact, in such events were unaware of the fire till such time the occupants informed them to initiate action of calling fire brigade. This led to loss of time and heavy buildup of smoke which prevented safe evacuation of more than 150 occupants.

  • The building was multi tenanted and multi owned too. This led to lack of ownership for emergency management.

  • There was no history of training and nomination of fire wardens / ERT members in Carlton Towers.

  • Whereabout of the occupants were not known to anyone. All were on their own.

  • Smoke generated from the interiors of air-conditioned buildings is thick and heavy and contains a lot of poisonous gases due to the nature of synthetic materials used inside the buildings. It travels up-words through stair cases and as a consequence the stairwells were chocked with smoke, preventing evacuation. Therefore there is a need for timely evacuation.

  • Fire tenders took anywhere between 40mins to 90 mins to arrive at the scene. There were no building management personnel to guide them to the source of fire nor did anyone of authority brief them.

  • We did not see the fire hydrant hoses of the building being used at all. They were presumably non functional / no one was trained to use them.

  • Occupants were not aware of the fire exits.

  • The building compound architecture was such that fire tenders had to park almost 100 ft away from the building, thus rendering their ladders ineffective.

  • Being on the main road (airport road), there were a lot of bystanders/observers. This hindered movement of traffic and completely jammed the space for movement of fire tenders and ambulances.

  • The fire tender with snorkel arrived after 2 hrs, delaying the complete evacuation process, especially of the 4-6 floors.

  • There were a lot of civilian population especially youngsters who were good Samaritans. Their contribution was more than the fire brigade people. The fire brigade people were completely stunned and motionless due to poor information of the building as well as outdated equipment. The ladders they carried were woefully ineffective.

  • It is very important to note that inside the building, breaking glass doors, windows and wooden doors should be resorted to only after ascertaining the temperature of the partition / door since it may lead to flash once the door / partition is opened due to influx of oxygen which will reignite / refuel the fire. This is a dangerous action and needs special training.

  • Bystanders who took over the evacuation process were ill informed about safe evacuation techniques. Occupants were asked to slither down the water hose pipes and ill equipped nets. Some slipped down and fell and succumbed to their injuries.

  • Lack of leadership in times of adversity was rampantly visible. Each one was on his own. It is understandable, but, at such times, managers should assume leadership and guide their teams to do the right actions.

---- End Message ----

I did hear the alarm, but it was feeble. It sounded like it was going off in another part of the building. I wouldn’t have heard if I had headphones on, like I usually do at work.

The 6th and 7th floors had the worst time of all because they had smoke coming in through the A/C ducts, their windows wouldn’t open easily, and their access to the stairway was locked up. (I use the stairs regularly and the 5th floor is never locked.)

Update: Edited out a few personal references, for clarity.

Finding one’s religion

Maneesh on Twitter is having a hard time understanding how I could manage humour while stuck in a fire. I had posted:

A Corner House treat to whoever gets a picture of me looking out of the window. Seriously, people, there’s no need to panic. Bad for you. #

My colleagues prayed. I didn’t. I was a skeptic when I entered my teens and a confirmed atheist when I left them.

I prefer to call myself a humanist now. I believe that humankind created God to explain the mystery of one’s own existence. God represents all that is unknown and inexplicable. The domain of God has receded with each new scientific advancement. We have gone from the vengeful gods of natural forces to a single capitalized God, representing the human notions of love, mercy and justice, in both the Dharmic and Abrahamic traditions. What was once an Act of God is now mere probability theory. And yet, God won’t go away, because there will always be an unknown, and we fear the unknown.

What was to be our fate, trapped in that building? Was the fire moving towards us? Would we be rescued? Who knew? That is why we pray, to seek Faith as relief from uncertainty. Our tools of prayer have changed with time. A ghee lamp used to be mandatory, but a pulsing LED on your car dashboard is now a good enough substitute. Caught in a pinch like we were, our tool of prayer was the mobile phone. We called anyone we could, asking them to pray for us as per the traditions we had been raised in.

My tradition, atheism, has disconnected me from the traditions of my parents. I can not pray the same way. As far as I was concerned, we were trapped and helpless, and could only be saved by the people outside.

That tweet, then, was my prayer.

Fire

When someone jumps out of a high-rise window to escape a fire, it doesn’t happen like in the movies. The fall doesn’t proceed in slow motion. There’s no drama, no close-up of the jumper’s face as they go through their emotions. One moment they are hurtling through the air, the next they are a shabby lump on the ground. You might as well have thrown out a sack of clothes. There’s no time for any sort of emotional response in the onlooker.

Douglas Adams delivered the most sage bits of advice over thirty years ago:

  1. Don’t Panic
  2. Knowing where one’s towel is

When the fire alarm went off at about 4 PM yesterday, nobody budged. We knew the drill. Someone would come knocking on the door, demanding that we keep with the program and get out. We’d reluctantly pack up stuff and lock the door on the way out, because fire drills are such a perfect opportunity for theft. Nobody wanted to be bothered with this. That is, until we saw smoke out of the window.

“Run!” demanded Sashi. “Don’t pack, just run.” Then we saw smoke coming in the front door. Thick, black, stinging smoke. And then it was coming in through the restrooms and the pantry, and leaking in from the ceiling. We were trapped. The fire was right outside and all we could do was shut the doors and stay in. Outside, black clouds billowed from floors above. Spectators had started to gather.

There was neither heat nor visible flame. We didn’t know where the fire was, but it sure seemed to be above us. Smoke continued to seep in. Anjan ran to the restroom and wetted his handkerchief. Getting the idea, I did too with my cycling hand-towel, then passed it on and ran rounds for the others, wetting their kerchiefs. The restroom got harder to enter with each round. I had to breathe deep, open the door, open tap, wet the kerchief, close tap, step out, close the door, and breathe out. One early breath and I’d be choking. Breathing outside air through the wet cloth made it bearable.

Being thus forced to the windows, we turned outwards to look down on the growing crowd. Someone jumped from the floor above. Someone else too. Bizarrely, this was like watching a high voltage action movie in immersive 3D, except we may not be going home at the end of it.

Or you could look at it as a giant fumigation operation. Smoke the building out and watch the humans flee through any exit available, however high off the ground it is.

What does one do at a time like this? Sashi called her husband. Anjan called his wife, produced a string of beads from somewhere, and proceed to sit in a corner and chant. Sanjay called his mother, carefully explained that we were stuck in a burning building, and asked if she could perform a puja for us, and no, this was not a joke call. Sangeetha, I don’t know what she did. As an asthmatic, she was at high risk of asphyxiation. Seetharaman had the worst time of all. As a single father, he had to explain to his very young daughter that this may be a goodbye.

Carlton Towers burning

I didn’t call anyone. I didn’t want to set off panic. I stayed by the window, watching the crowd below, the fire brigades trying to make their way through, the men assembling mattresses and a cloth net for additional jumpers. We were going to be rescued and would be going home shortly. There was no need to panic.

Except, something was missing. Where was the documentation? So I took out my phone and posted:

Carlton Towers is burning and six of us are trapped inside. The fire’s above but there’s smoke everywhere. Saw people jump to their death. #

Then I took a picture of the crowd and posted that too:

Crowd outside Carlton Towers

http://ping.fm/p/DIQQv - Fire at Carlton Towers #

I had no idea what I was setting off when I did this. Friends started to call almost immediately. The typical conversation went like this:

“Hi”
“Hi”
“Umm, are you all right?”
“I’m stuck inside a burning building.”
“You are… inside?”
“Yes, I’m inside, trapped, and it’s burning.”
“Umm, can I do anything to help?”
“No, it’s okay, I’ll be fine.”

By the time I hung up on one, another would be on call waiting, asking too if they could do anything to help. I could no longer post pictures or text. Seriously, people, if you’re not at the site of the emergency, don’t call. Your concern is appreciated, but by blocking all channels during those precious minutes, you’re being a hindrance. I posted a request:

Don’t call me folks, you can’t help. Will keep posting. #

It went mostly unheeded. People called anyway. Bala from DNA made the first press call. And now that it had hit the news, it was time to call family. I called Zainab first and asked her to tell mom, and to tell her to please not panic. Another person jumped and collapsed.

The firemen meanwhile had assembled a ladder and were attempting to scale up the other end of the building. The ladder went up to the fourth, while I could see many hands waving from the fifth, our floor. They were tossing a rope up for someone to catch. Elsewhere, men were bringing in a bamboo ladder. The men with the cloth net caught two jumpers, who were quickly whisked away to a waiting ambulance. (Apparently, one died.) Then they started to put in place a ladder directly below us. A ladder rescue, it was going to be. We waited. We continued hollering for attention, actually. The ladder was taking forever.

A Corner House treat to whoever gets a picture of me looking out of the window. Seriously, people, there’s no need to panic. Bad for you. #

And then there was a knock on the door. A fireman was outside. The smoke had cleared sufficiently for us to walk down the stairs. I quickly unplugged my desktop, grabbed my gear and stuffed as much as I could into my pockets. Seetha switched off the UPS. There had been no power since before the fire started, but we didn’t want to be the cause of another mishap, what with all the soot flying around. I went out last to watch for anyone stumbling ahead of me. We passed a small fire on the fourth floor and exited on the first, walked across the roof of the ground floor and down the B wing stairs. The firemen had blocked entry to the ground floor of our building. It was still sputtering.

Sliding down a fire hose

As we walked across, I noticed another rescue operation in progress on the inner side of the building and stopped to watch. Someone had let down a fire hose from the roof and folks were swinging down one at a time. The staff of the restaurant downstairs were also there. They said it was suspected to be an electric fire. I posted:

http://ping.fm/p/7gxrg - Heard it’s not a fire, just an electric short-circuit. Only smoke (itself quite dangerous). #

Pavanaja called to say a local TV channel wanted to interview me. I accepted and went on the air explaining what I had seen in my broken Kannada. They wanted to know how many people were on the floor and what sort of companies they were. I had no idea, so I made guesses from what I remembered of the directory downstairs. Deepa Kurup from The Hindu was next, followed by a series of publications and channels that I can no longer remember. I was on the phone almost continuously for the next hour.

The crowd outside had swelled to cut off all transportation:

http://ping.fm/p/jCEPJ - Massive crowd outside. This must have choked traffic for kilometres around. #

Crowd outside Carlton Towers

Sashi’s husband, a senior executive at Dell, arranged for a medical check-up at Dell’s campus up the road. The doctor gave me a clean chit. Blood pressure normal, breathing normal, just a lot of soot in my nose and hopefully not in my lungs.

We settled into a conference room to let our nerves settle. NDTV called next, and attending to this, I have to say, was a mistake. They tried to keep me on line for as long as they could while they interviewed the fire chief and others, asking me what I thought of the arrangements. I eventually got fed up, told them politely I had to talk to my family too, and hung up. (The clip is only 3 minutes, but the call went on for over twenty.) My phone said I had sixteen missed calls and several more messages waiting. One was from CNN-IBN, who ended up reading out my tweets. I was in no mood to return calls or do any more interviews, so I posted:

Cycling home. Won’t take calls. Please feel free to use my pictures as needed. #

The very helpful folks at Dell insisted that I take a cab home. They had booked a large car so I could fit the cycle in it. I insisted on cycling home. They didn’t think it was appropriate after facing this sort of trauma. I pointed out that I wasn’t the least bit traumatised and the doctor had confirmed. They relented, and I cycled home to parents who had been unable to watch television until then, fearing the worst.

Calls and messages continued to pour in late at night, and again this morning, thanks to the newspaper coverage.

All this media attention is being a lot more stressful than the fire itself. #

Requests for interviews continue as I type this, forcing me to switch off.

Turned down two phone calls requesting in-person interviews. Switching off phone for today. Will be on Twitter though. #

What is the point of an interview? It sells advertising for the interviewer, but will it do anything at all to improve fire safety? Will it make up for the disruption caused to the lives of the affected?

Fun with micropayments and microcharges

Jeffrey Friedl accepts donations as low as $0.01 for his Lightroom plugins. He says for donations that low, Paypal takes all of it as processing fee and he gets nothing, but he’s cool with that. I found this most intriguing and had to try it. PayPal lets me send money in US dollars or rupees, but my rupee transactions started failing after Verified by Visa and Mastercard SecureCode were made mandatory in August, so I paid in dollars. $0.01. It went through. Nifty! (To be fair, I was registering three plugins and didn’t have the heart to do this all three times.)

Minutes later, HDFC Bank sent me an SMS receipt for the payment. They charged me Re 1. One rupee is roughly two cents, but PayPal had asked for and received only one cent.

Question: Where did the other cent go? I know it’s part of the processing costs and is too small for anyone to be bothered about, but that’s not the point. How is this missing cent accounted for? PayPal didn’t receive it, and I sent it, so it’s somewhere on HDFC Bank’s books. In a physical cash transaction, it’s normal to round off to the nearest rupee before it goes on the books, but this transaction was handled by software that rounds off to Rs 0.01, the minimum unit of currency your bank recognises. Where’s my 50 paise?

Here’s the best part: HDFC Bank tacks three additional charges on every foreign currency transaction: a currency conversion charge, service tax on the currency conversion, and education cess on the service tax (tip of the hat to our world-famous Indian bureaucracy). What’s your guess on what these charges are going to be for the $0.01 transaction? Remember, they’ve already charged me 100% over the actual payment.

Open source as infrastructure

On the last day of FOSS.in 2009, some of us gathered in the speakers’ hotel to hang on to that sense of wonder for just a bit longer. Ramkumar Ramachandra and I ended up discussing open source philosophy late into the night. Ram’s consolidated his thoughts from that evening into a pair of posts on open source as infrastructure and community and business interaction. Both were posted earlier this month, but I somehow missed them.

My own understanding of the infrastructure angle to open source comes from Doc Searls’s writing around 2001. Doc has a more recent write-up on understanding infrastructure (Apr 2008) that’s well worth reading.

Budget for Dharamsala trip

Deepak asked if he could see some numbers for my sabbatical year. I haven’t sorted out my accounts yet, but for a taste, here’s the budget and actual costs for my trip to Dharamsala in August. I had more time than money at this point, so I went slow and watched every rupee.

Columns with missing values indicate heads I hadn’t budgeted for.

Item Date Per Unit Count Est. Cost Actuals
Total Rs 6697 Rs 4609
Transport to railway station 19/08 120
Train: Bangalore to Delhi 19/08 729 1 729 729
Food on train 19/08 50 4 200 200
Food in Delhi 21/08 221
Train: Delhi to Pathankot 21/08 222 1 222 222
Bus: Pathankot to Dharamsala 22/08 350 1 350 166
Accommodation in Dharamsala 22/08 350 5 1750 450
Food in Dharamsala 22/08 50 15 750 665
Bus: Dharamsala to Delhi 26/08 700 1 700 450
Food in Delhi 27/08 100 2 200 475
Train: Delhi to Bangalore 28/08 1546 1 1546 729
Food on train 28/08 50 5 250 182
Transport from railway station 29/08 20

We were a group of four. The others started from Delhi and had already booked train tickets to Pathankot, so I tagged along, even though a direct bus between Delhi and Dharamsala made more sense. I took a sleeper class train coach from Bangalore to Delhi. I wasn’t sure it would be comfortable—36 hours in a metal box without air-conditioning—and budgeted for a three-tier AC ticket on the way back, but found I liked it and returned the same way.

Accommodation in Delhi was free, courtesy friends. In Mcleodganj, we found a guest house with a good view of the valley for Rs 450, split between two occupants over two nights. I wanted to stay longer but the others had work to return to. I had no expenses other than food and transport.

Concerns with Apple’s business model

When Apple debuted the iTunes Music Store in 2003, I enthusiastically signed up and downloaded music. I had a check card with a US billing address that I made gleeful use of. I loved the store. And yet, something didn’t feel right. It took me a while to articulate what.

I was no longer in the US at that time and my checking account was rapidly depleting. Apple wouldn’t accept an international card. Their licensing terms with the music labels only allowed selling music within the US, they said. Fair enough, but something still nagged.

Apple made (and still does make) excellent computers and iPods, but selling music was a different game. It was no longer a one-time transaction for the hardware, but a regular, sustained interaction for your content fix. And US only. iTunes updates now came thick and fast, but my new Indian billing address was no longer welcome. I could only sit by and watch what I could have had access to. Meanwhile, the rest of the iLife suite and Mac OS X felt ignored while all attention went to iTunes.

I knew what was bothering me then. Apple was seeking a tighter and more direct, long-term relationship with their customers, but in the process ignoring anyone in a market where it was too much effort to set up a relationship. This wasn’t how it was with a Mac. Apple’s computers were severely marked-up in India at the time, but you could get one abroad or pay extra and get it locally. Beyond the barrier of price, you would get the exact same Mac experience as anyone else anywhere in the world. All the software there was for the Mac was available to you too.

This would have been a trivial ’plaint about music licensing, but 2007 rolled around and with it the iPhone, sold locked with a carrier contract, US only. Apple once again not just selling a fantastic device, but making the business deals that ensured a great user experience. Where they had no deals, you got no device.

As of Jan 2010, you still can’t buy music from the iTunes Store or buy an iPhone 3G S in India. Apple can’t work out suitable deals, so you as a customer are irrelevant to them. Meanwhile, you can still buy a Mac at a price that is now nearly the same as in the US, and all the apps you want for it are still available. It seems like Apple will have you as a customer only if (a) they can guarantee the quality of the all-round experience, or (b) are willing to abdicate that responsibility. There’s no middle ground.

And this is the crux of it. As Indians, we’re used to technology that isn’t quite right for us, whether it’s the address book that insists you split your initials into “first name, last name”, the app that wants dates in MM/DD/YYYY format despite your locale settings asking for DD/MM/YYYY, or in general software that is overpriced in US dollars, compelling everyone to use a pirated copy. It isn’t for us, but we use it anyway and step around the quirks. We’re cool with that. Now here’s an entity that essentially says, “this is very cool, but it’s not for you and we don’t know when it will be, so you’re not getting any of it.” That’s plain arrogance.

Apple has spectacularly bungled the iTunes Store and iPhone’s presence in India. Everyone agrees that they are due to launch a tablet later this month that will be more of the same, with the device’s experience tightly bound to content distribution. I bet they will bungle this too in India.

If there’s a weak spot in Apple’s business model for a competitor to take a stab at, this is it. But Nokia, that elephant in the room, has lost its mojo. If only Google regarded Android as anything more than an engineering wet dream…

Being an outsider

Last evening I sat across a physicist and a mathematician and watched them discuss clusterings of Wikipedia editors based on edit behaviour. Snatches of familiar but meaningless phrases hit my ears. Markov chains. Undirected graphs. Distances. Eventually the physicist squealed in delight and said she had won a bet with the mathematician. I nodded. Then they said “computationally expensive” and I took my cue and pointed out that for an extended period of revision history, one could take a given revision and consider that editor’s other edits only within a small window rather than across the entire period. That would cut clutter from the dataset and allow long term analysis. We only need to agree on what the window’s size should be. We could even come up with a way to identify a pair of editors responding to each other, as against working independently to contribute new material or clean up a page.

And thereby having said something intelligent, I sat back and watched their faces again, slipping back into incomprehension. We parted agreeing to keep in touch on the new ideas, but I’m at a loss to tell you exactly what the new ideas are. Their math makes no sense to me, for I’m an outsider: the chap butting his way into a discipline claiming to have some solutions, but with no understanding of the fundamentals.

The previous day I had a most fascinating conversation with one of the presenters at WikiWars, the significance of whose insight was again wasted on me. He talked of Edward Said and Satyajit Ray, of the latter’s biography on Wikipedia, the trouble with too many of the citations referring to a single biographer, and of how that could be understood in the context of Said’s work. He recommends Said’s Culture and Imperialism. I can feel the warmth from a dim bulb glowing somewhere.

He asked about me. I said I’ve spent the last few years in the rural development space. “Fooled around,” is more like it, for I went into the space armed with claims of pioneering web development experience and programming prowess, and found the most intense technical task they had was to install an operating system, open a web browser, point it at a government website, and explain to all parties concerned whose fault it was that the page wasn’t loading. Day-to-day life revolved around the size of the cash float, which investor was willing to fund it, scheduling meetings with the ISP for CEO-to-CEO face-offs on how a screenshot of our bandwidth consumption was insufficient, and visiting the very abrasive government bureaucrat to assure him that I did indeed have top-notch programmers working full time to bring him his daily report. Stick some Python in there to make it all better, will ya?

Which is why when I met the geeky young man working towards a PhD in agriculture, you will understand why I begged him to recommend a book that explained all this. There has to be some intelligence in this chaos, but I’m too much of an outsider to spot it.

I’m a programmer, I keep telling myself. I write code. Good code. Fast code. All these people waving their arms and speaking a strange dialect of English need me because, on the internet, code talks like nothing else. I can sit cluelessly around them, bewildered even, knowing that in the end someone will turn to me and ask if I can help.

Conversations move on. An hour later, at another location, the physicist says she’s working on a doctoral thesis. I say that nearly everyone in my life has a PhD or is working on one. I would have been too, if it wasn’t such a long, circuitous route. How am I going to justify trekking all the way through undergrad at this age just to get to the interesting bits? In academia, I’m the ultimate outsider. I’ve never been through any of their systems, turn up as this chap that no one is quite sure how to engage with, and yet have gained entry to more than one of their circuits and even published papers. The geek hat does carry one far.

The geek hat is also suspected. Bangalore’s ruined by the techies, they wail. I’ve been to endless meetings on problems that wouldn’t exist if they used Firefox instead of Internet Explorer, or something as trivial, except the Mozilla Foundation isn’t making an offer to fund a major e-governance project. I keep my mouth shut. People in the habit of routinely shooting at feet will eventually shoot their own, and then they won’t turn up at the next meeting. Suspicion of techies and the biases behind their ideas carries all the way into the realm of the bizarre. At a music concert one evening, this dear old lady, proud of her daughter who wrote for an advertising supplement, didn’t ask what I did. She didn’t want bad news. She simply said “don’t tell me you’re a techie.” A friend jumped to my defence, pointing to the camera and explaining that I was a photographer. I played along, for revealing that you’re a techie generally tends to make life more expensive in these parts, and I was foraying into yet another new discipline. A few years have passed and I’ve clicked much. Today I no longer wield a camera but still wear the geek hat.

At dinner last, the wikipedian from Taiwan made conversation. He had helped launch a minority language Wikipedia that the official system of language Wikipedias wouldn’t recognise and had successfully lobbied for its inclusion. He wanted to interview me for the wikipedians back home. As a local Wikipedia editor, how did I relate to the English language Wikipedia? But wait, me representing the local editors? With just a hundred odd edits on my account when the local chapter had editors with 50,000+ edits? I made the call to another (real) Wikipedian asking if he was in the neighbourhood. He suggested I go ahead anyway since I was a valid rep.

Later still, the Taiwanese wikipedian asked that fatal question: “So, what do you do?” I responded with the one-liner I reserve for such occasions. “I’m a programmer, I write code.” He pointed at my shirt. “You work for Yahoo?” No, I said, “that’s just a conference t-shirt.” I then attempted a weak explanation of my rural development stint.

The truth is, in the eleven plus years of my working life I’ve never worked at a software house, have never attended a computer class, and have no certifications. I wrote code through the ’90s, code and little else, telling everyone I was going to be a “software developer” when I grew up, and ultimately falling out of the academic system. But when it came to going to work, did I do the expected thing and join a software house? No, sir, I went into print publishing. What one does first sets the template, and this one sure did. I’ve put my foot into all manner of disciplines other than computer science, playing the saviour who produces the code, but bearing no certifications. I could afford it because I had put in my 10,000 hours already. After that much exposure, learning becomes automatic and incremental. I haven’t looked at a technology guide book in over a decade because I don’t need to. The book on my bedside today is on law. The one below it on film studies.

An increasingly ragged hat

My expeditions into new disciplines have gotten deeper and longer over the years, but they’ve also taken me farther away from the primary identity I’ve defined for myself. The last major piece of code I wrote was in 2002. Everything since has been relatively minor scripting. My open source code contribution track record is astonishingly sparse. I’ve gained proficiency at just one new programming language in the ’00s, down from five in the ’90s. I regularly encounter bewildering new technical constructs these days. It’s bad enough to feel like retirement.

I’m slowly, but surely, being ejected from the one discipline I considered myself an insider at. What’s one to do?

I suppose this is the part where life gets really interesting.

A year in recap

Long time, no post. So much to say, but where’s the time to write with all this activity? Remind me to post on:

  • What it cost me to take a year off,
  • What I’ve been reading through these months,
  • What I did with the time and how I ended up doing each, and
  • What I’m up to now, back here in the land of the gainfully employed.
View from my new office window
The view from my new office window.

Netbook theme for Ubuntu

Upgraded to Karmic last night. The refresh of the Human theme is quite nice, but the bright orange icons no longer work, so I made a quick remix. Download:

Both versions are designed for 1024×600 netbook screens. For best results, you should also install maximus and window-picker-applet, and setup a single panel at top containing the applet.

Installation

Go to SystemPreferencesAppearance and install from there, or better, extract the tarball to /usr/share/themes as root. The latter will get it to work for system applications too.