Design Rationale
This page collects the why behind ChainRender's most important design choices. If you want to understand not just how the system works but why it was built this way, start here.
Why fully on-chain, not just "decentralized"?
Because a decentralized backend with a centralized frontend is only half-trustless. The contract can't be changed, but the website that talks to it can — and that website is what the user actually interacts with. Putting the UI on-chain makes the interface as immutable and verifiable as the contract. The whole point is to remove the last trusted party: the person hosting your frontend.
Why content addressing instead of a mutable app registry?
A mutable "current version" pointer is exactly the trust surface we're trying to eliminate. If someone can repoint an app to new bytes instantly, they can swap it under its users. So immutability is the floor, and controlled mutability is layered on top only where it's provably safe: presentation (zero-gas, doesn't change identity) and versioning (timelocked, observable, append-only). See Content Addressing.
Why a closed vocabulary instead of arbitrary HTML/JS?
Arbitrary code is unanalyzable, and unanalyzable means unsafe. If an app could embed arbitrary JavaScript, no grade could be trusted and no action could be verified — the code could do anything. A closed vocabulary is the price of provable safety: because every construct is known, the enforcer can prove what an app can and cannot do, and the grade can be honest. You trade unlimited expressiveness for verifiable trust. For a UI that signs transactions, that's the right trade.
Why declared actions instead of raw calldata?
If a button carried arbitrary calldata, a malicious app could hide any transaction behind an innocent label. So
actions are declared — a button names its target, selector, and argument shape, and the runtime constructs
the calldata from that declaration (construct-for-sign). The constructed transaction can only be the declared
selector with the declared argument shape. There is no path to smuggle a different call. See
Actions & Signing.
Why a trust grade instead of a safe/unsafe badge?
Because trust is a spectrum and honesty demands we show it as one. A binary badge would either be too strict (flagging safe-but-pinned apps) or too lax (blessing apps with hidden live dependencies). The graded, itemized approach tells the exact truth: here is the trust cost, here is every reason for it. This reflects the project's deepest rule — marketing must never exceed what the code enforces.
Why is presentation render-time but content baked?
Because they answer different questions. How an app looks (color, spacing, font, size) doesn't change what it is or does — so forcing a redeploy (and a new identity) for a cosmetic tweak would be both wasteful and meaningless. But what an app says and does (its text, structure, actions) IS its identity — changing it should produce a new, separately-verifiable version. So: skin is a free render-time parameter; substance is immutable. This division is what makes ChainRender both trustworthy and pleasant to iterate on. See Theming and Versioning.
Why ownerless, immutable contracts?
Every registry and renderer in ChainRender is deployed without an owner, admin, or upgrade key. This isn't dogma — it's the only way the guarantees hold. A registry with an admin is a registry whose admin you must trust. An upgradeable renderer is a renderer that could change what your app displays. Ownerlessness is what lets a user verify the system once and rely on it forever, without trusting any continuing operator.
The throughline
Every one of these choices trades some convenience for a specific, nameable trust guarantee — and always discloses the trade honestly. That's the design philosophy in one sentence: make the trustless thing the default, make every deviation from it visible, and never claim more than the code enforces.