mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
feat(relay): channel labels (#8175)
🤖 ## Summary A plugin or agent often needs to tag its own channels and then find them again, for example "my attention-config channel" or "all channels of this workspace". Today a channel has no labels: the relay ignores any `t` tags in create or edit requests. This PR adds channel labels. - A create request can carry up to 8 labels as `["t", "<label>"]` tags. - An edit request with `t` tags replaces the label set. `["t", ""]` alone clears it, the same convention as `["ttl", ""]`. Only owners and admins can change labels, as for the name. - The relay publishes each label as a `t` tag on the channel's state events, after the channel type tag. Part 2 of 3, stacked on part 1. ### Related issue None found. ### Details - **Rules.** A label uses 1–64 characters from `a-z`, `0-9`, `.`, `:` and `-`. The relay refuses a label equal to a channel type name (`stream`, `forum`, `dm`, `workflow`, `system`), so a label cannot make a channel look like another type. - **No trust from labels.** Any admin can set a label. Only the relay-written creator tag (part 1) says who made a channel. - **Storage.** New migration `0061_channel_labels.sql` adds `channels.labels TEXT[] NOT NULL DEFAULT '{}'`. Channels without labels look and behave as before. - **Readers.** The CLI and `buzz-acp` read the channel type from the first `t` tag. Before this PR they used the last one, which would now be a label. - **Spec.** NIP-CL gets a "Channel labels" section with these rules. ### Testing - Relay tests: labels at creation appear after the type tag, edit replaces and clears them, invalid labels are refused, and members who are not admins cannot change them. - Unit tests for the label rules: allowed characters, length, reserved type names, the 8-label cap and the clear marker. - `buzz-acp` test: the channel type comes from the first `t` tag, not a label. ### Stack These are two stacks. Merge them in this order. Client support (creating, labeling and finding these channels) will be in block/buzz-app, not the buzz SDK and CLI. **Stack 1: channel labels and creator lookup (#8174–#8176)** 1. #8174 channel type and creator tags, and the channel creation time 2. #8175 channel labels ← this PR 3. #8176 database-side label and creator match, and the rule for choosing one channel **Stack 2: system channels (#8180–#8182)**, stacked on stack 1 because it uses labels and the creator tag 4. #8180 the system channel type 5. #8181 leave system channels out of reads that do not name them 6. #8182 channel-state filters opt in to system channels --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -21,13 +21,18 @@ plain NIP-29, a client cannot:
|
||||
- tell which of two channels is older. The `created_at` of a kind:39000 event
|
||||
is the time of the last edit, not the time of creation.
|
||||
|
||||
A client also cannot mark its own channels so that it can find them again.
|
||||
Channel labels (below) do this.
|
||||
|
||||
## Channel identity tags
|
||||
|
||||
On every channel-state event that the relay signs, the relay MUST put these
|
||||
tags in this order, before any other `t` or `P` tag:
|
||||
|
||||
1. `["t", <channel type>]`, for example `stream`, `forum`, `dm`, `workflow`.
|
||||
2. `["P", <creator public key, hex>]`.
|
||||
2. `["t", <label>]` for each channel label, in stored order (see
|
||||
[Channel labels](#channel-labels)).
|
||||
3. `["P", <creator public key, hex>]`.
|
||||
|
||||
kind:39000 also carries `["created_at", <unix seconds>]`.
|
||||
|
||||
@@ -42,6 +47,7 @@ Example kind:39000 (signing fields and unrelated tags omitted):
|
||||
["d", "9b353519-f4fe-4757-aef4-bec6cc0ae54c"],
|
||||
["name", "agent-config"],
|
||||
["t", "stream"],
|
||||
["t", "agent-config"],
|
||||
["P", "<creator public key>"],
|
||||
["created_at", "1759939200"]
|
||||
]
|
||||
@@ -51,11 +57,12 @@ Example kind:39000 (signing fields and unrelated tags omitted):
|
||||
| Tag | Kinds | Meaning |
|
||||
| --- | --- | --- |
|
||||
| first `t` | 39000–39002 | Channel type. |
|
||||
| later `t` | 39000–39002 | Channel labels. |
|
||||
| `P` | 39000–39002 | Public key that signed the kind:9007 that created the channel. |
|
||||
| `created_at` | 39000 | Time the channel was created, in unix seconds, as a decimal string. |
|
||||
|
||||
**Channel type.** Clients MUST read the channel type from the first `t` tag.
|
||||
A relay can add other `t` tags after it.
|
||||
Later `t` tags are labels.
|
||||
|
||||
**Creator.** The relay writes `P` from its own record of the kind:9007 that
|
||||
created the channel. No client can set it. It does not change when ownership
|
||||
@@ -76,6 +83,28 @@ until the channel next changes, for example on an edit or a membership
|
||||
change. A client MUST treat a missing tag as unknown. A missing `P` does not
|
||||
mean that a key did not create the channel.
|
||||
|
||||
## Channel labels
|
||||
|
||||
A label groups channels across channel types, for example `workspace` or
|
||||
`agent-config`. Labels have no meaning to the relay.
|
||||
|
||||
**Set labels.** A kind:9007 (create group) MAY carry up to 8 distinct
|
||||
labels as `["t", <label>]` tags. On kind:9002 (edit metadata):
|
||||
|
||||
- `t` tags replace the whole label set;
|
||||
- `["t", ""]` tags and no other `t` tag clear the set;
|
||||
- no `t` tag leaves the labels as they are.
|
||||
|
||||
Only an owner or admin of the channel MAY change its labels.
|
||||
|
||||
**Rules.** A label is 1–64 characters from `a-z`, `0-9`, `.`, `:` and `-`. A
|
||||
label MUST NOT be `stream`, `forum`, `dm`, `workflow` or `system`. These are
|
||||
channel type names, so this rule stops a label from making a channel look like
|
||||
another type. The relay MUST reject the whole event if it has more than 8
|
||||
distinct labels, an invalid label, a reserved name, or `["t", ""]` together
|
||||
with other labels. The relay stores repeated labels once, so a repeat does
|
||||
not count toward the 8, and repeated `["t", ""]` tags clear the set like one.
|
||||
|
||||
## Finding channels
|
||||
|
||||
Use ordinary NIP-01 filters on the channel-state kinds.
|
||||
@@ -83,17 +112,26 @@ Use ordinary NIP-01 filters on the channel-state kinds.
|
||||
| To find | Filter |
|
||||
| --- | --- |
|
||||
| Channels that a key created | `{"kinds": [39000], "#P": ["<creator>"]}` |
|
||||
| Channels with a label | `{"kinds": [39000], "#t": ["<label>"]}` |
|
||||
| One creator's channels with a label | `{"kinds": [39000], "#P": ["<creator>"], "#t": ["<label>"]}` |
|
||||
| Channels of one type | `{"kinds": [39000], "#t": ["<type>"]}` |
|
||||
|
||||
A `#P` filter does not return a channel whose events lack `P` (see
|
||||
**Older events**). So it can miss channels that a key created before the relay
|
||||
supported this NIP.
|
||||
|
||||
Values in one tag filter combine with OR, as in NIP-01. So
|
||||
`"#t": ["workspace", "notes"]` finds channels with either label. To require a
|
||||
label and a creator, use `#t` and `#P` together.
|
||||
|
||||
## Trust
|
||||
|
||||
A client that relies on these tags MUST check that the relay's key signed the
|
||||
event, as for all NIP-29 group-state events.
|
||||
|
||||
Labels give no trust. Any owner or admin can set them. Only `P` says who
|
||||
created a channel.
|
||||
|
||||
## Relation to other NIPs
|
||||
|
||||
- NIP-29: the group-state events that this NIP adds tags to.
|
||||
|
||||
Reference in New Issue
Block a user