TonAssemble
2026A Telegram Mini App that builds and publishes real websites for .ton blockchain domains.
TypeScript · Fastify · React · SQLite · Docker · TON Connect
- Workspaces
- 8
- Services
- API · Bot · Web
- Network
- TON mainnet
The problem
A .ton domain is a blockchain record, not a hosting account. Owning one gives you a name but no way to put a page behind it — you need to understand TON DNS record formats, run storage or a reverse proxy, and sign an on-chain transaction to point the name at your content. That’s a lot of friction for someone who just wants a page on a name they already own.
TonAssemble closes that gap inside Telegram: link a wallet, prove you own the domain, fill in a template, publish. The blockchain mechanics stay in the background.
How it works
Three services share one TypeScript monorepo and five internal packages:
- The bot (Telegraf) is the entry point. It opens the Mini App and handles helper commands, including one-time admin access keys.
- The Mini App (React + Vite + Tailwind + TON Connect UI) runs inside Telegram’s webview and authenticates every API call with Telegram’s signed
initData. - The API (Fastify) verifies that signature, stores users, domains and sites in SQLite, renders templates, and prepares the TON DNS transaction the user’s wallet signs.
Shared packages isolate each concern — db, blockchain, dns, storage, templates — so the API stays thin and the TON-specific logic is testable on its own.
Authentication without passwords
There is no login form. Telegram signs an initData payload with a secret derived from the bot token, and the API recomputes that HMAC on every request:
const secretKey = crypto
.createHmac('sha256', 'WebAppData')
.update(botToken)
.digest();
const computedHash = crypto
.createHmac('sha256', secretKey)
.update(dataCheckString)
.digest('hex');
if (computedHash !== hash) {
throw new Error('Invalid Telegram init data hash');
}
The check also rejects payloads older than 24 hours, so a captured initData string stops working. Identity comes from Telegram; the API never stores a credential.
Ownership is proven, not claimed
Anyone can type example.ton into a form. Before a domain becomes usable, the API resolves it on TON mainnet and compares the on-chain owner against the address the user linked through TON Connect. Verification is a property of the domain row, and the publish path refuses to build a transaction for an unverified one.
Serving published sites
The API does double duty: it serves JSON under /api, the built Mini App at the root, and published sites by Host header — a request arriving for a .ton host gets that domain’s latest published render instead of the app shell. Public slug routes give every site a shareable preview URL before it goes on-chain.
What I’d change
The SQLite layer runs migrations at startup, which is fine for one node but would need a real migration tool before scaling out. The two publishing modes (ADNL reverse proxy and TON Storage) are configured by environment variables and share an interface, but only one can be active per deployment — making that a per-site choice would be the natural next step.
Demos
running live
The production deployment, opened through the Telegram bot with full TON Connect wallet linking and a real TON DNS publish.