A better web through spreadsheets

I’ve spent the past couple of days basically living in spreadsheets, crunching sales data, entering equations, building pivot tables and forecasts, and painstakingly toggling cell borders… Your typical spreadsheety stuff. (And I didn’t go crazy at all.)

While spreadsheet work is a task an engineer would often dismiss, loathe, and try to pawn off onto an intern or manager, I’ve come to realize the opportunity we as an industry have missed.

 

A world on a web

The World Wide Web has been a part of most people’s lives for a couple of decades now. It has transformed society, and we take it for granted today. Before the web, communication wasn’t quite so pleasant. We had to visit our friends in person if we wanted to talk or play a game together. The events of a wild party stayed mostly in the minds of the participants, and couldn’t easily be shared with millions of people around the world. We didn’t even know that cats and cheeseburgers went so well together.

That's not what I meant!

That’s not what I meant!

It was the dark ages, and frankly, we should be embarrassed to even talk about it.

Then a wonderful man named Sir Tim Berners-Lee created the Web. There were probably other people involved, but it doesn’t matter really. The point is, he did a pretty great job and we should all buy him a drink if he’s in town.

Let me briefly explain how the web works on a technical level, using a common analogy of computers as tactical submarines. Imagine you’re in a US submarine (your computer) and you want to get some cat pictures from the guy in the Russian submarine (a Russian server). You know where in the sub he is (a “URL”), and know that the only way to get to him is through an unsecured port (we call this a “HTTP port”) or a mostly-secured-but-sometimes-not port (“HTTPS port”).

You’d load a torpedo with a letter asking for cat pictures (these are “packets”) and fire it off through their port (“HTTP/HTTPS”) into the location of the guy with the pictures (“URL”). Being trained to handle this, the torpedo would be intercepted, a new one stuffed with cat pictures, and fired back at your submarine.

This is the primary use of the web. Not so much torpedoes.

This is the primary use of the web. Not so much torpedoes.

That’s… basically how the web works.

Oh and there’s also HTML. This is the universal language of web pages. It comes with a family of other technologies, like CSS, JavaScript, VBScript, Dart, Silverlight, Flash, Adobe Flex, Java, ActiveX, and a myriad of innovative plugins.

Where was I? .. Oh yeah, spreadsheets.

(Spreadsheets are more like Battleship. A5! B12!)

 

The missed opportunity

We have built the world’s communication, social interaction, and repositories of cat pictures on top of the web, and therefore HTML (and co).

What I’ve realized over the past two days is that building it on top of HTML was a mistake. We should have built it on top of spreadsheets.

We could have had this!

We could have had this!

Hear me out.

Spreadsheets have been around a long time, and unlike HTML/CSS/JavaScript, people just naturally understand them. They’re simple, intuitive, and fun!

In the dark times before tab-based browsing, a time when browser manufacturers thought window management should most resemble the winning animation of Solitaire, Spreadsheets had multiple tabs. The right technology coud have put us years ahead of where we are now.

As developers, we face religious wars over table-based layouts vs. non-table-based layouts. We waste thousands of man years on this. Spreadsheets, being nothing but table-based, would have saved us all a whole lot of trouble.

It took a long time for the world to realize JavaScript could be used for more than scrolling status bar updates and trailing mouse cursors; it could be used to write useful things, like Facebook and Twitter! All the while, spreadsheet power users were writing complicated macros to do anything they could ever want. I mean, look at this guy who wrote a freaking RPG in Excel!

Look at those graphics.

Look at those graphics. Look at them.

Spreadsheets are inherently social. You can save them, edit them, pass them out to your friends. You can’t do that with your Facebook wall. Ever try to save or edit someone else’s webpage? Yeah, I bet that worked out great for you.

Developers, how many different third-party APIs are you dealing with in order to generate some meaningful statistics and reports for your app/startup? How much money are you paying to generate those reports? How much code did you have to write to tie any of this together? In the spreadsheet world, you’d just stick some pivot tables and graphs on the page and call it a day, spend some time with your family.

