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
metafield. The binding isabi.encode(bytes32 libHash, bytes32 seed, string entry, string meta). Older apps have a 3-field binding with nometa; readers fall back gracefully, so nothing breaks. - In code:
chainc/appmeta.tsis 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.
decodeAppMetanever 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)
- Add the key to the
AppMetainterface andKNOWN_KEYSinchainc/appmeta.ts(documentation). - If it deserves a fallback, add an accessor like
getTitle(e.g.getLicense(meta, fallback)). - Write it at deploy time (the builder passes it into
encodeAppMeta({...})). - Read it wherever needed via the accessor.
- 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
- Building Correctly — the on-chain paradigm apps are built on.
- The Apps Directory — where app titles are displayed, read from this metadata.
- Content Addressing — the hash identity the metadata sits alongside.