sveltia-cms — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited sveltia-cms (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Complete skill for integrating Sveltia CMS into static site projects.
Sveltia CMS is a Git-based lightweight headless content management system built from scratch as the modern successor to Decap CMS (formerly Netlify CMS). It provides a fast, intuitive editing interface for content stored in Git repositories.
Use Sveltia for:
Use TinaCMS for:
Both are valid - Sveltia complements TinaCMS for different use cases.
Use the appropriate setup pattern based on your framework choice.
Hugo is the most popular static site generator for Sveltia CMS.
Steps:
mkdir -p static/admin <!-- static/admin/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js" type="module"></script>
</body>
</html> # static/admin/config.yml
backend:
name: github
repo: owner/repo
branch: main
media_folder: static/images/uploads
public_folder: /images/uploads
collections:
- name: posts
label: Blog Posts
folder: content/posts
create: true
slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Date', name: 'date', widget: 'datetime' }
- { label: 'Draft', name: 'draft', widget: 'boolean', default: true }
- { label: 'Tags', name: 'tags', widget: 'list', required: false }
- { label: 'Body', name: 'body', widget: 'markdown' } hugo server http://localhost:1313/admin/Template: See templates/hugo/
Jekyll is commonly used with GitHub Pages and Sveltia CMS.
Steps:
mkdir -p admin <!-- admin/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js" type="module"></script>
</body>
</html> # admin/config.yml
backend:
name: github
repo: owner/repo
branch: main
media_folder: assets/images/uploads
public_folder: /assets/images/uploads
collections:
- name: posts
label: Blog Posts
folder: _posts
create: true
slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
fields:
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'post' }
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Date', name: 'date', widget: 'datetime' }
- { label: 'Categories', name: 'categories', widget: 'list', required: false }
- { label: 'Body', name: 'body', widget: 'markdown' } bundle exec jekyll serve http://localhost:4000/admin/Template: See templates/jekyll/
11ty works well with Sveltia CMS for flexible static sites.
Steps:
mkdir -p admin <!-- admin/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js" type="module"></script>
</body>
</html> # admin/config.yml
backend:
name: github
repo: owner/repo
branch: main
media_folder: src/assets/images
public_folder: /assets/images
collections:
- name: blog
label: Blog Posts
folder: src/posts
create: true
slug: '{{slug}}'
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Description', name: 'description', widget: 'text' }
- { label: 'Date', name: 'date', widget: 'datetime' }
- { label: 'Tags', name: 'tags', widget: 'list', required: false }
- { label: 'Body', name: 'body', widget: 'markdown' } module.exports = function(eleventyConfig) {
eleventyConfig.addPassthroughCopy('admin');
// ... rest of config
}; npx @11ty/eleventy --serve http://localhost:8080/admin/Template: See templates/11ty/
Astro is a modern framework that works well with Sveltia CMS.
Steps:
mkdir -p public/admin <!-- public/admin/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js" type="module"></script>
</body>
</html> # public/admin/config.yml
backend:
name: github
repo: owner/repo
branch: main
media_folder: public/images
public_folder: /images
collections:
- name: blog
label: Blog Posts
folder: src/content/blog
create: true
slug: '{{slug}}'
format: mdx
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Description', name: 'description', widget: 'text' }
- { label: 'Published Date', name: 'pubDate', widget: 'datetime' }
- { label: 'Hero Image', name: 'heroImage', widget: 'image', required: false }
- { label: 'Body', name: 'body', widget: 'markdown' } npm run dev http://localhost:4321/admin/Template: See templates/astro/
Applies to: Gatsby, Next.js (SSG mode), SvelteKit, Remix, or any framework
Steps:
static/public/static/public/ mkdir -p <public-folder>/admin <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js" type="module"></script>
</body>
</html> backend:
name: github
repo: owner/repo
branch: main
media_folder: <your-media-path>
public_folder: <your-public-path>
collections:
# Define based on your content structure http://localhost:<port>/admin/Choose the authentication method that fits your deployment platform.
Best For: Cloudflare Pages, Cloudflare Workers, any deployment
This uses the official sveltia-cms-auth Cloudflare Worker for OAuth.
Steps:
# Clone the auth worker
git clone https://github.com/sveltia/sveltia-cms-auth
cd sveltia-cms-auth
# Install dependencies
npm install
# Deploy to Cloudflare Workers
npx wrangler deployOr use one-click deploy:
# Set GitHub credentials
npx wrangler secret put GITHUB_CLIENT_ID
# Paste your Client ID
npx wrangler secret put GITHUB_CLIENT_SECRET
# Paste your Client Secret
# Optional: Restrict to specific domains
npx wrangler secret put ALLOWED_DOMAINS
# Example: yourdomain.com,*.yourdomain.com # admin/config.yml
backend:
name: github
repo: owner/repo
branch: main
base_url: https://your-worker.workers.dev # ← Add this line/admin/Complete guide: See references/cloudflare-auth-setup.md
Template: See templates/cloudflare-workers/
Best For: Vercel deployments
Steps:
// api/auth.ts
export default async function handler(req, res) {
// OAuth handling logic
// See templates/vercel-serverless/api-auth.ts
} GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret backend:
name: github
repo: owner/repo
branch: main
base_url: https://yourdomain.com/api/authTemplate: See templates/vercel-serverless/
Note: Sveltia CMS deliberately omits Git Gateway support for performance reasons.
If deploying to Netlify, use either:
For local development only - no authentication proxy needed.
Requirements:
Setup:
repo (full control of private repositories) # admin/config.yml
backend:
name: github
repo: owner/repo
branch: main
local_backend: true # Enable local modeNote: This is for development only - production requires OAuth proxy.
# admin/config.yml
# Backend (Git provider)
backend:
name: github # or gitlab, gitea, forgejo
repo: owner/repo
branch: main
base_url: https://your-auth-worker.workers.dev # OAuth proxy
# Media storage
media_folder: static/images/uploads # Where files are saved
public_folder: /images/uploads # URL path in content
# Optional: Multiple media libraries
media_libraries:
default:
config:
max_file_size: 5242880 # 5 MB in bytes
slugify_filename: true
transformations:
raster_image:
format: webp
quality: 85
width: 2048
height: 2048
svg:
optimize: true
# Optional: Custom branding
logo_url: https://yourdomain.com/logo.svg
# Collections (content types)
collections:
- name: posts
label: Blog Posts
folder: content/posts
create: true
fields:
# Field definitionsCollections define content types and where they're stored.
#### Blog Post Collection
collections:
- name: posts
label: Blog Posts
folder: content/posts
create: true
slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
format: yaml # or md, toml, json
fields:
- label: Title
name: title
widget: string
- label: Date
name: date
widget: datetime
date_format: 'YYYY-MM-DD'
time_format: false # Date only
- label: Draft
name: draft
widget: boolean
default: true
- label: Featured Image
name: image
widget: image
required: false
- label: Excerpt
name: excerpt
widget: text
required: false
- label: Tags
name: tags
widget: list
required: false
- label: Body
name: body
widget: markdownTemplate: See templates/collections/blog-posts.yml
#### Documentation Page Collection
collections:
- name: docs
label: Documentation
folder: content/docs
create: true
slug: '{{slug}}'
format: mdx
fields:
- label: Title
name: title
widget: string
- label: Description
name: description
widget: text
- label: Order
name: order
widget: number
value_type: int
hint: Sort order in sidebar
- label: Category
name: category
widget: select
options:
- Getting Started
- API Reference
- Tutorials
- Advanced
- label: Body
name: body
widget: markdownTemplate: See templates/collections/docs-pages.yml
#### Landing Page Collection (Structured Content)
collections:
- name: pages
label: Landing Pages
folder: content/pages
create: true
slug: '{{slug}}'
format: json
fields:
- label: Title
name: title
widget: string
- label: SEO
name: seo
widget: object
fields:
- { label: Meta Title, name: metaTitle, widget: string }
- { label: Meta Description, name: metaDescription, widget: text }
- { label: OG Image, name: ogImage, widget: image }
- label: Hero Section
name: hero
widget: object
fields:
- { label: Headline, name: headline, widget: string }
- { label: Subheadline, name: subheadline, widget: text }
- { label: Hero Image, name: image, widget: image }
- label: CTA Button
name: cta
widget: object
fields:
- { label: Text, name: text, widget: string }
- { label: URL, name: url, widget: string }
- label: Features
name: features
widget: list
fields:
- { label: Title, name: title, widget: string }
- { label: Description, name: description, widget: text }
- { label: Icon, name: icon, widget: image }Template: See templates/collections/landing-pages.yml
Sveltia CMS has first-class i18n support with multiple structure options.
#### Multiple Files Structure (Recommended)
Best for: Hugo, Jekyll with separate locale files
i18n:
structure: multiple_files
locales: [en, fr, de, ja]
default_locale: en
collections:
- name: posts
label: Blog Posts
folder: content/posts
create: true
i18n: true # Enable i18n for this collection
fields:
- label: Title
name: title
widget: string
i18n: true # Translatable field
- label: Date
name: date
widget: datetime
i18n: duplicate # Same value across locales
- label: Body
name: body
widget: markdown
i18n: trueResult: Creates files like:
content/posts/hello-world.en.mdcontent/posts/hello-world.fr.mdcontent/posts/hello-world.de.md#### Multiple Folders Structure
Best for: Next.js, Astro with locale directories
i18n:
structure: multiple_folders
locales: [en, fr, de]
default_locale: en
collections:
- name: blog
label: Blog Posts
folder: content/{{locale}}/blog # {{locale}} placeholder
create: true
i18n: true
fields:
# Same as aboveResult: Creates files like:
content/en/blog/hello-world.mdcontent/fr/blog/hello-world.mdcontent/de/blog/hello-world.md#### Single File Structure
Best for: i18n libraries that manage translations in one file
i18n:
structure: single_file
locales: [en, fr, de]
default_locale: en
collections:
- name: translations
label: Translations
files:
- name: ui
label: UI Strings
file: data/translations.json
i18n: true
fields:
- label: Navigation
name: nav
widget: object
i18n: true
fields:
- { label: Home, name: home, widget: string, i18n: true }
- { label: About, name: about, widget: string, i18n: true }Reference: See references/i18n-patterns.md for complete guide.
Sveltia CMS includes one-click translation using DeepL.
Setup:
# admin/config.yml
backend:
name: github
repo: owner/repo
i18n:
structure: multiple_files
locales: [en, fr, de, es, ja]
default_locale: en
# DeepL integration
deepl:
api_key: your-deepl-api-key
# Or use environment variable: DEEPL_API_KEYNote: Translation quality depends on DeepL's AI - always review translations.
This skill prevents 8 common errors encountered when setting up Sveltia CMS.
Error Message:
https://api.netlify.com/auth instead of GitHub loginSymptoms:
Causes:
base_url in backend configSolution:
Step 1: Verify config.yml has `base_url`:
backend:
name: github
repo: owner/repo
branch: main
base_url: https://your-worker.workers.dev # ← Must be presentStep 2: Check GitHub OAuth App callback:
https://your-worker.workers.dev/callbackhttps://yourdomain.com/callbackStep 3: Verify Worker environment variables:
npx wrangler secret list
# Should show: GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRETStep 4: Test Worker directly:
curl https://your-worker.workers.dev/health
# Should return: {"status": "ok"}Prevention:
base_url when not using GitHub direct authError Message:
+++ delimitersSymptoms:
Causes:
Solution:
Use YAML instead of TOML (recommended):
collections:
- name: posts
folder: content/posts
format: yaml # or md (Markdown with YAML frontmatter)
# NOT: format: tomlIf you must use TOML:
Migration from TOML to YAML:
# Convert all posts from TOML to YAML
for file in content/posts/*.md; do
# Use Hugo's built-in converter
hugo convert toYAML "$file"
donePrevention:
Error Message:
Symptoms:
Causes:
Solution:
Step 1: Validate YAML:
# Install yamllint
pip install yamllint
# Check all content files
find content -name "*.md" -exec yamllint {} \;Step 2: Common fixes:
Problem: Multiple documents in one file
---
title: Post 1
---
--- # ← Remove this extra separator
title: Post 2
---Problem: Incorrect indentation
# ❌ Bad - inconsistent indentation
fields:
- name: title
label: Title # Extra space
- name: date
label: Date
# ✅ Good - consistent 2-space indentation
fields:
- name: title
label: Title
- name: date
label: DateProblem: Smart quotes
# ❌ Bad - smart quotes from copy-paste
title: "Hello World" # Curly quotes
# ✅ Good - straight quotes
title: "Hello World" # Straight quotesStep 3: Auto-fix with yamlfmt:
# Install
go install github.com/google/yamlfmt/cmd/yamlfmt@latest
# Fix all files
find content -name "*.md" -exec yamlfmt {} \;Prevention:
Error Message:
Symptoms:
Causes:
Solution:
Step 1: Verify folder path matches actual files:
# Config says:
collections:
- name: posts
folder: content/posts # Expects files here
# Check actual location:
ls -la content/posts # Files must exist hereStep 2: Match format to actual files:
# If files are: content/posts/hello.md with YAML frontmatter
collections:
- name: posts
folder: content/posts
format: yaml # or md (same as yaml for .md files)
# If files are: content/posts/hello.toml
collections:
- name: posts
folder: content/posts
format: toml
extension: tomlStep 3: Check file extensions:
# Config expects .md files
ls content/posts/*.md # Should show files
# If files have different extension:
# Either rename files OR set extension in configStep 4: Verify Git backend connection:
backend:
name: github
repo: owner/repo # Must be correct owner/repo
branch: main # Must be correct branchPrevention:
folder paths relative to repository rootformat to actual file formatError Message:
Uncaught ReferenceError: SVELTIA is not definedSymptoms:
Causes:
type="module" attributeSolution:
Step 1: Use correct script tag:
<!-- ✅ Correct -->
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js" type="module"></script>
<!-- ❌ Wrong - missing type="module" -->
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js"></script>
<!-- ❌ Wrong - incorrect path -->
<script src="https://unpkg.com/sveltia-cms/dist/sveltia-cms.js" type="module"></script>Step 2: Verify CDN is accessible:
# Test CDN URL
curl -I https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js
# Should return: 200 OKStep 3: Use version pinning (optional but recommended):
<!-- Pin to specific version for stability -->
<script src="https://unpkg.com/@sveltia/[email protected]/dist/sveltia-cms.js" type="module"></script>Step 4: Check for CSP blocking:
<!-- If you have Content Security Policy, add: -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' https://unpkg.com;
style-src 'self' 'unsafe-inline' https://unpkg.com;
connect-src 'self' https://api.github.com https://your-worker.workers.dev;
">Prevention:
Error Message:
/admin/Symptoms:
/admin/ returns 404Causes:
Solution:
Step 1: Verify admin directory location:
Hugo: static/admin/ Jekyll: admin/ 11ty: admin/ (with passthrough copy) Astro: public/admin/ Next.js: public/admin/ Gatsby: static/admin/
Step 2: Check files exist:
ls -la static/admin/ # Hugo example
# Should show: index.html, config.ymlStep 3: Framework-specific fixes:
Hugo: Files in static/ are automatically copied
Jekyll: Add to _config.yml:
include:
- admin11ty: Add to .eleventy.js:
module.exports = function(eleventyConfig) {
eleventyConfig.addPassthroughCopy('admin');
};Astro: Files in public/ are automatically copied
Step 4: Verify deployment:
# After build, check output directory
ls -la public/admin/ # or _site/admin/ or dist/admin/Prevention:
Error Message:
Symptoms:
Causes:
max_file_size)Solution:
Step 1: Convert HEIC to JPEG:
On Mac:
# Convert single file
sips -s format jpeg image.heic --out image.jpg
# Batch convert
for f in *.heic; do sips -s format jpeg "$f" --out "${f%.heic}.jpg"; doneOn iPhone:
Or use online converter: https://heic.to/
Step 2: Enable image optimization to auto-convert:
# admin/config.yml
media_libraries:
default:
config:
max_file_size: 10485760 # 10 MB
transformations:
raster_image:
format: webp # Auto-converts to WebP
quality: 85
width: 2048
height: 2048Step 3: Increase max file size if needed:
media_libraries:
default:
config:
max_file_size: 10485760 # 10 MB in bytes
# Default is often 5 MBPrevention:
Error Message:
Symptoms:
Causes:
Cross-Origin-Opener-Policy headerSolution:
Step 1: Adjust COOP header:
Cloudflare Pages (_headers file):
/*
Cross-Origin-Opener-Policy: same-origin-allow-popups
# NOT: same-origin (this breaks OAuth)Netlify (_headers file):
/*
Cross-Origin-Opener-Policy: same-origin-allow-popupsVercel (vercel.json):
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Cross-Origin-Opener-Policy",
"value": "same-origin-allow-popups"
}
]
}
]
}Step 2: Add OAuth proxy to CSP:
<meta http-equiv="Content-Security-Policy" content="
connect-src 'self' https://api.github.com https://your-worker.workers.dev;
">Step 3: For Cloudflare Pages, allow API access:
# admin/config.yml (if using Cloudflare Pages webhooks)
# Add to CSP: https://api.cloudflare.comPrevention:
same-origin-allow-popups by defaultSveltia CMS is a drop-in replacement for Decap CMS (formerly Netlify CMS).
Step 1: Update script tag:
<!-- OLD: Decap CMS -->
<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
<!-- NEW: Sveltia CMS -->
<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js" type="module"></script>Step 2: Keep existing config.yml:
# Your existing Decap config.yml works as-is!
backend:
name: github
repo: owner/repo
collections:
# ... no changes neededStep 3: Test locally:
# Start your site's dev server
hugo server # or jekyll serve, or npm run dev
# Visit /admin/ and test:
# - Login
# - Content listing
# - Editing
# - Saving
# - Media uploadsStep 4: Deploy:
git add static/admin/index.html # or your admin path
git commit -m "Migrate to Sveltia CMS"
git pushThat's it! Your content, collections, and workflows remain unchanged.
Config Compatibility: 100% compatible
UI Changes:
Performance Improvements:
New Features:
Not Supported:
Workaround: Use Cloudflare Workers or Vercel OAuth proxy instead.
Before fully migrating, test these workflows:
Complete guide: See references/migration-from-decap.md
Best For: Static sites with Cloudflare ecosystem
Steps:
Build command: hugo # or jekyll build, or npm run build
Build output directory: public # or _site, or dist
Root directory: / backend:
base_url: https://your-worker.workers.devhttps://yourdomain.pages.dev/admin/Best For: Next.js, Astro, or any framework with Vercel deployment
Steps:
Framework Preset: <Auto-detected>
Build Command: <Default>
Output Directory: <Default> GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secretBest For: JAMstack sites, legacy Netlify CMS migrations
Steps:
Build command: <your-build-command>
Publish directory: public # or _site, or distBest For: Jekyll sites, simple static sites
Steps:
# _config.yml
include:
- admin # .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: jekyll build # or your build command
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site https://username.github.io/repo/admin/All templates available in templates/:
Comprehensive guides in references/:
Automation tools in scripts/:
Estimated Savings: 60-65% (~9,000 tokens saved)
Without Skill (~14,000 tokens):
With Skill (~5,000 tokens):
This skill prevents 8 common errors (100% prevention rate):
# 1. Create Hugo site
hugo new site my-blog
cd my-blog
# 2. Create admin directory
mkdir -p static/admin
# 3. Copy templates
cp [path-to-skill]/templates/hugo/index.html static/admin/
cp [path-to-skill]/templates/hugo/config.yml static/admin/
# 4. Deploy OAuth Worker
git clone https://github.com/sveltia/sveltia-cms-auth
cd sveltia-cms-auth
npx wrangler deploy
# 5. Configure and test
hugo server
open http://localhost:1313/admin/# 1. Create Jekyll site
jekyll new my-site
cd my-site
# 2. Create admin
mkdir admin
cp [path-to-skill]/templates/jekyll/index.html admin/
cp [path-to-skill]/templates/jekyll/config.yml admin/
# 3. Add to _config.yml
echo "include:\n - admin" >> _config.yml
# 4. Deploy
git add .
git commit -m "Add Sveltia CMS"
git push# 1. Update script tag in admin/index.html
sed -i 's|decap-cms|@sveltia/cms|g' static/admin/index.html
sed -i 's|decap-cms.js|sveltia-cms.js" type="module|g' static/admin/index.html
# 2. Test locally
hugo server
open http://localhost:1313/admin/
# 3. Deploy
git add static/admin/index.html
git commit -m "Migrate to Sveltia CMS"
git pushIssues? Check references/common-errors.md first
Still Stuck?
sveltia-cmsLast Updated: 2025-10-24 Skill Version: 1.0.0 Sveltia CMS Version: 0.113.3 (Beta) Status: Production-ready, v1.0 GA expected early 2026
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.