⚠️ Alpha · testnets only. ChainRender is in active alpha and deployed on testnets only — Ethereum Sepolia, the Robinhood Chain Testnet, and the Circle Arc Testnet — not on any mainnet. Contracts, addresses, and APIs may change. Do not use with real funds or production assets.
Concepts

App Metadata — The Expandable Seam for App Info

ChainRender apps carry descriptive metadata (title, author, description, kind, tags, …) in a single, versioned, open-ended container stored on-chain. This page documents the seam so that adding a new piece of app info in the future is a one-line change, never an archaeology expedition.

Why this exists

The scene registry stores an app's floor and binding, but originally had nowhere to put a human title — which is why the Apps page once showed generic id-names like "3D Scene a1b2c3". Rather than bolt on a single title field, ChainRender defines an open metadata container: title is simply its first resident. The whole class of "an app needs to carry more descriptive info" (author, description, category, license, thumbnail pointer, a "verified" flag, …) becomes a trivial addition to one module.

Where it lives

  • On-chain: a compact, versioned JSON string carried in the scene binding's trailing meta field. The binding is abi.encode(bytes32 libHash, bytes32 seed, string entry, string meta). Older apps have a 3-field binding with no meta; readers fall back gracefully, so nothing breaks.
  • In code: chainc/appmeta.ts is the single source of truth — encodeAppMeta, decodeAppMeta, and accessors (getTitle, getAuthor, …). Every surface (Apps page, viewer, builder) reads metadata through this module, so there is no second copy to drift out of sync.

Fields in v1

, , , , (Lucide name for the app's card), (grouping in the collection, e.g. DeFi/Art/Games/Tools), , . The Apps page reads , , , and to render a categorized, filterable collection; the builder writes them at deploy from the project name + the App-collection-info UI.

The container's guarantees

  • Versioned. Each record stamps a schema version. A reader that only understands v1 can still read a v3 app's title — it reads the keys it knows and ignores the rest. Old readers never choke on new data; new readers gracefully handle old or missing data.
  • Open. Metadata is a key→value map, not a fixed struct. Adding a field needs no schema migration — you add the key. Unknown keys are preserved verbatim on decode (forward-compatibility).
  • Fail-safe. decodeAppMeta never throws: malformed, empty, or missing input returns an empty record, so a missing title simply falls back to an id-derived name. Nothing ever blanks or errors.
  • Bounded. Serialized metadata is size-capped, keeping deploy cost predictable. Large or binary data (e.g. a thumbnail image) is never inlined — only a hash pointer to it.

How to add a new field (the repeatable move)

  1. Add the key to the AppMeta interface and KNOWN_KEYS in chainc/appmeta.ts (documentation).
  2. If it deserves a fallback, add an accessor like getTitle (e.g. getLicense(meta, fallback)).
  3. Write it at deploy time (the builder passes it into encodeAppMeta({...})).
  4. Read it wherever needed via the accessor.
  5. If the record shape changed meaningfully, bump APP_META_VERSION. Adding an optional key does not require a bump — readers ignore unknown keys.

That's the whole change, in one module. Every surface picks it up because they all read through the same accessors.

Honest limits

  • Descriptive, not functional. This carries info an app has (title, author, tags). Functional gaps (e.g. live-data bridges) use their own seams; app-metadata is deliberately scoped to self-description.
  • Only new deploys. On-chain bindings are immutable, so a new field only appears on apps deployed after it's added. Existing apps keep whatever metadata they were deployed with (often none → fallback).
  • References, not payloads. Big things live behind a hash pointer in the metadata, not inline.

Related