Browsed by
Author: Steve

AirPort Extreme guest network with pfSense

AirPort Extreme guest network with pfSense

The AirPort Extreme is a great wireless router. I’m using it strictly as an access point (bridge mode) in front of pfSense.
Setup is a breeze, except for the guest network. You’ll need to configure a few things in pfSense:

  • Create a VLAN (tag 1003)
  • Create an interface for the VLAN
  • Enable DHCP on the VLAN interface
  • Create a rule to allow VLAN traffic
  • Configure NAT for VLAN IPs

Create a VLAN

Interfaces > Assign > VLANs > Add (+)

Parent interface should be your LAN interface (or wherever your AirPort is connected).
VLAN tag should be 1003 (the number hard-coded for AirPort guest network traffic).

Create VLAN interface

Interfaces > Assign > Add (+)

After clicking the plus, you’ll see a new interface (likely OPT1, which I later renamed GST).
Select VLAN 1003 from the dropdown, then click on the title of the new interface.

Enable the interface and select Static IPv4 from the IPv4 Configuration Type dropdown.
Set the IPv4 address. It should differ from your LAN’s IP address to avoid conflicts.

Enable DHCP

Services > DHCP Server > GST

Enable DHCP and set the Range of IP addresses.

Allow VLAN traffic

Firewall > Rules > GST

Add a rule to pass any type of of traffic, where the destination is not a LAN address.
This will allow users on the guest network to access the Internet, but not your network.

Configure NAT

Firewall > NAT > Outbound

Add a mapping for the VLAN IP address range.

Error: MultipartParser.end(): stream ended unexpectedly: state = START

Error: MultipartParser.end(): stream ended unexpectedly: state = START

It’s been over a year since I last wrote! Wow. I finally hit a bug so frustrating that I couldn’t not write about it. Haha.

File uploads in Node.js with Express 4 and Formidable

As you probably know, in Express 4, bodyParser was removed and must be included as a module.

Furthermore, file uploads (multipart/form-data) via req.files is no longer supported. So, a module like formidable or multiparty must be used to parse the data. Fortunately, it’s super simple. You can set up a route like this:

Just grab a form object, set a few preferences, and call parse. The file(s) are saved to your upload directory and the associated metadata is returned in files. However, you may receive an err that reads Error: MultipartParser.end(): stream ended unexpectedly: state = START. That means the connection ended before the file(s) finished transferring… but why?

It took me a long time to figure this one out. I noticed that if I utilized the raw-body module, the problem went away. No idea why, though. Something to do with reading the stream completely, I guess. I tried using the multiparty module instead, but it returned a similar error. Then, it occurred to me that Nginx could be botching the upload somehow! But, hitting the node server directly didn’t help.

Finally, I found it. The second line in the code above. res.send(200) should be within the formidable callback. Calling it at the beginning of the route sends a response before the upload is complete, thus… terminating the connection early. Arggggg!! It’s times like these when I feel like I should just find another line of work. Haha. But, that’s just how it goes sometimes… 🙂

Displaying the git branch on your bash prompt

Displaying the git branch on your bash prompt

My bash prompt looks something like this:


Pretty sweet, eh? At a glance, I can see what git branch I’m in and whether or not it’s dirty (red or green). To get this functionality, I utilized Sexy Bash Prompt. For my needs, I found Sexy Bash Prompt to be a little overweight; so here’s my stripped-down version:

#!/usr/bin/env bash
# Determine what type of terminal to use for tput
if [[ $COLORTERM = gnome-* && $TERM = xterm ]] && infocmp gnome-256color >/dev/null 2>&1; then export TERM=gnome-256color
elif [[ $TERM != dumb ]] && infocmp xterm-256color >/dev/null 2>&1; then export TERM=xterm-256color
if tput setaf 1 &> /dev/null; then
# Colored terminal available
tput sgr0
if [[ $(tput colors) -ge 256 ]] 2>/dev/null; then
# 256 colors available
COLOR_DARK=$(tput setaf 27)
COLOR_PREPOSITION=$(tput setaf 7)
COLOR_LIGHT=$(tput setaf 39)
COLOR_DIR=$(tput setaf 76)
COLOR_GIT_STATUS=$(tput setaf 154)
COLOR_GIT_STATUS_DIRTY=$(tput setaf 196)
# 16 colors available
COLOR_DARK=$(tput setaf 5)
COLOR_PREPOSITION=$(tput setaf 7)
COLOR_LIGHT=$(tput setaf 4)
COLOR_DIR=$(tput setaf 2)
COLOR_GIT_STATUS=$(tput setaf 10)
COLOR_GIT_STATUS_DIRTY=$(tput setaf 9)
RESET=$(tput sgr0)
# Use ANSI escape sequences for coloring
function get_git_branch() {
REF="$(git symbolic-ref HEAD 2> /dev/null | sed -e 's/refs\/heads\///')"
if [[ $REF != "" ]]; then
echo $REF
echo "no branch"
parse_git_dirty () {
if [[ -n "$(git status --porcelain 2> /dev/null)" ]]; then
echo 1
function is_on_git() {
git rev-parse 2> /dev/null
get_git_info () {
if [[ $(parse_git_dirty) == 1 ]]; then
if [[ $BRANCH != "" ]]; then
# Define the prompt
\[$COLOR_LIGHT\]\h \
\$( is_on_git && \
echo -n \" \" && \
echo -n \"\[\$(get_git_info)\]\" && \
echo -n \"\[$NORMAL\]\") \
$ \[$RESET\]"

It’s fairly simple to implement. Just save the script to your home directory (e.g. ~/.bash_prompt), make sure it’s executable, and add the following line to the end of your .bashrc:

. ~/.bash_prompt

P.S. You can use this juggernaut command to display a list of possible colors for tput:

( x=`tput op` y=`printf %$((${COLUMNS}-6))s`;for i in {0..256};do o=00$i;echo -e ${o:${#o}-3:3} `tput setaf $i;tput setab $i`${y// /=}$x;done; )
Using ng-model with contenteditable

Using ng-model with contenteditable

The ng-model directive is glorious! That is, until you try to use it with contenteditable elements. You’d think it would just… work… but it doesn’t. Fortunately, there’s a way to stiff-arm your way through this problem; just pop the following directive into your application:

@myApp.directive 'contenteditable', ->
restrict: 'A' # match the 'contenteditable' attribute
require: '?ngModel' # gain access to the associated model
priority: 1 # apply this directive before others
link: (scope, element, attrs, ngModel) ->
# no model? no poblem...
return if not ngModel
# set the view according to the model
ngModel.$render = -> element.html(ngModel.$viewValue || '')
# set the model according to the view
changeModel = -> ngModel.$setViewValue(element.text())
# watch for changes
element.bind 'blur keyup change', -> scope.$apply(changeModel)