Just a Theory

By David E. Wheeler

Fascism Is Violence

Aleksandar Hemon, writing for Literary Hub:

[Fascism’s] ideas are enacted first and foremost upon the bodies and lives of the people whose presence within “our” national domain is prohibitive. In Bannon/Trump’s case, that domain is nativist and white. Presently, their ideas are inflicted upon people of color and immigrants, who do not experience them as ideas but as violence. The practice of fascism supersedes its ideas, which is why people affected and diminished by it are not all that interested in a marketplace of ideas in which fascists have prime purchasing power.

The error in Bannon’s headlining The New Yorker Festival would not have been in giving him a platform to spew his hateful rhetoric, for he was as likely to convert anyone as he himself was to be shown the light in conversation with Remnick. The catastrophic error would’ve been in allowing him to divorce his ideas from the fascist practices in which they’re actualized with brutality.

The New Yorker Festival bills itself as “Conversations on culture and politics,” but the important thing to understand about fascism — and its cohorts nationalism and white supremacism — is that it’s not a conversation. It’s not a set of ideas. No. Fascism is violence. One does not dialog with fascism. Fascism is a violent terror to be stopped.

Flaked, Brewed, and Docked

I released Sqitch v0.9998 this week. Despite the long list of changes, only one new feature stands out: support for the Snowflake Data Warehouse platform. A major work project aims to move all of our reporting data from Postgres to Snowflake. I asked the team lead if they needed Sqitch support, and they said something like, “Oh hell yes, that would save us months of work!” Fortunately I had time to make it happen.

Snowflake’s SQL interface ably supports all the functionality required for Sqitch; indeed, the implementation required fairly little customization. And while I did report a number of issues and shortcomings to the Snowflake support team, they always responded quickly and helpfully — sometimes revealing undocumented workarounds to solve my problems. I requested that they be documented.

The work turned out well. If you use Snowflake, consider managing your databases with Sqitch. Start with the tutorial to get a feel for it.

Bundle Up

Of course, you might find it a little tricky to get started. In addition to long list of Perl dependencies, each database engines requires two external resources: a command-line client and a driver library. For Snowflake, that means the SnowSQL client and the ODBC driver. The PostgreSQL engine requires psql and DBD::Pg compiled with libpq. MySQL calls for the mysql client and DBD::mysql compiled with the MySQL connection library. And so on. You likely don’t care what needs to be built and installed; you just want it to work. Ideally install a binary and go.

I do, too. So I spent the a month or so building Sqitch bundling support, to easily install all its Perl dependencies into a single directory for distribution as a single package. It took a while because, sadly, Perl provides no straightforward method to build such a feature without also bundling unneeded libraries. I plan to write up the technical details soon; for now, just know that I made it work. If you Homebrew, you’ll reap the benefits in your next brew install sqitch.

Pour One Out

In fact, the bundling feature enabled a complete rewrite of the Sqitch Homebrew tap. Previously, Sqitch’s Homebrew formula installed the required modules in Perl’s global include path. This pattern violated Homebrew best practices, which prefer that all the dependencies for an app, aside from configuration, reside in a single directory, or “cellar.”

The new formula follows this dictum, bundling Sqitch and its CPAN dependencies into a nice, neat package. Moreover, it enables engine dependency selection at build time. Gone are the separate sqitch_$engine formulas. Just pass the requisite options when you build Sqitch:

brew install sqitch --with-postgres-support --with-sqlite-support

Include as many engines as you need (here’s the list). Find yourself with only Postgres support but now need Oracle, too? Just reinstall:

export HOMEBREW_ORACLE_HOME=$ORACLE_HOME
brew reinstall sqitch --with-postgres-support --with-oracle-support

In fact, the old sqitch_oracle formula hasn’t worked in quite some time, but the new $HOMEBREW_ORACLE_HOME environment variable does the trick (provided you disable SIP; see the instructions for details).

I recently became a Homebrew user myself, and felt it important to make Sqitch build “the right way”. I expect this formula to be more reliable and better maintained going forward.

Still, despite its utility, Homebrew Sqitch lives up to its name: It downloads and builds Sqitch from source. To attract newbies with a quick and easy method to get started, we need something even simpler.

Dock of the Bae

Which brings me to the installer that excites me most: The new Docker image. Curious about Sqitch and want to download and go? Use Docker? Try this:

curl -L https://git.io/fAX6Z -o sqitch && chmod +x sqitch
./sqitch help

That’s it. On first run, the script pulls down the Docker image, which includes full support for PostgreSQL, MySQL, Firebird, and SQLite, and weighs in at just 164 MB (54 MB compressed). Thereafter, it works just as if Sqitch was locally-installed. It uses a few tricks to achieve this bit of magic:

  • It mounts the current directory, so it acts on the Sqitch project you intend it to
  • It mounts your home directory, so it can read the usual configuration files
  • It syncs the environment variables that Sqitch cares about

