133 lines
4.9 KiB
Markdown
133 lines
4.9 KiB
Markdown
# 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 prefix
|
|
- `user.public.*`: safe viewer/triggering-user display information
|
|
- `platform.discord.guild.*`: safe Discord guild statistics from the configured guild
|
|
- `platform.twitch.channel.*`: safe locally configured Twitch channel/runtime values
|
|
- `platform.youtube.channel.*`: safe locally configured or hydrated YouTube channel/runtime values
|
|
- `okf.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 output
|
|
- `user`: user-specific content, only for fields whose output audience allows it
|
|
- `moderator`: moderator/support visible content
|
|
- `admin`: admin-only content
|
|
- `internal`: internal implementation/runtime content
|
|
- `secret_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:
|
|
|
|
```html
|
|
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.
|