Services First.
Interface Second.
VelvetCMS is a service-oriented CMS. All logic lives in frontend-agnostic services. Three consumers — CLI, REST API, and Web admin — expose them independently. Use all three, two, one, or none.
$ php velvet content:create "My First Post"
The admin panel. Dark mode by default. Every feature is a service — the panel is just one way to access them.
Three Interfaces. One Codebase.
Business logic lives in services. Consumers are thin, optional wrappers. Disable any interface with a single config toggle.
php velvet content:listconsumers.api = falseconsumers.web = falseCompose with Blocks, Not HTML
Drag-and-drop content blocks with real-time preview. Text, images, code, embeds, custom types. Every block is self-contained with its own schema and rendering. Paste a YouTube URL — it resolves to a rich embed automatically.
- Custom block types — define schema, template, CSS
- Multiple editing modes: visual blocks, Markdown, code
- Content locking for concurrent editing
- Reusable content templates across pages
$blocks->register('callout', [
'label' => 'Callout Box',
'icon' => 'megaphone',
'fields' => [
'style' => ['type' => 'select', 'options' => ['info', 'warning', 'tip']],
'title' => ['type' => 'text'],
'content' => ['type' => 'rich-text'],
],
'template' => 'blocks.callout',
]);
Edit Without Fear
Every save creates an automatic revision. Diff any two versions side-by-side with field-level and block-level change highlighting. Restore with one click. For bigger changes, use per-content staging — edit multiple pages, preview the result, deploy everything atomically.
- Visual side-by-side diff viewer in the admin
- Named snapshots for milestone backups
- Per-content staging, not a full site clone
- Atomic deployment with one-click rollback
$changeset = $staging->createChangeset('Homepage redesign');
$staging->stage($homepage, $changeset);
$staging->stage($aboutPage, $changeset);
// Preview before going live
$staging->preview($changeset); // → staging URL
// Ship it
$staging->deploy($changeset); // → goes live atomically
From Draft to Published — Your Rules
Define review pipelines with approvals, role gates, and automatic transitions. A blog might need just draft → published. An enterprise site might go through legal review, editor approval, and scheduled publication. Shareable draft links let external reviewers see unpublished content without a CMS account.
- Custom states and transitions per content type
- Role-based gates with transition comments
- Automatic actions on state change (notify, index, cache)
- Shareable draft links for external reviewers
Extend Everything
Hooks, a typed extension registry, modular architecture, and a queue system. Built to be extended, not forked.
Hooks System
Filters mutate data. Actions trigger side effects. WordPress-style patterns, but typed, testable, and without the global soup.
// Filter: modify content before saving
$hooks->filter('content.before_save', function (Content $content) {
$content->slug = Str::slug($content->title);
return $content;
});
// Action: trigger side effects after publishing
$hooks->action('content.published', function (Content $content) {
$search->index($content);
$cache->invalidate('sitemap');
});
Extension Registry
Typed registry for UI extension points — sidebar items, settings tabs, dashboard widgets, block types. Register once, render everywhere.
Module System
Self-contained packages with routes, views, migrations, and commands. Version constraints, dependency resolution, conflict detection.
Queue System
Offload heavy work — media processing, imports, search indexing, webhook delivery. Database-backed with retry logic and priority lanes.
Everything You Need, Built In
19 submodules covering content, media, users, SEO, and operations. No plugins required for the basics.
Media Library
Auto image processing, WebP conversion, focal point cropping, folder organization, usage tracking.
Taxonomies
Categories, tags, custom taxonomies. Hierarchical or flat. Attach to any content type.
Users & Roles
Granular permissions, registration modes, password policies, user metadata.
Dual Auth
Session-based for web, token-based for API with scoped permissions. Optional TOTP 2FA.
Menu Builder
Drag-and-drop navigation with unlimited nesting. Define menus, assign to locations.
SEO & Structured Data
Auto meta tags, OpenGraph, JSON-LD, sitemap.xml, readability scoring.
Privacy-First Analytics
Page views and referrers without cookies or third-party scripts. GDPR-compliant by design.
Scheduling
Future publishing with calendar view. Expiration dates and auto-unpublish.
Content Relationships
Bidirectional links, series, translation connections between any content types.
Redirects
301, 302, 307, 308 with exact, wildcard, regex matching. CSV bulk import.
Activity & Audit Log
Field-level diffs showing what changed, not just that something changed.
Trash & Recovery
Centralized soft-delete with full snapshots. Configurable auto-expiry.
Operations & Monitoring
Know what's happening. Fix it before your users notice.
Health Checks
Database, cache, storage, disk, queue, scheduler. Reports healthy/degraded/unhealthy. Kubernetes-compatible probes.
VelvetCMS Insight
Local-only metrics: response times, cache hit rates, query counts. Bottleneck detection. Nothing sent externally.
Snapshots
Full site backups on schedule. Content, media metadata, menus, settings. Export production, import to staging.
RSS & Atom Feeds
Auto-generated feeds per site, content type, taxonomy, author. Autodiscovery tags included.
Coming from WordPress?
Built-in WXR importer handles posts, pages, media, authors, categories, tags, custom fields, and featured images. Everything gets mapped to VelvetCMS equivalents automatically.
- CSV and JSON import formats also supported
- Chunked processing for large sites
- Dry-run mode to preview before committing
- Author mapping via CSV
$ php velvet import:wordpress export.xml \
--author-map=authors.csv \
--dry-run
Parsed 847 posts, 42 pages, 1203 media items.
Mapped 3 authors. 0 conflicts found.
Run without --dry-run to import.
Built on a Purpose-Built Framework
VelvetCMS runs on Core — a lightweight, explicit PHP framework with no facades, no service locators, no magic. Every dependency is injected. Every call is traceable.
class PostController
{
public function __construct(
private ContentService $content,
private CacheInterface $cache
) {}
public function show(Request $req, string $slug): Response
{
$post = $this->cache->remember(
"post:{$slug}", 600,
fn() => $this->content->findBySlug($slug)
);
return view('posts.show', ['post' => $post]);
}
}
Licensing
Core
Apache 2.0Free forever. Commercial use included. No strings.
VelvetCMS
Dual LicenseFree for non-commercial. License for business use.
Fleet
CommercialFor agencies & scale. Priced for the value delivered.