How to Fix Grandma’s Network on Verizon FiOS

In my family, the person with the fastest Internet connection is… Grandma, a Vietnam War refugee who has never used a computer in her life. This is by virtue of her residence on a main road in the great state of Delaware, which gets fiber TV and Internet service through Verizon FiOS. She subscribes to the cheapest Internet plan so that the grandkids can tap away at their tablets during family gatherings. And on FiOS, the “lowest tier” is a blazing-fast symmetric connection: 100 Mbps down, 100 Mbps up.

It really isn’t fair, is it?

Grandma’s 20th-century tract home, like Grandma herself, was thrust only reluctantly into the digital age. It has no data cabling whatsoever besides two landlines and two coax ports, which, naturally, are both located on the extreme corners of the house—the worst possible positions to place a Wi-Fi access point. So for many years, the family ISP shitbox sat on one end of the house or the other, saddling the opposite side with all of the classic symptoms of crappy Wi-Fi coverage: buffering videos, sluggish webpages, frequent disassociations, and frustrated kids. This fall, I discovered one of my uncles (bless his heart) had attempted to cover the dead spot with a cheap access point and powerline networking kit from TP-Link. Immediately, my heart sank—powerline networking is almost always bad news. I marshaled together two laptops and ran iperf to test the performance of the link. It was a bottleneck… to put it mildly. On a network with a 100 Mbps uplink, the powerline connection achieved a whopping 12 Mbps.

The prehistoric MI-424WR is a dime a dozen on the Philadelphia-area Craigslist.

Right then and there, I decided it was time to blow up Grandma’s home network and start over. It had to go, all of it—the extra AP; the powerline adapters; even the Verizon router itself, a venerable Actiontec MI-424WR that hasn’t received a security patch in over a decade. My plan was to junk everything and install a whole-home Wi-Fi mesh system using Ethernet-over-coax (MoCA) technology for reliable backhaul. (A cross between “option 9” and “option 10,” for those of you who made their way here from DSL Reports’ FiOS guide.) We’re talking 802.11ac, dual-band, wired backbone, baby. At first, I set my sights on Google Wifi, but I found the price tag of Linksys Velop—the economy dual-band model can be had in 2-packs for just $100-150 total—a little more palatable. I wasn’t worried about cheaping out because, thanks to the MoCA backbone, I wouldn’t be relying on the performance (or lack thereof) of Velop’s wireless repeating.

I consider Belkin a third-rate brand, but I have to hand it to them for the job they’ve done on their Velop product. For example, if you run a home network with multiple access points, it’s important that they support roaming assistance—the 802.11k, v, and r standards—without which Wi-Fi clients tend to “stick” to the first AP they see and refuse to switch to another station, even if the-signal-quality-is-garbage-and-another-AP-is-right-there-so-why-the-hell-wouldn’t-you-switch-god-damnit. In the consumer space, basically nothing supports roaming assistance except for whole-home mesh systems—including, of course, Linksys Velop. Velop also autodetects the presence of an Ethernet connection between its nodes, and makes use of it for backhaul. (Some contemporary mesh systems, unbelievably, lack Ethernet ports altogether!) And bonus points for Velop’s online management interface; it’s refreshing to be able to manage a network without installing yet another smartphone app.

For my MoCA adapters, I cheaped out and bought a pair of Actiontec WCB3000N‘s, which regularly go for $20 each, used. Testing with iperf clocked their maximum speed at about 100 Mbps. (The newest stuff on the market can exceed gigabit speeds, but I couldn’t justify paying triple the cost for speeds nobody in Grandma’s house would ever need or use.) Each WCB3000N comes with a pair of very outdated 802.11n Wi-Fi radios, the idea being that the device can act as a coax-backed “Wi-Fi extender.” Um, thanks, but no thanks; I’d just like the MoCA part, please. But what’s this? No web interface option to disable Wi-Fi? WTF?! Hilariously, there is indeed a control—it’s just hidden by a little bit of CSS.

And for my next trick, I shall make the “Wireless Radio” checkbox disappear!

You see, WCB3000N’s are so cheap because the market is flooded with examples that were handed out by ISP’s. Mine came from Spectrum, who apparently removed the ability to disable Wi-Fi to make their product “idiot-proof.” One brave soul on GitHub got the GPL source to compile and released a custom build that restores the missing control. Unfortunately, there’s one bug still unresolved: The setting to disable the 2.4GHz radio doesn’t stick after a reboot. Pooey. I turned off SSID broadcast on that network and called it a day.

