nips/76.md
Vitor Pamplona d60412c81f breaks the rp keys into multiple tags instead of a single list
offers guidance for multiple `prp` tags to be a logical OR
2024-09-13 16:24:45 -04:00

2.4 KiB

NIP-76

Relay Read Permissions

draft optional

Tag names rp (read permission) and prp (probabilistic read permission) define which keys are authorized to download an event from the relay.

Events with an rp or prp require AUTH to be downloaded.

Read Permission

The rp tag takes a pubkey. Multiple rp tags represent a logical OR.

["rp", "<pubkey1>"]
["rp", "<pubkey2>"]
["rp", "<pubkey3>"]

Relays MUST check if the authed user is one of the keys in the rp before sending the event to the client.

Probabilistic Read Permissions

Probabilistic permissions use bloom filters of a set of authorized pubkeys. They are represented by a colon-separated value with:

  1. the number of bits in the bit array
  2. the number of hashing rounds used by the filter
  3. the bit array in Base64.
["prp", "<bits>:<rounds>:<base64>"]

Bloom filters MUST use SHA256 functions of the key + iterating index as the pseudocode below demonstrates:

class BloomFilter(size: Int, rounds: Int, buffer: ByteArray) {
    val bits = BitArray(buffer)

    fun bitIndex(value: ByteArray, index: Byte) {
        return BigInt(sha256(value || index)) % size
    }

    fun add(pubkey: HexKey) {
        val value = pubkey.hexToByteArray()

        for (index in 0 until rounds) {
            bits[bitIndex(value, index)] = true 
        }
    }

    fun mightContains(pubkey: HexKey): Boolean {
        val value = pubkey.hexToByteArray()

        for (index in 0 until rounds) {
            if (!bits[bitIndex(value, index)]) {
                return false
            }
        }

        return true
    }

    fun encode() {
        return size + ":" + rounds + ":" + base64Encode(bits.toByteArray()) 
    }
    
    fun decode(str: String): BloomFilter {
        val parts = str.split(":")
        return BloomFilter(parts[0].toInt(), parts[1].toInt(), base64Decode(parts[2]))
    }
}

Relays MUST check if the authed user is in the filter before returning the event.

Multiple prp tags represent a logical OR.

Test cases

The filter below has 100 bits, with 10 rounds of hashes that should be able to match 10,000,000 keys without a single false positive.

["prp", "100:10:QGKCgBEBAAhIAApO"]

It includes keys ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b and 460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c