None of this nonsense with disagreements between slow-moving standards bodies that keep going back-and-forth on everything. Instead, I think we’d all feel comforted knowing we could leave this all in the hands of Microsoft.

 

What can you do…

I know, I know. It seems so obvious in retrospect. I guess all I can say on their behalf is that the web was once a new, experimental project, and such things are rarely perfect. Even my projects have some flaws.

Sir Tim, call me. We’ll get this right next time.

Remembering the Line Ride

I spent the holidays at Disneyland this year with my girlfriend and my family. We stood in numerous lines for hours on end during the busiest week of the year, waiting to see Disney’s take on classic rides such as the Haunted Mansion and Small World.

Their take was fantastic, but this post is not about that.

Standing in line for the Haunted Mansion, listening to people murmur about how agonizing the lines were, it dawned on me that not everybody understood nor appreciated the true origins of these amazing amusement parks. My sister certainly didn’t know, and neither did my girlfriend.

You may not either, so allow me to share a bit of history.

Back to the middle ages

Much of what we’ve come to enjoy in amusement parks originated from fairs in the Middle Ages [1]. The food, the shows. They were further inspired over time by other events and inventions throughout the centuries that followed. One of the innovations in amusement technology that really sparked the modern era of amusement park rides was a classic mechanical ride, the steam-powered carousel, built by Thomas Bradshaw at the Alysham Fair in 1861 [2].

The problem with technological innovations is that they overshadow the simpler pleasures that came before them.

The Line Ride

Long before the carousel, in 1733, people enjoyed a simpler tradition. The humble fairgrounds in those days were unlike the marvels we have today, but were still full of events for children and adults of all ages.

One of the most beloved traditions in those days was known as the Rope Line Ride, or the Line Ride for short. Long lines of rope, attached to tall stakes in the ground, would be laid out in all sorts of patterns, forming paths for the kids to traverse. Common patterns included the spiral, the back-and-forth, and the weave.

Participating in the Line Ride was simple. A person would start at one end, following the line, seeing where it took them (by a garden, perhaps, or a wall of funny drawings), eventually coming out on the other side.

Remember, these were the days when Kick the Can and Hoop Rolling were the rage. The Line Ride was so popular that it was often nearly full of people, but this gave them time to socialize and join together in the admiration of their surroundings.

Evolution of the Line Ride

Times change, as they often do. While once a fun and common attraction, the younger generations began to grow weary of the Line Ride. In 1861, Thomas Bradshaw, the aforementioned inventor of the steam-powered carousel, forever changed the Line Ride by making it a means to an end. He put the carousel at the very end of the Alysham Fair Line Ride.

Now, instead of simply enjoying the Line Ride for what it was, people were passing through it, with great impatience, just to get to the all-new steam-powered carousel.

A new tradition was born. The Line Ride no longer became an attraction itself, but rather simply the Line, a way to control the flow of people leading up to an attraction. This was seen as a very controversial change in its day — after-all, the Line Ride was a tradition going back over a hundred years — and with it came a distrust of the newer attractions by the older generations. Of course, time passed, and the Line became the norm.

The spirit carries on

While often forgotten as an attraction, the Line Ride’s spirit remains today in our terminology and our parks. We’re all familiar with celebrities walking down the rope line, or hearing about people “working the rope line.”

And, of course, the long, grueling lines leading up to the popular attractions at amusement parks and carnivals around the world.

Using Freshdesk with PagerDuty for Better Customer Support

At Beanbag, we’ve been using Freshdesk to handle customer support for Review Board, Power Pack, and RBCommons.

We’ve also been using PagerDuty to inform us on any critical events, such as servers going down, memory/CPU load, or security updates we need to apply to our servers.