The script even syncs your username, full name, and host name, in case you haven’t configured your name and email address with sqitch config. The only outwardly obvious difference is the editor:1 If you add a change and let the editor open, it launches nano rather than your preferred editor. This limitation allows the image ot remain as small as possible.

I invested quite a lot of effort into the Docker image, to make it as small as possible while maximizing out-of-the-box database engine support — without foreclosing support for proprietary databases. To that end, the repository already contains Dockerfiles to support Oracle and Snowflake: simply download the required binary files, built the image, and push it to your private registry. Then set $SQITCH_IMAGE to the image name to transparently run it with the magic shell script.

Docker Docket

I plan to put more work into the Sqitch Docker repository over the next few months. Exasol and Vertica Dockerfiles come next. Beyond that, I envision matrix of different images, one for each database engine, to minimize download and runtime size for folx who need only one engine — especially for production deployments. Adding Alpine-based images also tempts me; they’d be even smaller, though unable to support most (all?) of the commercial database engines. Still: tiny!

Container size obsession is a thing, right?

At work, we believe the future of app deployment and execution belongs to containerization, particularly on Docker and Kubernetes. I presume that conviction will grant me time to work on these improvements.


  1. Well, that and connecting to a service on your host machine is a little fussy. For example, to use Postgres on your local host, you can’t connect to Unix sockets. The shell script enables host networking, so on Linux, at least, you should be able to connect to localhost to deploy your changes. On macOS and Windows, use the host.docker.internal host name.

The Farce of Consent

Cornell Tech Professor of Information Science Helen Nissenbaum, in an interview for the Harvard Business Review:

The farce of consent as currently deployed is probably doing more harm as it gives the misimpression of meaningful control that we are guiltily ceding because we are too ignorant to do otherwise and are impatient for, or need, the proffered service. There is a strong sense that consent is still fundamental to respecting people’s privacy. In some cases, yes, consent is essential. But what we have today is not really consent.

It still feels pretty clear-cut to me. I chose to check the box.

Think of it this way. If I ask you for your ZIP code, and you agree to give it to me, what have you consented to?

I’ve agreed to let you use my ZIP code for some purpose, maybe marketing.

Maybe. But did you consent to share your ZIP code with me, or did you consent to targeted marketing? I can combine your ZIP code with other information I have about you to infer your name and precise address and phone number. Did you consent to that? Would you? I may be able to build a financial profile of you based on your community. Did you consent to be part of that? I can target political ads at your neighbors based on what you tell me. Did you consent to that?

Well this is some essential reading for data privacy folx. Read the whole thing.

I myself have put too much emphasis on consent when thinking about and working on privacy issues. The truth is we need to think much deeper about the context in which consent is requested, the information we’re sharing, what it will be used for, and — as Nissenbaum describes in this piece — the impacts on individuals, society, and institutions. Adding her book to my reading list.

(Via Schneier on Security)

ACM Code of Ethics

From the Preamble to the ACM Code of Ethics and Professional Conduct, updated this summer after two years of development:

The Code as a whole is concerned with how fundamental ethical principles apply to a computing professional’s conduct. The Code is not an algorithm for solving ethical problems; rather it serves as a basis for ethical decision-making. When thinking through a particular issue, a computing professional may find that multiple principles should be taken into account, and that different principles will have different relevance to the issue. Questions related to these kinds of issues can best be answered by thoughtful consideration of the fundamental ethical principles, understanding that the public good is the paramount consideration. The entire computing profession benefits when the ethical decision-making process is accountable to and transparent to all stakeholders. Open discussions about ethical issues promote this accountability and transparency.

The principles it promotes include “Avoid harm”, “Be honest and trustworthy”, “Respect privacy”, and “Access computing and communication resources only when authorized or when compelled by the public good”. The ACM clearly invested a lot of time and thought into the updated code. Thinking about joining, just to support the effort.

They Is Smart

Self-professed language nerd Amy Devitt says They is Smart:

Mixing singular and plural is pretty common in most people’s speech and even writing:

“The analysis of all the results from five experiments support that claim.”

And one common expression mixing singular and plural even sounds a lot like “They is” (and is often pronounced that way):

“There’s two kinds of people in this world.”
“There’s lots of reasons we shouldn’t go to that party.”

So maybe it won’t sound so weird after all:

“Sam volunteers at the homeless shelter. They’s someone I really admire.”

Some varieties of English already match plural “they” with a singular verb:

“they wasn’t satisfied unless I picked the cotton myself” (Kanye West line in New Slaves)
“They is treatin’ us good.” (Dave Chappelle Terrorists on the Plane routine)
“They wasn’t ready.” ​(Bri BGC17 commenting on Oxygen Bad Girls Club experience)

