Best wait screen ever!

Every website will run into issues once in a while, but it takes a bright spark to deal with the people waiting for the site to load just as much as fixing/upgrading the internals. This is why I was particularly pleased with Lowes‘ recent wait screen.

These are a series of animated clips strung together of various characters waiting in line as one or more poof in and out of the line, all to the tune of soothing elevator music. What’s nice is that it immediately takes away the frustration of not having access to what you were looking for in the first place (in my case plumbing fittings).

Somebody's Kung Fu Fighting

Somebody’s Kung Fu Fighting

Mr. Roboto

Mr. Roboto

WOW... Literally

WOW… Literally

A little bummed I couldn’t get to the plumbing fittings, but suddenly, I don’t mind as much.

Don’t worry, I’ll be posting a real update soon.

A video game style electromagnetic gun (from 1934)

So you think your video game has a railgun (or maybe a coilgun) from Doom, Quake, Mass Effect or what have you, that will defeat any armor and is incomprehensibly cool. Alas, it’s made of pixels.

Well, Virgil Rigsby from San Augustine Texas patented in 1934 what he called an “electric gun” - an apt description.

Via modernmechanix.com

Via modernmechanix.com

Mr. Rigsby next to a mockup of his "electric machine gun" (Via davidszondy.com). How badass looking is that?

Mr. Rigsby next to a mockup of his “electric gun” (via davidszondy.com).
How badass looking is that? The pith helmet he’s wearing makes it even better.

Mounted on the deck of a ship or on the back of a truck (if it had a nuclear reactor supplying power), this would have been formidable weapon.

Mounted on the deck of a ship or on the back of a truck (if it had a nuclear reactor supplying power), this would have been formidable weapon.

Bolts. I love bolts.

The premise behind this is that it’s essentially a coilgun with relatively fast reload time (even in those days, EM guns took a while to “recharge”) and rather than relying on complex triggering circuitry which would have been impractical in a time before the silicon chip, he went with a triggering wheel.

Coilguns frequently are confused with Railguns, however the principles of operation are very different. Although they both work on electromagnetism and require a vast amount of power, in a railgun, there are no coils per-se. The projectile/armature and the two rails on either side create one giant electromagnet. Whereas in a coilgun, each coil is an electromagnet momentarily energized sequentially as the projectile gets near each one.

This requires coilguns to be more complex, with multiple stages of acceleration that require intricate triggering mechanisms and careful timing. Therefore, coilguns are more awesome.

This schematic shows how each coil is arranged to be energized in sequence as the arm in the triggering wheel rotates. Note how each coil becomes smaller in width.

This schematic shows how each coil is arranged to be energized in sequence as the arm in the triggering wheel rotates. Note how each coil becomes smaller in width.

As the projectile gets near the next coil in sequence, it is energized. As the projectile enters the coil, it is de-energized and the next coil is triggered, pulling it through the barrel in this fashion until it exits the gun at tremendous velocity. Because the time the projectile spends in each coil is shorter and shorter as it accelerates, Mr. Rigsby wisely chose to reduce the width of each subsequent coil in the barrel.

Detail of the triggering wheel shows how the surface area of each contact plate in sequence becomes smaller as the amount of time the projectile spends in each coil becomes shorter. This is low tech timing in the days before advanced triggering circuitry.

Detail of the triggering wheel shows how the surface area of each contact plate in sequence becomes smaller as the amount of time the projectile spends in each coil becomes shorter. This is low-tech timing in the days before advanced triggering circuitry.

These days, coilguns employ a bank of High Voltage capacitors powering each coil. Modern circuits may also employ a second coil next to the acceleration coil, essentially to function as a simple metal-detector, to trigger the next acceleration coil in the sequence. Some advanced designs may use hall-effect sensors or an optical trigger that fires when the projectile breaks a laser/IR beam between the source and sensor. The optical triggers require slots to be cut into the barrel or often use barrels made of transparent borosilicate glass which is resistant to high temperatures and stress, while still being reasonably permeable to an electromagnetic field.

The biggest downside to this design (besides looking too awesome in front of the enemy, thereby ending the fight before it begins) was that it would have required a monumental amount of electricity to fire each round. These projectiles aren’t paper weights so they required a corresponding level of power at very high amps for each coil multiplied by each time fired. Due to inefficiencies of conversion from electromagnetic energy to kinetic energy, there will also be a lot of heat produced in the coils and triggering mechanisms.

