Notes on a New World Order

Would you like fries with your Universe?

Content

Content Overhaul
2009-08-06 17:50

This site has undergone some major overhauling to get to the (new) current state. The code is now completely custom, using the filesystem as a content backend (so I can use Mercurial to version content).

The design is simpler, and more focused on the content, though I imagine I will be refining some elements over time; though, I certainly couldn't have designed anything like this myself. I give tremendous credit to Rodrigo Galindez for making the design and providing it for free use.

That being said, there are some quirks to the design, especially at screen resolutions smaller than 1024x768 and with WebKit.

Most of the same content that was here is still here, hopefully under the same URLs, though a few are different. Though there are a number of articles that were imported, and imported again, and so on from some time ago that may have broken text or links. I apologize, and will be working through correcting them as I have time. Along with fixing bugs in the code. If you see a 500 error on the site, please email me, it's not something that is someone else's fault, it's my code, I'd like to know when it breaks.

WRT54G and OpenEmbedded
2009-07-24 17:00

Some time ago I bought a cheap `Linksys WRT54G`_ because I aquired a laptop with WiFi. It worked well enough, but being the technologist I am, I soon found out that the device ran an embedded Linux distribution, and that it was possible to replace the firmware with a more capable (if more arcane) variant. I did so, using OpenWRT_. I was quite satisfied with this distro, though wireless was flaky due to requiring the use of a proprietary 2.4 kernel-only Broadcomm driver for the wireless. To make a boring story as short as possible, I'm now using a separate device as an access point and using the WRT54G solely as a router.

Thus, I take my first stab at using a 2.6 kernel on the device, upgrading to a recent build of OpenWRT_. Unfortunately, I don't like what the project has done recently, and want less magic configuration. UCI seems like a nice system, but I just want what I'm used to when configuring a Linux system, a bunch of files in /etc. I don't need a unified syntax, I already know the tools and the configuration syntax for the functionality I need. I don't want to learn yet another specialized language for doing the same damn thing.

Don't get me wrong, I understand the need for UCI, I just don't have the same needs. (Dual-stack IPv6/v4 with firewalling and VPN, DNS and static DHCP leases, to start...) So I'm left with radically modifying the OpenWRT build system, or rolling my own.

So I turned to the OpenEmbedded_ project, which provides a customizable and flexible cross-build system for many targets, including the WRT54G.

Setting up OpenEmbedded

First things first, in order to build an image for the WRT54G, you need the OpenEmbedded_ build environment set up. Follow the `Getting Started`_ guide and come back when you're done.

Then, create a local.conf file with the appropriate values for the WRT54G:

BBFILES = "/stuff/openembedded/recipes/*/*.bb"
DISTRO = "wrt54oe"
MACHINE = "wrt54"

PREFERRED_PROVIDERS += " virtual/${TARGET_PREFIX}gcc-initial:gcc-cross-initial"
PREFERRED_PROVIDERS += " virtual/${TARGET_PREFIX}gcc:gcc-cross"
PREFERRED_PROVIDERS += " virtual/${TARGET_PREFIX}g++:gcc-cross"
PREFERRED_PROVIDERS += " virtual/${TARGET_PREFIX}binutils:binutils-cross"

Just to make sure everything is working, try building task-base:

$ cd /stuff/build/
$ bitbake task-base

This will take some time, so feel free to go do something else for a while.

I had this part fail several times, needing manual downloads, md5 corrections, etc. Your milage may vary.

Docutils System Messages

System Message: ERROR/3 (<string>, line 1); backlink

Unknown target name: "linksys wrt54g".

System Message: ERROR/3 (<string>, line 1); backlink

Unknown target name: "openwrt".

System Message: ERROR/3 (<string>, line 10); backlink

Unknown target name: "openwrt".

System Message: ERROR/3 (<string>, line 23); backlink

Unknown target name: "openembedded".

System Message: ERROR/3 (<string>, line 29); backlink

Unknown target name: "openembedded".

System Message: ERROR/3 (<string>, line 29); backlink

Unknown target name: "getting started".

google-voice-call.py
2009-04-30 17:29

Simple python script that automates the Google Voice web interface to make outgoing calls. Requires httplib2, simplejson, and BeautifulSoup

This script is not tested on Windows but should work, it looks for a file in the users home directory named .gvoice_auth to determine the credentials to use and the default number to use for the 'from' leg of the call. The file is a JSON encoded object with the following attributes:

user
Your username / email address that you use to login to Google Voice
passwd
The password you use to login to Google Voice
default_from : optional
The number to use for your leg of the call if you don't specify a different one.

For example:

{'user':'me@gmail.com',
 'passwd':'lamepasswd',
 'default_from':'+15855551234'}

Then, simply run the script:

$ python google-voice-call.py 5855551235
Call request returned:  {"ok":true,"data":{"code":0}}

Here's the code, though you probably want to grab google-voice-call.py directly, cut and paste can be annoying.

#!/usr/bin/env python
'''
    A ugly script to pretend that Google Voice has an API for calling out.
'''

import sys, os, re
import urllib, httplib2
from os import path
from simplejson import load
from BeautifulSoup import BeautifulSoup

def main():
    if not path.exists(path.join(
                        path.expanduser('~'),
                        '.gvoice_auth')):
        return """
You need to create a config file in your home directory\
called '.gvoice_auth' with your Google Account's credentials.\
"""
    else:
        creds = load(open(path.join(path.expanduser('~'),'.gvoice_auth')))

    if len(sys.argv) == 2:
        fromnum = creds['default_from']
        tonum = sys.argv[1]
    elif len(sys.argv) == 3:
        fromnum = sys.argv[2]
        tonum = sys.argv[1]
    else:
        return "Invalid number of arguments\nUsage: %s <number> from_number]" % sys.argv[0]

    auth_tok = authenticate(creds['user'], creds['passwd'], 'grandcentral')


    r = call(tonum, fromnum, auth_tok)

    print "Call request returned: ", r


def call(number, fromnum, auth):
    data = {
        'outgoingNumber': number,
        'forwardingNumber': fromnum,
        'remember': 0,
        }

    main_uri = "https://www.google.com/voice"
    call_uri = "https://www.google.com/voice/call/connect/"
    headers = { 'Content-Type': 'application/x-www-form-urlencoded',
                'Cookie': 'gv=%s' % auth }

    h = httplib2.Http()

    response, content = h.request(main_uri, 'GET', headers=headers)
    if not response['status'].startswith('2'):
        raise Exception(response['status'] + ': ' + content)

    soup = BeautifulSoup(content)
    _rnr_se = soup.find('input', attrs={'name':'_rnr_se'})

    data['_rnr_se'] = _rnr_se['value']

    response, content = h.request(call_uri, 'POST', body=urllib.urlencode(data),
                                  headers=headers)
    if response['status'].startswith('2'):
        return content
    else:
        raise Exception(response['status'] + ': ' + content)


def authenticate(user, passwd, service):
    '''
        Basic perform ClientLogin method of authenticating a Google Account.

        Returns the Auth=... bit required in the Authorization header.
    '''
    h = httplib2.Http()
    uri = 'https://www.google.com/accounts/ClientLogin'

    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    request = urllib.urlencode({
        'Email': user,
        'Passwd': passwd,
        'service': service,
        })

    response, content = h.request(uri, 'POST', body=request, headers=headers)
    if response['status'] == '200':
        return re.search('Auth=(\S*)', content).group(1).strip()
    else:
        raise Exception(response['status'] + ': ' + content)

if __name__=='__main__':
    sys.exit(main())

UPnP ATSC Live Video Streaming
2009-04-30 16:15

This article will guide you through the process of setting up live video streaming from a DVB (ATSC) capture card in Linux to a DLNA UPnP Media Renderer that supports MPEG-2 Transport Streams (such as a PS3).

What you need

Mediatomb
Mediatomb is a DLNA/UPnP media server for Linux. You'll want to set up mediatomb to play regular files to your MediaRenderer before attempting the configuration in this guide. It helps to know how the javascript import and transcoding configuration in Mediatomb works, but this guide should be adequate as long as you don't need to do anything fancy.
DVBStreamer
This is the tool that directly talks to your DVB capture card and streams the video. This would be all that you need if your MediaRenderer understands how to listen to a UDP stream. The can't do this (or I couldn't figure out how to do this) so we need one more tool...
NetCat
The swiss-army knife of networking, this tool allows us to take the UDP stream that DVBStreamer gets us and pipe it through a FIFO with a simple script.

Configuring Mediatomb

The tricky bit to get streaming working correctly is getting Mediatomb to do the hard work of talking to the DLNA devices — in a language they understand.

You'll want to set up Mediatomb and get it talking to your UPnP media renderer before trying to do this; you can try to do it all at once, but don't say I didn't warn you.

Firstly, you need to use an import script rather than the built-in import provided by mediatomb for scanning files. The XML config for this will look something like the following:

<scripting script-charset="UTF-8">
    <common-script>/usr/share/mediatomb/js/common.js</common-script>
    <playlist-script>/usr/share/mediatomb/js/playlists.js</playlist-script>
    <virtual-layout type="js">
      <import-script>/etc/mediatomb/import.js</import-script>
    </virtual-layout>
</scripting>

This config goes in the "<import>" tag of the "config.xml" file.

You also need to add a map from a file extension to a mimetype, I used ".live" -> "video/x-dvb-stream", but you could use whatever you like, the mimetype doesn't escape mediatomb, it's just used so we can tell mediatomb to "transcode" the files into something else. Add the following to the "<extension-mimetype>" tag:

<map from="live" to="video/x-dvb-stream"/>

Finally, you need to add a transcoding profile for the "video/x-dvb-stream" mimetype to transcode it into a "video/mpeg" stream. The "<transcoding>" tag should look something like the following:

<transcoding enabled="yes">
  <mimetype-profile-mappings>
      <transcode mimetype="video/x-dvb-stream" using="dvb-stream"/>
  </mimetype-profile-mappings>
  <profiles>
      <profile name="dvb-stream" enabled="yes" type="external">
          <mimetype>video/mpeg</mimetype>
          <accept-url>no</accept-url>
          <first-resource>yes</first-resource>
          <hide-original-resource>yes</hide-original-resource>
          <agent command="/path/to/bin/dvbstream-live" arguments="%in %out"/>
          <buffer size="524288" fill-size="262144" chunk-size="1024"/>
      </profile>
  </profiles>
</transcoding>

Don't worry about the "dvbstream-live" script just yet, we need to get DVBStreamer set up first.

The final piece to the puzzle is the "import.js" script. This is where having some knowledge of javascript and what you want to do helps quite a bit. You should copy the default import.js script from wherever it's installed, and then add the following code:

function videoIsLiveChannel(obj) {
    var pattern = /(.*)\.live/;
    var match = pattern.exec(obj.title);

    if( match ) {
        var container_chain = new Array('video', 'television' );

        obj.title = match[1];

        print("Parsed channel info: " + obj.title);
        addCdsObject(obj, createContainerChain(container_chain));
        return true;
    } else {
        return false;
    }
}

You also need to add the following to the top of the "addVideo" function:

if(videoIsLiveChannel(obj)) {
    return;
}

This tells the import script that if the file to be imported is a ".live" file, we want to add it to the "video/television" section of the UPnP media library, sans ".live" ending. Strictly speaking, adding this to the import script is not necessary, but it makes the listings look a bit nicer.

Setting up DVBStreamer

DVBStreamer is a simple tool that reads from a DVB capture card and can stream (and filter) the broadcast to a "udp://" url. To get DVBStreamer up and running you will need to already have a "channels.conf" file, generated by a program like "dvbscan". Then simply run the "setupdvbstreamer" command with the appropriate options to get dvbstreamer working.

Use the DVBStreamer Wiki to look up how to do some basic tests, stream some video to a file, or use mplayer to watch a UDP stream; essentially, you want to verify that DVBStreamer is working correctly and doing what you expect.

To stream using the scripts below you need to run DVBStreamer as a daemon and set to stream to "udp://localhost:12346". Create a file "dvbstreamer.startup" with a single line:

System Message: ERROR/3 (<string>, line 145)

Cannot find pygments lexer for language "none"

.. sourcecode:: none

    setmrl udp://localhost:12346

Start DVBStreamer like this:

dvbstreamer -f dvbstreamer.startup -d

Now all you need to do is get the scripts to do the "transcoding" and create some ".live" files for your television channels.

The "transcoding" script(s)

There are two scripts that are used by the streaming system, one changes the channel in DVBStreamer so the correct video is streamed, the other pushes the stream from the UDP stream to a file (FIFO) which mediatomb provides as an argument.

Here's the first, the channel changing script. It requires python >=2.5 (and <3.0). It expects the path to a ".live" file as it's first (and only) argument, and connects to the DVBStreamer instance (using the default authentication credentials) to select the DVBStreamer service corresponding to the name of the file. e.g. "/home/media/21-1 WXXI-HD.live" would select the "21-1 WXXI-HD" DVBStreamer service.