So why not singular “they” with a singular verb?

“They wasn’t going to the party alone.”

Using singular verbs when we’re using “they” to refer to one person might not be so weird after all.

We have the same issue in some ways with singular “you.” Standard English varieties tend to use a plural verb even with singular “you.” So “you are a fine person,” not “you is a fine person.”

Except lots of varieties and lots of speakers do use “you is.”

I’ve been thinking about this piece a lot since I read it a couple weeks ago. It wasn’t that long ago that I surrendered to using “they” as a gender-neutral singular pronoun; today I’m embarrassed that my grammarian elitism kept me from denying it for so long. No more of that! If anyone asks me to use singular verbs with singular “they”, I’ll happily do it — and correct my habitual mouth when it fails me. For they who wants to be referred to using singular verbs, I will use singular verbs.

Intellectually, I find this whole idea fascinating, mostly because it never occurred to me, feels unnatural in my stupid mouth, but seems so obvious given the examples. Some dialects have used this form since forever; the internal logic is perfect, and only cultural elitism and inertia have repressed it. They wasn’t satisfied indeed.

But logic is a flexible thing, given varying semantics. In an addendum to the piece, Amy writes:

Edited: Chatting with my linguist friends Anne, Peter, and Jim gave me a new way to talk about this topic. The form of the verb “are” (“They are”) might be plural, but in the context of a singular “they” the verb would have singular meaning, too. We do that with “you.” You are a good friend, Sue. The “are” is singular just as the “you” is. So if we do start using “they” as the sole singular pronoun, we wouldn’t have to change the form of the verb to make it singular. It would already be heard as singular.

We are creative and flexible in using language. What a wonderful thing!

So just as “they” can be used as a singular pronoun, plural conjugations become singular when used with singular “they” if we just say they are. Everybody’s right! So make a habit of using the most appropriate forms for your audience.

More about…

pgenv

For years, I’ve managed multiple versions of PostgreSQL by regularly editing and running a simple script that builds each major version from source and installs it in /usr/local. I would shut down the current version, remove the symlink to /usr/local/pgsql, symlink the one I wanted, and start it up again.

This is a pain in the ass.

Recently I wiped my work computer (because reasons) and started reinstalling all my usual tools. PostgreSQL, I decided, no longer needs to run as the postgres user from /usr/local. What would be much nicer, when it came time to test pgTAP against all supported versions of Postgres, would be to use a tool like plenv or rbenv to do all the work for me.

So I wrote pgenv. To use it, clone it into ~/.pgenv (or wherever you want) and add its bin directories to your $PATH environment variable:

git clone https://github.com/theory/pgenv.git
echo 'export PATH="$HOME/.pgenv/bin:$HOME/.pgenv/pgsql/bin:$PATH"' >> ~/.bash_profile

Then you’re ready to go:

pgenv build 10.4

A few minutes later, it’s there:

$ pgenv versions
pgsql-10.4

Let’s use it:

$ pgenv use 10.4
The files belonging to this database system will be owned by user "david".
This user must also own the server process.
#    (initdb output elided)
waiting for server to start.... done
server started
PostgreSQL 10.4 started

Now connect:

$ psql -U postgres
psql (10.4)
Type "help" for help.

postgres=# 

Easy. Each version you install – as far back as 8.0 – has the default super user postgres for compatibility with the usual system-installed version. It also builds all contrib modules, including PL/Perl using /usr/bin/perl.

With this little app in place, I quickly built all the versions I need. Check it out:

$ pgenv versions
     pgsql-10.3
  *  pgsql-10.4
     pgsql-11beta2
     pgsql-8.0.26
     pgsql-8.1.23
     pgsql-8.2.23
     pgsql-8.3.23
     pgsql-8.4.22
     pgsql-9.0.19
     pgsql-9.1.24
     pgsql-9.2.24
     pgsql-9.3.23
     pgsql-9.4.18
     pgsql-9.5.13
     pgsql-9.6.9

Other commands include start, stop, and restart, which act on the currently active version; version, which shows the currently-active version (also indicated by the asterisk in the output of the versions command); clear, to clear the currently-active version (in case you’d rather fall back on a system-installed version, for example); and remove, which will remove a version. See the docs for details on all the commands.

How it Works

All this was written in an uncomplicated Bash script. I’ve ony tested it on a couple of Macs, so YMMV, but as long as you have Bash, Curl, and /usr/bin/perl on a system, it ought to just work.