That said… I’m totally building one of these when I have time.

DIY Hashing (if you must)

Every so often, I come across more and more clever schemes implemented by programmers wanting to one-up on what’s already available in their favorite software libraries. My response is DON’T DO IT! For one very simple reason: Crypto is hard. Good crypto is harder. PHP developers in particular are notorious for this, but I’ve seen C# devs do the same.

Even if you come up with a clever hashing scheme of your own, you don’t really want to play around next to something that’s tried and proven, like say bcrypt. Plus there are innumerable instances where very, very smart people have gone out and created their own schemes, only to be defeated by an attack a short while later. It happens. That’s reality.

Consider password hashing

You shouldn’t use anything other than bcrypt. There I said it. There are many examples on the web on how to use bcrypt, but just in case you’re wondering, here are couple of functions :

/**
 * Hash password with blowfish + salt
 *
 * @param string $pass Raw password
 * @param string $salt Stored salt stored in the db or generated
 */
public function password( $pass, $salt ) {
	if ( empty( $pass ) || empty( $salt ) ) {
		return false;
	}
	return crypt( $pass, '$2y$09$'. $salt . '$' );
}

/**
 * Verify password hash against the stored value
 *
 * @param string $pass Raw password
 * @param string $salt Password salt as stored in the database
 * @param string $storedPass Hashed password to match against
 */
public function verifyPassword( $pass, $salt, $storedPass ) {
	if ( empty( $pass ) || empty( $salt ) || empty( $storedPass ) ) {
		return false;
	}
	$pass = $this->password( $pass, $salt );

	if ( $storedPass === $pass ) {
		return true;
	}
	return false;
}

These are very simple and are fairly self-explanatory. You’re seeing the PHP “crypt” function being used here to create a blowfish hash with a cost of 9 (this is the work factor used to calculate the hash; the higher it is, the longer it takes to hash, thus more expensive to brute-force). The verifyPassword function does the hash again with the salt (even though bcrypt has built in salting, I prefer adding a randomly generated one of my own for more entropy) to see if it matches the stored hash.

Now the DIY version

Simply re-hashing a bunch of times isn’t usually good enough, especially if it’s just md5. I’ve lost count of how many times I’ve seen that. There are well known software packages out there that still insist on putting md5 in a loop 1000x or more, but realistically, these are fairly easy to crack these days with the proliferation of fast GPUs.

If for some reason, you still want to do your own hashing (as mentioned above, I don’t recommend it), I’ve written an alternative you can use :

/**
 * DIY hash, best to use this for something non-critical
 */
public function saltedHash( $str, $salt = NULL ) {
	if ( NULL === $salt ) {
		$salt = bin2hex( $this->IV( 30 ) );
	}
	
	for ( $i = 0; $i < 5000; $i++ ) {
		$str	= hash( 'ripemd320', $str );
		$arr	= str_split( $str, 8 );
		$j	= $i;
		
		foreach( $arr as &$a ) {
			$salt	= strrev( $salt ) . $j;
			$str	= hash( 'ripemd160', strrev( $a ) . $salt );
			$j++;
		}	
	}

	return $salt . 'x' . $str ;
}

/**
 * Verify DIY hash
 */
public function matchSaltedHash( $stored, $sent ) {
	if ( empty( $sent ) || empty( $stored ) ) {
		return false;
	}
	
	$pkg = explode( 'x', $stored );
	if ( 1 <= count( $pkg ) ) {
		return false;
	}
	
	if( $pkg[1] === $this->saltedHash( $sent, $pkg[0] ) ) {
		return true;
	}
	
	return false;
}

/**
 * @returns mixed Binary IV
 */
public function IV( $size ) {
	return mcrypt_create_iv( $size, MCRYPT_DEV_URANDOM );
}

This function is expensive enough to be not a simple re-iteration that can create hashes which are easily cracked on a modern GPU in short order. Because the hash is also salted, it should be a bit more difficult to simply run reiterations. It won’t make it uncrackable, but certainly more secure than simply running md5 a thousand times.

If you’re interested in a class for cryptographic purposes (encryption/decryption, password hashing/generation etc…) I’ve created a complete example. Note: I’m using a defined constant called SALT_KEY, which is basically an application specific random string. You can create your one of your own via the WordPress salt generator (do NOT share the same salt among multiple applications).

I’ve already published much of this code previously, but this version includes improvements.