There are some special considerations to mind when working on a FiOS network. First, the cable boxes require an IP connection to download TV guide data from Verizon. Although some models (including the one my Grandma has) have Ethernet ports, they are not activated, and the connection has to be made using their builtin MoCA adapters. Fortunately, the boxes can link with any commodity MoCA adapter—including the WCB3000N I was using to network the Velops. Second, the remote DVR and on-screen caller ID features won’t work if a Verizon router isn’t the gateway. In my case, the loss of neither of these mattered to Grandma…

Another complication is the connection from the router (my base Velop node) to the Optical Network Terminal on the side of the house, which can be made via either MoCA or Ethernet. Contemporary installs use Ethernet, but older FiOS installs—including, you guessed it, Grandma’s—used coax, probably so the installers could spare themselves the trouble of running a new Ethernet line. The coax connector on a FiOS-branded router conceals two MoCA adapters: the “LAN-side” one, which runs on channel D1 and connects to the cable boxes, and the “WAN-side” one, which runs on the less-common channel C4 and connects to the ONT. The use of differing frequencies keeps both MoCA network segments logically separate.

Network SegmentMoCA FrequencyConnected Devices
WANC4Router; ONT
LAND1Router; cable boxes; Wi-Fi; Ethernet

Running a new Ethernet line wasn’t an option—Grandma would’ve strangled me if I broke out the drill and started punching holes in her precious house. So, I needed a MoCA adapter that could operate on channel C4 and talk to the ONT. Turns out these adapters have gone nearly extinct! The Arris MEB1100, which Verizon distributes to FiOS customers, seems to be the only one still in production. But I wondered if it was possible to dodge this purchase by placing Grandma’s existing FiOS router into bridge mode. The Actiontec web interface has no obvious option to do this; but as it turns out, it is indeed possible!

The key is the “Network Connections” screen, which allows you to modify the router’s internal network topology to your heart’s content. You can accomplish a bridging configuration by detaching the “Ethernet/Coax” interface from the “Network” bridge and bridging it with the “Broadband Connection” interface. Unfortunately, if your model lacks the ability to separate the Ethernet and coax interfaces, you’ll have to disable the LAN-side MoCA adapter.

(A screenshot found on Google Images. Not my actual configuration!)

By doing this, you’ll lose all access to the web interface except through the Wi-Fi hotspot, which will become its own, isolated network segment. Just set the SSID to something unique, like MI424WR_Admin, and use the same WPA password printed on the unit so that it’s easy to remember in the event you need to access the configuration screen again. (It’s not a security concern to keep an Actiontec router in service like this, because the web interface is not accessible from the Internet—or even your own LAN.) Then, when you plug the WAN port of your own router into one of the LAN ports on the Actiontec, your router will receive a public IP address, and you’ll be off to the races.

So, several equipment overhauls and a few coax splitters later, Grandma’s network went from this:

To this:

Did I over-engineer the crap out of it? Probably. But at least you can start a download anywhere in Grandma’s house and get the full 100 Mbps.

The 30-Second WebRTC Guide

(Web technology changes fast! Mind the date this post was written, which was November 2019.)

I get the feeling nobody uses WebRTC in the real world, since all of the tutorials use the same toy examples that don’t involve any actual network connectivity. That’s a shame, because WebRTC makes peer-to-peer communication a cakewalk. Somewhere in our imaginations, there’s a whole category of decentralized web apps, just waiting to get written!

Anyway, this post serves as a quick, practical guide to WebRTC. The first thing to realize is that it’s not just another web API that’s ready to go out of the box—WebRTC requires three distinct services to work its magic. Fortunately, the browser handles much of the communication behind the scenes, so you don’t need to worry about all of the nitty-gritty details.

A network diagram illustrating the relationships between signalling, STUN, and TURN servers and browsers.
The relationships between browsers and servers in WebRTC. Diagram courtesy of draw.io.
let me = { isInitiatingEnd: () => { ... },
           sendToOtherEnd: (type, data) => { ... } };

To use WebRTC, you need some kind of out-of-band signalling system—in other words, a middleman—to deliver messages between the browsers. This is how they exchange the networking information necessary to negotiate a direct connection. Obviously, if they could deliver it directly, then they would have no need for WebRTC!

The design of the signalling system itself is left entirely up to you. Choose any technology you please—WebSocket, QR code, email, carrier pigeon. As we will see, WebRTC provides the necessary hooks to abstract over the underlying technology.

const STUN_SERVERS = { urls: ["stun:stun.l.google.com:19302"] },
      TURN_SERVERS = { urls: "stun:stun.example.com", username: ..., credential: ... };
let rtc = new RTCPeerConnection({ iceServers: [STUN_SERVERS, TURN_SERVERS]});

