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 that include an `rp` or `prp` require AUTH to be downloaded. ## Read Permission The `rp` tag accepts a list of pubkeys ```json ["rp", "", "", ""] ``` 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 Bloom filters are bit arrays that encode keys `n` times. They are represented by a base64 encoded tag value with the `n` as the third element. ```json ["prp", "::"] ``` Bloom filters MUST use `SHA-256` functions of the key + iterating index as the psedocode below: ```js class BloomFilter(size: Int, n: 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..n) { bits[bitIndex(value, index)] = true } } fun mightContains(pubkey: HexKey): Boolean { val value = pubkey.hexToByteArray() for (index in 0..n) { if (!bits[bitIndex(value, index)]) { return false } } return true } fun encode() = size + ":" + rounds + ":" + base64Enc(bits.toByteArray()) // base64 might include extra 0 bits to fill the last byte fun decode(str: String): BloomFilter { val parts = str.split(":") return BloomFilter(parts[0].toInt(), parts[1].toInt(), base64Decode(bits.toByteArray())) } } ``` Relays MUST check if the authed user is in the filter before returning the event. ### Test cases The filter below has 100 bits, with 10 rounds or hashes that should be able to match 10,000,000 keys without a single false positive. ```json ["prp", "100:10:QGKCgBEBAAhIAApO"] ``` It includes keys `ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b` and `460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c`