<?php

/**
 * Encryption/Decryption, Encoding/Decoding and password related functions.
 * 
 * The license below was inherited from the project this originally went into.
 * If you need a different license for some reason, just email me.
 * 
 * @author Eksith Rodrigo <reksith at gmail.com>
 * @license http://opensource.org/licenses/ISC ISC License
 */

class Crypto {
	
	/**#@+
	 * Instance getter, constructor and destructor
	 */
	
	private static $_instance;
	public static function getInstance() {
		if ( !self::$_instance instanceof self ) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	
	private function __construct() {}
	
	/**#@-*/
	
	
	
	/**#@+
	 * Password related functions
	 */
	
	
	/**
	 * Hash password with blowfish + salt
	 *
	 * @param string $pass Raw password
	 * @param string $decodedSalt Stored salt after being decoded or freshly generated
	 */
	public function password( $pass, $decodedSalt ) {
		// This will reject NULL, 0, FALSE etc... but these are terrible passwords anyway
		if ( empty( $pass ) || empty( $decodedSalt ) ) {
			return false;
		}
		
		return crypt( $pass, '$2y$09$'. $decodedSalt . '$' );
	}
	
	
	/**
	 * Verify password hash against the stored value
	 * 
	 * @param string $pass Raw password
	 * @param string $encodedSalt Password salt as stored in the database
	 * @param string $storedPass Hashed password to match against 
	 */
	public function verifyPassword( $pass, $encodedSalt, $storedPass ) {
		if ( empty( $pass ) || empty( $encodedSalt ) || empty( $storedPass ) ) {
			return false;
		}
		
		$salt = $this->decode( $encodedSalt, SALT_KEY );
		$pass = $this->password( $pass, $salt );
		
		if ( $storedPass === $pass ) {
			return true;
		}
		return false;
	}
	
	
	/**
	 * Hashes the passed password and creates a matching salt (also encoded)
	 * This function does NOT create a new password.
	 * 
	 * @param string $pass User provided password
	 * @param string $salt Generated salt (encoded using the password)
	 */
	public function pairPasswordSalt( &$pass, &$salt ) {
		if ( empty( $pass ) ) {
			return false;
		}
		
		$salt = bin2hex( $this->IV( 30 ) ); 
		$pass = $this->password( $pass, $salt );
		
		$salt = $this->encode( $salt, SALT_KEY );
		
		return true;
	}
	
	
	/**
	 * Random password generator
	 * 
	 * @param int $len Password length
	 * @return string Random password with commonly confused characters removed
	 */
	public function randomPassword( $len ) {
		// Extra padding to accomodate removed chars
		$r = $this->randomStr( $len + 15 );
		
		// Remove confusing chars
		$r = str_replace( array( '1', 'i', 'l', '0', 'o', '5', 's' ), '', $r );
		
		return substr( $r, 0, $len );
	}
	
	/**#@-*/

	
	
	/**#@+
	 * Random number/string generators
	 */
	
	/**
	 * Generates a random string of a given length (or random between 5 and 10 if length is empty)
	 * 
	 * @param int $len Random string length. If empty, will revert to a number between 10 - 20
	 * @return string Alphanumeric string
	 */
	public function randomStr( $len = 0 ) {
		if ( empty( $len ) ) {
			$len = $this->_rnd( 10, 20 );
		}
		
		$r = base64_encode( $this->IV( 30 ) );
		$r = str_replace( array( '+', '/', '-', '_', '=' ), '', $r );
		if ( strlen( $r ) > $len ) {
			return substr( $r, 0, $len );
		}
		return $r;
	}
	
	
	/**
	 * Generates a random number
	 * 
	 * @param int $min Minimum number size. Defaults to 1
	 * @param int $max Maximum number size. Defaults to maximum allowed in 32 bit PHP
	 */
	public function randomInt( $min = 1, $max = 0x7FFFFFFF ) {
		$d = $max - $min;
		if ( $d < 0 || $d > 0x7FFFFFFF ) {
			$d = 1;
		}
		
		$r = $this->IV( 4 );
		$p = unpack( "Nint", $r );
		$f = ( float ) ( $p['int'] & 0x7FFFFFFF ) / 2147483647.0;
		
		return  round( $f * $d ) + $min;
	}
	
	/**#@-*/
	
	
	
	/**
	 * Encrypt and serialize data with a given key
	 * 
	 * @param mixed $data Data to encrypt (assumes strings are UTF-8)
	 * @param string $key Encryption key
	 * @return string Serialized and encrypted
	 */
	public function cryptSerialize( $data, $key ) {
		if ( empty( $data ) ) {
			return '';
		} else {
			$data = json_encode( $data );
			return $this->encryption( $data, $key, 'encrypt' );
		}
	}
	
	
	/**
	 * Deserialize and decrypt data
	 * This is safer than PHP's 'deserialize' especially with cookie data
	 *
	 * @param string $data Data to decrypt (this should be completely unaltered)
	 * @param string $key Decryption key
	 * @return mixed Orignally serialized object
	 */
	public function cryptDeserialize( $data, $key ) {
		if ( empty( $data ) ) {
			return '';
		} else {
			$data = $this->encryption( $data, $key, 'decrypt' );
			if ( empty( $data ) ) {
				return '';
			} else {
				try {
					return json_decode( $data );
				} catch ( Exception $e ) {
					return '';
				}
			}
		}
	}
	

	/**
	 * Encode ( so called because this is only ECB ) in Rijndael 256 with Base64 encoding
	 *
	 * @param string $data The content to be encoded
	 * @param string $salt Required nonce
	 * @return string Encoded and optionally base64 encoded string
	 */
	public function encode( $data, $salt ) {
		$enc = mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $salt, $data, MCRYPT_MODE_ECB, '' );
		return base64_encode( $enc );
	}
	
	
	/**
	 * Decode as encoded above 
	 *
	 * @param string $data The content to be decoded
	 * @param string $salt Required matching nonce to the encrypted data
	 * @return string Decoded content
	 */
	public function decode( $data, $salt ) {
		$data = base64_decode( $data );
		$dec = mcrypt_decrypt( MCRYPT_RIJNDAEL_256, $salt, $data, MCRYPT_MODE_ECB, '' );
		
		// Padding is automatic, but unpad is not
		return $this->_unpad( $dec, 32 );
	}
	