How it works is by building each version in its own directory: ~/.pgenv/pgsql-10.4, ~/.pgenv/pgsql-11beta2, and so on. The currently-active version is nothing more than symlink, ~/.pgenv/pgsql, to the proper version directory. There is no other configuration. pgenv downloads and builds versions in the ~/.pgenv/src directory, and the tarballs and compiled source left in place, in case they’re needed for development or testing. pgenv never uses them again unless you delete a version and pgenv build it again, in which case pgenv deletes the old build directory and unpacks from the tarball again.

Works for Me!

Over the last week, I hacked on pgenv to get all of these commands working. It works very well for my needs. Still, I think it might be useful to add support for a configuration file. It might allow one to change the name of the default superuser, the location Perl, and perhaps a method to change postgresql.conf settings following an initdb. I don’t know when (or if) I’ll need that stuff, though. Maybe you do, though? Pull requests welcome!

But even if you don’t, give it a whirl and let me know if you find any issues.

More about…

Deep Expertise

Zeynep Tufekci, in an editorial for The New York Times, following the news that Elon Musk got defensive about his rejected mini-sub plan to rescue the kids in the Thai Cave last week:

The Silicon Valley model for doing things is a mix of can-do optimism, a faith that expertise in one domain can be transferred seamlessly to another and a preference for rapid, flashy, high-profile action. But what got the kids and their coach out of the cave was a different model: a slower, more methodical, more narrowly specialized approach to problems, one that has turned many risky enterprises into safe endeavors — commercial airline travel, for example, or rock climbing, both of which have extensive protocols and safety procedures that have taken years to develop.

This “safety culture” model is neither stilted nor uncreative. On the contrary, deep expertise, lengthy training and the ability to learn from experience (and to incorporate the lessons of those experiences into future practices) is a valuable form of ingenuity.

So important.

Sadly, Musk seems unable to take this lesson. Instead, following criticism from one of the British divers who led the effort to save the kids, Musk posted a series of profoundly disgusting tweets suggesting that the diver is a pedophile.

Stay classy, Silicon Valley cult-of-personality.

Irredeemable

Virginia Heffernan finds that Donald Trump Has No Values:

He has no honor among thieves, no cosa nostra loyalty, no Southern code against cheating or lying, none of the openness of New York, rectitude of Boston, expressiveness and kindness of California, no evangelical family values, no Protestant work ethic. No Catholic moral seriousness, no sense of contrition or gratitude. No Jewish moral and intellectual precision, sense of history. He doesn’t care about the life of the mind OR the life of the senses. He is not mandarin, not committed to inquiry or justice, not hospitable. He is not proper. He is not a bon vivant who loves to eat, drink, laugh. There’s nothing he would die for — not American values, obviously, but not the land of Russia or his wife or young son.

Read the whole thing — it’s short, and utterly devastating. There really is nothing redeemable about Donald Trump. Not one thing.

(Via Charlotte Clymer)

Lincoln on the Declaration

Abraham Lincoln, interpreting the Declaration of Independence, in 1858:

We hold these truths to be self evident: that all men are created equal; that they are endowed by their Creator with certain unalienable rights; that among these are life, liberty and the pursuit of happiness.”

This was their majestic interpretation of the economy of the Universe. This was their lofty, and wise, and noble understanding of the justice of the Creator to His creatures.

Yes, gentlemen, to all His creatures, to the whole great family of man. In their enlightened belief, nothing stamped with the Divine image and likeness was sent into the world to be trodden on, and degraded, and imbruted by its fellows.

They grasped not only the whole race of man then living, but they reached forward and seized upon the farthest posterity. They erected a beacon to guide their children and their children’s children, and the countless myriads who should inhabit the earth in other ages. Wise statesmen as they were, they knew the tendency of prosperity to breed tyrants, and so they established these great self-evident truths, that when in the distant future some man, some faction, some interest, should set up the doctrine that none but rich men, or none but white men, were entitled to life, liberty and the pursuit of happiness, their posterity might look up again to the Declaration of Independence and take courage to renew the battle which their fathers began—so that truth, and justice, and mercy, and all the humane and Christian virtues might not be extinguished from the land; so that no man would hereafter dare to limit and circumscribe the great principles on which the temple of liberty was being built.

See also his 1957 Speech on the Dred Scott Decision.

(Via James Gleick)

What Elizabeth Warren Saw at the Border

Elizabeth Warren describes what she saw in the immigration camps near McAllen, Texas, this past Sunday:

At the time of separation, most of the mothers were told their children would be back. One woman had been held at “the icebox,” a center that has earned its nickname for being extremely cold. When the agent came to take her child, she was told that it was just too cold for the child in the center, and that they were just going to keep the child warm until she was transferred. That was mid-June. She hasn’t seen her child since.

One mother had been detained with her child. They were sleeping together on the floor of one of the cages, when, at 3:00am, the guards took her away. She last saw her 7-year-old son sleeping on the floor. She cried over and over, “I never got to say goodbye. I never got to say goodbye.” That was early-June, and she hasn’t seen him since.