Our customers’ problems are just as important to us as our servers’ problems, but we were lacking a great way to really get our attention when our customers needed it most. After we started using PagerDuty, the solution became obvious: Integrate PagerDuty with Freshdesk! But how? Neither side had any native integration with the other.

Enter Freshdesk webhooks

Freshdesk’s webhooks support is pretty awesome. Not only can you set them up for any custom condition you like, but payload they send is completely customizable, allowing you to easily construct an API request to another service – like PagerDuty!

This is super useful. It means you won’t need any sort of proxy service or custom script to be set up on your server. All you need are your Freshdesk and PagerDuty accounts.

Deciding on your setup

There are probably many ways you can configure these two to talk, and we played with a couple configurations. Here are the general rules we settled on:

  • Only integrate PagerDuty for paying customers (with whom we have support contracts). We don’t want alerts from random people e-mailing us.
  • When a paying customer open new tickets or reply to existing tickets, assign them to a “Premium Support” group, and create an alert in PagerDuty.
  • When an agent replies or marks a ticket in “Premium Support” as resolved, resolve the alert in PagerDuty.

We always resolve the alert instead of acknowledging it, in order to prevent PagerDuty from auto-unacknowledging a period of time after the agent replies. When the customer replies, it will just reuse the same alert ID, instead of creating new alerts.

Also note that we’re setting things up to alert all of our support staff (all two of us founders) on any important tickets. You may wish to adjust these rules to do something a bit more fine-grained.

Okay, let’s get this set up.

Setting up PagerDuty

We’re going to create a custom Service in PagerDuty. First, log into PagerDuty and click “Escalation Policies” at the top. Then click the New Escalation Policy button.

Name this policy something like “Premium Support Tickets,” and assign your agents.

Next, click “Services” at the top. Then click the Add New Service button and set these fields:

title

Click Add Service. Make a note of the Service API Key. You’ll need this for later.

Edit your service and Uncheck the Incident Ack Timeout and Incident Auto-Resolution checkboxes. Click Save.

Optionally, configure some webhooks to point to other services you want to notify. For instance, we added Slack, so that we’ll instantly see any support requests in-chat.

Okay, you’re done here. Let’s move on to Freshdesk.

Setting up Freshdesk

Freshdesk is going to require four rules: One Dispatch’r and three Observers.

I’ll provide screenshots on this as a reference, along with some code and URLs you can copy/paste.

Start by logging in and going to the Administration page.

Dispatch’r Rule: Alert PagerDuty for important customer tickets

Click “Dispatch’r” and add a new rule.

Dispatchr rule

Set the Rule name and Description to whatever you like. We added a little reminder in the description saying that this must be updated as we add customers.

For the conditions, we’re matching based on company names we’ve created in Freshdesk for our customers. You may instead want to base this on Product, From E-mail, Contact Name, or whatever you like.

For the Callback URL, use https://events.pagerduty.com/generic/2010-04-15/create_event.json. Keep note of this, because you’ll use it for all the payloads you’ll set.

Now set the Content to be the following.

{
    "service_key": "YOUR SECRET KEY GOES HERE",
    "event_type": "trigger",
    "description": "Ticket ID {{ticket.id}} from {{ticket.requester.company_name}}: {{ticket.subject}}",
    "incident_key": "freshdesk_ticket_{{ticket.id}}",
    "client": "Freshdesk",
    "client_url": "{{ticket.url}}",
    "details": {
        "ticket ID": "{{ticket.id}}",
        "status": "{{ticket.status}}",
        "priority": "{{ticket.priority}}",
        "type": "{{ticket.ticket_type}}",
        "due by": "{{ticket.due_by_time}}",
        "requester": "{{ticket.requester.name}}",
        "requester e-mail": "{{ticket.from_email}}"
    }
}

Make note of the whole YOUR SERVICE KEY GOES HERE part in line 2. Remember the service key in PagerDuty? You’ll set that here. You’ll also need to do this for all the webhook payloads I show you from here on out.

