QubicTypeScript
Packages

@qubic.org/tcp

Direct TCP transport for Qubic nodes — binary framing, connection pool with automatic failover, and typed request helpers.

Introduction

Why TCP instead of HTTP?

The Qubic HTTP RPC gateway (@qubic.org/rpc) is simpler and covers most application needs. Go to TCP when:

  • You need to query a specific peer rather than through a gateway
  • You want to broadcast a transaction without going through the gateway
  • You're building node monitoring or peer discovery tooling
  • You need the lowest possible latency and can manage connection state

Frame format

Every Qubic TCP message uses an 8-byte frame header:

[size: 3 bytes LE][type: 1 byte][dejavu: 4 bytes LE][payload: N bytes]

dejavu is a random nonce echoed in the response to pair requests and responses. The client sends one frame; the server may respond with multiple packets terminated by END_RESPONSE (type 35).

Connection pool

createNodePool maintains a list of peer addresses and fails over automatically. On connection failure or timeout it moves to the next peer, up to maxRetries attempts.

const pool = createNodePool(["node1.qubic.org", "node2.qubic.org"], {
  timeoutMs: 5000,
})

Pass AbortSignal.timeout(ms) to any request to bound total wait time.


Installation

bun add @qubic.org/tcp
npm install @qubic.org/tcp
pnpm add @qubic.org/tcp

API reference

PageWhat's covered
ConnectioncreateNodeConnection, createNodePool — open and manage TCP connections
RequestsrequestCurrentTickInfo, requestEntity, broadcastTransaction, exchangePublicPeers
Frame utilitiesencodeHeader, decodeHeader, MESSAGE_TYPE, payload decoders

All exports

import {
  // Connections
  createNodeConnection,
  createNodePool,

  // Requests
  requestCurrentTickInfo,
  requestEntity,
  broadcastTransaction,
  exchangePublicPeers,

  // Frame utilities
  encodeHeader,
  decodeHeader,
  HEADER_SIZE,
  DEFAULT_PORT,
  MESSAGE_TYPE,

  // Payload decoders
  decodeCurrentTickInfo,
  decodeExchangePublicPeers,
  decodeRespondEntity,

  // Error classes
  QubicTcpError,
  QubicTcpConnectionError,
  QubicTcpTimeoutError,
} from "@qubic.org/tcp"

Examples

Query tick and entity balance from a pool

import { createNodePool, requestCurrentTickInfo, requestEntity } from "@qubic.org/tcp"
import { identityToPublicKey } from "@qubic.org/crypto"
import { toIdentity } from "@qubic.org/types"

const pool = createNodePool([
  "node1.qubic.org",
  "node2.qubic.org",
  "node3.qubic.org",
])

try {
  const { tick, epoch } = await requestCurrentTickInfo(pool, {
    signal: AbortSignal.timeout(5000),
  })
  console.log(`Epoch ${epoch}, tick ${tick}`)

  const pk = identityToPublicKey(
    toIdentity("CFBMEMZOIDEXQAUXYYSZIURADQLAPWPMNJXQSNVQZAHYVOPYUKKJBJUCTVJL")
  )
  const { entity, identity } = await requestEntity(pool, pk)
  const balance = entity.incomingAmount - entity.outgoingAmount
  console.log(`${identity}: ${balance} QU`)
} finally {
  pool.close()
}

Discover peers

import { createNodeConnection, exchangePublicPeers } from "@qubic.org/tcp"

const conn = await createNodeConnection("node1.qubic.org")
const { peers } = await exchangePublicPeers(conn)
console.log("Known peers:", peers)
conn.close()

Broadcast a transaction directly over TCP

import { createNodePool, broadcastTransaction } from "@qubic.org/tcp"
import { buildTransaction, signTransaction } from "@qubic.org/tx"
import { publicKeyFromSeed } from "@qubic.org/crypto"
import { toSeed } from "@qubic.org/types"

const pool = createNodePool(["node1.qubic.org"])

const seed            = toSeed("aaaa...")
const sourcePublicKey = publicKeyFromSeed(seed)
const destinationPk   = new Uint8Array(32)

const unsigned = buildTransaction({
  sourcePublicKey,
  destinationPublicKey: destinationPk,
  amount: 0n,
  targetTick: tick + 5,
  inputType: 0,
})
const signed = await signTransaction(unsigned, seed)

// Raw bytes — not base64
await broadcastTransaction(pool, signed)
console.log("Broadcast via TCP")
pool.close()

Handle connection errors

import { createNodePool, requestCurrentTickInfo, QubicTcpConnectionError, QubicTcpTimeoutError } from "@qubic.org/tcp"

const pool = createNodePool(["node1.qubic.org", "node2.qubic.org"])

try {
  const info = await requestCurrentTickInfo(pool, { signal: AbortSignal.timeout(3000) })
  console.log("Tick:", info.tick)
} catch (e) {
  if (e instanceof QubicTcpTimeoutError) {
    console.error("Request timed out — all nodes busy or unreachable")
  } else if (e instanceof QubicTcpConnectionError) {
    console.error("Could not connect to any node")
  } else {
    throw e
  }
} finally {
  pool.close()
}

On this page