Concepts, value, and typical clone scenarios — less code.
Concepts, value, and typical clone scenarios — less code.
Preparing Ring content
Preparing Ring content
Preparing Ring content
Use Founder / Developer tabs in the docs sidebar to filter this page. Shared sections apply to both audiences.
Ring white-labels share one Layer1 codebase: ring/web (hostname ring-platform.org is the docs portal, not a checkout directory). Verticals are selected by preset name in ring-config.json. Typed catalogs live under features/*/presets/<preset>.ts (and home under components/pages/home-presets/<preset>.tsx). Optional L2 packs sit at ring-presets/<pack>/ and compose between Layer1 and the clone overlay.
| Source | What lives there | When it wins |
|---|---|---|
Platform (ring-platform.org) | Shared niches (platform, agricultural, …) | Base of every build |
Clone project dir (e.g. ring-greenfood-live) | Branding, ring-config.json, custom <preset>.ts + registry overlays | Overwrite on collision after platform copy |
Use ringdom-clone-build (ringdom-mcp):
ring-platform.org → build tmp (skips node_modules / .next / .git)Do not expect a thin overlay alone to be a runnable app — merge first.
| Tier | What | Where |
|---|---|---|
| 1 — data | Category lists, badge tokens, labels | ring-config.json maps + locales/ |
| 2 — typed logic | Entity catalogs, zod/forms, niche field UIs | features/*/presets/<preset>.ts (platform and/or clone overlay) |
GreenFood sets entities / productFields / productBadges to "agricultural" and home.preset to "mvm-landing".
All use the configured *.preset string. If a singular key is unset, entities and productBadges fall back to productFields.preset, then "platform" — no hard-coded niche if/else.
getEntitiesPreset() → features/entities/presets registrygetHomePreset() → components/pages/home-presets registrygetPresetPack() → presets.pack folder name under ring-presets/<pack>/ (null when unset)getOverlayFeature() → overlay.featureId (null on bare Layer1)getProductFieldsPreset() / getProductFieldsPresets()getProductBadgesPreset() / getProductBadgesPresets()Rule: preset names appear in ring-config + the registry map. Consumers call getEntityTypes() / getEntityTypeList() — never import agricultural.ts directly.
Custom niche on a clone: add features/entities/presets/<name>.ts in the clone project, overlay index.ts to register it, set entities.preset to that name, then ringdom-clone-build.
Bundle discipline: sync catalogs are lightweight UI lists. Heavy ERP field catalogs live in *-erp.ts modules loaded only via async loadErpEntityTypes().
presets/<name>.ts): ids, emoji, English fallback names/descriptionslocales/*/modules/entities.json → types.<id> + types.<id>Desccomponents/entities/entity-type-icons.tsx TYPE_VISUALS mapuseEntityTypeLabel() / useEntityTypeDescription() — i18n first, catalog fallbackfeatures/entities/presets/<name>.ts (+ optional <name>-erp.ts)ENTITIES_PRESET_REGISTRY (+ ERP_CATALOG_REGISTRY if needed)components/pages/home-presets/<name>.tsxproductFieldsPresets / productBadgesPresetslocales/*/modules/entities.jsonring-config.template.jsonTarget roster: platform, agricultural, then tourism, sport, business, pet, auto, connect, technopark, manufacturing.
.reggie-propagate-exclude.json protects overlay-only paths from being overwritten by Reggie:
ring-config.jsonpublic/logo*.svg, favicon.ico, public/branding/locales/{en,uk,ru}/config.json, vendor.jsonfeatures/*/presets/<clone-only>.ts (+ registry overlay) when not upstreamed yetlib/overlay/registry.ts only (maps). Do not exclude lib/overlay/types.ts or lib/overlay/runtime.ts — those are platform SSOT.| Path | Owner | Role |
|---|---|---|
lib/overlay/types.ts | Layer1 | Contracts (OverlayMessages, registries) |
lib/overlay/runtime.ts | Layer1 | loadPackMessages / loadOverlayMessages / resolveOverlayHomeRail |
lib/overlay/registry.ts | Layer1 empty maps; clone overlays maps | Domain feature loaders |
locales/{locale}/pack.json | Layer1 ships {}; pack overwrites at compose | Pack i18n namespaces |
Consumers import from @/lib/overlay/runtime. Clones never duplicate loaders in registry.ts. Do not add pack to LocaleFileId / assembleMessages — pack merge happens in buildMessages outside 'use cache', same as the overlay.
| Stage | Loader | Cached? |
|---|---|---|
Layer1 locales/{locale}/**/*.json | assembleMessages | Yes |
Pack locales/{locale}/pack.json | loadPackMessages | No — skipped when getPresetPack() is null |
Clone appendOverlayMessages | loadOverlayMessages | No — skipped when getOverlayFeature() is null |
Later leaf keys win (mergeJsonRecords in lib/i18n.ts). Nested objects merge, so a clone overlay can override one string without replacing the pack namespace.
Layer1 keeps an empty pack.json for every fallback locale (en, uk, ru, es, de) so webpack always resolves @/locales/${loc}/pack.json on bare Layer1 and on the empire overlay.
In block comments, write locales/{locale}/pack.json. A glob locales/*/pack.json inside /* … */ terminates the comment at */.
Pack copy is how a vertical (news, marketplace, …) ships extra labels without editing Layer1 lib/i18n.ts. Your clone still wins brand leaves through the overlay plug. Icon ids for pack nav slots are a separate Layer1 map — see Whitelabel navigation.
Operator checklist (calculator / Order Lab): Ringization playbook. Shared code ships from Layer1 ring/web. Clone-local custom presets stay in the clone dir and win at compose (ringdom-clone-build / merge-dev).
if (productPreset === 'agricultural') in accessorscustom-types.ts / overrides.ts without a named preset moduleswitch (vertical) in feature code — use the registrypack to LocaleFileId / putting pack.json inside 'use cache'lib/navigation/primary-nav-icons.ts with clone-named Lucide importsDeep-dive: message merge order, pack.json shape, and locale env.
Next-step: closed icon map and overlay remap-only nav socket.
Same-workflow: Tier-1/2/3 ringize and overlay registry plug.
Prerequisite: config-first branding before named presets.
Use Founder / Developer tabs in the docs sidebar to filter this page. Shared sections apply to both audiences.
Ring white-labels share one Layer1 codebase: ring/web (hostname ring-platform.org is the docs portal, not a checkout directory). Verticals are selected by preset name in ring-config.json. Typed catalogs live under features/*/presets/<preset>.ts (and home under components/pages/home-presets/<preset>.tsx). Optional L2 packs sit at ring-presets/<pack>/ and compose between Layer1 and the clone overlay.
| Source | What lives there | When it wins |
|---|---|---|
Platform (ring-platform.org) | Shared niches (platform, agricultural, …) | Base of every build |
Clone project dir (e.g. ring-greenfood-live) | Branding, ring-config.json, custom <preset>.ts + registry overlays | Overwrite on collision after platform copy |
Use ringdom-clone-build (ringdom-mcp):
ring-platform.org → build tmp (skips node_modules / .next / .git)Do not expect a thin overlay alone to be a runnable app — merge first.
| Tier | What | Where |
|---|---|---|
| 1 — data | Category lists, badge tokens, labels | ring-config.json maps + locales/ |
| 2 — typed logic | Entity catalogs, zod/forms, niche field UIs | features/*/presets/<preset>.ts (platform and/or clone overlay) |
GreenFood sets entities / productFields / productBadges to "agricultural" and home.preset to "mvm-landing".
All use the configured *.preset string. If a singular key is unset, entities and productBadges fall back to productFields.preset, then "platform" — no hard-coded niche if/else.
getEntitiesPreset() → features/entities/presets registrygetHomePreset() → components/pages/home-presets registrygetPresetPack() → presets.pack folder name under ring-presets/<pack>/ (null when unset)getOverlayFeature() → overlay.featureId (null on bare Layer1)getProductFieldsPreset() / getProductFieldsPresets()getProductBadgesPreset() / getProductBadgesPresets()Rule: preset names appear in ring-config + the registry map. Consumers call getEntityTypes() / getEntityTypeList() — never import agricultural.ts directly.
Custom niche on a clone: add features/entities/presets/<name>.ts in the clone project, overlay index.ts to register it, set entities.preset to that name, then ringdom-clone-build.
Bundle discipline: sync catalogs are lightweight UI lists. Heavy ERP field catalogs live in *-erp.ts modules loaded only via async loadErpEntityTypes().
presets/<name>.ts): ids, emoji, English fallback names/descriptionslocales/*/modules/entities.json → types.<id> + types.<id>Desccomponents/entities/entity-type-icons.tsx TYPE_VISUALS mapuseEntityTypeLabel() / useEntityTypeDescription() — i18n first, catalog fallbackfeatures/entities/presets/<name>.ts (+ optional <name>-erp.ts)ENTITIES_PRESET_REGISTRY (+ ERP_CATALOG_REGISTRY if needed)components/pages/home-presets/<name>.tsxproductFieldsPresets / productBadgesPresetslocales/*/modules/entities.jsonring-config.template.jsonTarget roster: platform, agricultural, then tourism, sport, business, pet, auto, connect, technopark, manufacturing.
.reggie-propagate-exclude.json protects overlay-only paths from being overwritten by Reggie:
ring-config.jsonpublic/logo*.svg, favicon.ico, public/branding/locales/{en,uk,ru}/config.json, vendor.jsonfeatures/*/presets/<clone-only>.ts (+ registry overlay) when not upstreamed yetlib/overlay/registry.ts only (maps). Do not exclude lib/overlay/types.ts or lib/overlay/runtime.ts — those are platform SSOT.| Path | Owner | Role |
|---|---|---|
lib/overlay/types.ts | Layer1 | Contracts (OverlayMessages, registries) |
lib/overlay/runtime.ts | Layer1 | loadPackMessages / loadOverlayMessages / resolveOverlayHomeRail |
lib/overlay/registry.ts | Layer1 empty maps; clone overlays maps | Domain feature loaders |
locales/{locale}/pack.json | Layer1 ships {}; pack overwrites at compose | Pack i18n namespaces |
Consumers import from @/lib/overlay/runtime. Clones never duplicate loaders in registry.ts. Do not add pack to LocaleFileId / assembleMessages — pack merge happens in buildMessages outside 'use cache', same as the overlay.
| Stage | Loader | Cached? |
|---|---|---|
Layer1 locales/{locale}/**/*.json | assembleMessages | Yes |
Pack locales/{locale}/pack.json | loadPackMessages | No — skipped when getPresetPack() is null |
Clone appendOverlayMessages | loadOverlayMessages | No — skipped when getOverlayFeature() is null |
Later leaf keys win (mergeJsonRecords in lib/i18n.ts). Nested objects merge, so a clone overlay can override one string without replacing the pack namespace.
Layer1 keeps an empty pack.json for every fallback locale (en, uk, ru, es, de) so webpack always resolves @/locales/${loc}/pack.json on bare Layer1 and on the empire overlay.
In block comments, write locales/{locale}/pack.json. A glob locales/*/pack.json inside /* … */ terminates the comment at */.
Pack copy is how a vertical (news, marketplace, …) ships extra labels without editing Layer1 lib/i18n.ts. Your clone still wins brand leaves through the overlay plug. Icon ids for pack nav slots are a separate Layer1 map — see Whitelabel navigation.
Operator checklist (calculator / Order Lab): Ringization playbook. Shared code ships from Layer1 ring/web. Clone-local custom presets stay in the clone dir and win at compose (ringdom-clone-build / merge-dev).
if (productPreset === 'agricultural') in accessorscustom-types.ts / overrides.ts without a named preset moduleswitch (vertical) in feature code — use the registrypack to LocaleFileId / putting pack.json inside 'use cache'lib/navigation/primary-nav-icons.ts with clone-named Lucide importsDeep-dive: message merge order, pack.json shape, and locale env.
Next-step: closed icon map and overlay remap-only nav socket.
Same-workflow: Tier-1/2/3 ringize and overlay registry plug.
Prerequisite: config-first branding before named presets.
Use Founder / Developer tabs in the docs sidebar to filter this page. Shared sections apply to both audiences.
Ring white-labels share one Layer1 codebase: ring/web (hostname ring-platform.org is the docs portal, not a checkout directory). Verticals are selected by preset name in ring-config.json. Typed catalogs live under features/*/presets/<preset>.ts (and home under components/pages/home-presets/<preset>.tsx). Optional L2 packs sit at ring-presets/<pack>/ and compose between Layer1 and the clone overlay.
| Source | What lives there | When it wins |
|---|---|---|
Platform (ring-platform.org) | Shared niches (platform, agricultural, …) | Base of every build |
Clone project dir (e.g. ring-greenfood-live) | Branding, ring-config.json, custom <preset>.ts + registry overlays | Overwrite on collision after platform copy |
Use ringdom-clone-build (ringdom-mcp):
ring-platform.org → build tmp (skips node_modules / .next / .git)Do not expect a thin overlay alone to be a runnable app — merge first.
| Tier | What | Where |
|---|---|---|
| 1 — data | Category lists, badge tokens, labels | ring-config.json maps + locales/ |
| 2 — typed logic | Entity catalogs, zod/forms, niche field UIs | features/*/presets/<preset>.ts (platform and/or clone overlay) |
GreenFood sets entities / productFields / productBadges to "agricultural" and home.preset to "mvm-landing".
All use the configured *.preset string. If a singular key is unset, entities and productBadges fall back to productFields.preset, then "platform" — no hard-coded niche if/else.
getEntitiesPreset() → features/entities/presets registrygetHomePreset() → components/pages/home-presets registrygetPresetPack() → presets.pack folder name under ring-presets/<pack>/ (null when unset)getOverlayFeature() → overlay.featureId (null on bare Layer1)getProductFieldsPreset() / getProductFieldsPresets()getProductBadgesPreset() / getProductBadgesPresets()Rule: preset names appear in ring-config + the registry map. Consumers call getEntityTypes() / getEntityTypeList() — never import agricultural.ts directly.
Custom niche on a clone: add features/entities/presets/<name>.ts in the clone project, overlay index.ts to register it, set entities.preset to that name, then ringdom-clone-build.
Bundle discipline: sync catalogs are lightweight UI lists. Heavy ERP field catalogs live in *-erp.ts modules loaded only via async loadErpEntityTypes().
presets/<name>.ts): ids, emoji, English fallback names/descriptionslocales/*/modules/entities.json → types.<id> + types.<id>Desccomponents/entities/entity-type-icons.tsx TYPE_VISUALS mapuseEntityTypeLabel() / useEntityTypeDescription() — i18n first, catalog fallbackfeatures/entities/presets/<name>.ts (+ optional <name>-erp.ts)ENTITIES_PRESET_REGISTRY (+ ERP_CATALOG_REGISTRY if needed)components/pages/home-presets/<name>.tsxproductFieldsPresets / productBadgesPresetslocales/*/modules/entities.jsonring-config.template.jsonTarget roster: platform, agricultural, then tourism, sport, business, pet, auto, connect, technopark, manufacturing.
.reggie-propagate-exclude.json protects overlay-only paths from being overwritten by Reggie:
ring-config.jsonpublic/logo*.svg, favicon.ico, public/branding/locales/{en,uk,ru}/config.json, vendor.jsonfeatures/*/presets/<clone-only>.ts (+ registry overlay) when not upstreamed yetlib/overlay/registry.ts only (maps). Do not exclude lib/overlay/types.ts or lib/overlay/runtime.ts — those are platform SSOT.| Path | Owner | Role |
|---|---|---|
lib/overlay/types.ts | Layer1 | Contracts (OverlayMessages, registries) |
lib/overlay/runtime.ts | Layer1 | loadPackMessages / loadOverlayMessages / resolveOverlayHomeRail |
lib/overlay/registry.ts | Layer1 empty maps; clone overlays maps | Domain feature loaders |
locales/{locale}/pack.json | Layer1 ships {}; pack overwrites at compose | Pack i18n namespaces |
Consumers import from @/lib/overlay/runtime. Clones never duplicate loaders in registry.ts. Do not add pack to LocaleFileId / assembleMessages — pack merge happens in buildMessages outside 'use cache', same as the overlay.
| Stage | Loader | Cached? |
|---|---|---|
Layer1 locales/{locale}/**/*.json | assembleMessages | Yes |
Pack locales/{locale}/pack.json | loadPackMessages | No — skipped when getPresetPack() is null |
Clone appendOverlayMessages | loadOverlayMessages | No — skipped when getOverlayFeature() is null |
Later leaf keys win (mergeJsonRecords in lib/i18n.ts). Nested objects merge, so a clone overlay can override one string without replacing the pack namespace.
Layer1 keeps an empty pack.json for every fallback locale (en, uk, ru, es, de) so webpack always resolves @/locales/${loc}/pack.json on bare Layer1 and on the empire overlay.
In block comments, write locales/{locale}/pack.json. A glob locales/*/pack.json inside /* … */ terminates the comment at */.
Pack copy is how a vertical (news, marketplace, …) ships extra labels without editing Layer1 lib/i18n.ts. Your clone still wins brand leaves through the overlay plug. Icon ids for pack nav slots are a separate Layer1 map — see Whitelabel navigation.
Operator checklist (calculator / Order Lab): Ringization playbook. Shared code ships from Layer1 ring/web. Clone-local custom presets stay in the clone dir and win at compose (ringdom-clone-build / merge-dev).
if (productPreset === 'agricultural') in accessorscustom-types.ts / overrides.ts without a named preset moduleswitch (vertical) in feature code — use the registrypack to LocaleFileId / putting pack.json inside 'use cache'lib/navigation/primary-nav-icons.ts with clone-named Lucide importsDeep-dive: message merge order, pack.json shape, and locale env.
Next-step: closed icon map and overlay remap-only nav socket.
Same-workflow: Tier-1/2/3 ringize and overlay registry plug.
Prerequisite: config-first branding before named presets.
See-also: locale-config SSOT used by pack locale fallback.
{
"presets": { "pack": "news-station" },
"entities": { "preset": "platform" },
"home": { "preset": "platform" },
"productFields": { "preset": "platform" },
"productBadges": { "preset": "platform" },
"productFieldsPresets": {
"platform": { "storeCategories": ["commerce", "education", "…"] },
"agricultural": { "storeCategories": ["organic-produce", "honey-sweets", "…"] }
},
"productBadgesPresets": {
"platform": { "productBadges": ["new", "featured", "popular"] },
"agricultural": { "productBadges": ["organic", "local", "regenerative"] }
}
}
ringdom-clone-build({ project_name: "ring-greenfood-live", dry_run?: true })
→ .ringdom-build/<project>/<timestamp>
features/entities/presets/
types.ts # EntityTypeCatalog contract
platform.ts # export const entityTypes (26 industries)
agricultural.ts # healthy-living UI catalog (lightweight)
agricultural-erp.ts # heavy ERP field catalogs — async-only via loadErpEntityTypes()
index.ts # ENTITIES_PRESET_REGISTRY + getEntityTypes()
components/pages/
home.tsx # platform default landing
home-presets/mvm-landing.tsx # multi-vendor-marketplace e-commerce landing
home-content-resolver.tsx # HOME_PRESET_REGISTRY + getHomePreset()
ensure ring-config: entities/productFields/productBadges = agricultural, home = mvm-landing
keep branding + locale deltas in ring-greenfood-live
ringdom-clone-build({ project_name: "ring-greenfood-live" })
build/deploy from .ringdom-build/ring-greenfood-live/<stamp>See-also: locale-config SSOT used by pack locale fallback.
{
"presets": { "pack": "news-station" },
"entities": { "preset": "platform" },
"home": { "preset": "platform" },
"productFields": { "preset": "platform" },
"productBadges": { "preset": "platform" },
"productFieldsPresets": {
"platform": { "storeCategories": ["commerce", "education", "…"] },
"agricultural": { "storeCategories": ["organic-produce", "honey-sweets", "…"] }
},
"productBadgesPresets": {
"platform": { "productBadges": ["new", "featured", "popular"] },
"agricultural": { "productBadges": ["organic", "local", "regenerative"] }
}
}
ringdom-clone-build({ project_name: "ring-greenfood-live", dry_run?: true })
→ .ringdom-build/<project>/<timestamp>
features/entities/presets/
types.ts # EntityTypeCatalog contract
platform.ts # export const entityTypes (26 industries)
agricultural.ts # healthy-living UI catalog (lightweight)
agricultural-erp.ts # heavy ERP field catalogs — async-only via loadErpEntityTypes()
index.ts # ENTITIES_PRESET_REGISTRY + getEntityTypes()
components/pages/
home.tsx # platform default landing
home-presets/mvm-landing.tsx # multi-vendor-marketplace e-commerce landing
home-content-resolver.tsx # HOME_PRESET_REGISTRY + getHomePreset()
ensure ring-config: entities/productFields/productBadges = agricultural, home = mvm-landing
keep branding + locale deltas in ring-greenfood-live
ringdom-clone-build({ project_name: "ring-greenfood-live" })
build/deploy from .ringdom-build/ring-greenfood-live/<stamp>See-also: locale-config SSOT used by pack locale fallback.
{
"presets": { "pack": "news-station" },
"entities": { "preset": "platform" },
"home": { "preset": "platform" },
"productFields": { "preset": "platform" },
"productBadges": { "preset": "platform" },
"productFieldsPresets": {
"platform": { "storeCategories": ["commerce", "education", "…"] },
"agricultural": { "storeCategories": ["organic-produce", "honey-sweets", "…"] }
},
"productBadgesPresets": {
"platform": { "productBadges": ["new", "featured", "popular"] },
"agricultural": { "productBadges": ["organic", "local", "regenerative"] }
}
}
ringdom-clone-build({ project_name: "ring-greenfood-live", dry_run?: true })
→ .ringdom-build/<project>/<timestamp>
features/entities/presets/
types.ts # EntityTypeCatalog contract
platform.ts # export const entityTypes (26 industries)
agricultural.ts # healthy-living UI catalog (lightweight)
agricultural-erp.ts # heavy ERP field catalogs — async-only via loadErpEntityTypes()
index.ts # ENTITIES_PRESET_REGISTRY + getEntityTypes()
components/pages/
home.tsx # platform default landing
home-presets/mvm-landing.tsx # multi-vendor-marketplace e-commerce landing
home-content-resolver.tsx # HOME_PRESET_REGISTRY + getHomePreset()
ensure ring-config: entities/productFields/productBadges = agricultural, home = mvm-landing
keep branding + locale deltas in ring-greenfood-live
ringdom-clone-build({ project_name: "ring-greenfood-live" })
build/deploy from .ringdom-build/ring-greenfood-live/<stamp>