or Why I Stopped Worrying and Wrote A Divination System Considered Harmful
Trope subtitles are the worst.
In this post I’ll be talking about TarotFS from Grimm Labs, my divination system filesystem for Plan 9. In part, I’m using this as an exercise to figure out what parts are going to make the cut for my EMFcamp talk.
I’ll hopefully talk in depth about this at another time in another place, but the tl;dr is that my first practical foray into occultism was through divination systems. I can’t remember if I got a tarot deck first (thanks tj!) or a set of Elder Futhark runes (thanks Joey and Mae!), but the value was pretty easy to see from the point of view of a person struggling so much with their emotional and mental health during COVID. I was able to pick out images at random and see how they resonated with me, and what the historical symbolism was around these archetypes that could be plumbed further to find more interpretations and develop my own associations.
Looking back it makes a lot of sense - people think in terms of narratives, allegories and symbols, so having these arrayed out in front of you, drawn at random such as they’re “presented by the universe” (a trope deeply embedded in culture and religion) strikes a chord and drives engagement with what is essentially a system for introspection and decision making with an RNG (random number generator) built in.
Okay, so one of my biggest projects and the one I talk about the most if you
know me in person (or on IRC or on gridchat) is TarotFS. It’s really the first
use case for Plan 9 from Bell Labs (or the fork 9front which I’m going to
lovingly refer to as ⑨ from here on in, despite the fact that it completely
fucks with vt
not to use a single width character but here we are) that I had
which was really an occult system. And ⑨ really suits the use-case. One of the
most famous if not totally fundamental ideas behind Plan 9 is putting the Unix
concept of “everything is a file” to shame, showing it as the true hyperbole
that it is. In ⑨ just about everything actually is a file, and the real
case-in-point here is the 9p protocol. When you access the web, you can do that
using webfs using the 9p protocal bridging with HTTP. Want to make a TCP
connection? Do the same. Want to access a file? You guessed it, use 9p.
So when I started playing around with ⑨, I was really interested in this concept and so I started playing with webFS and really liked how it effectively meant you could leave your HTTP transactions on disk. Around the same time, I was also reading Techniques of High Magic by Francis King and Stephen Skinner (or at least that’s how I remember it? It could be that the book’s right in front of me…) and about various divination systems including geomancy and runecasting. Tarot stood out because it’s well known in Western society as being… well… goth as fuck quite frankly, and I was in a place where archetype systems were incredibly appealing to me. Possibly more on that elsewhere or later or something.
At any rate, the mode of transaction with webfs really struck a chord:
† cd /mnt/web/
† ls
clone
ctl
† cat clone
0
† cd 0
† ls
body
ctl
parsed
postbody
† echo 'url https://grimmwa.re' > ctl
† cat parsed/url
https://grimmwa.re/†
†
† cat body
<html>
<head>
<title>(((GRIMMWA.RE</title></head>
<meta charset="UTF-8">
... and so on ...
The workflow goes like this:
1. “clone” a new session by reading the clone
file. This will return an
integer which will be the name of the directory which will be created for
your session (which you can see we cd
into.
2. Enter any parameters for the session into the “control” file for the
session, ctl
- in this case we’re entering the (somewhat necessary) url
parameter. We can then verify that we entered it correctly by reading out
the parsed/url
file.
3. Read out the request body by reading the body
file! When we do this, it
looks like we’re reading a file from disk, but actually we’re dispatching
the HTTP request that we’ve prepared by creating the session and telling it
the URL, and this is essentially the response body!
Like I say, you have the transaction apparently “on disk” and are able to set parameters and headers and the like by writing to files and reading responses by reading them. A query and a response, all effectively encoded statefully in a way that you can sensibly manage and retrospect on.
Something twigged with me here, because it was at the intersection of ideas - I was exploring ⑨ which I was clearly going to do from a participatory viewpoint, and I was also beginning to start to understand some of the less supernatural value in divination systems, and frankly I understand by doing and I needed a distraction.
And somewhere in here is where TarotFS was born as a concept.
Yes, we’re doing this in skip-to-the-end style, mostly because I didn’t make enough notes on the way so I can’t actually remember what I did and what problems I hit and why. A big warning here is that the code’s a bit of a trash fire, so I’m going to present you with a couple of things: a TarotFS session, which I’ll use to explain a high level idea of the basic usage and rationale, and a manpage which I’ll dump at the end.
† tarot -p definitions -dkP
definitions/Smith-Waite
We launch tarotFS with a definitions directory, and descriptions (-d
),
keywords (-k
) and paths (-P
) enabled in the card output. This is pretty
much the whole selection as we’ll see in a bit! Essentially these three options
only do anything if you specify a definitions directory as they simply read
data for the relevant card from it. TarotFS helpfully then tells you which
subdirectory of definitions it’s using because I haven’t fixed that old shitty
debug output yet!
† cd /mnt/tarot
† ls
clone
/mnt/tarot
is the default mountpoint - not much to see here yet apart from
the clone
file!
† cat clone
1
deckstyle Smith-Waite
definitions_path definitions/Smith-Waite
keywords 1
description 1
orientation on
When we read the file it does what webfs does - creates a new session for us!
This file actually redirects reads and writes from the ctl
file from the new
spread, so we could actually have cloned the session by overriding a setting
with a write instead if we really knew what we were doing.
Helpfully, you can see that when I hacked in the orientation
parameter
recently I didn’t check my boolean convention and have instead used “off” and
“on”. Some parts of tarotFS are wildly inconsistent, and for my part I will
actually fix this. At some point. Maybe.
† cd 1
† ls
ctl
draw
remaining
shuffle
Other than the ctl
file, it should be pretty much immediately obvious what
each of these files will do if you read them!
† echo spread past present future advice > ctl
† cat ctl
1
deckstyle Smith-Waite
spread past present future advice
definitions_path definitions/Smith-Waite
keywords 1
description 1
orientation on
For the sake of showing off we’re going to specify the names of some of the
cards which will be drawn. By default, the card filenames are just incrementing
numbers, but these arguments will make so that we get names e.g. 1:past
,
2:present
and so on. Part of the advantage of this is you can’t decide on one
spread and then change your mind half way through when you get a card you don’t
like ;)
† cat remaining
78
78 cards in a standard Rider-Smith-Waite deck!
† cat draw
Four of Pentacles
reversed
path definitions/Smith-Waite/Pentacles/Four/
UPRIGHT: Saving money, security, conservatism, scarcity, control.
REVERSED: Over-spending, greed, self-protection.
The Four of Pentacles shows a man sitting on a stool, beyond the
boundaries of his hometown. His arms are wrapped tightly around a coin
as if he fears he may lose it if he loosens his grip. He balances
... <SNIP> ...
So what we’re seeing here is the contents of one of the cards being drawn, containing the orientation (on by default, cards can be upright or reversed), the path of the card definition that the keywords and definition text are coming from, and then the keywords followed by the definitions.
The path is useful if you want to do things like ship the card images! It makes it simple to script a setup to view the spread graphically!
† cat remaining
77
Just in case you doubted that these are actually being drawn from a deck…
At any rate, that was a bit more verbose than we care about right now so we can toggle the descriptions and the orientations off:
† echo description > ctl
† echo orient > ctl
† cat ctl
1
deckstyle Smith-Waite
spread past present future advice
definitions_path definitions/Smith-Waite
keywords 1
description 0
orientation off
Now when we draw, we’ll just get the path and the keywords:
† cat draw
Seven of Cups
path definitions/Smith-Waite/Cups/Seven/
UPRIGHT: Opportunities, choices, wishful thinking, illusion.
REVERSED: Alignment, personal values, overwhelmed by choices.
† cat draw
Ⅷ: Justice
path definitions/Smith-Waite/Major/Justice/
UPRIGHT: Justice, fairness, truth, cause and effect, law
REVERSED: Unfairness, lack of accountability, dishonesty
† cat draw
ⅩⅩ: Judgement
path definitions/Smith-Waite/Major/Judgement/
UPRIGHT: Judgement, rebirth, inner calling, absolution
REVERSED: Self-doubt, inner critic, ignoring the call
So what about the card names and the state on disk I was talking about? Well here we can see that the output can be read again by reading out the respective numbered file:
† ls
1:past
2:present
3:future
4:advice
ctl
draw
remaining
shuffle
† cat remaining
74
A lot of this has grown organically, so there are a few warts, gotchas and implementation details:
/lib/tarot/decks
)! The
default is Smith-Waite
(as in the famous Rider-Waite deck, for whom Rider
is a publisher and Pamela Coleman-Smith is the largely unknown artist who
actually did most of the work), but we also have the Marseille deck which
uses slightly different card and suit names, and also Elder-Futhark
which
is me showing off that actually technically you can use TarotFS to do
runecasting as well: † tarot
† echo deckstyle Elder-Futhark > /mnt/tarot/clone
† cd /mnt/tarot/1
† cat draw
ᚺ
reversed
rio
window, with a tarot cloth as a background and everything, but it requires a
patched version of rio and also a patch which I’ve written for page
to
auto-resize the image which I’ve not gotten round to documenting yet (and
this post is getting pretty long anyway!)I’ve got a few plans here. For one, I’d like to be able to use arbitrary bitstreams as an entropy source given that none of the randomness here is used for cryptographic purposes, so it would be great to be drawing the randomness from the processes of the universe (like gridchat!). Some more work to improve the consistency of the interface and documentation would certainly be good, and I’d also like to do more work on convenience scripts for graphically rendering in Rio for example. Finally, a good default definitions source which is appropriately licensed for redistribution would also be great.
This needs updating! I’ve not actually added the instructions on using the
orient
command and a couple of other things, but fixing instead of writing
is how I end up never writing things.
TAROT(4) TAROT(4)
NAME
tarot - tarot card filesystem
SYNOPSIS
tarot [ -DdkPh ] [ -p definitions ] [ -m mtpt ]
DESCRIPTION
Tarotfs presents a file system interface to generating and
interacting with tarot spreads. Tarotfs mounts itself at
mtpt (default /mnt/tarot). The -D flag enables 9P debug
prints and other debug files and -h displays the short list
of options.
Tarotfs also optionally supports displaying keywords and
definitions when reading files for the drawn cards by pro-
viding a definitions directory with the flag -p wherein the
directory structure should follow the structure:
Name/Suit/Card/ and contain the files keywords and
description, both of which can be freeform text files. The
keywords should ideally contain a short list of keywords to
associate with both the upright and reversed configuration
of each file. The Name/Suit/Card naming convention has the
following restrictions:
Name This can be any directory name, and isn't actually uti-
lized (yet). Currently tarotfs will just pick the
first named pack inside the supplied definitions direc-
tory.
Suit and Card
These must match exactly the form given in `suits`, and
`major` and `minor` for each deck definition, but with
anything preceding and including a single colon charac-
ter stripped, and all spaces removed. For example: "I:
The Magician" should map to "TheMagician". Any major
arcana cards will always map to the suit name `Major`.
The flags -dkP enable displaying the definitions, keywords
and definition paths when reading the card files respec-
tively when -p is supplied.
The top level contains the file clone, and the directory
card_library, the latter of which contains a tree of all the
available cards.
Reading or writing to the clone file creates a new numbered
subdirectory for a new tarot spread and redirects the opera-
tion to the ctl file at the top of the directory.
Each "spread" directory contains the files: ctl, draw,
shuffle and remaining.
The ctl file is used to control the various settings for the
spread by writing commands and arguments to the file:
spread
Sets the names for the drawn cards in addition to their
numbers. Arguments are space separated.
keywords
Toggles whether keywords are displayed when reading
card files
descriptions
Toggles whether descriptions are displayed when reading
card files
paths
Toggles whether definition paths are displayed when
reading card files
Reading the ctl file will return the spread number (matching
the spread's directory) on the first line. If a deck defini-
tion was supplied, it will also print on the following lines
the attributes deckpath,keywords,description each followed
by a space and then the path of the deck definition and the
booleans for whether keywords and descriptions are enabled
for the spread respectively.
Reading draw draws a card, reading remaining shows how many
cards are remaining in the deck and shuffle will reshuffle
the deck.
If the -D option is passed on startup, there will also be
the deck file which outputs the integer representations of
all the cards in the deck along with their number order.
This is currently only used for debugging purposes.
EXAMPLE
The following shows a simple example of cloning a new
spread, setting the spread name and then drawing all the
cards from the spread.
† tarot
† ls /mnt/tarot
/mnt/tarot/card_library
/mnt/tarot/clone
† cat /mnt/tarot/clone
1
† cd /mnt/tarot/1
† ls
ctl
draw
remaining
† echo spread past present future advice > ctl
† for(i in `{seq 1 4}) cat draw
Four of Pentacles
upright
Ⅱ: The High Priestess
reversed
King of Wands
upright
Seven of Cups
reversed
† ls
1:past
2:present
3:future
4:advice
ctl
draw
remaining
† cat remaining
74
SOURCE
https://git.sr.ht/~grimmware/tarotfs