	/**
	 * Generates a random IV for encryption and password/string generation
	 * 
	 * @returns mixed Binary IV
	 */
	public function IV( $size ) {
		return mcrypt_create_iv( $size, MCRYPT_DEV_URANDOM );
	}
	
	
	/**
	 * Decode as encoded above in Rijndael 256 with optional Base64 decoding 
	 *
	 * @param string $data The content to be decoded.
	 * @return string Decoded content
	 */
	public function encryption( $str, $key, $mode = 'encrypt', $time = null ) {
		// Open Rijndael-256 in CBC mode
		$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
		
		// Find the IV size for the mcrypt module
		$size = mcrypt_enc_get_iv_size( $td );
		
		// Block sizes are needed to calculate the padding ( further down )
		$bsize = mcrypt_enc_get_block_size( $td );
		
		// Key size
		$ksize = mcrypt_enc_get_key_size( $td );
		
		// Hashed key ( for consistency )
		$hkey = hash( 'ripemd160', $key );
		
		// Hash the key for consistency and use only the key size needed from the front
		$key = substr( $hkey, 0, $ksize );
		
		// Salt for encoding/decoding is extracted from the back of the key hashed again
		$salt = substr(  hash( 'ripemd160', $hkey ), ( -1 * $ksize ) );
		
		if ( $mode == 'encrypt' ) {
			/// We're encrypting. Create a new initialization vector
			$iv = $this->IV( $size );
		} else {
			// Unwrap the combined IV and data packet and extract the IV from the front (using IV size)
			$str = $this->decode( $str, $salt );
			$iv = mb_substr( $str, 0, $size );
			
			// Isolate the data by removing the IV altogether and decode in preparation for decryption
			$str = mb_substr( $str, mb_strlen( $iv ) );
			$str = base64_decode( $str );
		}
		
		// Initialize mcrypt
		mcrypt_generic_init( $td, $key, $iv );
		if ( $mode == 'encrypt' ) {
			// Prepare string by padding to match block size and encrypt
			$str = $this->_pad( $str, $bsize );
			$enc = mcrypt_generic( $td, $str );
			
			// Add the IV to the front and Encode
			$out = $this->encode( $iv . base64_encode( $enc ), $salt );
		} else {
			// Decrypt the data ( we removed the IV and decoded above ) and remove padding
			$str = mdecrypt_generic( $td, $str );
			$out = $this->_unpad( $str, $bsize );
		}
		
		// Clean up
		mcrypt_generic_deinit( $td );
		mcrypt_module_close( $td );
		
		// Return encrypted/decrypted string
		return $out;
	}
	
	
	/**#@+
	 * Helpers
	 */
		
