Token System
Dynamic content injection engine with RBAC enforcement, secure field access, and XSS-resistant output.
On this page
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_FIELDSinsrc/services/token/engine.ts:28
1.2 Relation Access: RBAC Enforced
When resolving {{ entry.manufacturer.name }}, the engine:
- Looks up the relation field in the collection schema
- Verifies the user has
readpermission on the related collection - Admins get fast-path access
- Unauthorized returns
[Access Denied]with an audit log entry
Implementation:
canAccessCollection()+resolveRelationToken()insrc/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.