asyncapi: 3.0.0
info:
  title: Tripport — Robinhood JSON-RPC Pub/Sub WS
  version: 0.2.0-n9-fast
  description: |
    Robinhood WebSocket surface: `newHeads`, `logs`, and the operator-controlled
    `robinhood_pendingBlock`. `newHeads` is normally served from the continuously warm
    earliest-wins header fan. Subscription ids are issued locally. If that layer is silent,
    new subscriptions fall back to the previous per-session upstream path until it recovers.
    `newPendingTransactions` is intentionally absent: subscriptions were acknowledged
    but delivered zero messages on every measured endpoint, so capability was not observed.
defaultContentType: application/json
servers:
  production: {$ref: '_shared.yaml#/servers/production'}
  staging: {$ref: '_shared.yaml#/servers/staging'}
channels:
  /ws/robinhood:
    address: /ws/robinhood
    description: |
      Robinhood pub/sub. `newHeads` normally comes from the warm merged header layer;
      `logs` keeps its existing upstream path. `robinhood_pendingBlock` is disabled by
      default and can be enabled only by the operator. When disabled, subscribing returns
      JSON-RPC `-32601` with message `robinhood_pendingBlock is not enabled`.
    messages:
      Subscribe: {$ref: '#/components/messages/Subscribe'}
      Notification: {$ref: '#/components/messages/Notification'}
      PendingBlockDisabled: {$ref: '#/components/messages/PendingBlockDisabled'}
      WSError: {$ref: '_shared.yaml#/components/messages/WSError'}
operations:
  sendSubscribe:
    action: send
    channel: {$ref: '#/channels/~1ws~1robinhood'}
    messages: [{$ref: '#/channels/~1ws~1robinhood/messages/Subscribe'}]
  receiveNotification:
    action: receive
    channel: {$ref: '#/channels/~1ws~1robinhood'}
    messages: [{$ref: '#/channels/~1ws~1robinhood/messages/Notification'}, {$ref: '#/channels/~1ws~1robinhood/messages/PendingBlockDisabled'}, {$ref: '#/channels/~1ws~1robinhood/messages/WSError'}]
components:
  schemas:
    SubscribeRequest:
      type: object
      required: [jsonrpc, id, method, params]
      properties:
        jsonrpc: {type: string, const: '2.0'}
        id: {oneOf: [{type: integer}, {type: string}]}
        method: {type: string, const: eth_subscribe}
        params:
          type: array
          minItems: 1
          maxItems: 2
          prefixItems:
            - {type: string, enum: [newHeads, logs, robinhood_pendingBlock]}
            - {type: object, properties: {address: {type: string}, topics: {type: array, items: {}}}}
      description: >-
        `newHeads` and `robinhood_pendingBlock` receive opaque locally issued subscription
        ids. The second filter parameter applies only to `logs`.
      x-newHeads-source:
        primary: continuously-warm-header-fan
        subscription-id: locally-issued-opaque-128-bit
        silence-fallback: previous-per-session-upstream-path
        recovery: restore-warm-header-fan
    NotificationEnvelope:
      type: object
      required: [jsonrpc, method, params]
      properties:
        jsonrpc: {type: string, const: '2.0'}
        method: {type: string, const: eth_subscription}
        params:
          type: object
          required: [subscription, result]
          properties:
            subscription: {type: string}
            result:
              oneOf:
                - {$ref: '#/components/schemas/NewHeadResult'}
                - {$ref: '#/components/schemas/LogsResult'}
                - {$ref: '#/components/schemas/PendingBlockResult'}
    NewHeadResult:
      type: object
      additionalProperties: true
      required: [number]
      properties:
        number: {type: string, pattern: '^0x[0-9a-fA-F]+$'}
    LogsResult:
      type: object
      additionalProperties: true
    PendingBlockResult:
      type: object
      additionalProperties: false
      examples:
        - number: 42
          parentHash: '0x0000000000000000000000000000000000000000000000000000000000000000'
          txHashes: ['0x0000000000000000000000000000000000000000000000000000000000000001']
          at_ms: 1789468016629
      required: [number, parentHash, txHashes, at_ms]
      properties:
        number: {type: integer, minimum: 0, description: JSON number, not an Ethereum hex quantity.}
        parentHash: {type: string, pattern: '^0x[0-9a-fA-F]{64}$'}
        txHashes:
          type: array
          items: {type: string, pattern: '^0x[0-9a-fA-F]{64}$'}
          description: Empty array is valid; null is not.
        at_ms: {type: integer, minimum: 1, description: Unix time in milliseconds.}
    PendingBlockDisabledError:
      type: object
      additionalProperties: false
      required: [jsonrpc, id, error]
      properties:
        jsonrpc: {type: string, const: '2.0'}
        id: {oneOf: [{type: integer}, {type: string}]}
        error:
          type: object
          additionalProperties: false
          required: [code, message]
          properties:
            code: {type: integer, const: -32601}
            message: {type: string, const: robinhood_pendingBlock is not enabled}
  messages:
    Subscribe:
      title: eth_subscribe
      payload: {$ref: '#/components/schemas/SubscribeRequest'}
    Notification:
      title: newHeads, logs, or robinhood_pendingBlock notification
      payload: {$ref: '#/components/schemas/NotificationEnvelope'}
    PendingBlockDisabled:
      title: robinhood_pendingBlock disabled response
      payload: {$ref: '#/components/schemas/PendingBlockDisabledError'}