If you expect your WebRTC session to traverse different networks, your clients will also need access to a Session Traversal Utilities for NAT (STUN) server. This is a service that informs browsers of their public IP address and port number, which can only be determined from a host on the public Internet. (STUN servers consume very little resources, so there are many that are freely available.)

Sometimes, despite the browsers’ best efforts, the network topology is too restrictive to achieve a direct connection. When this happens, WebRTC can fallback to a Traversal Using Relays around NAT (TURN) server, which is another middleman that can forward network traffic between clients. It’s like your signalling server, except it uses a standardized protocol explicitly designed for high-bandwidth streams. The more clients need such a middleman, the more bandwidth the TURN server will consume; therefore, if you want one, you will most likely need to run your own.

if (me.isInitiatingEnd())
        rtc.addEventListener("negotiationneeded", async (event) => {
                await sdpOffer = await rtc.createOffer();
                await rtc.setLocalDescription(sdpOffer);
                me.sendToOtherEnd("SDP-OFFER", sdpOffer);
        });
rtc.addEventListener("icecandidate", async (event) => {
        if (event.candidate)
                me.sendToOtherEnd("ICE-CAND", event.candidate);
});

me.receiveFromOtherEnd = async (type, data) => {
        switch (type) {
        case "SDP-OFFER":
                await rtc.setRemoteDescription(data);
                const sdpAnswer = await rtc.createAnswer();
                await rtc.setLocalDescription(sdpAnswer);
                me.sendToOtherEnd("SDP-ANSWER", sdpAnswer);
                break;
        case "SDP-ANSWER":
                await rtc.setRemoteDescription(data);
                break;
        case "ICE-CAND":
                await rtc.addIceCandidate(data);
                break;
        }
};

Okay, this is the big one—here, the browsers use your signalling system to perform a two-phase pairing operation. First, in the Session Description Protocol (SDP) phase, they share information about audio, video, and data streams and their corresponding metadata; second, in the Interactive Connectivity Establishment (ICE) phase, they exchange IP addresses and port numbers and attempt to punch holes in each other’s firewalls.

WebRTC provides the negotiationneeded and icecandidate events to abstract over your signalling system. The RTCPeerConnection object fires these events whenever the browser needs to exchange SDP or ICE information (respectively), which can happen multiple times over the course of a WebRTC session as network conditions change.

Only the side that initiates the connection need be concerned with negotiationneeded. There’s a specific protocol both sides need to follow when responding to these events, or to messages from each other—it’s best to let the code speak for itself.

let dataChannel = rtc.createDataChannel("data", { negotiated: true, id: 0 });
dataChannel.addEventListener("open", (event) => { ... });

Finally, set up your media and data streams. (For data channels, you can get away with a negotiated opening, which means the stream is pre-programmed on both ends and doesn’t require another handshake.) Wait for any open events to be fired.

You’re all done!

For Alon Levy, the Future Is Not Retro, but It Might Still Surprise

“The Future is not Retro,” declares a recent Pedestrian Observation post that has been my metaphorical pea under the mattress for the last several weeks. Its tone is so bombastic and cavalier that the piece is difficult to take entirely seriously—much like another Alon Levy hot take, “The NTSB Wants American Trains to Be Less Safe,” or that infamous Market Urbanism tweet about rebuilding Notre Dame as a contemporary mixed-use skyscraper—but as an indicator of the way we urban planning nerds think and talk about cities, it should be taken very seriously, indeed.

Much of the post details Levy’s vision for the future of urban development, which goes something like this: In a few decades from now, the cities of the West—the largest of them, anyway—will look increasingly like the crowded, transit-oriented metropolises of East Asia. They will be crisscrossed by driver-less metros, whose stations will be surrounded by clusters of high-rise offices and residential towers, and be linked together by high-speed rail for zero-emissions, long-distance travel. Vacations will entail a bullet train ride to San Francisco or Miami instead of a road trip to some bygone natural wonder. (As for mid-to-lower tier cities, and the National Park Service, the outlook is rather grim—Levy fully expects those to shrivel up and die.)

Continue reading “For Alon Levy, the Future Is Not Retro, but It Might Still Surprise”

Movie Review: Moana (2016) Was Quintessential Late-Stage Disney

So far, 2019 has seen the release of the new Dumbo, the new Aladdin, and the new Lion King—and a new Mulan, by the way, is in the works, too. All-mighty Disney used to inspire kids to sing about the “circle of life”; now they’ve got critics jeering cynically about the “circle of franchise reboots.” Et tu, Mickey? At least, Disney partisans can reassure themselves, creative bankruptcy is a relatively new look for the studio. In 2016, a year that wasn’t so long ago (best remembered, or forgotten, for a presidential election of biblical proportions), Walt Disney Animation released the surprisingly clever Zootopia, a thinly-veiled allegory for present-day identity politics and the millennial urban condition. Then November saw the release of Moana, a slightly-less-clever movie about a South Pacific teenage girl endowed with magical powers and a destiny to save the world.

Continue reading “Movie Review: Moana (2016) Was Quintessential Late-Stage Disney”

Introducing Sia Slice, My Absurdly Cheap Block Storage Solution

Sia Slice in action. (On a remote system, with tmux.)

I dabble in cryptocurrencies, occasionally. I hesitate to get too partisan on a subject the Internet takes very seriously, but it seems to me that the fairest judge of a coin’s value is the utility it provides to its holders. So Bitcoin is useful because everyone recognizes and accepts Bitcoin, Monero is useful because it facilitates anonymous transactions, Ethereum has that smart contracts thing going for it, and so on and so forth.

Continue reading “Introducing Sia Slice, My Absurdly Cheap Block Storage Solution”

Movie Review: John Wick: Chapter 3 – Parabellum

On the night I opened my browser to buy a ticket to see John Wick: Chapter 3, I had never actually seen any of the John Wick films before. Fortunately, the premise of this sequel-to-a-sequel isn’t terribly complicated: John Wick himself (Keanu Reeves) is a professional assassin who has run afoul of the High Table, a world-spanning secret society of assassins. Wick is a master of his occupation, which entails committing dozens upon dozens of grisly on-screen murders with improvised weapons.

My movie buddies–human repositories of John Wick lore and repeat viewers of all his films–assured me the movie’s selling point would be its fight choreography. And at first, it indeed wowed me: The action started out as quirky, exhilarating fun. But as the gun-slinging, knife-slashing power fantasy dragged on, I couldn’t escape the feeling that that initial novel spark was gradually devolving into empty spectacle.

Continue reading “Movie Review: John Wick: Chapter 3 – Parabellum”

How to Assign IPv6 Addresses to LXD Containers on a VPS

This post was rewritten on July 26, 2019 to incorporate a cleaner solution. The original version can be viewed here.

LXD is my favorite containerization stack. For my use case, which is running various services on the same machine with isolated root filesystems, it’s more flexible and easier to use than Docker, particularly in terms of networking capabilities.

Using LXD, I can bridge all of my containers to my local LAN, thereby providing each of them a unique local IPv4 and global IPv6 address. This makes it very easy to forward ports and set firewall rules to open services to the outside world—no more fumbling around with awkward PORT directives and multiple levels of NAT44, as is the case in Docker land.

But this setup gets complicated when you use attempt to use LXD on a commodity Virtual Private Server (VPS), because the IPv6 configuration these providers use is rather strange and counter-intuitive. (I’ll tell you exactly why when we get there.) So, here is how you can get globally routable, public-facing IP addresses for your containers on your $30/year VPS, without any application-level hacks like TCP/UDP proxying, port forwarding, or that abomination known as NAT66.

The Setup: The host is a VPS running Ubuntu, or your choice of contemporary distribution. The provider has allocated a virtualized network interface, net0, to connect to the Internet with IPv4 and IPv6 addresses. The containers will be attached to lxdbr0, a bridge interface managed by LXD.

$ ip link show
...
2: net0: <BROADCAST,MULTICAST,ALLMULTI,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 54:52:00:4f:5c:b3 brd ff:ff:ff:ff:ff:ff
3: lxdbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether fe:34:61:66:1b:ae brd ff:ff:ff:ff:ff:ff

So far, every VPS seller I’ve purchased from assigns each customer an entire /64 “prefix” (or a small subset of a prefix, or even a single address), but instead of using prefix delegation to advertise and route this prefix—as an Internet provider or cellular operator with native IPv6 would—they unceremoniously dump your server, and the servers of your “neighbors,” onto a common /48 prefix with a static gateway.

The following table, copied verbatim from my VPS provider’s network configuration page, suggests this is the result of a misguided attempt to translate a legacy IPv4 configuration into IPv6-speak:

IP AddressGatewayNetmask
2602:ff75:7:373c::/642602:ff75:7::1/48
104.200.67.206104.200.67.1255.255.255.0

The Red Herring: You can’t just bridge your containers with net0, because the VPS’s network usually drops traffic from unexpected MAC addresses. Try it yourself: run ip link set net0 address 112233445566 and see if you lose connectivity.

The Solution

