NIP-76 ====== Relay Read Permissions ---------------------- `draft` `optional` Tags `rp` (read permission) and `prp` (probabilistic read permission) specify which keys are authorized to download an event from the relay. Events tagged with `rp` or `prp` require AUTH for download. ## Read Permission The `rp` tag takes a pubkey in lowercase hex as value. ```json ["rp", ""] ["rp", ""] ["rp", ""] ``` When responding to `REQ`s, if an event contains `rp` tags, relays MUST verify that the authenticated user is either the event's author or one of the keys in the `rp` set before delivering it to the client. ## Probabilistic Read Permissions Probabilistic permissions are implemented using Bloom filters that represent a set of authorized pubkeys. These permissions are expressed as a colon-separated value comprising: 1. the number of bits in the bit array, 2. the number of hash rounds applied, and 3. the bit array encoded in Base64. 4. the salt encoded in Base64. ```json ["prp", ":::"] ``` Bloom filters MUST use `SHA256` functions applied to the concatenation of the key, salt, and index, as demonstrated in the pseudocode below: ```js class BloomFilter(size: Int, rounds: Int, buffer: ByteArray, salt: ByteArray) { val bits = BitArray(buffer) fun bitIndex(value: ByteArray, index: Byte) { return BigInt(sha256(value || salt || 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()) + ":" + base64Encode(salt) } fun decode(str: String): BloomFilter { val [sizeStr, roundsStr, bufferB64, saltB64] = str.split(":") return BloomFilter(sizeStr.toInt(), roundsStr.toInt(), base64Decode(bufferB64), base64Decode(saltB64)) } } ``` When responding to `REQ`s, if an event contains `prp` tags, relays MUST verify that the authenticated user is either the event's author or matches any of the filters before delivering it to the client. If both `rp` and `prp` tags are present, the authenticated user MUST either be in the `rp` set or match any `prp` filter. ### Test cases The filter below has 100 bits and uses 10 rounds of hashing, which should be capable of handling up to 10,000,000 keys without producing any false positives. ```json ["prp", "100:10:AAAkAQANcYQFCQoB:hZkZYqqdxcE="] ``` It includes keys `ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b` and `460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c`