GRIDS: Gajumaru Remote Instruction Dispatch System v1
- Document Created: 2025-01-17
- Document Last Modified: 2026-05-13
- Document Author: Craig Everett
<craigeverett@qpq.swiss> - Document Version: 5 (For Implementation)
- Wiki Page Created: 2026-05-02
- Wiki Page Last Modified: 2026-05-13
- Wiki Page Author: Peter Harpending
<peterharpending@qpq.swiss> - Notes: Copied from random PDF I (PRH) found on my computer written by Craig, dated 2025-01-17.
The GRIDS protocol defines methods for encoding three basic actions within the Gajumaru ecosystem:
- A method for encoding "spend" transactions in a URL where the host section indicates a specific endpoint node to use, the required network ID being then queried from the endpoint.
- A method for encoding "transfer" transactions in a URL where the host section indicates a network to use but leaves resolving an endpoint node up to the client.
- A method for performing digital "dead drop" signatures, queuing signature requests on a server, retrieving them with a client, and returning the resulting signed output to the server.
The Problem
In computing there is a strong tradeoff between usability and security. In the cryptocurrency world this is compounded by the inherent complexity of the systems involved. This has resulted in a common pattern of developers coding wallet features directly into the local execution environments of the applications they wish to integrate with a cryptocurrency system (typically a blockchain).
For example, it is common for games based around on-chain tokens to integrate wallet features directly into the game client itself. This prevents the user from having to leave the game, open another application (their wallet), sign a transaction, and then return to the game.
As another example, it is overwhelmingly common for developers to write wallets as browser plugins that interact directly with in-page applications running in the same browser.
In both cases the incentives for bad actors to compromise the execution environment itself in order to capture private key data or seed phrases is overwhelming. Separation of signature and app execution context is needed. Separating the execution context requires a way for apps requesting signatures and apps capable of providing signatures to communicate not only across execution environments on a single system, but also across completely separate systems.
GRIDS defines a way to to combine communication of limited, known transaction types (such as simple spends) with a way to convey arbitrarily complex transaction data that may require out-of-band communication with a signature client.
The Schema
The GRIDS schema is very similar to HTTP/HTTPS, and even translates directly to an HTTP request URL in the case of a dead-drop instruction.
The basic schema is:
[grid(s)]://[host]/[version]/[instruction]/[path](?a=[amount]&p=[payload])
Where:
-
The schema identifier is either
gridsorgrid. If the instruction is a dead-drop signature then the schema may begridto indicate that the initial pick up request must be made over HTTP rather than HTTPS. If the instruction is not a dead-drop, then there is no difference between the two. -
The
hostelement is interpreted differently based on instruction type:- For
s(short for "spend") it is the network ID where the spend is to take place. - For
t(short for "transfer") it is ahost:portlocation of a node where the client can retrieve the network ID. - For
d(short for "dead-drop") it is ahost:portlocation of a service where a message to be signed is awaiting pickup.
- For
-
The
versionelement is the protocol version. -
As mentioned above, the
instructionelement is one ofs(for "spend"),t(for "transfer"—same as spend but with different host semantics) ord(for "dead-drop"). -
In the case of dead-drops, the
pathelement is the HTTP path to the pickup location. In the case of spends/transfers it is the address of the party to be paid, with (optional) amount and payload details packed into query args of the formsa=[amount]denominated in pucks, represented in the numerals[0-9]p=[payload]represented as URL-encoded binary data
The simple case: SpendTX
Spends are simple on-chain transactions that transfer coins (Gajus/Pucks) from one account to another.
Defined as a record, the inner data in a SpendTX looks like this:
-record(spend_tx,
{sender_id :: string(),
recipient_id :: string(),
amount :: non_neg_integer(),
gas_price :: pos_integer(),
gas :: pos_integer(),
ttl :: pos_integer(),
nonce :: pos_integer(),
payload :: binary()}).
All elements are required with the exception of the payload, which can be
empty. The payload is used to tag spend transactions with meaningful data such
as text indicating what a transaction was for or an invoice number useful for
tracking within an accounting system. Elements such as the gas_price, gas,
ttl and nonce are all elements that a client wallet is expected to
populate on its own.
The external data required for a SpendTX is the network ID of the specific chain that the spend transaction is intended for. The network ID is part of the external encoding of the TX, the encoded representation of the transaction (including the network ID) is what the client ultimately signs. The combination of the nonce and network ID prevents TXs from being replayed on the same network or replayed across different networks.
The information required from a party requesting an on-chain payment is therefore reduced to:
- The account to be paid
- The amount
- Any payload desired (an invoice number, for example)
- The network ID or a host address of a node from which the network ID can be obtained.
This data is encoded in a GRIDS URL as noted above.
GRIDS URLs should be provided to users as both plain text and a QR code to make it easy to verify their content as well as make it possible to easily use both desktop (copy-paste) and mobile (camera input) wallet apps.
Example Spends
Let's say a point-of-sale system at shop needs to charge a customer 25 Gajus.
The POS system knows its own account address for the register, knows the
invoice number, knows the amount to charge, and knows that it is operating on
the groot.mainnet network, but does not know the
customer's account number.
Assume the following payment information:
- Store account:
ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U - Amount: 25 Gajus
- Invoice number:
20250117-0001
The POS system can generate a GRIDS request encoded as:
grids://groot.mainnet/1/s/ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U?a=25000000000000000000&p=20250117-0001
Alternately, if the POS system is operating on an associate chain that isn't as common, or is running its own backend node for efficiency purposes, it could request a "transfer" instruction, specifying its node's public address and the wallet can retrieve the network ID from there:
grids://some.shop:4321/1/t/ak_srFAGY9Dq6p8LVoPSQ8o86s6EFAqWsXHKLDvpzPFcZ8txtR6U?a=25000000000000000000&p=20250117-0001
If the node running at some.shop:4321 is a part of groot.mainnet, then
these two request URLs are effectively identical.
Dead-Drops
The most flexible case for GRIDS is the dead-drop. Using this technique anything can be communicated to a client, signed, and returned. Dead-drops can be used for signatures of contract calls, login messages, or any other kind of message signature one might require.
As mentioned above, dead-drop GRIDS URLs are converted to HTTP URLs by stripping off the GRIDS version and instruction tags and reassembling the request as an HTTP or HTTPS request.
For example:
grids://foo.com/1/d/some_random_path -> https://foo.com/some_random_path
grid://foo.com/1/d/some_random_path -> http://foo.com/some_random_path
After that conversion takes place, the client makes a GET request to the HTTP URL indicated. The response from the server is a JSON object containing the unsigned data and some metadata that tells the client what kind of message is being signed so that the client can select the correct signature method.
After signing, the JSON object is updated with the signed data and the public half of the signing key. The client then submits in a POST request the updated JSON object back to the original endpoint from which the unsigned data was retrieved.
Signature Request Messages (initial GET)
The response to the initial GET request takes the form of a JSON object with this shape:
{"grids" : 1,
"chain" : "gajumaru",
"network_id" : "groot.mainnet",
"type" : "tx",
"public_id" : "ak_2SLsxkPavd1oDwUgCyZkMoQJJvMkUWymg1BEK7egx5rVU8xanV",
"payload" : "Any data here"}
- The
"grids"version will always match the version encoded in the request URL. - The
"chain"element exists in case other projects want to adopt the GRIDS protocol, in which case the protocol rules of the specific chain family will dictate things like serialization details. - The
"network_id"is necessary for the client (and end-user) to verify that activity is occurring where expected and binds the resulting signature to that specific network. - The
"type"may be"message","binary","tx"or"ack".- A
"message"signature is a UTF-8 string used either for verifiable messages or login schemes. - A
"binary"signature is the same as a"message"signature, but is performed over binary data encoded as base64. - A
"tx"is a request for signature of a transaction (usually call data or contract deployment). - An
"ack"is an optional response to a"tx"or"message"response POST.
- A
- The
"public_id"can either be a public key (on-chain account) or the booleanfalse(note absence of quotes).- If the
public_idisfalse, then it is up to the client to pick a key to use (and then populate this field). - If an ID is specified, then the client should select that account or fail if it is not available.
- If the
- The
"payload"is the data to be signed. In the case of a"tx"request (a contract call, for example), this data will be transaction data encoded according to the Gajumaru protocol's rules. Any Gajumaru signature routine will understand how to interpret and handle this data. Each signature context has its own requirements:"tx"signatures must be handled according to the Gajumaru protocol rules for transaction signatures."message"signatures are for simple UTF8 strings in plain text that the client interprets as a binary string. The binary string<<"Gajumaru Signed Message:\n">>is prepended to the message text before the signature is performed. Prefixing message request payloads with this string prevents attackers from disguising transaction signature requests as text message signatures."binary"signatures are for data encoded in base64. The contents are first decoded from base64, then the binary prefix<<"Gajumaru Signed Binary:">>is prepended to the binary data before the signature is performed.
Signed Data Messages (subsequent POST)
After the client signs the payload, it will populate the
"public_id" element (if it was false previously) and add
"signature" element with the requested signature. The
resulting object that must be POSTed back to the origin is
therefore:
{"grids" : 1,
"chain" : "gajumaru",
"network_id" : "groot.mainnet",
"type" : "tx",
"public_id" : "ak_2SLsxkPavd1oDwUgCyZkMoQJJvMkUWymg1BEK7egx5rVU8xanV",
"payload" : "Any data here",
"signature" : "XiuCkEOHINkQN83QUxmIAfmiiRN2XflktnNMtdZDzgW0TQK/YQaurQjfL3R5eoXJvCuUZRgPpKfCvtsXbphDCQ=="}
The server (or any other observer of this message) now has enough information to establish for certain whether whoever signed this message has control of the private key that corresponds to the public key indicated in the message.
Signing of randomized and timestamped messages can be used to implement First-Party Single Sign-On (1pSSO) schemes without any requirement that the server side maintain private data.