Gem Version | Locale | Lexicon SHA |
---|---|---|
0.4.0 | Miami | 3bfbbf9 |
Locale will not change until v1.0 release
StoryKey is a proof of concept Brainwallet inspired by BIP39 written in Ruby. It converts an arbitrary string of data, such as a cryptocurrency private key, into an English paragraph intended for longterm human memory. It also assists in decoding the story back into its original form. Optionally, a visual representation of the paragraph is also provided using OpenAI DALL-E.
Each story is provided in multiple formats:
Each token of the story, which may be a single word or short compound phrase, encodes 10 bits. The checksum length is variable based on the input size and space available in the last two tokens after accounting for a 4-bit footer. Here are a few example key sizes along with their respective story and checksum sizes.
Key bits | Story tokens | Checksum bits |
---|---|---|
64 | 8 | 12 |
128 | 14 | 8 |
192 | 21 | 14 |
256 | 27 | 10 |
384 | 40 | 12 |
512 | 53 | 14 |
An example key with its associated story, seed phrase, and image are shown below.
Key:
CgCLLXvoch7sLaQWe5Y3Evtzety2Vr9XJGRmAq9YZUXY
Story:
In Miami I saw
1. a practical theorist eating toast with a shopkeeper,
2. a jobless macaw disowning a scientist,
3. a toothsome brother eating dumplings with Paul Cezanne,
4. a rabid outsider leveling a vulture,
5. a hysterical Marge Simpson threatening Moe Szyslak,
6. a rancid cyborg demanding a jeweler,
7. and a wife paging Jimi Hendrix.
Seed Phrase:
miami practical theorist eating-toast shopkeeper jobless macaw disowning scientist toothsome brother eating-dumplings paul-cezanne rabid outsider leveling vulture hysterical marge-simpson threatening moe-szyslak rancid cyborg demanding jeweler wife paging jimi-hendrix
This paragraph or seed phrase can be deterministically decoded back into its original form using the same version of StoryKey. The locale of the story (e.g. Miami
) identifies that version. During key recovery, an exception will be raised if:
version slug
does not match the current version of StoryKeychecksum
does not match the expected valueThe lexicon was selected using the following criteria:
Add this line to your application's Gemfile:
gem 'story_key'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install story_key
This library may be used directly from the command line or by calling Ruby methods.
If you want to generate images of the story along with the text, create a file named .env
in the project directory and add your OpenAI key as an environment variable:
# .env
OPENAI_KEY=<your-api-key>
You must have ImageMagick installed locally.
Invoke the command line interface by running bin/storykey
.
StoryKey commands:
storykey decode [STORY] # Decode a story passed as an argument or from a file
storykey encode [KEY] # Encode a key passed as an argument or from a file
storykey help [COMMAND] # Describe available commands or one specific command
storykey new [BITSIZE] # Create a new key/story (default 256 bits, max 512)
storykey recover # Decode a story interactively
To see help on a specific command, run bin/storyke --help [command]
.
The command line also features an interactive recovery tool to aid in converting a story back into its source key. Run bin/storykey recover
to initiate the process:
After installing the gem, you may run bin/console
or require
the gem in your own project.
Generate a new random key and associated story.
# StoryKey.generate
=>
["4eqfoXzMDyqQW6p8zAQj7c8KkynK5K2BW6D5Vfp7xCaQ",
#<struct StoryKey::Story
text=
"In Miami I saw a dim Balrog eat hummus with an appraiser, a facetious scholar play badminton with an economist, a witty uncle insure Bruce Willis, an appreciative dolphin blare at a cyclist, a blissful James Bond undercut a connoisseur, a green Hugh Jackman eat cheese with a bison, and Elvis Presley snorkel with a counselor.",
humanized=
"In \e[31mMiami\e[0m I saw\n1. a \e[36mdim\e[0m \e[33mBalrog\e[0m \e[35meat hummus\e[0m with an \e[33mappraiser\e[0m,\n2. a \e[36mfacetious\e[0m \e[33mscholar\e[0m \e[35mplay badminton\e[0m with an \e[33meconomist\e[0m,\n3. a \e[36mwitty\e[0m \e[33muncle\e[0m \e[35minsure\e[0m \e[33mBruce Willis\e[0m,\n4. an \e[36mappreciative\e[0m \e[33mdolphin\e[0m \e[35mblare\e[0m at a \e[33mcyclist\e[0m,\n5. a \e[36mblissful\e[0m \e[33mJames Bond\e[0m \e[35mundercut\e[0m a \e[33mconnoisseur\e[0m,\n6. a \e[36mgreen\e[0m \e[33mHugh Jackman\e[0m \e[35meat cheese\e[0m with a \e[33mbison\e[0m,\n7. and \e[33mElvis Presley\e[0m \e[35msnorkel\e[0m with a \e[33mcounselor\e[0m.",
tokenized=
"dim balrog eat-hummus appraiser facetious scholar play-badminton economist witty uncle insure bruce-willis appreciative dolphin blare cyclist blissful james-bond undercut connoisseur green hugh-jackman eat-cheese bison elvis-presley snorkel counselor">]
Produce an English paragraph given input data (e.g. a cryptocurrency private key):
# StoryKey.encode(key: '4eqfoXzMDyqQW6p8zAQj7c8KkynK5K2BW6D5Vfp7xCaQ')
=>
#<struct StoryKey::Story
text=
"In Miami I saw a dim Balrog eat hummus with an appraiser, a facetious scholar play badminton with an economist, a witty uncle insure Bruce Willis, an appreciative dolphin blare at a cyclist, a blissful James Bond undercut a connoisseur, a green Hugh Jackman eat cheese with a bison, and Elvis Presley snorkel with a counselor.",
humanized=
"In \e[31mMiami\e[0m I saw\n1. a \e[36mdim\e[0m \e[33mBalrog\e[0m \e[35meat hummus\e[0m with an \e[33mappraiser\e[0m,\n2. a \e[36mfacetious\e[0m \e[33mscholar\e[0m \e[35mplay badminton\e[0m with an \e[33meconomist\e[0m,\n3. a \e[36mwitty\e[0m \e[33muncle\e[0m \e[35minsure\e[0m \e[33mBruce Willis\e[0m,\n4. an \e[36mappreciative\e[0m \e[33mdolphin\e[0m \e[35mblare\e[0m at a \e[33mcyclist\e[0m,\n5. a \e[36mblissful\e[0m \e[33mJames Bond\e[0m \e[35mundercut\e[0m a \e[33mconnoisseur\e[0m,\n6. a \e[36mgreen\e[0m \e[33mHugh Jackman\e[0m \e[35meat cheese\e[0m with a \e[33mbison\e[0m,\n7. and \e[33mElvis Presley\e[0m \e[35msnorkel\e[0m with a \e[33mcounselor\e[0m.",
tokenized=
"dim balrog eat-hummus appraiser facetious scholar play-badminton economist witty uncle insure bruce-willis appreciative dolphin blare cyclist blissful james-bond undercut connoisseur green hugh-jackman eat-cheese bison elvis-presley snorkel counselor">
key
may be in the form of a hexidecimal (ab29f3
), a binary string (1001101
), a decimal (230938
), or a base58 string (uMBca
). If not in the default base58, format
must be provided.
Recover source data (e.g. a cryptocurrency private key) based on the English paragraph:
# StoryKey.decode(story: 'In Miami I saw an official Benjamin Franklin transport Matt Damon')
=> "4NTM"
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
When editing the lexicon, be sure to:
rake lexicon:build
to re-generate the data fileversion.rb
as well as this README (if publishing)When incrementing the semantic version post-1.0, be sure to:
VERSION_SLUG
, adhering to the locale conventionBug reports and pull requests are welcome on GitHub at https://github.com/jcraigk/story_key.
The gem is available as open source under the terms of the MIT License.