Go ahead and add any other actions you may want (such as adding watchers, or setting the priority), and click Save.

Click Reorder and place that rule at the top.

Setting up Freshdesk Observer

Now we need to set up a few observers. Go back to the Administration page and click “Observer.” We’ll be adding three new rules.

Observer Rule #1: Resolve PagerDuty alerts on close

Add an event. You’ll set:

Resolve on Close rule

Use the same Callback URL as earlier, and set the Content to:

{
    "service_key": "YOUR SERVICE KEY GOES HERE",
    "event_type": "resolve",
    "description": "{{ticket.agent.name}} resolved ticket {{ticket.id}}: {{ticket.subject}}",
    "incident_key": "freshdesk_ticket_{{ticket.id}}",
    "details": {
        "ticket ID": "{{ticket.id}}",
        "status": "{{ticket.status}}",
        "priority": "{{ticket.priority}}",
        "type": "{{ticket.ticket_type}}",
        "due by": "{{ticket.due_by_time}}",
        "requester": "{{ticket.requester.name}}",
        "requester e-mail": "{{ticket.from_email}}"
    }
}

Don’t forget that service key!

Observer Rule #2: Resolve PagerDuty alerts on agent reply

Let’s add a new event. This one will resovle your PagerDuty alert when an agent replies to it.

Resolve on Reply rule

Again, same Callback URL, with this Content (and your service key):

{
    "service_key": "YOUR SERVICE KEY GOES HERE",
    "event_type": "resolve",
    "description": "{{ticket.agent.name}} acknowledged ticket {{ticket.id}}: {{ticket.subject}}",
    "incident_key": "freshdesk_ticket_{{ticket.id}}",
    "details": {
        "ticket ID": "{{ticket.id}}",
        "status": "{{ticket.status}}",
        "priority": "{{ticket.priority}}",
        "type": "{{ticket.ticket_type}}",
        "due by": "{{ticket.due_by_time}}",
        "requester": "{{ticket.requester.name}}",
        "requester e-mail": "{{ticket.from_email}}"
    }
}

Observer Rule #3: Alert PagerDuty on customer reply

title

Here’s your Content:

{
    "service_key": "YOUR SERVICE KEY GOES HERE",
    "event_type": "trigger",
    "description": "{{ticket.agent.name}} acknowledged ticket {{ticket.id}}: {{ticket.subject}}",
    "incident_key": "freshdesk_ticket_{{ticket.id}}",
    "details": {
        "ticket ID": "{{ticket.id}}",
        "status": "{{ticket.status}}",
        "priority": "{{ticket.priority}}",
        "type": "{{ticket.ticket_type}}",
        "due by": "{{ticket.due_by_time}}",
        "requester": "{{ticket.requester.name}}",
        "requester e-mail": "{{ticket.from_email}}"
    }
}

Done!

You should now be set. Any incoming tickets that match the conditions you set in the Dispatch’r rule will be tracked by PagerDuty.

Now you have no excuse for missing those important support tickets! And your customers will thank you for it.

Breaking back into your network with the Synology Web UI

Have you ever left town, or even just took a trip to the coffee shop, only to find that you’re locked out of your home network? Maybe you needed a file that you forgot to put in Dropbox, or felt paranoid and wanted to check on your security cameras, or you just wanted to stream music. I have…

The end of a long drive

Last night, I arrived at my hotel after a 4 hour drive only to find my VPN wasn’t working. I always VPN in to home, so that I can access my file server, my VMs, security cameras, what have you. I didn’t understand.. I was sure I had things set up right. You see, I recently had my Xfinity router replaced, and had to set it up to talk to my Asus N66U, but I was absolutely sure it was working. Almost sure. Well, I thought it was working…

So I tried SSHing in. No dice. Hmm.. Any web server ports I exposed? Guess not. Maybe port forwarding was messed up somewhere?