There’s no other way to put it: The Trump administration’s immigration policies are a humanitarian crime. The people who implemented it should be prosecuted for human rights violations.

Democracy Over Civility

Michelle Goldberg in a bracing piece for The New York Times:

Millions and millions of Americans watch helplessly as the president cages children, dehumanizes immigrants, spurns other democracies, guts health care protections, uses his office to enrich himself and turns public life into a deranged phantasmagoria with his incontinent flood of lies. The civility police might point out that many conservatives hated Obama just as much, but that only demonstrates the limits of content-neutral analysis. The right’s revulsion against a black president targeted by birther conspiracy theories is not the same as the left’s revulsion against a racist president who spread birther conspiracy theories.

The demand for civility in the face of deplorable lies and inhumane policies enables those lies and policies. Angry voices on the left will return to civility once violent rhetoric has ceased and civil rights have been restored.

(Via @jonfavs)

Facebook Identity Theft

I get email:

Action Required: Confirm Your Facebook Account

Needless to say, I did not just register for Facebook.

Hrm. That’s weird, since my Facebook account dates back to 2007. Wait, there’s another email:

(219) 798-8705 added to your Facebook account

That’s not my phone number.

I’ve never seen that phone number before in my life. In fact, I removed my phone number from Facebook not long ago for privacy reasons. So what’s going on?

A quick look at the email address tells the story: It’s my Gmail address. Which I never use. Since I never use it, it’s not associated with any account, including Facebook. What’s happened is someone created a new Facebook account with my Gmail address. If I were to click the “Confirm your account” button, I would give someone else a valid Facebook account using my identity. It’d be even worse if I also approved the phone number. Doing so would cede complete control over this Facebook account to someone else. These kinds of messages are so common that it wouldn’t surprise me if some people just clicked those links and entered the confirmation code.

It’s only Facebook, you might think. But Facebook, isn’t “only” anything anymore. It’s a juggernaut. Facebook is so massive, and has promoted itself so heavily as an identity platform, that many organizations rely on it for identity proofing vias social logins. That means someone can “prove” they’re me by logging into that Facebook account. Via that foothold, they can gradually control other online accounts and effectively control the identity associated with my Gmail address.

That would not be good.

So after inspecting the email to make sure that its URLs are all actually on facebook.com, I visit the “please secure your account” link:

Secure your account?

This isn’t right…

This is a little worrying. It’s not that I think someone else is logging into my account. It’s that someone else has created an account using my Gmail address, and therefore a slice of my identity. Still, locking it down seems like a good idea. I hit the “Secure Account” button.

Secure your account?

What? Fuck no.

Now we’ve reached to the point point where I’m at risk of actually associating my physical photo ID with an account someone else created and controls? Fuck no. I don’t want to associate a photo ID with my real Facebook account, let alone one set up by some rando cybercriminal. Neither should you.

I close that browser tab, switch to another browser, and log into my real Facebook account. If the problem is that someone else wants proof of control over my Gmail address, I have to take it back. So I add my Gmail address to the settings for my real Facebook account, wait for the confirmation email, and hit the confirmation link.

Contact Email Confirmation

That should do it.

Great, that other account no longer has any control over my Gmail address. Hope it doesn’t have any other email addresses associated with it.

Oh, one more step: Facebook decided this new address should be my primary email address, so I had to change it back.

I don’t know how people without Facebook accounts would deal with this situation. Facebook needs to give people a way to say: “This is not me, this is not my account, I don’t want an account, please delete this bogus account.” It shouldn’t require uploading a photo ID, either.

Tony

David Simon, pitch-perfect as usual, on his friend Tony:

Go, move, see, feel, eat – grow. The Church of Bourdain was founded not merely on the ever-more-vulnerable national credo that all Americans are created equal, but on the much more ambitious insistence that this declaration might be applied wherever you wandered and with whomsoever you cooked or shared a meal. He remains, for many of us, the American that we wish ourselves to be in the world’s sight. To have him widely displayed as our countryman, open to and caring about the rest of the world, and being so amid our current political degradation — this was ever more important and heroic. To lose him now, amid so many fear-mongering, xenophobic tantrums by those engaged in our misrule, is hideous and grievous.

But make no mistake: It wasn’t love of food that led Bourdain to the embrace of a shared human experience, of a world merely hiding its great commonalities behind vast and obvious culinary variations. It was the other way around. Tony was intensely political, a man always aware of those at the margins, or those who seem never to be reached by wealth or status or recognition.

Don’t miss the Kissinger story.

Plain Text Figures

