5.4 KiB
NIP-73
Spreadsheets
draft
optional
This NIP provides a simple way to save spreedsheets on Nostr.
Event kind 35337
describes a workbook with data
tags that contain the value of each cell as well as optional styling.
{
"kind": 35337,
"tags": [
["d", "<unique identifier>"],
["title", "Name of this spreadsheet"], // public title
["data", "<sheet name>", "<column letter>", "<row number>", "<value>"],
["data", "<sheet name>", "<column letter>", "<row number>", "<value>", "<style>"],
["style", "<TBD>"], // Need to specify all options here.
],
"content": "", // reserved for private content below
// ...
}
Sheets MUST be rendered in the order of their tags.
As an example:
{
"id": "32360b52b11616ea331aacac516494e36bd4079d8908edc8f26ad1e4acab5a53",
"kind": 35337,
"tags": [
[ "d", "SheetStr Demo" ],
[ "data", "Sheet1", "J", "25", "3" ],
[ "data", "Sheet1", "J", "26", "5" ],
[ "data", "Sheet1", "J", "27", "=SUM(J25:J26)" ]
],
"created_at": 1713819120,
"content": "",
"pubkey": "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c",
"sig": "880eb3d67fc66ca2d4e7819ae9b9ca577df35950fb5d11d24f95f350cfeab0b4532646c52113d5bb629cf9a2e4d8ef646ff434b59f1c894c8f719f65d59ed8f0",
}
Styling
TBD
Private Spreadsheets
A private spreadsheet has tags in a JSON-stringified and NIP-44-encrypted to the user's own pubkey .content
{
"kind": 35337,
"tags": [
["d", "<unique identifier>"]
],
"content": nip44Encrypt([
["title", "Name of this spreadsheet"], // private title
["data", "<sheet name>", "<column letter>", "<row number>", "<value>"], // private data
["data", "<sheet name>", "<column letter>", "<row number>", "<value>", "<style>"],
["style", "<TBD>"], // Need to specify all options here.
// ... other tags
], author.privateKey, author.pubkey),
// ...
}
Sharing Encrypted Spreadsheets with View-only permission to a Group
Ready-only sharing is achieved by adding a p
tag to each receiver with a shared secret to decrypt the .content
.
The shared secret is a new Nostr Private Key in hex, NIP-44-encrypted to each p
tag and placed as a 4th value in each tag.
The .content
is then encrypted by a conversation key between the new private and the public key.
val keyPair = nostr.generateKeyPair()
{
"pubkey": author.pubkey
"kind": 35337,
"tags": [
["d", "<unique identifier>"]
["p", "<pubkey 1>", "<relay url>", nip44Encrypt(keyPair.privateKeyHex, author.privateKey, "<pubkey 1>") ]
["p", "<pubkey 2>", "<relay url>", nip44Encrypt(keyPair.privateKeyHex, author.privateKey, "<pubkey 2>") ]
["p", "<pubkey 3>", "<relay url>", nip44Encrypt(keyPair.privateKeyHex, author.privateKey, "<pubkey 3>") ]
],
"content": nip44Encrypt([
["title", "Name of this spreadsheet"], // private title
["data", "<sheet name>", "<column letter>", "<row number>", "<value>"], // private data
["data", "<sheet name>", "<column letter>", "<row number>", "<value>", "<style>"],
["style", "<TBD>"], // Need to specify all options here.
// ... other tags
], keyPair.privateKey, keyPair.publicKey),
"sig": signWith(author.privateKey)
// ...
}
with nip44Encrypt("text to encrypt", sender.privatekey, receiver.publickey)
.
To decrypt, receivers SHOULD find the p
-tag ciphertext for their key, decrypt that to get the shared private key and use the shared private key to decrypt the .content
.
Clients SHOULD include the author as a p
tag to save the secret that will allow the author to decrypt and modify the event further.
Sharing Encrypted or Public Spreadsheets with Edit permission to a Group
This method allows anyone listed as the p
tag to edit the Spreadsheet. It follows a similar process to read-only spreadsheets, but instead of signing the event with the author's main key, the event is signed by the shared secret as well.
val keyPair = nostr.generateKeyPair()
{
"pubkey": keyPair.publicKey
"kind": 35337,
"tags": [
["d", "<unique identifier>"]
["p", "<pubkey 1>", "<relay url>", nip44Encrypt(keyPair.privateKeyHex, keyPair.privateKey, "<pubkey 1>") ]
["p", "<pubkey 2>", "<relay url>", nip44Encrypt(keyPair.privateKeyHex, keyPair.privateKey, "<pubkey 2>") ]
["p", "<pubkey 3>", "<relay url>", nip44Encrypt(keyPair.privateKeyHex, keyPair.privateKey, "<pubkey 3>") ]
["title", "Name of this spreadsheet"], // public title
["data", "<sheet name>", "<column letter>", "<row number>", "<value>"], // public data
["data", "<sheet name>", "<column letter>", "<row number>", "<value>", "<style>"],
["style", "<TBD>"], // public styles
],
"content": nip44Encrypt([
["title", "Name of this spreadsheet"], // private title
["data", "<sheet name>", "<column letter>", "<row number>", "<value>"], // private data
["data", "<sheet name>", "<column letter>", "<row number>", "<value>", "<style>"],
["style", "<TBD>"], // private styles
// ... other tags
], keyPair.privateKey, keyPair.publicKey),
"sig": signWith(keyPair.privateKey)
// ...
}
To edit, receivers SHOULD find the ciphertext for their key, decrypt that to get the shared private key and use the shared private key to encrypt the .content
and sign the event.
The Client knows when it's user has the permission to edit when the pubkey of the secret matches the pubkey of the event.
Spreadsheets MAY have private and public parts at the same time.