Delegate your /64 prefix, or some subset of it, to lxdbr0, and configure LXD to use your choice of SLAAC or DHCPv6 to assign addresses to your containers. Then use the NDP Proxy Daemon to advertise the presence of your containers to the wider /48 prefix.

Set up LXD networking

Assign an IPv6 prefix to lxdbr0 with LXD. If you allocate your entire /64, you may use SLAAC:

$ lxc network set lxdbr0 ipv6.address 2602:ff75:7:373c::1/64

But if you want to reserve parts of your prefix for other purposes, you must use stateful DHCPv6:

$ lxc network set lxdbr0 ipv6.address 2602::ff75:7:373c::ea:bad:1/112
$ lxc network set lxdbr0 ipv6.dhcp.stateful true
$ lxc network set lxdbr0 ipv6.dhcp.ranges 2602::ff75:7:373c::ea:bad:2-2602::ff75:7:373c::ea:bad:255 # optionally

Sample configuration:

$ lxc network show lxdbr0
config:
  ipv4.address: none
  ipv6.address: 2602:ff75:7:373c::1/64
  ipv6.dhcp: "false"
  ipv6.firewall: "true"
  ipv6.nat: "false"
  ipv6.routing: "true"

Set up host networking

You must use on-link addressing for net0; do not attach the shared /48 prefix. If the prefixes assigned to two different interfaces (e.g., a /48 on net0 and a /64 on lxdbr0) overlap, dnsmasq will seemingly fail to send Router Advertisements, breaking automatic IPv6 configuration.

On Ubuntu, netplan is supposed to be able to configure this, but the on-link addressing option is currently broken for IPv6. (May 2020 update: Micha Cassola of Foundyn found a way to accomplish this with pure netplan; see this thread.) Therefore, you must use ifupdown, augmented with some scripted iproute2 glue:

# apt install ifupdown
# cat >>/etc/network/interfaces
auto net0
iface net0 inet static
        address 104.200.67.206/24
        gateway 104.200.67.1
        up ip -6 address add 2602:ff75:7:373c::/128 dev net0
        up ip -6 route add 2602:ff75:7::1/128 onlink dev net0
        up ip -6 route add default via 2602:ff75:7::1
        down ip -6 route delete default via 2602:ff75:7::1
        down ip -6 route delete 2602:ff75:7::1/128 onlink dev net0
        down ip -6 address delete 2602:ff75:7:373c::/128 dev net0

Your IPv6 routing table should thus resemble:

$ ip -6 route show
2602:ff75:7::1 dev net0 metric 1024 pref medium
2602:ff75:7:373c:: dev net0 proto kernel metric 256 pref medium
...
default via 2602:ff75:7::1 dev net0 metric 1024 pref medium

Set up NDP proxying

Finally, use ndppd to make your containers “appear” on the same broadcast domain attached to net0. Here is a sample configuration file (for further information, see the manual):

# cat >/etc/ndppd.conf
proxy net0 {
    rule 2602:ff75:7:373c::/64 {
        iface lxdbr0
        router no
    }
}

Alternatively, you can use the kernel’s builtin NDP proxy facility. You have to insert each address one-by-one, and the command does not stick across reboots:

# sysctl -w net.ipv6.conf.all.proxy_ndp=1
# ip -6 neighbour add proxy 2607:f8b0:4004:811::2 dev net0
# ip -6 neighbour add proxy 2607:f8b0:4004:811::3 dev net0
...

Conclusion

You’re all done!

$ lxc list
+------------+---------+------+--------------------------------------------+------------+-----------+
|    NAME    |  STATE  | IPV4 |                    IPV6                    |    TYPE    | SNAPSHOTS |
+------------+---------+------+--------------------------------------------+------------+-----------+
| container1 | RUNNING |      | 2602:ff75:7:373c:216:3eff:fedd:3f4e (eth0) | PERSISTENT | 0         |
+------------+---------+------+--------------------------------------------+------------+-----------+
| container2 | RUNNING |      | 2602:ff75:7:373c:216:3eff:fe5d:5f6a (eth0) | PERSISTENT | 0         |
+------------+---------+------+--------------------------------------------+------------+-----------+
$ lxc exec container1 -- ip -6 addr show eth0
13: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 2602:ff75:7:373c:216:3eff:fedd:3f4e/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 3145sec preferred_lft 3145sec
    inet6 fe80::216:3eff:fedd:3f4e/64 scope link 
       valid_lft forever preferred_lft forever
