Skip to content

Documentation

Token System

Dynamic content injection engine with RBAC enforcement, secure field access, and XSS-resistant output.

6/10/2026
4 min read Edit on GitHub

This is the content templating token system{{ entry.title }}, {{ user.email }}, etc. For authentication secrets (API keys, JWT, website tokens), see Secrets & Tokens Inventory.

The SveltyCMS Token System injects dynamic content into entries, templates, and automation configurations. Every token resolution passes through RBAC checks — you can only access content you have permission to read.


1. Security Model

1.1 User Fields: Allowlist (Not Blocklist)

Only explicitly allowed user fields are accessible via {{ user.* }}:

✅ Allowed: _id, email, username, role, avatar, language, name
❌ Blocked: password, hash, salt, tokens, secrets, and all other fields

Implementation: ALLOWED_USER_FIELDS in src/services/token/engine.ts:28

1.2 Relation Access: RBAC Enforced

When resolving {{ entry.manufacturer.name }}, the engine:

  1. Looks up the relation field in the collection schema
  2. Verifies the user has read permission on the related collection
  3. Admins get fast-path access
  4. Unauthorized returns [Access Denied] with an audit log entry

Implementation: canAccessCollection() + resolveRelationToken() in src/services/token/relation-resolver.ts

1.3 No Value Caching

Token values are resolved in real-time on every request — never cached. This ensures:

  • User-specific data (email, role) is always current
  • Time-based tokens (system.now) reflect actual time
  • One user cannot see another user’s cached token values

Only the token definition registry is cached for 5 minutes (which tokens exist for a collection, not their values).


2. Syntax

Tokens are enclosed in double curly braces: {{ token_path }}.

{{ entry.title }}        → resolves to the title of the current entry
{{ user.email }}         → resolves to the current user's email
{{ site.SITE_NAME }}     → resolves to the site name
{{ system.now }}         → resolves to current ISO timestamp

2.1 Modifiers

Transform token values using pipe modifiers:

{{ system.now | date("MMM do, yyyy") }}
{{ entry.title | uppercase }}
{{ entry.body | truncate(100) }}

2.2 Escaping

Use \{{ literal text }} to display curly braces without token resolution.

2.3 Error Handling

Scenario Behavior
Unknown token path Preserved as-is (or throws if throwOnMissing: true)
Unauthorized relation Returns [Access Denied]
Blocked user field Returns empty string
Empty token {{}} Syntax validation error
Nested tokens {{ {{ }} }} Rejected by validator

3. Token Categories

3.1 Entry Tokens (entry.*)

Access fields from the current entry.

{{ entry.title }}                 → field value
{{ entry.slug }}                  → URL slug
{{ entry.author.name }}           → related author's name (RBAC checked)
{{ entry.tags.0.name }}           → first tag's name
{{ entry.manufacturer.website }}  → relation → nested field

3.2 User Tokens (user.*)

Access allowed fields from the authenticated user. Blocked fields return empty string.

{{ user.name }}       → full name
{{ user.email }}      → email address
{{ user.role }}       → role (admin, editor, guest)
{{ user.avatar }}     → avatar URL
{{ user.password }}   → returns "" (blocked)

3.3 Site Tokens (site.*)

Access global site configuration. Tenant-scoped in multi-tenant deployments.

{{ site.SITE_NAME }}   → site name
{{ site.HOST_PROD }}   → production URL

3.4 System Tokens (system.*)

Access dynamic system values. Always resolved in real-time.

{{ system.now }}    → current ISO timestamp
{{ system.year }}   → current year

4. Depth & Recursion Limits

Limit Value Purpose
Max recursion depth 10 Prevents infinite loops from self-referencing tokens
Nested token limit rejected {{ {{ }} }} is a syntax error
Empty token limit rejected {{}} is a syntax error

5. Visual Validation

Fields containing tokens are marked with a code icon:

  • Valid: Blue/primary color
  • Invalid: Red/error color (unmatched braces, syntax errors)
  • Hover: Shows the specific error message

6. API Resolution

Tokens are automatically resolved in JSON API responses (/api/*) via the handle-token-resolution middleware hook:

  • Only processes successful (2xx) JSON responses
  • Skips non-JSON content types
  • Skips error responses (preserves error body integrity)

For SSR pages using ContentSystem directly, tokens remain unresolved unless explicitly processed in the load function.


7. Automation Integration

The Automation Service resolves tokens in webhook bodies, email templates, and AI agent prompts before execution. This enables dynamic payloads like:

{
  "webhook_url": "https://example.com/hooks/{{ entry.slug }}",
  "email_subject": "New post: {{ entry.title }}",
  "ai_prompt": "Summarize: {{ entry.body }}"
}

Token resolution happens at execution time, not configuration time — ensuring current data.


Related

contenttokensdynamic-contentsecurityrbac
Was this page helpful?