The structure of MetaHandle?

The base data structure of MetaHandles is very simple:

OP_RETURN 1NYJFDJbcSS2xGhGcxYnQWoh4DAjydjfYU H(handle) VersionNumber txid [...]

It's common to reference to the parts of OP_Return outputs as s1-sn. So a Metahandle has this structure:
s1: 1NYJFDJbcSS2xGhGcxYnQWoh4DAjydjfYU. This is the address used to identify the metahandle protocol. It is registered as a BitCom protocol (but has only descriptional entries).
s2: The SHA256 Hash of the Handle.
s3: The VersionNumber of the handle. This is in the format 000000. First two digits are the handle family, next two the subtype, last two the version of it. This allows to create a lot of different handles and update them.
s4: The ID of the transaction containing the content the handle references to. It can be encrypted, but must not.
s5-sn: Additional field with more information about the handle, like title, description, tags, salt for encryption and so on.

Types of Handles

Currently nine types of handles exist. You can identify them with the VersionNumber.

010103: Basic handle.
"s5": E(title, handle): Title of the referenced txid, encrypted with the handle as a password.
"s6": E(description, handle): Description of the referenced content, encrypted with the handle as a password.
To not make processing overly slow, the key creation for encryption and decryption configures scrypt so that it doesn't need so much hardware capacity. It uses the function create_aes_key_extra_light(handle) from encrypt.js. For more details about encryption see the chapter about encryption in the docu.

010202: Basic handle with encrypted txid and better title/description encryption
"s4": E(txid, handle): Encrypts the txid too
"s5": E(title, handle)
"s6": E(description, handle)
"s7": salt
This time the txid is encrypted too. This is made to protect against someone spamming your handle by copying all the data but replaces the transaction id. This is not possible with this handle type. But encrypting the txid destroys cross references, so it is not enabled by default.

For this handle, the key generation for encryption / decryption uses a stronger variant of scrypt, requiring more hardware capacity. The function is create_aes_key(handle, salt) in encrypt.js. More details about it below.

010303: Tag Handle: A basic handle with up to three tags. This is an example for a tag handle with three tags:
"s5": E(title, handle)
"s6": E(description, handle)
"s7": salt (not really needed here)
"s8": Hash(tag1)
"s9": E(handle, tag1)
"s10": Hash(tag2)
"s11": E(handle, tag2)
"s12": Hash(tag3)
"s13": E(handle, tag3)
Like the basic handle it uses create_aes_key_extra_light(handle) to create the key for encryption / decryption. To decrypt title and description it first needs to decrypt the handle with the tag as a password.

010402: Tag handle with encrypted txid and stronger encryption.
This handle is nearly the same as 010303, expect that
"s4": E(txid, handle)
Further, it uses the stronger function to create the key for encryption and decryption, create_aes_key(handle, salt).

020102: Value handle.
This handle is only valid if the transaction creating it has an unspent output. Structure is identical with 010103, but it is processed differently when searched: it needs a request to a node or blockexplorer to check if the transaction still has an unspent output.
The Value Handle can be destroyed by spending the outputs.

020201: Value handle with encrypted txid.
Structure is identical with 010202, but only valid when there is an unspent output.

020303: Value handle with tags.
Structure is identical with 010303.

020402: Value handle with tags and encrypted txid.
Structure is identical with 010402.

020501: Exclusive handle.
This is a basic handle which is only valid when it has a value of at least 0.1 BSV AND it is the first handle claiming a word. If an exclusive handle is valid no other handle should be shown in the search. It should be the only valid handle for this word. Structure is identical with 010103.

Analyze a raw handle transaction

Using crawler2.php with GET you can crawl the raw handle transactions. For example crawler2.php?handle=blog

This example is a handle with the version number 020301. This is an old version number, which has been updated since. It is a value handle with three tags, referencing an onchain blogpost in transaction a96309503b50bfad79d6ce62e8ead8a8932733d549782f225665253967222997. When experimenting with handles, this raw mode helped me a lot to understand.

Querry an Handle

Searching an Handle is a very basic bitquerry operation. If you don't have, read the docu for Planaria.

The code for querrying it is this:
var query = {
  "v": 3,
  "q": {
     "find": {
       "out.b0": { "op": 106 },
       "out.s1": "1NYJFDJbcSS2xGhGcxYnQWoh4DAjydjfYU",
       $or: [ {"out.s2": handlehash }, {"out.s8": handlehash }, {"out.s10": handlehash }, {"out.s12": handlehash } ]
     "limit": 120,
     "sort": { "blk.i": 1 }

Querry a value Handle

If the result of s3 indicates a Value Handle, an additional step needs to be taken to process the handle: You need to lookup if the address has an unspent output.

The perfect way to do would be to ask a full node with gettxout, but this could be quite burdensome. So for most users the best solution is to ask an block explorer like whatsonchain or bitindex.


Metahandle uses a lot of encryption. Data like title and description are always encrypted, the referenced transaction id is encrypted optionally. Also, the handle itself is hashed.

The purpose of it is not to provide a strong secrecy of the handle, but to prevent searchability. To find something, you should know what you are searching for.

For encryption, AES is used in this the JavaScript implementation of Ricmoo. To create the key, Scrypt-Async for JavaScript from dchest is used. The key is created from the handle, salt is either created as a random value with the function generateId(length) with length=128 or, in the weaker encryption, just the handle itself.

The file encrypt.js lists all the functions for encryption.