$ lxc exec container1 -- ip -6 route show
2602:ff75:7:373c::/64 dev eth0 proto ra metric 1024 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
default via fe80::e432:28ff:fe6c:b421 dev eth0 proto ra metric 1024 hoplimit 64 pref medium
$ lxc exec container1 -- ping -c 4 google.com
PING google.com(bud02s28-in-x0e.1e100.net (2a00:1450:400d:805::200e)) 56 data bytes
64 bytes from bud02s28-in-x0e.1e100.net (2a00:1450:400d:805::200e): icmp_seq=1 ttl=47 time=153 ms
64 bytes from bud02s28-in-x0e.1e100.net (2a00:1450:400d:805::200e): icmp_seq=2 ttl=47 time=153 ms
64 bytes from bud02s28-in-x0e.1e100.net (2a00:1450:400d:805::200e): icmp_seq=3 ttl=47 time=153 ms
64 bytes from bud02s28-in-x0e.1e100.net (2a00:1450:400d:805::200e): icmp_seq=4 ttl=47 time=153 ms

--- google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3003ms
rtt min/avg/max/mdev = 153.202/153.324/153.412/0.486 ms

Enjoy having end-to-end connectivity on your containers, the way the Internet was intended to be experienced.

Post-script: If you still need IPv4 (looking at you, ppa.launchpad.net), you can let LXD handle the NAT44 configuration, or use a public NAT64/DNS64 gateway.

Walking with Southeast Bakersfield on MLK Jr. Day

Last week, I swung by the Wilson Library to attend Bakersfield’s 2019 Martin Luther King Jr. Day march. To me, it was a march that lacked bite, because the adjectives I would use to describe that event—docile, tame, by-the-book, ordered—are not words I would normally associate with a march for civil rights. Yet how else would I describe a civil rights march that had an itinerary? It had been sent out in the Bakersfield Californian in advance: set off at 6 o’ clock, march to the tune of “We Shall Overcome,” and then attend a service at the neighborhood church. As it turned out, we and our police and media escorts followed this kindergarten-field-trip schedule to the letter.

The march began just past 6. The crowd—about people 30 to 40 in size, dressed in coats and down jackets, appropriate for the chilly January night—was small, according to two of the marchers, both of whom had been regular attendants for years. One of them told me that the march was abbreviated, too, because it used to extend “all around town”; now, considering the age of the participants—mostly middle-aged, some elderly, several with Vietnam veteran’s caps—the march had been truncated to a half-mile walk to the community church. We hope more young people like you will join us in the future, both of them said.

Nonetheless, the parade moved surprisingly briskly. About a third of the marchers formed a line in front, standing side-by-side with their arms locked, singing “We Shall Overcome.” The rest formed a vaguely organized mass of people, some of them talking among themselves, some of them joining in the singing, some of them holding up their smartphones to recording the proceedings.

As we walked, the cameramen darted nimbly in and out of the crowd, capturing the scene from all sorts of angles—one of them equipped with a large, brilliant rectangular torch as bright as any construction site work light—while the police barricaded the intersecting roads with their squad cars and shut off the lone traffic signal along the marchers’ route. The attention our crowd received was proportionate to its small size, for there was little foot traffic in this ramshackle corner of Southeast Bakersfield—populated as it was with dilapidated strip malls, humble taco trucks, and working-class ranch homes—but some neighbors did observe the goings-on, out of curiosity, from the chain link fences that lined their modest front yards.

But where was the shock value, the spontaneity? I kept waiting for someone to take the initiative, to heckle the cops or to shout a defiant headliner into the cameras. Public demonstrations are supposed to be calls to action, not just background roll on the evening news. After all, for every feelgood speech that MLK made, there was also a sit-in, bus boycott, freedom ride, or some other provocation that deliberately pissed a lot of people off.

Okay, I suppose an MLK march has to be inclusive of all ages, not just rebellious 20-somethings like myself. Besides, who am I to assume what Bakersfield’s Black community wants and needs? Bakersfield is a conservative town, and the issues faced by the coastal metropolises—affirmative action and blatant racism, among other issues raised by attendees at their MLK marches—seem many worlds away on these sleepy streets.

There was an informal meet and greet before the march began at 6, through which I was treated to an anthropological cross-section of the marchers. I saw friends meeting old friends and exchanging greetings, news, and small talk under the harsh streetlights of the Wilson Library parking lot. I also saw church buses, vans, and carpools bringing more groups of people to the march. As one might expect, most of the marchers were Black, but people of all races were in attendance—some Whites, some Latinos, and a few Asians—which would have pleased one spirited marcher, who made an impromptu speech expressing hope for a multiracial march in the spirit of MLK Jr. himself.

