wss://<relay>/ws/node. Messages are JSON text frames with a type field. The Go file
panofx-node/internal/intake/protocol.go is the single source of truth for the wire format; the relay’s
TypeScript mirrors it.
Connect
1
Challenge, relay → node
2
Hello, node → relay
signature is EIP-191 over panofx-node-hello|<nonce>|<address>|<name>. A node that quotes a Solana
chain adds solanaAddress and an ed25519 signature over panofx-node-hello|<nonce>|<solanaAddress>|<name>.
The relay keeps the corridors on chains it serves and ignores the rest. On a relay that requires
registration, an unregistered address is closed with “the relay only admits registered resolvers”.3
Welcome, relay → node
RFQ, relay → node
sellAmount is base units of what the taker sells. replyBy is unix milliseconds. On Solana executor is
the program and the RFQ carries the rest of the settlement terms: feePayer, feeMint, feeBps,
treasury, gasFee (in the fee mint), computeUnitLimit, computeUnitPrice.
Offer, node → relay
inputToken is what the node gives; outputToken is what it receives. On Solana the offer carries
transaction (the maker-signed canonical settlement, base64) instead of order, signature is the
maker’s base58 ed25519 signature, and deadline is the fill’s expire_at. An offer carries exactly one
of order or transaction.
Decline, node → relay
Heartbeat, node → relay, every heartbeatIntervalMs
signature is EIP-191 over panofx-node-heartbeat|<ts>|<address>. state is quoting while any chain
quotes, else degraded or paused. A node is offered RFQs on a chain only while that chain’s state is
quoting. On Solana allowance equals balance. A node whose heartbeat is older than 35 seconds is stale.
Rate band, relay → node, every 10 seconds
Lifecycle, relay → node
type is fill, lost or expired. On Solana txHash is the transaction signature.
Error, relay → node
Registration
panofx-node register posts to POST /v1/resolvers/register with
an EIP-191 signature over panofx-register|<chainId>|<address>|<name>|<alertEmail>|<region>|<issuedAt>.

