Skip to content

relay: kind-3407 REJECT is not admitted — the buyer's delivery-refusal path cannot reach relay.maxplayer.ai #961

Description

@orveth

maxplayer-core publishes kind-3407 REJECT and the seller subscribes to it, but the vendored relay never admits that kind. The buyer's delivery-refusal path therefore cannot reach relay.maxplayer.ai.

The gap, with its denominator

crates/maxplayer-core/src/kinds.rs declares 9 kinds. buzz-relay/src/handlers/ingest.rs defines a const for 8 of them. Exactly one is absent, and it is the only one:

  • JOB_REJECT_KIND = 3407 — no const anywhere in ingest.rs.

required_scope_for_kind's trade arm covers 34013406 plus the 30340 heartbeat. Kind 3407 falls through to the _ => catch-all, which returns Ok(Scope::MessagesWrite) only when open_ingest_enabled(). On a closed-ingest deployment it is refused.

We publish it, subscribe to it, and act on it

  • crates/maxplayer-core/src/gateway.rs:910 builds the rejection draft; the test at :1165 asserts "rejection uses kind 3407".
  • crates/maxplayer-core/src/seller_node/run.rs:1286 subscribes Kind::Custom(JOB_REJECT_KIND).
  • run.rs:3940 dispatches it: k if k.as_u16() == JOB_REJECT_KIND => self.on_reject(&event).await.
  • run.rs:8217 asserts the live seller-node subscription must include that kind.

So the seller is listening for an event the relay will not accept, and has a test pinning that it listens.

Measured on the live relay

Five kinds requested on one anonymous connection against wss://relay.maxplayer.ai (read-only, nothing signed, nothing published):

  • 3401 OFFER — 500
  • 3403 RESULT — 500
  • 3405 AWARD — 500
  • 3406 ACCEPT — 500
  • 3407 REJECT — 0

The four siblings are at the query's own limit: 500, so they are "≥500" and serve as positive controls on the same connection. REJECT is the only zero.

Bound, stated rather than glossed: n=0 is consistent with the relay refusing the kind and with nothing ever having attempted to publish one; reads alone cannot separate those. The relay-would-refuse half is read off the code above, not demonstrated by a push. Settling it empirically needs a signed 3407 publish against production, which is why it is not in this issue.

Why it matters

REJECT is the buyer's verified delivery refusal. With it unpublishable there is no on-wire way to refuse a delivery, and on_reject can never fire. Refusal otherwise resolves toward quarantine-or-paid, so this sits next to money rather than in cosmetics.

#926 is blocked by this and does not say so. It asks to "reject a delivery with no checks attestation before paying" — exactly this flow. Whoever picks it up will build the buyer half and find the event silently never lands. #682 (closed) reads like the lineage: "no buyer-authored terminal event for an award that never delivers" is the problem 3407 exists to solve, though its body never names the kind. #117 is the adjacent reject taxonomy, not this.

Shape of the fix

Add the const and extend the existing trade arm — the same one-line-per-kind pattern 3406 already follows (#329). Two things a fix must carry:

  1. A positive control. A test asserting 3407 maps to MessagesWrite should be able to go red; assert the mapping, not merely that the const exists.
  2. A redeploy. A relay change is inert until nixos-rebuild switch --flake .#relay runs against the box — and note that no CI job compiles crates/buzz (it is not a root-workspace member and ci.yml never names buzz), so a green PR here proves nothing about this crate. The first compile happens at deploy time.

Measured 2026-09-02 against main and the live relay. The kind enumeration was taken from both files programmatically rather than by eye.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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