#!/usr/bin/env python
from __future__ import with_statement

import sys
import socket
from contextlib import closing
from os import path

class DVBStreamerException(Exception):
    pass

def get_response(streamer):
    resp = []
    while True:
        try:
            resp.append(streamer.recv(4096))
        except socket.error, e:
            if resp and e.args[0] == 11: # temp unavail
                break
            elif e.args[0] == 11:
                pass
            else:
                raise
    if not resp:
        raise DVBStreamerException("Connection closed?")
    resp = ''.join(resp).splitlines()
    if  not resp[-1].startswith('DVBStreamer'):
        return resp + get_response(streamer)

    code = resp[-1].rsplit('/', 1)[-1]
    if int(code.split()[0]) != 0:
        raise DVBStreamerException(resp)
    else:
        return resp[:-1]

def dvbstreamer_cmd(cmd, auth=False, adapter=0):
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
        sock.connect(('localhost', 54197+adapter))
        sock.setblocking(0)
        #print "connected, waiting for ready...",
        get_response(sock)
        #print 'got ready: '
        if auth:
            #print 'sending auth...',
            sock.send("auth dvbstreamer control\n")
            get_response(sock)
            #print 'auth success'
        print 'sending cmd: ', cmd.strip()
        sock.send(cmd)
        return get_response(sock)

def main():
    #l = open('/tmp/channel.log', 'w')
    #print >>l, sys.argv
    channel = sys.argv[1]
    if channel.endswith(".live"):
        channel = path.splitext(path.basename(channel))[0]
    #print >>l, channel
    dvbstreamer_cmd("select %s\n" % channel, auth=True)

    #l.close()

if __name__=='__main__':
    main()

Here's the (much simpler) script that calls the above script and hands off to netcat to push the stream to the FIFO.

#!/bin/bash
/path/to/dvbstream-channel "$1"
exec nc -l -u -p 12346 localhost > "$2"

Put these two scripts someplace you can remember and ensure they can be executed by the user running mediatomb.

Wrap-Up

That should be all there is to it, you can download a tarball containing the scripts and config snippets in the article to make life easier.

Random Panic Linux Kernel Module
2009-04-20 00:00

This kernel module, entirely useless, and probably not even very funny, is meant to torment people you don't like. And who let you have root access anyway.