Ah HA! I could reach my wonderful Synology NAS’s web UI. If you haven’t used this thing, it’s like a full-on desktop environment with apps. It’s amazing. Only thing it’s really missing is a web browser for accessing the home network (get on this, guys!). After spending some time thinking about it, I devised a solution to get me back into my home network, with full VPN access (though, see the end of the story for what happened there).

Christian’s step-by-step guide to breaking in with Synology

No more stories for now.

To get started, I’m assuming you have three things:

  1. Remote access (with admin rights) to your Synology NAS’s web console.
  2. A Linux server somewhere both sides can log into remotely (other than your local machine, as I’m assuming yours isn’t publicly connected to the network).
  3. A local Linux or Mac with a web browser and ssh. You can make this work on Windows with Putty as well, but I’m not going into details on that. Just figure out SSH tunneling and replace step 7 below.

All set? Here’s what you do.

  1. Log into your NAS and go to Package Center. Click Settings -> Package Sources and add:
  2. Name: MissileHugger
    Location: http://packages.missilehugger.com/
  3. Install the “Web Console” package and run it from the start menu.
  4. Web Console doesn’t support interactive sessions with commands, so you’ll need to have some SSH key set up on your linux server’s authorized_keys, and have that key available to you. There’s also no multi-line paste, so you’ll need to copy this key through Web Console line-by-line:

    Locally:

    $ cat ~/.ssh/id_dsa

    On Web Console:

    $ echo "-----BEGIN DSA PRIVATE KEY-----" > id_dsa
    $ echo "<first line of private key>" >> id_dsa
    $ echo "<second line of private key>" >> id_dsa
    $ ...
    $ echo "-----END DSA PRIVATE KEY-----" >> id_dsa
    $ chmod 600 id_dsa
  5. Establish a reverse tunnel to your Linux box, pointing to the web server you’re trying to reach (we’ll say 192.168.1.1 for your router).

    Remember that Web Console doesn’t support interactive sessions, or pseudo-terminal allocation, so we’ll need to tweak some stuff when calling ssh:

    $ ssh -o 'StrictHostKeyChecking no' -t -t -i id_dsa \
          -R 19980:192.168.1.1:80 youruser@yourlinuxserver

    The ‘StrictHostKeyChecking no’ is to get around not having any way to verify a host key from Web Console, and the two -t parameters (yes, two) forces TTY allocation regardless of the shell.

  6. If all went well, your Linux server should locally have a port 19980 that reaches your web server. Verify this by logging in and typing:
    $ lynx http://localhost:19980
  7. On your local machine, set up a tunnel to connect port 19980 on your machine to port 19980 on your Linux server.
    $ ssh -L 19980:yourlinuxserver:19980 youruser@yourlinuxserver
  8. You should now be able to reach your router. Try it! Open your favorite browser and go to http://localhost:19980
  9. Clean up. Delete your id_dsa you painfully hand-copied over, if you no longer need it, and kill your SSH sessions.

Epilogue

While this worked great, and I was able to get back in and see my router configuration, I wasn’t able to spot any problems.