	/**
	 * DIY hash, best to use this for something non-critical
	 */
	public function saltedHash( $str, $salt = NULL ) {
		if ( NULL === $salt ) {
			$salt = bin2hex( $this->IV( 30 ) );
		}
		
		for ( $i = 0; $i < 5000; $i++ ) {
			$str	= hash( 'ripemd320', $str );
			$arr	= str_split( $str, 8 );
			$j	= $i;
			
			foreach( $arr as &$a ) {
				$salt	= strrev( $salt ) . $j;
				$str	= hash( 'ripemd160', strrev( $a ) . $salt );
				$j++;
			}
			
		}
		
		return $salt . 'x' . $str ;
	}
	
	/**
	 * Verify DIY hash
	 */
	public function matchSaltedHash( $stored, $sent ) {
		if ( empty( $sent ) || empty( $stored ) ) {
			return false;
		}
		
		$pkg = explode( 'x', $stored );
		if ( 1 <= count( $pkg ) ) {
			return false;
		}
		
		if( $pkg[1] === $this->saltedHash( $sent, $pkg[0] ) ) {
			return true;
		}
		
		return false;
	}
	
	
	/**
	 * Workaround for mt_rand abnormalities
	 */
	public function _rnd( $min, $max ) {
		$r = 0;
		if ( $min > $max ) {
			$min ^= $max;
			$max ^= $min;
			$min ^= $max;
		}
		while( 0 === $r || $r < $min || $r > $max ) {
			$r = mt_rand( $min, $max );
		}
		
		return $r;
	}
	
	
	/**
	 * Pad data to encryption block size.
	 * Michael Corleone says hello.
	 */
	private function _pad( $str, $bsize ) {
		// Find the pad size for this block size and string length
		$pad = $bsize - ( mb_strlen( $str ) % $bsize );
		
		// Repeat the equivalent character up to the pad size
		$str .= str_repeat( chr( $pad ), $pad );
		
		return $str;
	}
	
	
	/**
	 * Remove extra padding added during encryption.
	 * This is a bit of a hack, so if you have improvements, please add them [ and let me know :) ].
	 * Thanks!
	 */
	private function _unpad( $str, $bsize ) {
		$len = mb_strlen( $str );
		
		// Find the pad character ( last one )
		$pad = ord( $str[$len - 1] );
		
		// If padding would have been applied to the string...
		if ($pad && $pad < $bsize) {
			// ...find the pad
			$pm = preg_match( '/' . chr( $pad ) . '{' . $pad . '}$/', $str );
			
			// Pad found, strip it.
			if( $pm ) {
				return mb_substr($str, 0, $len - $pad);
			}
		}
		return $str;
	}
		
	/**#@-*/
}

Log All The Things!

Recently on Hacker News, a new product called Heap was linked and quickly rose to the front page. Heap is an analytics tool that captures everything that happens on your web site. I mean everything: clicks, submissions, even perhaps mouse movements, and it’s all dumped to a database that you can analyze later for marketing etc…

It’s a UI firehose

I’m not knocking the product since they’ve obviously spent a lot of time and energy and I can respect that. I hope they’re successful. But anyone using things like these have to keep one thing in mind…

Data is like a plucked flower; it starts to die and go stale the moment it’s captured. How soon it goes stale depends on the type of data, but you then need to make sure it’s shifted (another firehose) to analytics ASAP. It needs to be looked at for actual usable information and interpreted somehow to monetize, if that’s your intention, or feed further research.

By simply capturing everything, you’re effectively creating a very large feed of data that you may or may not use. You’re facing a similar conundrum as the U.S. intelligence agencies who (we’re told) collect and log virtually every cell transmission and, allegedly, every email passing through computers within and, also allegedly, outside our borders.

Who said or did what, and more importantly, why? Are they really interested? Curious? Can be driven to become interested? Marketed to? Can they get their friends, family and colleagues interested? Can they give me money? These are more important questions to answer first before you go about Logging All The Things. I have a criticism about this further below.

Which brings us to those users who don’t fall under the umbrella of “marketable” for technical reasons.

The problem of backward compatibility

