# 0005. Namespaced content IDs - **Status:** Accepted - **Date:** 2026-06-27 ## Context Every single piece of registered content (blocks, items, recipes, biomes, entities, etc.) needs a perfectly stable identifier. This identifier has to be completely unambiguous across the engine, data packs, Lua mods, recipe references, and save files. First-party content and third-party content must be able to coexist without ever colliding, and these identifiers must be able to survive being written to disk and read back later. ## Decision We identify all content using a **namespaced string** in the exact format `"namespace:id"`. - We strictly reserve the `core:` namespace for first-party content (e.g., `"core:stone"`, `"core:iron_sword"`). Modders must choose their own short namespace (e.g., `"mymod:weird_dirt"`). - This format is absolutely mandatory. A bare ID with no `:` is an **immediate error at registration or parse time**. We will never silently coerce it to `core:`. This exact rule applies everywhere: engine scripts, data packs, Lua mods, recipe references, and save files. - **Charset rules:** Both the namespace and the ID must match `[a-z0-9_-]+` and have exactly one `:` sitting between them. We only allow lowercase ASCII. No uppercase letters, no Unicode, no spaces, no dots, and no slashes. - At registration time, we intern each ID string into a small integer handle (for example, `BlockId(u32)`). All hot paths compare these integer handles for speed. We only keep the original string around for display purposes, save/load routines, and the Lua API. Notice that the reserved namespace is deliberately called `core:` rather than naming it after the project itself. This ensures the namespace stays completely stable regardless of any future rebranding. ## Consequences - Identifiers are highly greppable, inherently filesystem-safe, and totally unambiguous in both logs and save files. - First-party and mod content simply cannot collide. By refusing to provide a silent default namespace, we ensure mistakes blow up immediately rather than silently corrupting data. - We pay a tiny runtime cost at registration to intern the strings, but in exchange, we get lightning-fast handle comparisons on all hot paths. - This strict formatting is a permanent contract. If we relaxed it later (for example, by defaulting bare IDs), we would fundamentally change the meaning of existing save files, making such a change effectively irreversible.