Surrounding us were the policemen who would close the streets and the television crews who would send images of the march to the rest of the city. Already, the cameramen were mingling in the crowd, shooting footage and interviewing some of us. The policemen stood idly by their squad cars, watching, preparing to halt traffic on Wilson Road on a moment’s notice. Later on, when we got the church, I saw some of them shaking hands with the marchers.

Sitting in one of the pews of the Church of Christ, watching Black leaders from Arvin and Fresno deliver speeches that struck a delicate balance between religious sermon and political lecture—Moses’ delivery of the Israelites out of Egypt as a metaphor for the civil rights struggle was the highlight of the night, as was a firm reminder that social equality had not brought about economic equality—I realized I had learned more about Black culture than I had in all my years of growing up in Bakersfield. The media, to their credit, were watching diligently, too. They had set up a camera inside of the chapel, and one of their anchors also live-streamed a very moving performance by the church’s choir.

Maybe I’m the one who has the wrong idea about MLK Jr. Day. The event is as much a focal point for the Black community as it is a rallying cry for social justice. Sure, we didn’t create headlines, but how important is a few minutes of a fame in our present-day outrage culture, anyway? I like to think that between the marchers, the church, the police, and the media, we all learned a little bit more about each other that evening. Isn’t that what Mr. King would have wanted?

So You Want to Fix Your City

A contemporary review of The Power Broker, Robert Caro’s biographical masterpiece.


Big City, USA, in 2019—where the traffic doesn’t move at rush hour, the roads are full of potholes, the mass transit is useless, and the schools and parks are overcrowded and falling apart—and despite all of that, the rent is still too damn high.

Some say the modern American city increasingly resembles one of those generic science-fiction dystopias, neatly divided into the privileged and the underclass, beholden to the whims of Amazon and Alphabet and other such faceless corporations. Well, screw that. It’s about time we fixed our cities so they started to work for us again. All we have to do is agree on our diagnosis and its proper cure—easy peasy, right?

I’ve heard a lot of ideas on how to give our cities a good government kick, ideas from property tax reform to zoning reform to congestion pricing to privatization. Most of them are just plain stupid, but a handful sound like they might work. You probably have a few ideas yourself (don’t worry, they’re some of the good ones—I promise.) That’s great, because we need more interested citizens like you. But before you rush off to moonlight as a civic activist, I’d like you to meet this interesting figure, a reformer who worked in New York City throughout much of the twentieth century.

His name is Robert Moses, and he will teach you some valuable lessons about how cities work.

Continue reading “So You Want to Fix Your City”

Debunking the Urbanist Fairy Tale

Put succinctly, urbanism is the belief that we can fix everything wrong with our cities by building them upward instead of outward, shunning suburbs, homesteads, and cars for row homes, bike lanes, and public transit.

The case goes something like this: Climate change? Density reduces the land footprint of cities and the amount of resources consumed by their residents. Traffic congestion? Density encourages public transportation usage, reducing the numbers of cars on crowded city streets. Housing affordability? Density increases the number of homes available to renters and buyers, bringing down the price of housing for all.

With most of our cities facing kind of urban crisis–subways and water pipes failing, rents rising without a ceiling in sight, traffic congestion ensnaring recovering economies–urbanism has evolved into a national movement. Its political advocacy is actualized by YIMBY (“Yes In My Backyard”) parties that are thriving in every large American city, loosely organized around a single issue: build, baby, build more housing. Like their cyberspace counterpart, the Pirate Party, the “YIMBY Party” are brazen, cocksure, and social media-savvy.

I know this because I used to call myself an urbanist. It was the natural label for someone like me, who cycles for transportation, rides public transit, and has never owned a car; I simply wanted to build better cities where these things were both viable and encouraged. So I jumped on the urbanist bandwagon. I subscribed to the Twitter feeds, read articles from Streetsblog and Curbed, and proselytized the fruits of market urbanism on my Daily Texan column. And for awhile, all was well–until I made some observations that were difficult to reconcile with urbanism’s precepts. I began to reexamine my beliefs–and cities, it turns out, are pretty complicated.

For one thing, urbanism’s prescriptions for cities don’t actually work.

Urbanism says that the way to fix cities is to build them up, and that the way to build cities up is to abolish land-use restrictions. This would pave the way for property owners to construct more housing units and thereby more dense, vibrant, and livable neighborhoods. At least, so goes the theory. But it’s an open secret among those who pay attention to cities that that is not what really happens when private infill development is let loose by urban planners and politicians.

