4.9 KiB
Placeholder System Guidelines
This note is the working guideline for Lumi placeholder and variable support. Future placeholder work should follow this model unless the architecture is explicitly revised.
Architecture
Placeholders are registered server-side through src/services/placeholders.js.
The frontend must not declare placeholder permissions, allowed plugins, or
sensitivity. Editable fields reference a trusted field_id, and the server
uses that field policy to decide which placeholders are available.
Core and plugins can register:
- placeholder definitions with metadata and a resolver function
- field policies for template/input destinations that support placeholders
Catalog, preview, save validation, and runtime rendering must share the same permission-checking path. Saved templates are not trusted permanently; they must be revalidated at render time because plugin availability, policies, settings, or sensitivity can change.
Naming
Use namespaced double-brace tokens:
{{core.main.bot_name}}{{core.main.command_prefix}}{{user.public.display_name}}{{platform.discord.guild.member_count}}{{okf.file.community.currency.primary_name}}{{plugin.throne_wishlist.item_name}}
Avoid unqualified names for new work. Compatibility aliases may exist for old templates, but the catalog should present canonical namespaced tokens.
Custom admin-defined placeholders, if added later, should live under the
{{custom.*}} namespace and be managed from a trusted admin settings page.
Core Namespaces
Current core-owned namespaces include:
core.main.*: safe Lumi core settings such as bot/site name and command prefixuser.public.*: safe viewer/triggering-user display informationplatform.discord.guild.*: safe Discord guild statistics from the configured guildplatform.twitch.channel.*: safe locally configured Twitch channel/runtime valuesplatform.youtube.channel.*: safe locally configured or hydrated YouTube channel/runtime valuesokf.file.*: file-backed OKF frontmatter values, with legacy aliases such as{{community.currency.primary_name}}
API-backed platform statistics such as Twitch follower/subscriber counts or future stream schedules should be registered here only after the integration has a reliable server-side fetch/cache layer and clear sensitivity rules.
Sensitivity
Supported sensitivity levels:
public_safe: safe in user-visible outputuser: user-specific content, only for fields whose output audience allows itmoderator: moderator/support visible contentadmin: admin-only contentinternal: internal implementation/runtime contentsecret_never_render: must not be registered, listed, previewed, or rendered
Never expose API keys, tokens, cookies, passwords, database URLs, session IDs, raw secrets, private file paths, sensitive query strings, or unredacted raw diagnostics through placeholders. If diagnostics are needed, register explicit redacted safe variants.
Field Policies
Every placeholder-compatible input must use a stable field_id whose policy is
registered by trusted backend code. A field policy should define:
field_id- label and field type
- output audience
- minimum editor role
- allowed namespaces or placeholder IDs
- maximum sensitivity
Security is based on the intersection of:
- current editor role/capability during catalog, preview, and save
- field policy
- output audience that will see rendered content
- placeholder sensitivity
- placeholder minimum viewer role
- plugin availability
- runtime context
An admin editing a user-visible template is not enough to allow admin-only placeholders. The output audience still limits what can render.
Plugin Registration
Plugins should register placeholders during backend plugin initialization using
the placeholder service passed to init or available through
web.placeholders. Plugin definitions should include plugin_id so disabled or
missing plugins can fail safely.
Resolvers must return safe display values and avoid leaking raw internal data. Renderers that send chat messages or HTML should still sanitize or escape for their output context after placeholder resolution.
Frontend
Frontend fields use:
data-placeholder-field="plugin.example.message_template"
data-placeholder-output-audience="user"
The frontend requests /api/placeholders/catalog?field_id=... and uses the
returned catalog for autocomplete and tree browsing. Inline JSON placeholder
lists are a transitional fallback only and should not be used for new fields.
Validation
Templates must be checked:
- when the editor opens a catalog
- during preview
- before save
- at runtime before rendering output
Unauthorized placeholders should fail closed. User-visible output should show a generic unavailable marker or leave legacy unknown tokens unchanged, depending on the existing renderer contract, but it must not reveal the restricted value.