A couple weeks ago, I implemented JSON Feed for Just a Theory (subscribe here). A nice feature of the format is that support for plain text content in addition to the expected HTML content. It reminds me of the Daring Fireball plain text feature: just append .text to any post to see its Markdown representation, like this. I’m a sucker for plain text, so followed suit. Now you can read the wedding anniversary post in plain text simply by appending copy.text to the URL (or via the JSON Feed).

Markdowners will notice something off about the formatting: the embedded image looks nothing like Markdown. Here it is:


{{% figure
  src     = "dance.jpg"
  title   = "dance.jpg"
  alt     = "First Dance"
  caption = "First dance, 28 May 1995."
%}}

This format defines an HTML figure in the Hugo figure shortcode format. It’s serviceable for writing posts, but not beautiful. In Markdown, it would look like this:

![First Dance](dance.jpg "First Dance")

Which, sadly, doesn’t allow for a caption. Worse, it’s not great to read: it’s too similar to the text link format, and doesn’t look much like an image, let alone a figure. Even Markdown creator John Gruber doesn’t seem to use the syntax much, preferring the HTML <img> element, as in this example. But that’s not super legible, either; it hardly differs from the shortcode format. I’d prefer a nicer syntax for embedded images and figures, but alas, Markdown hasn’t one.

Fortunately, the copy.text output needn’t be valid Markdown. It’s a plain text output intended for reading, not for parsing into HTML. This frees me to make figures and images appear however I like.

Framed

Still, I appreciate the philosophy behind Markdown, which is best summarized by this bit from the docs:

The overriding design goal for Markdown’s formatting syntax is to make it as readable as possible. The idea is that a Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions.