Is it just me or IE8 is just not considered by web startups anymore ? Sorry to deviate a little but every time I try to look at a “Show HN” in IE8 (work computer), it fails for about 85% of the time. I could understand that some startups heavily depend on latest browsers but what about others ? – codegeek

No it’s not your imagination.

The web doesn’t all flock to Firefox or Chrome or Webkit or, the soon to be former independent, Opera. By limiting themselves to a relatively smaller subset of web users, Heap and similar products have effectively decided not to bother with the rest. To be fair to them: they’re probably not wrong.

The vast majority of analytics, web apps, shiny new HTML5 things are all geared toward the tech-savvy crowd who are connected to everything all the time and, for better or for worse, are geared toward the browser as an OS substitute. Those interested in these things in the first place will likely be running a browser capable of those very things, but this now takes away the potential consumption of the web.

The web is a product delivery network

It used to be a means of just communication, then trade, then sharing and now it’s all about content delivery. “Content” as in data is now a product itself so along with your shoes, fishing rods/reels, computers etc… you now have data being sold as well.

To sell data, you must first gather it, and then you bring yourself back full circle to the aforementioned stagnation problem. Captured data is useless if it cannot be applied in a meaningful way and it becomes harder to apply to anything useful if you have too much of it.

As DevOps Borat put so eloquently :

I don’t want to be marketed to

This isn’t really a secret, but I don’t think I’ve mentioned it here before. When generally I browse for leisure (which I sadly don’t get to do as often these days) and it doesn’t involve me watching YouTube videos or playing a game or some form of interaction, I browse with JavaScript and all plugins disabled in Firefox.

I realise this involves killing advertising on sites I enjoy and I don’t completely feel comfortable doing it, but the alternative is more intrusive and objectionable to me and I don’t think I’m alone in this.

As the old addage goes: If you’re using a service for free, you’re the product.

This holds true of YouTube, Facebook, Google+, Gmail and yes, even WordPress. Advertising and value added features are how these services stay afloat and I can appreciate that. I also hope that they can appreciate the sheer volume of crap constantly targeted at me through the use of cookies, JavaScript and of course my IP (I’m sure) no matter where I go and what I do.

I’m old fasioned

I remember a time when web pages were hastily constructed bits of content consisting of tables, poorly contrasting background images, tags and barely functioning CSS that broke in any browser other than IE5 or Netscape. It looks like we may be returning to these bad old days with newer technology.

Governments and universities controlled most of the internet connectivity — for better or for worse – and the few companies that did let you build a site for free on the newly emerging “web” were Tripod, Geocities, AOL Hompages et al… and they too made sure there was ample advertising (or value added as in the case of AOL).

But you know what?

Aside from the odd virus or two, since those are also ubiquitus (and antivirus was/is Snake Oil), the blistering popup storm that can be managed if you knew how to tweak Netscape or installed the latest popup blocker, It was still managable.

I could actually consume the web without being consumed

I didn’t mind the ads that sold me dates to college students, mostly cause I was still in junior high, but also cause they didn’t know which site I visited before or where I was looking or what I liked to buy (eBay started in 1995 and I thought it was the best idea since sliced bread).

But these days suddenly web sites that have nothing to do with what I was looking at before, know which ads to show me.

I’m visiting a site on chemistry books that’s showing me ads to fishing reels. How did they know I was looking for fishing reels before?

I’m visitng a site on telescopes and they’re showing me ads on test tubes and beakers.

I’m visiting a site on printing and homemade paper and there’s an ad on star tracking scopes and GPS.

What is this madness?

Now as for the folks at Heap who may be getting, an undeserved, flogging from me for contributing to this tracking malarkey, I apologize for coming off as somewhat irascible. It’s not your fault since you’re only contributing to the demand.

What worries me is that there is demand.

NC Teacher: "I Quit"

Reblogged from Diane Ravitch's blog:

A letter from a disgusted teacher:

I QUIT

Kris L. Nielsen
Monroe, NC 28110

Union County Public Schools
Human Resources Department
400 North Church Street
Monroe, NC 28112

October 25, 2012

To All it May Concern:

I’m doing something I thought I would never do—something that will make me a statistic and a caricature of the times. Some will support me, some will shake their heads and smirk condescendingly—and others will try to convince me that I’m part of the problem.

Read more… 1,549 more words

This is the most eloquent elaboration of the horrors taking place in the North Carolina school system. I hope someone of influence will read this and actually do something rather than play more lip-service.