As the description states, when loaded it will set a timer for a random amount of time into the future, 0-65535 seconds, to call panic() with a randomly selected message (grep'ed from the kernel source tree, see panic_msgs.h)

This really should serve to frustrate and annoy anyone attempting to use the machine if the module were loaded automatically on boot. Don't do it without expecting a nosebleed when they find out it was you.

The source can be downloaded from the mercurial repository or you can download a tarball.

Not much to it, type make and it'll compile. insmod it and it'll do it's thing.

risk-calculator.py
2008-11-12 18:22

[Game of World Domination] battle probability calculator. A python script to calculate the success of conquering a territory.

Download

#!/usr/bin/env python

from __future__ import division
import sys

def cross(*args):
    if not args:
        yield []
        return
    for i in args[0]:
        for n in cross(*(args[1:])):
            yield tuple([i] + list(n))

# http://linux.derkeiler.com/Mailing-Lists/Fedora/2005-11/4432.html
def combinations_r( list_of_items, number_of_picks ):
    k = number_of_picks
    n = len(list_of_items)
    if k==0 or n==0:
        return
    b = [0]*k # make list [0,0,0,....,0] - of length k
    bdone = [0]*k
    while True:
        choice = [list_of_items[i] for i in b]
        yield tuple(choice)
        # Modify b in place (an addition operation in base-n)
        for i in range(k):
            b[i] += 1
            if b[i] < n:
                break # break our of for loop, no need to carry
            b[i] = 0 # carry
        if b == bdone:
            break # break out of while loop, no more choices left
    return

RANGE = range(1,7)

DIE_PROB = 1/max(RANGE)

SINGLE_DIE = list((x,) for x in RANGE)
TWO_DICE = list(combinations_r(RANGE, 2))
THREE_DICE = list(combinations_r(RANGE, 3))

dice_rolls = {
    1: SINGLE_DIE,
    2: TWO_DICE,
    3: THREE_DICE,
}

def risk_wins(attack, defense):
    at_victory = 0
    de_victory = 0
    for at,de in zip( sorted(attack, lambda x, y: y-x),
                      sorted(defense, lambda x, y: y-x) ):
        if at > de:
            at_victory += 1
        else:
            de_victory += 1
    return at_victory, de_victory


def make_probability(nattack, ndefense):
    attacker = dice_rolls[nattack]
    defender = dice_rolls[ndefense]

    probs = {
        (2, 0): 0,
        (1, 0): 0,
        (1, 1): 0,
        (0, 1): 0,
        (0, 2): 0,
    }
    values = list(cross(attacker, defender))
    for at_roll, de_roll in values:
        at, de = risk_wins(at_roll, de_roll)
        probs[(at,de)] += 1

    l = len(values)
    for k in probs:
        probs[k] /= l

    return probs

def calculate_round(nattack, ndefense):
    probs = make_probability(nattack, ndefense)
    diffs = {}
    for k, v in probs.items():
        at, de = k
        diffs[(-de, -at)] = v
    return diffs

def decision_sum(decision):
    ats, des = 0, 0
    cum_prob = 1.0
    for (at, de), prob in decision:
        ats += at
        des += de
        cum_prob *= prob
    return ats, des, cum_prob

def make_decisions(nattack, ndefense, iterations):
    decisions = []
    from pprint import pprint
    for i in range(iterations):
        if not decisions:
            decisions = [ [(o, p)] for o, p in calculate_round(min(nattack-1, 3), min(ndefense, 2)).items() if p != 0.0]
            continue
        for decision in decisions[:]:
            at, de, _ = decision_sum(decision)
            try:
                round = calculate_round(min(nattack+at-1, 3), min(ndefense+de, 2))
            except (KeyError, AssertionError):
                continue
            for outcome, prob in round.items():
                if prob == 0.0:
                    continue
                try:
                    decisions.remove(decision)
                except:
                    pass
                decisions.append(decision + [(outcome,prob)])
    return decisions

def calculate_outcomes(nattack, ndefense, iterations):
    decisions = make_decisions(nattack, ndefense, iterations)
    results = []
    for decision in decisions:
        ats, des, cum_prob = decision_sum(decision)
        if cum_prob == 0.0:
            continue
        results.append( (nattack+ats, ndefense+des, cum_prob) )
    assert str(sum(prob for na, nd, prob in results)) == "1.0"
    return results

def print_outcomes(results, n):
    winp = 0.0
    losep = 0.0
    draws = {}
    for nat, nde, prob in results:
        if nat == 1:
            losep += prob
        elif nde == 0:
            winp += prob
        else:
            if (nat, nde) not in draws:
                draws[(nat,nde)] = 0
            draws[(nat,nde)] += prob
    draws = [ (nat, nde, prob) for (nat, nde), prob in draws.items() ]

    print "Chance of victory in %d battles:               %3.2f%%" % (n, winp*100)
    print "Chance of defeat (1 remaining) in %d battles:  %3.2f%%" % (n, losep*100)
    for at, de, prob in draws:
        print "%3.2f%% chance of:" % (prob*100)
        print "\tAttacker remaining: %d" % at
        print "\tDefender remaining: %d" % de

def print_result(probs):
    for result, prob in probs.items():
        if prob == 0.0:
            continue
        at, de = result
        print "Attacker Loses: %d, Defender Loses: %d (probability %1.2f)" % (de, at, prob)

def testmain():
    for at, de in cross(range(1,4),range(1,3)):
        print "------------------------------------------------------------------------"
        print "  Attacker: %d dice        Defender: %d dice" % (at, de)
        print "------------------------------------------------------------------------"
        print_result(make_probability(at, de))

def main():
    at, de, n = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])
    print "Attacking Units: %d\t\tDefending Units: %d\t\t# of Battles: %d" % (at, de, n)
    print_outcomes(calculate_outcomes(at, de, n), n)

if __name__=='__main__':
    sys.exit(main())

On Open Minds and the Value of Information
2008-10-29 00:00

I came upon a thought recently, while wandering through my brain, and I feel like I should write it down to clarify and crystallize it. It's not fully formed, and is just a notion; I have no evidence to back it up.

Phrased as a question, it looks something like this:

Do skeptics value information less than the more open-minded?

To elaborate, I wonder if the nature of skepticism is to create a value judgment about information in general — that information is, by itself, worth very little, any one piece of information is only of trivial value. Then, I posit, it might be the case that the open-minded place a higher value on information; any piece of information is valuable and important in isolation. The open-minded put a value on knowing, regardless of what is known.