So how do you make an embedded image look like an image without any obvious tags? How about we frame it?

        {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
        {                                                          }
        {                      [First Dance]                       }
        {  https://justatheory.com/2018/05/twenty-three/dance.jpg  }
        {                                                          }
        {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
        {  First dance, 28 May 1995.                               }
        {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

Think of the braces and tildes like a gilded frame. In the top section, we have the bracketed alt text like a descriptive card, followed by the image URL. Below the image area, separated by another line of tildes, we have the caption. If you squint, it looks like an image in a frame, right? If you want to include a link, just add it below the image URL. Here’s an example adapted from this old post:

  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
  {                                                                      }
  {                      [*Vogue* on the new iPad]                       }
  {   https://farm8.staticflickr.com/7198/7007813933_bd7e86947c_z.jpg    }
  {     (https://www.flickr.com/photos/theory/7007813933/sizes/l/)       }
  {                                                                      }
  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
  {  Image content from *Vogue* on the new iPad. Not shown: the second   }
  {  that it's blurry while the image engine finishes loading and        }
  {  displaying the image.                                               }
  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

The link appears in parentheses (just like in the text link format). The format also preserves the alt text and caption Markdown formatting. Want to include multiple images in a figure? Just add them, as long as the caption, if there is one, appears in the last “box” in the “frame”:

  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
  {                                                                      }
  {                [*The New Yorker* on the 1st gen iPad]                }
  {   https://farm8.staticflickr.com/7059/6861697774_a7ac0d9356_z.jpg    }
  {      (https://www.flickr.com/photos/theory/6861697774/sizes/o/)      }
  {                                                                      }
  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
  {                                                                      }
  {      [*The New Yorker* on the 3rd gen iPad with retina display]      }
  {   https://farm8.staticflickr.com/7110/7007813821_6293e374eb_z.jpg    }
  {      (https://www.flickr.com/photos/theory/7007813821/sizes/o/)      }
  {                                                                      }
  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
  {  Text content from *The New Yorker* on the first generation iPad     }
  {  (top) and the third generation iPad with retina display (bottom).   }
  {  Looks great because it's text.                                      }
  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

You can tell I like to center the images, though not the caption. Maybe you don’t need a caption or much else. It could be quite minimal: just an image and alt text:

        {                      [First Dance]                       }
        {  https://justatheory.com/2018/05/twenty-three/dance.jpg  }

Here I’ve eschewed the blank lines and tildes; the dont’ feel necessary without the caption.

This format could be parsed reasonably well, but that’s not really the goal. The point is legible figures that stand out from the text. I think this design does the trick, but let’s take it a step further. Because everything is framed in braces, we might decide to put whatever we want in there. Like, I dunno, replace the alt text with an ASCII art1 version of the image generated by an conversion interface? Here’s my wedding photo again:

{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
{                                                                                    }
{  NNNmmdsyyyyyyysssssdhyssooo+++++++++++++ooymNNNNyo+/::::-----------------------:  }
{  NNmNmdssyyyyyssssssdhyssooo++++///++++++ooymNmmmyo+/:::--------...-------------:  }
{  mmddddsyyyyyyssssssdhyssoo++++/////++++osydmmmmNyo+/::--------.....-------------  }
{  Nmddmmsyyyyyyssssssdhysooo+++///////++osso+oymNNyo+/::--------.......-----------  }
{  mmmmmmyyyyyyysssssshhysooo+++//////+ys+//:///sdmho+/::-------..........---------  }
{  mmmmmmyyyyyyssssosshhysooo+++////+ydmy/:/:/+ossydo+/::------...........---------  }
{  mmmmmmyyyyyysssoosshhysosshdy+/+odmNNmdyddmmNNmdmms+::------............--------  }
{  mmmmmmyyyyyyssooosshdhyso:/ymhhdmNNNNNmyhNNNNNNNNNmmo:------.............-------  }
{  mdddmmhyyyysssooossdmdmho.-hmmNNNNNNNmdyhmNNNNNNNNNmh+/+/--..............-------  }
{  mmmmddhyyyysssoooymmNmNmo--yNNmNmmmmmhhyhdhydNNNNmmmdysshy:..............-------  }
{  mmmddmhyyyssssoosdNNNNNmssydmNddhssossyyhs::+ssyhmmh+///ohh-..............------  }
{  Nmmmmmdyyyssssoohhdmddhs:-:hdhyhdso+++///--::/:::+o/://oosy:-.............------  }
{  NNNmmmdyyyssssosdhhyyh+//oohdmmmh///+/::::::---:++/://+hddmdho:...........------  }
{  NNNmmmdyyyssssosmmdmdy+.-/mmdmho+//////::::::/sddddhs/.:sdmmmmy-..........------  }
{  NNmmmmdhyyssssooydmmd+/+sydNmmh+/+yddyo/://oydmmmmmmdy:..:ymds:............-----  }
{  mmmmmmdhyysssoooo+oo+ohdmmmmmddhhsyddddysyddddmmmdddho-..`./h/.............-----  }
{  mmNNNNmhyysssoooo:-/.-ymmmmmmmmmmmNmdddmmmdmdddhhhhs-```````/h-............-----  }
{  NNNNNNmhyysssooymddmddmmmdddddmdmmNmyddmddddhhhhhy:`   ` ```.oo............-----  }
{  NNNNNNmhyyssssymmmNNmmmddhhddddmddddhddmdhddhhhhs-     ` ```.:y............-----  }
{  NNmdmNmhyysssydNmmNmmmddddddddddddhdddhddhhhhhho.      ``````.y............-----  }
{  mmmddmmdhyssssdmmhdmmmddddddhhdhhhhhddhhhdhhho-`       `` ```.s...........------  }
{  ddddhdddhyysssymNmmmmmddddddddhhdhhhhdddddy+.``        `` ```.s............-----  }
{  NNNmmddhyyssssosdddmmmmdddddddhhdddmmmmd+..```        `` ````-s............-----  }
{  NNNmmhysssssssooooymmmmdddmmmdhhdmdmdddd/``.`        ``  ```.-o...........------  }
{  mNNmddhhysssssssssydmmmmdmmmmmddmmddddhdd+``        `` `````.-/...........------  }
{  NNNmmddhhhhyyyyyyyyhmmmmmmmmmmddddddddhhy.``    ` ``` ``````./-...........------  }
{  NNNmmmysssssssssssssdmmmmmmmddddddddddh+.`     ` ```   `````.+...........-------  }
{  mmmNmmhyyyyyhhhhhhhhdmmmmmmddddddddddy:`````` `````   ``````-:...........-------  }
{  mmmmmmmmmmmmmmmmmmddhdmmmddddddddddy/.`````` ````       ```./...........-------:  }
{  mmmmmmmmmmmmmmmmmmhyssdmmmmdddddho:.``````````-```  ``````./:...........-------:  }
{  mmmmmNmmmmmmmmmddddhysydmmddho:-...`````````:oh/```` ````.-:............-------:  }
{  mmmmNNmmmmmmmmmmmmmddyoydo:.``.`````````.:+ydddh-```````-/--............-------:  }
{  NNNNNNNmmmmmmmmmmmmdyyyoo.````...`````:ohdmdddddh+oosyyhdmo--..........--------:  }
{  NNNNNNNNmmmmmmmNmmmmhys+//-```...`.-+yddddmmmddddmmmmmmmmmm+--.......----------:  }
{  NNNNNNNNNmmmmNNNNNmmsyysohdyosyhyyhddddddddmmmmmdmmmmmmmmmmh--.......---------::  }
{  NNNNNNNNNNNNNNNNNNNNyyhhdmmdddmmdddddmddddmmmmmmmmmmmmmmmmmd-----------------:::  }
{  NNNNNNNNNNNNNNNNNNNNmmmmmmmmdddddddmmmmmmmmmmmmmmmmmmmmmmmmy-----------------:::  }
{  NNNNNNNNNNNNNNNNNNNNmmmmmmmmddddddmmmmmmmmmmmmmmmmmmmmmmmmm+-----------------:::  }
{  NNNNNNNNNNNNNNNNmNNNmmmmmmmmmdmdmmmmmmmmmmmmmmmmmmmmmmmmmmh:----------------::::  }
{  NNNNNNNNNNNNNNNNNNNmmmmmmdmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmo----------------:::::  }
{  NNNNNNNNNNNNNNNNNNNmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm/---------------::::::  }
{  NNNNNNNNNNNNNNNNNNNNNNmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmNNmy::::::::--:-:::::::://  }
{  NNNNNNNNNNNNNNNNNNNNNNmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmNmmo///:::::::::::::::////  }
{  NNNNNNNNNNNNNNNNNNNNNNmdddmmmmmmmmmmmmmmmmmmmmmmmmmmmNmmy///////////////////////  }
{  NNNNNNNNNNNNNNNNNNNNNNmdddddddddddddmmmmmmmmmmmmmmmmmNmm/::::::::::::::::::::::/  }
{  NNNNNNNNNNNNNNNNNNNNNNmdddddddddhddddddmmmmmmmmmmmmmmmmy::::::::::--:::::::::///  }
{  NNNNNNNNNNNNNNNNNNNNNNmmdddddddddddddddmmmmmmmmmmmNNNmNyo++++/////////////++++oo  }
{  NNNNNNNNNNNNNNNNNNNNNNmmdddddddddddddddmmmmmmmmmmNNNmmNhyyyyyyssssssssssssssssss  }
{  NNNNNNNNNNNNNNNNNNNNNmmmdddddddddddddddmmmmmmmmmNmNNmmmysssssooooo+++++/////////  }
{  NNNNNNNNNNNNNNNNNNNNNmmmmddddddddddddddmmmmmmmNNNNNmmmd/::::::::::::::://///////  }
{  NNNNNNNNNNNNNNNNNNNNNmmmmdmddddddddddddmmmmmmNNNmNmmmmd::::::::::::::://////////  }
{  NNNNNNNNNNNNNNNNNNNNNmmmmdmmmdddddddddmmmmmmNmmmNmmmmmh::::::::://///////++os+++  }
{  NNNNNNNNNNNNNNNNNNNNNmmmmdmmmmddddddddmmNmNNmmmNmNNmmNh/::::///////////+oo+++++o  }
{  NNNNNNNNNNNNNNNNNNNNmmmmmmmddddddddddmmNmmmmmmmNNNNmmNy////////+oossyyhhhdddmmmN  }
{                                                                                    }
{               https://justatheory.com/2018/05/twenty-three/dance.jpg               }
{                                                                                    }
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
{  First dance, 28 May 1995.                                                         }
{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

Silly? Maybe. But I’m having fun with it. I expect to wrangle Hugo into emitting something like this soon.


  1. Surely someone has come up with a way to improve on ASCII art by using box elements or something?

Token Dimensions

C’est mois, in my second post on Tokenization for the iovation blog:

These requirements demonstrate the two dimensions of tokenization: reversibility and determinism. Reversible tokens may be detokenized to recover their original values. Deterministic tokens are always the same given the same inputs.

The point is to evaluate the fields private data fields to be tokenized in order to determine where in along these dimensions they fall, so that one can make informed choices when evaluating tokenization products and services.

Sqitchers

In the last few years, I’ve not had a lot of time to hack on my open-source projects, including Sqitch. Last week’s call to adopt my modules garnered an unexpected quantity of interest in helping to maintain Sqitch specifically. It’s little different from my other Perl modules, being designed as a standalone app rather than a software development library. It deserves care and feeding from more than a single maintainer.

So I’m very pleased to announce two changes to the Sqitch ecosystem:

  1. I’ve moved all my Sqitch-related code, including Sqitch itself, from my personal GitHub account to the new “Sqitchers” GitHub organization. In addition to myself, the organization has four other owners: Dave Rolsky, Shawn Sorichetti, Curtis Poe, and Ștefan Suciu. However, I’d really like to balance all this great Perl talent with a few database folks. Even better to get some non-white-dudes involved. If that’s you, and you’d like to help Sqitch continue to improve, drop me a line.

  2. I’ve created a new mail list, sqitch-hackers, for folks who want to hack on Sqitch itself. This is an open list, like the existing sqitch-users list: anyone can subscribe and participate in the discussion of how to improve Sqitch, get hints for hacking on it, talk about approaches to implementing features, etc.

I’ll likely make a brain dump of stuff I’d like to see happen with the project and the community. Do join and send us your ideas, too!

Sqitch has become a pretty important tool for a lot of people, far and way my most-starred project on GitHub. It deserves a broader coalition of people to care for it going forward. I hope these changes help to galvanize the community to take it on collectively.