Quickstart: connect to a TON liteserver in 5 minutes
Connect to a TON liteserver in five minutes: liteserver entry anatomy, the global-config JSON format and its signed-int32 IP quirk, plus a runnable Node.js check.
A liteserver is the lowest-level public interface of a TON node: a binary ADNL-over-TCP endpoint that answers queries about blocks, accounts, and transactions directly from the node's state. No HTTP gateway, no JSON-RPC middleman. This page gets you from zero to a successful getMasterchainInfo call in Node.js.
What a liteserver entry consists of
Every liteserver is identified by exactly three values:
- Host or IP — where to open the TCP connection.
- Port — the ADNL listening port (arbitrary; there is no standard port).
- Ed25519 public key — the server's identity key, base64-encoded. The client uses it to perform the ADNL handshake and encrypt the channel.
The key is not a secret and not an API key — it authenticates the server to you, not you to the server. Anyone with these three values can connect, which is why paid providers gate access at the network level instead.
The global config format
Clients traditionally consume liteserver entries from a "global config" JSON file (mainnet: https://ton.org/global.config.json). The relevant part looks like this:
{
"liteservers": [
{
"ip": 1097649206,
"port": 29296,
"id": {
"@type": "pub.ed25519",
"key": "p2tSiaeSqX978BxE5zLxuTQM06WVDErf5/15QToxMYA="
}
}
]
}
Two things to know about the ip field:
It is an integer, not a dotted string. The IPv4 address is packed into a single 32-bit number: 1097649206 is 65.108.204.54.
It is a signed int32. Any address whose first octet is 128 or higher overflows into a negative number — -2018135749 is 135.181.177.59, and mainnet configs are full of such values. In JavaScript, bitwise operators already work on signed 32-bit integers, so one function handles both cases:
function intToIP(int) {
return [(int >> 24) & 0xff, (int >> 16) & 0xff, (int >> 8) & 0xff, int & 0xff].join('.');
}
In languages without int32 bitwise semantics, add 2**32 to negative values before converting.
Hostname alternative. The integer encoding is a wire-format legacy; modern client libraries take a plain connection string. ton-lite-client's engine accepts tcp://<host>:<port>, where <host> can be a DNS hostname — DNS resolution happens at the socket layer. If your provider gives you a hostname, use it directly and skip the integer conversion entirely. You only need the packed-int form when writing a global.config.json for tooling that requires that exact schema.
Minimal end-to-end check with ton-lite-client
Requires Node.js 18+ (for built-in fetch).
npm install ton-lite-client
Create check.mjs:
import { LiteClient, LiteRoundRobinEngine, LiteSingleEngine } from 'ton-lite-client';
function intToIP(int) {
return [(int >> 24) & 0xff, (int >> 16) & 0xff, (int >> 8) & 0xff, int & 0xff].join('.');
}
const { liteservers } = await fetch('https://ton.org/global.config.json').then((r) => r.json());
const engines = liteservers.map(
(ls) =>
new LiteSingleEngine({
host: `tcp://${intToIP(ls.ip)}:${ls.port}`,
publicKey: Buffer.from(ls.id.key, 'base64'),
}),
);
const engine = new LiteRoundRobinEngine(engines);
const client = new LiteClient({ engine });
const master = await client.getMasterchainInfo();
console.log('workchain:', master.last.workchain);
console.log('seqno:', master.last.seqno);
engine.close();
Run it:
node check.mjs
Expected output — the current masterchain block, incrementing roughly every 0.4 seconds:
workchain: -1
seqno: 48211377
If a public server in the config is down or overloaded you may see timeouts before the round-robin engine lands on a healthy one; that is normal for the free config and exactly the behavior you eliminate with a private endpoint.
Point it at your own liteserver
A TONNode plan gives you the same three values — host, port, base64 Ed25519 key — in the dashboard. It is a drop-in replacement for any global-config-compatible client; no code changes beyond the entry itself:
const engine = new LiteSingleEngine({
host: 'tcp://<your-host>:41000',
publicKey: Buffer.from('<base64-key>', 'base64'),
});
const client = new LiteClient({ engine });
Replace <your-host>, the port, and <base64-key> with the values from your dashboard. Since the host is a DNS name, no intToIP step is needed.
If some tool in your stack insists on the global-config JSON schema, pack the resolved IP back into a signed int32:
function ipToInt(ip) {
return ip.split('.').reduce((acc, oct) => (acc << 8) | parseInt(oct, 10), 0) | 0;
}
Keep public servers as fallbacks
Public-config liteservers are free but shared: expect throttling (toncenter allows 1 req/s anonymously), sporadic not ready errors, and ADNL timeouts under load. They have no guaranteed archive depth either. The TON documentation itself recommends a dedicated liteserver for production use.
A pragmatic setup is your private entry first, public entries behind it:
const engines = [
new LiteSingleEngine({
host: 'tcp://<your-host>:41000',
publicKey: Buffer.from('<base64-key>', 'base64'),
}),
// public-config servers as fallback:
...publicConfig.liteservers.map(
(ls) =>
new LiteSingleEngine({
host: `tcp://${intToIP(ls.ip)}:${ls.port}`,
publicKey: Buffer.from(ls.id.key, 'base64'),
}),
),
];
const client = new LiteClient({ engine: new LiteRoundRobinEngine(engines) });
Note that LiteRoundRobinEngine rotates across all ready engines rather than strictly preferring the first, so under normal operation some queries will still hit the slower public servers. If you need strict primary/fallback semantics, run two clients and fail over in your own code.
Next steps
- Query account balances and run get-methods against the same connection —
client.getAccountState(address, master.last)works out of the box. - Wire your liteserver into an AI agent via the MCP server:
npx -y @tonnode/mcpwithTON_LITESERVERS='[{"ip":"<your-host>","port":41000,"key":"<base64-key>"}]'. - Plans start at 10 req/s guaranteed; see pricing. Questions: t.me/tonnode_support.