The “urban villages” that developers build may have the signature high-rise condos, bike lanes, narrow streets, and roundabouts, but they bear little resemblance to the walkable, diverse, pre-automobile neighborhoods that urbanists so often celebrate. They are not walkable, but isolated and disconnected; nor vibrant and diverse, but sterile and exclusive. Far from Jane Jacobs’ intricate sidewalk ballet, the environment created by these new-age communities more closely resembles one of Le Corbusier’s dehumanizing machines for living.

Once again, I know this from firsthand experience. Austin is littered with many such failed New Urbanist experiments in community master-planning. Two of the most prominent are the Mueller and Domain neighborhoods, both designed to be incorporate all of the things urbanists strive for in a neighborhood–density, mixed-use, and transit-oriented design. Their tiny lots contain multi-story row homes and stores, woven together by narrow streets, wide promenades, and bike boulevards.

But closer inspection reveals some troubling details. Mueller, for example, is surrounded by a moat of empty space and parkland that segregates it from the rest of impoverished East Austin. And until recently, the neighborhood also lacked any basic bus service, although that did not stop Mueller residents and developers from lobbying for a streetcar line to downtown. The Domain, meanwhile, is filled with high-end chain stores (including one of Austin’s only Apple Stores, and a Whole Foods) and stories upon stories of covered parking garages. It has a stop for one of Austin’s rapid bus lines, but the bus lets off at a large parking lot, half a mile away from the shops.

The Domain, Austin’s much-hyped “second downtown.” (Credit: Austin Towers)

Nor has the neighborhoods’ design produced the social kumbaya that their high-minded architects envisioned. In Mueller, a neighborhood so liberal that it produced a nationally mocked Beto O’Rourke ad, residents of color routinely grapple with racial profiling. In the Domain, it’s no secret that the stores are largely stores for the affluent, and my UT friends tell me how “overwhelmingly white” the place feels compared to old Austin.

Mueller has attractive New Urbanist row homes and streetscape–but for whom? (Credit: Community Impact)

Worse still, elsewhere in Austin, as is the case in many of the world’s most affluent cities, new communities have been carved entirely out of old ones, leaving their once-vibrant streets safer and “cleaner” but also desolate and lifeless, not to mention the real human cost in terms of destroyed social networks and lost access to services and opportunities. Gentrification, contrary to urbanist rhetoric about “place-making” and “livable cities,” is both very real and morally reprehensible.

Why is it that every attempt to realize the urbanist formula seemingly ends in a gated community for the rich? Has urbanism simply not been given a fair shake–and if we kept critiquing its implementation, and iterating on it, the rents would eventually come down?

It isn’t so. The truth is that urbanism is allied with powerful forces that dominate municipal governments, forces that are not interested in just or equitable outcomes for cities. Urbanists like to portray themselves as a scrappy, millennial-led rebellion against backwards twentieth century urban planning paradigms. But far from fighting against the system, they’re part of it, complicit in its machinations.

Gone are the days when city governments simply apportioned services and infrastructure, if that ever was the case. Today, cities market themselves to global pools of capital and educated labor, competing for lucrative investments from the creative classes and the high-technology sectors. Urbanists may say that reinvestment in the urban core is a merely reflection of Americans’ desire to “return to the city,” but the reality is that urban redevelopment is the result of a conscious remaking of a city’s economy–a remaking that usually comes at the expense of its most marginalized residents.

In Austin, for example, the University of Texas has been the driving force behind the city’s meteoric transformation into the Silicon Valley of Texas, acquiring land to donate to high-tech firms to establish local headquarters–necessitating, of course, the removal of less fortunate residents who stood in the way. Similarly, Austin’s smart-growth policies–a prototypical form of urbanism that encouraged compact land development within the inner city–were forged by a compromise between Austin’s business and environmental elites, who sought to replace environmentally damaging suburban sprawl with “sustainable” urban development–which in turn entailed the displacement of powerless residents from East Austin.

The urban development projects that urbanists champion–no matter how dense, no matter how “inclusive,” no matter how transit-oriented–solely reflect the desires of the developers and politicians who implement them. And because they are intended for “desirable” residents–the creative classes, the wealthy–only “desirable” residents are welcome in them. This is why such glaring injustices as gentrification, and poor doors, and over-policing are all but inevitable, and why harebrained projects like Mueller and the Domain perpetuate inequality instead of disrupting it.

Urbanism is a huckster that reduces the problem of fixing cities to architectural fetishism, and cities aren’t defined by setbacks or parking minimums or any other boogeyman invented by urbanists to attack housing and transportation policies. Cities are defined by power–who has it, who doesn’t have it, and what the people ordained with it do with it. Only by acknowledging this difficult truth can we hope to make cities work for everyone, not just for the affluent few.