That’s when I realized my Mac’s VPN configuration was hard-coding my old IP address and not the domain for my home network. Oops :(

Hope this helps someone!

A new adventure begins

Act 1, Scene 1

August 23rd, 2004. A young kid, not even 21, freshly dropped out of college, passionate about open source and programming. He walks into his new office at his new job at VMware, his first job, ready to start the day, eager to impress and meet his new co-workers.

Nobody was there. Thumbs twiddled.

10AM starts to roll around, and finally, the first sign of life. Over the next couple hours, more people show up.

Over the next week, he’s set up and learning the ropes. Working on his first bug, soon his first feature. Attending his first team get-togethers. Making his first Bay Area friends.

Over the next few months, his first birthday celebration at work. His first glass of champagne. His first real responsibilities.

Over the next few years, bigger roles, leadership roles. He began to get a feel for where he’s truly going in this silly little world.

This, of course, was me, on my first adventure in the tech industry.

I was lucky to be placed in a fantastic team full of smart, hard-working, dedicated, and fun software engineers and managers. We’d discuss architecture, brainstorm ideas, joke around, watch YouTube videos, play poker, watch movies, go to events. The web of awesome people extended throughout the company as well.

Over the past nine years, I worked on a great many things.

  • Eight releases of VMware Workstation, including a three-year effort to build Workstation 8.0 (a major undertaking).
  • VMware Server 1.0. I was the primary Linux developer, pulling caffeine-fueled all nighters to meet insane deadlines.
  • Player and VMRC, which powers the VM console for our enterprise products.
  • The core foundation used in Fusion and other products.
  • Icons and artwork for the Linux products.
  • I introduced Unity to Workstation. (Sorry, guys…)
  • Helped in the creation of the current generation of the View client for Linux.
  • More recently, I developed WSX, an experiment in developing a pure web client and console for accessing remote VMs anywhere, from desktops and tablets.

Not a bad run.

This Thursday, August 1st, 2013, I’ll be leaving VMware.

Revision 1: “Add the reviewboard”

Several years ago, I began working with my good friend David Trowbridge on an open source project for keeping track of patches and easing the review process. We spent many years in the open source world looking at raw diffs on bug trackers and in e-mails, and things weren’t that much better at VMware. As Mr. Wonderful says, “There has to be a better way!”

So we slaved away in the late nights and weekends, iterating and iterating until we had something we could use. We named this product “Review Board” (or “the reviewboard,” as our first commit says). We put it out there for people to play with, if anyone was interested.

There was interest. Review Board is now used around the world at companies big and small. We’ve continued to improve and grow the product and turn it into something that developers actually want to use.

We later built a startup around this. Beanbag.

It’s dangerous to go alone. Take this.

Earlier this year, we met a local entrepreneur as part of a program we participate in. We quickly developed a rapport, and he offered to help and advise us in our efforts to grow our business. It wasn’t long after that we started discussing funding, and where that could get us.

We started pitching, and he reached out to his contacts. Before long, we had what we needed to give this a try for a couple years.

Step 3: Profit?

There’s a lot of hard work ahead of us, but we’re up to the challenge. It’s both exciting and terrifying.

Leaving my team behind at VMware is hard, but everyone has been so supportive.

IMG_0720

Basically.

In the coming months, Review Board’s going to grow in exciting new ways. We’ll be gearing up for a new 1.8 release, releasing our first commercial extension to Review Board, and improving our SaaS, RBCommons. We have a pretty good idea where we want to go from here, and now we can better focus on making it happen.

It’s going to be an awesome adventure.

WSX 1.1 beta is released, with bookmarkable VMs!

WSX

I’ve been pretty quiet on the WSX front since the release of WSX 1.0. A lot of work has been put into taking this from a prototype to something more solid, more functional.

Yesterday, we released a beta of WSX 1.1, which takes a big step in that direction, improving the reliability and access to your VMs, with a couple new features. Let’s go through them!

 

Bookmarkable VMs

To get to your favorite VM before, you’d have to connect to WSX and navigate to it every time, which was.. kind of a pain. No more, I say! Each VM now has its own URL, and that URL is bookmarkable. Place a bookmark in your browser’s toolbar for quick access, or bookmark to the home screen on your iPad.

Sure, you’ll have to log in if it’s been a while, but you won’t have to navigate all the way to your VM every time. As for the annoyance of constantly logging in to your servers…

 

Persistent Server Connections

Every new tab or reload disconnected all your server sessions before, due to how we mapped a browser’s connection to a server’s connection. That’s been made a lot smarter in 1.1. Now, once you connect, you can open as many other tabs/windows to WSX as you want and they’ll share your server sessions. You can even close all your tabs, and so long as you open WSX again within 5 minutes, you won’t have to log in again.

That means you can log in to a server and open each VM you want to work with in their own tabs without logging in more than once. Cool, right? Really handy for bookmarkable VMs.

 

Other Enhancements

Those were the two big features, but there were lots of other enhancements and fixes. In general:

  • New icon!
  • Various cursor and key fixes for Internet Explorer
  • Faster graphics performance
  • Key repeat now works
  • Caps Lock improvements

 

Get it while it’s hot!

Weird bugs: Django, timezones, and importing from eggs

Every so often you hit a bug that makes you question your sanity. The past several days have been spent chasing one of the more confusing ones I’ve seen in a long time.

Review Board 1.7 added the ability to set the server-wide timezone. During development, we found problems using SSH with a non-default timezone. This only happened when updating os.environ[‘TZ’] to something other than our default of UTC. We’d see the SSH process (rbssh, our wrapper for SSH communication) break due to an EOF on stdin and stdout, and then we’d see the development server reload itself.

Odd.

Since this originated with a Subversion repository, I first suspected libsvn. I spent some time going through their code to see if a timezone update would break something. Perhaps timeout logic. That didn’t turn up anything interesting, but I couldn’t rule it out.

Other candidates for suspicion were rbssh itself, paramiko (the SSH library), Django, and the trickster god Loki. We just had too many moving pieces to know for sure.

So I wrote a little script to get in-between a calling process and another process and log all communication between them. I tested this with rbssh and with plain ol’ ssh. rbssh was the only one that broke. Strange, since it wasn’t doing anything obviously wrong, and it worked with the default timezone. Unless it was Paramiko somehow…

For the heck of it, I tried copying some of rbssh’s imports into this new script. Ah-ha! It dropped its streams when importing Paramiko, same as rbssh. Interesting. Time to dig into that code.

The base paramiko module imports a couple dozen other modules, so I started by narrowing it down and reducing imports until I found the common one that breaks things. Well that turned out to be a module that imported Crypto.Random. Replacing the paramiko import in my wrapper with Crypto.Random verified that that was the culprit.

Getting closer…

I rinsed and repeated with Crypto.Random, digging through the code and seeing what could have broken. Hmm, that code’s pretty straight-forward, but there are some native libraries in there. Well, all this is in a .egg file (not an extracted .egg directory), making it hard to look through, so I extracted it and replaced it with a .egg directory.

Woah! The problem went away!

I glance at the clock. 3AM. I’m not sure I can trust what I’m seeing anymore. Crypto.Random breaks rbssh, but only when installed as a .egg file and not a .egg directory. That made no sense, but I figured I’d deal with it in the morning.

My dreams that night were filled with people wearing “stdin” and “stdout” labels on their foreheads, not at all getting along.

Today, I considered just ripping out timezone support. I didn’t know what else to do. Though, since I’m apparently a bit of a masochist, I decided to look into this just a little bit more. And finally struck gold.

With my Django development server running, I opened up a separate, plain Python shell. In it, I typed “import Crypto.Random”. And suddenly saw my development server reload.

How could that happen, I wondered. I tried it again. Same result. And then… lightbulb!

Django reloads the dev server when modules change. Crypto is a self-contained .egg file with native files that must be extracted and added to the module path. Causing Django to reload. Causing it to drop the spawned rbssh process. Causing the streams to disconnect. Ah-ha. This had to be it.

One last piece of the puzzle. The timezone change.

I quickly located their autoreload code and pulled it up. Yep, it’s comparing modified timestamps. We have two processes with two different ideas of what the current timezone is (one UTC, one US/Pacific, in my case), meaning when rbssh launched and imported Crypto, we’d get a bunch of files extracted with US/Pacific-based timestamps and not UTC, triggering the autoreload.

Now that the world makes sense again, I can finally fix the problem!

All told, that was about 4 or 5 days of debugging. Certainly not the longest debugging session I’ve had, but easily one of the more confusing ones in a while. Yet in the end, it’s almost obvious.