Skip to content

docs: NIP-CW and bridge-channel-window never show the POST /query request body, so the documented shape returns 400 #6477

Description

@scubashack808

Summary

Neither docs/nips/NIP-CW.md nor docs/bridge-channel-window.md shows a complete POST /query request body. Both present the window request as a bare filter object, which is what a reader implementing from the docs will send, and the relay rejects it with a 400. The body must be a JSON array of filters.

Reproduce

Send the request body exactly as the docs show it, with a valid NIP-98 header from a relay member:

POST /query
Content-Type: application/json
Authorization: Nostr <base64 kind:27235>

{"kinds":[9],"limit":1}

Response:

400 Bad Request
{"error":"invalid filters: invalid type: map, expected a sequence at line 1 column 0"}

A {"filters": [...]} wrapper is rejected identically. The array form works:

[{"kinds":[9],"limit":1}]
200 OK
[ ...signed events... ]

Why the docs read this way

docs/nips/NIP-CW.md §Request:

A window request is a standard filter plus extension fields, submitted wherever the relay accepts filters (for Buzz: the NIP-98-authenticated HTTP bridge POST /query):

followed by a fenced block containing a single object.

docs/bridge-channel-window.md §Request:

A standard bridge filter plus extension fields:

followed by the same single-object shape.

Both sentences are strictly true, because they describe the filter, not the request body. But neither document states that POST /query accepts an array, and no example anywhere in either file shows a complete body. /query is referenced three times across the two files and never once with a body a reader could copy.

The implementation is unambiguous

crates/buzz-relay/src/api/bridge.rs:

// Two-pass parse: preserve raw JSON for custom extension fields (before_id,
// depth_limit, feed_types) that nostr::Filter silently drops.
let raw_filters: Vec<Value> = serde_json::from_slice(body)
    .map_err(|e| api_error(StatusCode::BAD_REQUEST, &format!("invalid filters: {e}")))?;

The array is correct and consistent with NIP-01, where REQ carries one or more filters. The gap is only that the docs never say so.

Suggested fix

One line in each §Request, plus one complete example. For example in docs/nips/NIP-CW.md:

The request body is a JSON array of filters, as on any NIP-01 filter surface. A window request is one such filter:

[
  {
    "kinds": [9],
    "#h": ["<channel-id>"],
    "top_level": true,
    "limit": 50
  }
]

Minor, same area

The error text invalid filters: invalid type: map, expected a sequence at line 1 column 0 describes the serde failure rather than the contract. Something like expected a JSON array of filters would point a reader at the fix directly. Not important if the docs carry a full example.

Context

Found while implementing a browser client against the channel-window surface. The endpoint itself behaves exactly as specified once the body shape is right; top_level, the composite until + before_id cursor, and the 39006 bounds overlay are not in question here. This is a documentation issue only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions