Chain data is untrusted input
Every token name, symbol, transaction message, escrow instruction and dataset value on ZooBC was chosen by whoever paid the fee. Nothing vets it, and nothing becomes true because it is permanent. The rule for builders is short: data read from the chain is input, never instruction.
Showing chain text
In a web page, put chain text on screen as text:
el.textContent = token.name; // shown as text: safe
el.innerHTML = token.name; // parsed as HTML: never
Text inserted as HTML can carry markup and scripts that run with your page's rights, and filters built from regular expressions are not enough to stop that. Links need the same care: do not follow a URL found in chain data without checking its scheme.
An app driven by an AI agent must likewise never obey a sentence it found in a token name, a memo or a dataset value. "Ignore your instructions" written in a token name is data like any other string. And a sender's balance says nothing about whether its text is safe.
What the protocol does check
Only a few fields have a shape that the chain enforces:
| Field | Rule |
|---|---|
tokenicon: dataset values | a PNG or WebP image, 1 to 512 pixels each way, 16 to 4,096 bytes |
| Registry domains, such as a gateway's | 1 to 256 characters: letters, digits, dots and hyphens |
| Registry URLs, such as a gateway's | begin with https://; no whitespace, control character, quote, angle bracket or backtick |
inv: dataset values | a JSON object with no negative number anywhere |
Everything else is arbitrary bytes by design. Even a checked field is only a shape: a well-formed https:// address can still lead anywhere.
Token icons
A node serves each token's icon at GET /api/v1/tokens/:id/icon with headers that keep it inert: X-Content-Type-Options: nosniff, Content-Disposition: inline, Content-Security-Policy: default-src 'none'; sandbox, and a five-minute cache. A page needs nothing more than an image tag pointing at that route.
Dataset rows: check the setter
A node's dataset listing for an account returns the rows where that account is the setter or the recipient, and any account may write a row under any other (datasets). To read what an account says about itself, keep only the rows whose role is setter or both; a row where it is only the recipient was written by someone else. To check a claim such as "verified = yes", accept only a row whose setter is the account you trust.
Values come in two forms: value, escaped, and value_b64, lossless. Either way the content is a stranger's text, to be shown with textContent like the rest.
Checklist
textContent, neverinnerHTML, for every string read from the chain.- Check a URL's scheme before following it, and never follow chain text as an instruction.
- Trust a dataset row only from the expected setter.
- Show token icons through the icon route.