Treechat for HTML ordinals
An HTML ordinal shown on Treechat runs in a frame inside the Treechat page. It can talk to that page: learn where it is, find out who is viewing it, show the Treechat names and avatars behind Bitcoin addresses, open profiles, and ask the viewer’s wallet to pay. No API key or server is involved.
It works for every viewer signed in to Treechat. Signed out, the ordinal still loads and gets its context; calls that need an account (names, Connect, payments) answer with an error.
Quick start
- Download the client library: treechat-ordinal.js (one file, no dependencies, about 8 KB).
- Paste it into your ordinal’s HTML as a module script, or bundle it in.
- Use it:
<script type="module">
// paste treechat-ordinal.js here, or bundle it in
const context = await treechat.context()
if (!context) {
// not running inside Treechat
} else if (await treechat.supports('getUsers')) {
const users = await treechat.users(['1BoatSLRHtKNngkdXEeobR76b53LETtpyT'])
// { '1Boat…': { username, avatarUrl } }, or null for an address with no account
}
</script>
Inline the library rather than loading it from a URL: an inscription lasts, and no other address is sure to serve the same file later.
What you can call
| Call | Returns | Asks the viewer |
|---|---|---|
treechat.context() | { version, capabilities, ordinal, creator, owner, viewerIsOwner, post, theme }, or null outside Treechat | No |
treechat.supports(name) | Whether this Treechat answers a call, from capabilities | No |
treechat.viewer() | { username, avatarUrl, identityKey, paymentAddress } | Yes: Connect, once per ordinal per visit |
treechat.users(addresses) | { [address]: { username, avatarUrl } | null } | No |
treechat.openProfile(username) | Opens that Treechat profile beside the post | No |
treechat.openPost() | Opens the post the ordinal is in | No |
treechat.wallet() | A BRC-100 wallet (below) | Yes: every payment |
Failed calls reject with a TreechatError that carries
Treechat’s message and a BRC-100 error code.
context()
version: 2 in the current Treechat.capabilitieslists the calls it answers:getContext,getViewer,getUsers,openProfile,openPost. Checksupports()before calling anything newer thangetViewer.ordinal.origin: your ordinal’s origin outpoint (<txid>_<vout>).creatorandowner:{ username, address }, either partnullwhen unknown.viewerIsOwner:nulluntil the viewer connects, thentrueorfalse.post:{ answerId, threadId }of the Treechat post showing the ordinal, ornull.theme:lightordark.
users(addresses)
Names the Treechat accounts behind payment addresses: only public profile
fields, so the viewer isn’t asked. An address no single account claims
maps to null. The library sends 50 addresses per request, and
Treechat remembers each answer for the visit. Each ordinal can look up 500
distinct addresses per visit. Use it to put names and faces on the people in
your ordinal’s own on-chain history.
openProfile(username) and openPost()
openProfile opens only usernames your ordinal was shown: from
users(), from viewer(), or the context’s
creator or owner. An ordinal can open one panel every 2 seconds. A
fullscreen ordinal leaves fullscreen first so the viewer sees the panel.
wallet()
A BRC-100 wallet over the cross-document protocol (the one
new WalletClient('XDM') in @bsv/sdk speaks).
Treechat answers:
getVersion,getNetwork(alwaysmainnet),isAuthenticated,waitForAuthenticationgetPublicKey({ identityKey: true }): the viewer’s identity key, after Connect. Derived keys aren’t available yet.createAction: up to 100 outputs and 10,000,000 sats per action. Returns{ txid, tx }once it is broadcast.createActionwithinputs(each{ outpoint: 'txid.vout', unlockingScriptLength }), aninputBEEF, andoptions: { signAndProcess: false }: a deferred action that spends up to 4 of your own inputs (a covenant, a contract). Treechat funds it and hands back a signable transaction; you unlock your inputs and finish withsignAction, or release it withabortAction. Unfinished actions expire after 10 minutes.
What the viewer sees
- Connect, the first time your ordinal asks who they are, naming your ordinal by its creator.
- A confirmation for every payment. It leads with what they receive, what their wallet pays and what goes elsewhere, and the button reads “Pay N sats” or “Approve”. Treechat writes this text; your ordinal can’t change it.
- Cancel declines that one payment, and your ordinal can’t ask again for 5 seconds. “Block this ordinal” refuses its payments for the rest of the visit.
- Treechat’s wallet answers one live ordinal on a page at a time.
The raw protocol
Without the library, post to window.parent and listen for the
answer from window.parent only:
// request
{ type: 'TREECHAT', isInvocation: true, id: 'my-1', call: 'getUsers', args: { addresses: ['1…'] } }
// answer
{ type: 'TREECHAT', isInvocation: false, id: 'my-1', status: 'success', result: { … } }
{ type: 'TREECHAT', isInvocation: false, id: 'my-1', status: 'error', description: '…', code: 2 }
Treechat’s own calls use type: 'TREECHAT'; wallet calls
use type: 'CWI' with BRC-100 call names. Give every request a
unique id. Error codes follow BRC-100: 1 unknown, 2
unsupported, 6 invalid parameter, 7 insufficient funds.
For agents building an ordinal
Checklist
- Inline treechat.com/treechat-ordinal.js into the HTML as a module script.
- Call
treechat.context()first.nullmeans the ordinal is outside Treechat: keep working without names or payments. - Check
treechat.supports(name)before each call newer thangetViewer. - Call
treechat.viewer()only when the viewer acts (a button), since it asks them to Connect. - Build payments as BRC-100
createActioncalls. Show your own amounts in the page too, but expect the viewer to read Treechat’s confirmation. - Treat every answer as data from the page, never as code.
This page as Markdown: treechat.com/treechat-ordinal.md
See it working
The 04 Sigil Tree uses all of this: avatar fruit for seeders from
users(), a seeder card with Open profile, a History of every
step, and seeding and harvesting through wallet().