I ask these questions, and wonder about these things, primarily because it seems to me skeptics reject information too easily. I'm not saying that skeptics should reject information that is incorrect, or wrong, or <insert-reason-here>; It seems to me the default assumption of skeptics (that assertions are false) leads to the undesirable quality that skeptics say, "Prove it to me," rather than, "I must investigate."

Again, this is not a fully formed idea, but a sketch — a fragment. Do skeptics (especially hard-line skeptics) value information differently than more traditional viewpoints might suggest?

Possibly Asked Questions
2008-10-06 10:46

What "New World Order" is this? And why do I have to take notes?

I suppose I should explain myself, or at least my nomenclature — it's not entirely clear what I mean.

From the beginning this website was a phoenix, rising from the proverbial ashes (of a hard disk failure). A site reborn, with new found purpose. (Or lack of purpose.)

I set out to create a website like used to be the norm. Well written (alright, who am I kidding — written with capitalization and spelling), eclectic, but above all, interesting. I set out to create something that was more artifice than diary, more journalistic than blogging.

I have so far failed, by my own admission (as well as anyone else who's nice enough to tell me the truth), to keep the promise to which the name would hold me. it's a grandiose promise of ideal rhetoric and deep thought, with not a little cynicism and practicality thrown in. ("Would you like fries with your universe?" is not exactly the next great rallying cry.)

So, what is this "New World Order" I'm alluding to? The one my vision of which I wish to share, that holds such promise and hope?

I don't know what it looks like, or who to get there, but the feel of it is what I long for so deeply.

Ever since I read William Gibson's Neuromancer I've had a longing for a world with that kind of freedom. While Gibson's work is often seen as a critique of contemporary issues, it has sparked something deep within many to bring parts of his vision into reality. (Namely among technologists to bring the technology he portrays from speculative fiction into hard science.)

What I long for is not the dystopic social structure, the rule of corporations, or even the widespread use of cybernetics and the mutability of our human existence. Instead I long for a time when technology is really just a part of the human experience, considered part of our humanity rather than separate and opposite.

This really is the romantic in me (in the rather outmoded sense of the word) trying to reconcile the aesthetic and emotional impact and power of technology with humanity. As a technologist (of a sort), I cannot help but see the discontinuities between human and machine. In fact, it is often my task to break down those discontinuities, to smooth over technology so it is ready for "the masses."

I see the same problems and misunderstandings all the time, the same discontinuities between humanity and machine. Between the human experience we know so well and the machine experience we are only beginning to understand. And I cannot help but to think there must be a better way. that the fluid relationship shown in William Gibson's work can exist — perhaps even must emerge from the progression of technology — I want it, but we do not know if it is fantasy or future yet.

I want to find that relationship, that fluidity with on of humanity's few unique gifts — the objects of our creation. We need to bring our creative power back to ourselves, to reincorporate that which has for so long been associated only with unfeeling, callous, cold, industrial dominance, and corporate greed.

Computers and computing have opened the eyes of many to what might be possible, given them a glimpse of the freedom and power that lies at the heart of the ability to bend the world, the very laws of nature to out will. But it is only the beginning.

Without diving past the unfathomable Singularity, I don't know what will happen, whether we can build th world I long for so desperately. I do know that I do not fear the Singularity, nor the reckless advance of technology. We must be wary of the existential risks such power holds, but even so it seems humanity's only hope for a truly New World Order — not an utopia, but a substantively different world — lies in the hands of technologists.

We must not only be the change we wish to see in the world, we must build the world to our vision of how it ought to be. This is not a single task, but a lifestyle, a purpose for being — the resurgence of the DIY ethic speaks to the hunger to shape our world.

We must make our New World Order, it will not come by revolution.

Wait... what?

The name of the site notwithstanding, this is a place for me to put interesting things. The nominal plan was to have articles and essays, along with maybe a bit of fiction, and some technical crunch because I am a technologist.

My idea was that the site become a sort of idea repository, not necessarily a complete picture, just fragments in which you might glimpse the whole (like a hologram).

Well, what happened?

Not much. I'm busy, it happens. I haven't felt motivated to write in a long time. I've got lots of projects that don't really go anywhere, left half-unfinished as a reminder that I'm not nearly as awesome as I'd like to be.

I'll get back to some of it, eventually. Until then, there's not much to see here. — There is something though, and as I find time and interest I'll write it up and let it out into the wild internets.

So you say you want a Revolution...
2008-09-25 00:00

With all the fuss and politicking around the new (September 2008) rapidly approaching presidential election, many people are feeling like new leadership isn't going to fix anything. The problems we face seem endemic to the whole system of government we have.

(At least that's the explicit frustration I've heard from one person I know, and many other people I talk to seem to agree. But I don't really get out much so this opinion may be based on the intelligent-technical-college-student culture and not something more general. — he, at least my biases are fairly obvious.)

Anyway, it seems like the current governmental strucutre is brokne or at the very least malfunctioning. If, for a moment, we assume that is true, and some other system would be superior, then we have a few things we need to know:

  • What system is superior, and how?
  • In what manner can the new system be instituted, or what manner can it not be instituted?

To answer the first question requires answering a lot of other questions, at the very least, identifying the superior system.

The second question is somewhat easier to break apart because we only need to know about the current system; though, it is possible the superior system's specifics will inform the precise answer (or the optimal answer).

The tried and true, old school, tested and respected method of changing out governmental systems is, of course, revolution.

But revolution is the brute force approach, it's hard, messy, and inelegant — if romantic. It also doesn't work very well "at scale."

The United States military is one the most dominating forces in the world. When both sides of a revolution had muskets, it's one thing. When one side has hunting rifles and the other side has air support — The side the the air support wins.

So straight revolution is out, unless the military is leading the way, in which case, by historial example, things have probably Gone Horribly Wrong.

The next most obvious thing to do is to have the system change itself. Revolution from within, so to speak. This, of course, relies on the system being somewhat functional, and not broken in the very particular manner of trying to keep the power it has at all costs. That last bit, unfortunately, is usually a symptom of a system that needs changing.

If you go the route of infiltrating the system until you have enough control to enact the changes you want — you've just created a totalitarian state, just like the fascist leader of the nineteen thirties and forties. This may be acceptable if there is a single restructuring and the totalitarian control goes away; but again, history tells us that doesn't actually happen.

We (the proto-revolutionaries) could try to make the current system obsolete. We provide a compelling alternative to everything the system currently provides. Then the system gets replaced effectively, but not on paper. Eventually, when everyone realized the old system is not longer necessary the old system can be removed.

Again, there are a host of problems with this plan, the the least of which is the issue of providing services in competition with your government's while not being a recognized sovereign power.

The last option, the easiest of all, is to simply write about the change,s what you want, what needs to happen. Nothing will actually change, but you do get the melancholy satisfaction of saying "I told you so," when someone complains about the system.

I don't think there's an easy answer to find for changing the status quo. You need to look at your agenda, timetable, and resources to find the best option. If you've figured out the superior system — go to it, maybe we can avoid an ungraceful all from out decaying superpower status. Instead, what?

Go buck the system, start a revolution; but remember you New World Order is standing ont he shoulds of the giant before you.

And have a plan!

Google App Engine Pylons Authentication Decorator
2008-05-15 00:00

I've recently been playing with Google's latest strange (ad)venture, Google App Engine. I've been addicted to python for a while, and have been playing with some of the web frameworks like TurboGears and pylons. So I jumped on the opportunity to write something I wouldn't have to worry about getting slashdotted for. That is, if I could get slashdotted (or dugg, or whatever).

In any case, it's a interesting framework, the datastore is different from anything you've probably worked with before, but is definitely worth a look, if only to expand your mind.

But, I just recently started writing an application with the App Engine SDK using pylons, and had a need to restrict access to certain pages of the application to logged in, or administrative users. Google's user API allows this fairly easily, but I wanted something nice and simple, so I wrote a decorator for the controller actions.

  1. from decorator import decorator
  2. import google.appengine.api.users as users
  3.  
  4. @decorator
  5. def reqire_user(f, *args, **kws):
  6. if users.get_current_user() is None:
  7. redirect_to(users.create_login_url(request.path_info))
  8. else:
  9. return f(*args, **kws)
  10.  
  11. @decorator
  12. def require_admin_user(f, *args, **kws):
  13. if users.get_current_user() is None:
  14. redirect_to(users.create_login_url(request.path_info))
  15. elif not users.is_current_user_admin():
  16. return render('/need_admin.mako')
  17. else:
  18. return f(*args, **kws)

Free to use, put it in the public domain, no need to credit or anything. Just thought I'd throw this out there for all those people who are new to python and might not think of this solution first.