Available Now

The Framework for
Content Applications

VelvetCMS Core is a lightweight, explicit, no-magic PHP framework. It gives you the tools to build content-driven applications without the bloat of heavy full-stack frameworks.

$ composer create-project velvetcms/core my-app

Explicit by Design

Build with the same primitives you know and love, optimized for content.

๐Ÿ›ค๏ธ

Expressive Routing

Map URLs to closures or controllers with middleware, groups, and rate limiting.

+
  • Support for GET, POST, PUT, DELETE, PATCH methods
  • Named routes with programmatic URL generation
  • Required, optional, and wildcard parameters
  • Per-route and global middleware pipelines
$router->get('/posts/{slug}', [PostController::class, 'show'], 'posts.show');
$router->post('/contact', [ContactController::class, 'submit']);

// Optional parameters
$router->get('/archive/{year?}', function (Request $request, ?string $year = null) {
    return "Archive for " . ($year ?? date('Y'));
});

// Wildcard - captures everything including slashes
$router->get('/docs/{path*}', [DocsController::class, 'render']);
๐Ÿ’‰

Dependency Injection

Explicit core wiring for speed. Autowiring available for your classes when you want it.

+
  • Core services manually wired for maximum performance
  • Autowiring available as a pragmatic fallback for your classes
  • No static proxies or facades hiding dependencies
  • Standard injection patterns you already know
class PostController
{
    public function __construct(
        private PageService $pages,
        private CacheInterface $cache
    ) {}

    public function show(Request $request, string $slug): Response
    {
        $post = $this->pages->find($slug);
        return view('posts.show', ['post' => $post]);
    }
}
๐Ÿ—„๏ธ

Fluent Query Builder

Expressive SQL generation with joins, subqueries, and automatic query caching.

+
  • Prepared statements everywhere โ€“ SQL injection impossible
  • Fluent chaining for complex queries
  • Aggregates, ordering, limits, and offsets
  • Cache integration with the remember() pattern
// Fluent query building
$posts = db()->table('posts')
    ->select('id', 'title', 'slug')
    ->where('status', 'published')
    ->whereNotNull('published_at')
    ->orderBy('created_at', 'desc')
    ->limit(10)
    ->get();

// Find by primary key
$user = db()->table('users')->find(1);

// Pluck single column
$emails = db()->table('users')->pluck('email');
๐Ÿงฉ

Module System

PSR-4 autoloading, dependency resolution, and manifest-based loading.

+
  • Self-contained modules with routes, views, commands, and migrations
  • Version constraints and conflict detection
  • Service provider pattern for clean bootstrapping
  • Extend every part of the application lifecycle
// module.json
{
  "name": "blog",
  "entry": "Vendor\\Blog\\BlogModule",
  "version": "1.0.0",
  "requires": {
    "core": ">=1.0.0 <2.0.0"
  }
}

// BlogModule.php
class BlogModule extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(PostRepository::class, ...);
    }
}
๐ŸŽจ

View Engine

Blade-like syntax with layouts, partials, and inheritance. Namespace support for modules.

+
  • Familiar {{ }} and {!! !!} syntax
  • Control structures: @if, @foreach, @include
  • Layout inheritance with @extends, @section, @yield
  • Module namespaces for organized views
// Render a view with data
return view('posts.show', ['post' => $post]);

// Namespaced module views
return view('blog::posts.index', ['posts' => $posts]);

// Share data across all views
$engine->share('siteName', 'My Site');
โฐ

Task Scheduler

Fluent cron-style scheduling. Run commands or closures on any schedule.

+
  • Define schedules in PHP โ€“ no crontab editing
  • Schedule CLI commands or PHP closures
  • Fluent frequency API: ->daily(), ->hourly(), ->everyMinute()
  • WebCron endpoint for environments without cron access
$schedule = $this->app->make('schedule');

// Run a CLI command daily at 3:00 AM
$schedule->command('cache:clear')->dailyAt(3, 0);

// Run a callback every hour
$schedule->call(function () {
    // Sync data from external API
    $this->apiSync->run();
})->hourly();

// Run every minute
$schedule->command('heartbeat:send')->everyMinute();
โšก

Smart Caching

APCu, File, or Redis. Cache queries, pages, routes, and compiled templates.

+
  • Multiple drivers: File (default), Redis, APCu
  • Graceful degradation โ€“ app continues if cache fails
  • Simple remember() pattern for fetch-or-compute
  • Cache tags for granular invalidation
$cache = app('cache');

// Store for 5 minutes (300 seconds)
$cache->set('user:123', $userData, 300);

// Retrieve with fallback
$user = $cache->get('user:123', null);

// Remember pattern - fetch from cache or compute
$posts = $cache->remember('recent-posts', 600, function () {
    return db()->table('posts')
        ->orderBy('created_at', 'desc')
        ->limit(10)
        ->get();
});
๐Ÿ“

Markdown Engine

Pluggable drivers with frontmatter extraction, tables, and task lists.

+
  • Drivers: CommonMark (default), Parsedown, or HTML pass-through
  • YAML frontmatter for metadata (title, status, layout, custom fields)
  • CommonMark extensions: tables, strikethrough, autolinks, task lists
  • Custom template tags preserved in all drivers
---
title: Welcome to VelvetCMS
status: published
layout: default
category: announcements
---

Welcome to **VelvetCMS**, a modular content framework.

## Features

- Fast and lightweight
- Flexible content drivers
- Modern PHP architecture
โœ…

Validation Service

Standalone validator for controllers, CLI, API, and imports.

+
  • Reusable validation logic anywhere in your app
  • Rich rule set: required, email, url, min, max, regex, in, same, date, array
  • Custom error messages per field
  • Throws ValidationException with structured errors
// Basic validation
$validated = Validator::make($data, [
    'email' => 'required|email',
    'password' => 'min:8',
])->validate();

// In controllers
$validated = $request->validate([
    'title' => 'required|min:3',
    'status' => 'in:draft,published',
]);

// Handle errors
try {
    $validated = Validator::make($data, $rules)->validate();
} catch (ValidationException $e) {
    $errors = $e->errors(); // ['field' => ['Error message']]
}
๐Ÿ—๏ธ

Schema Builder

Database-agnostic migrations. Write once, run on SQLite, MySQL, or PostgreSQL.

+
  • Fluent Blueprint API for defining tables and columns
  • Generates appropriate SQL for each database driver
  • Migration system with up/down methods for version control
  • Column modifiers: nullable, default, unsigned
use VelvetCMS\Database\Schema\Schema;
use VelvetCMS\Database\Schema\Blueprint;

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->string('slug', 100)->unique();
    $table->text('content');
    $table->string('status')->default('draft');
    $table->timestamp('published_at')->nullable();
    $table->timestamps();
});
๐Ÿข

Multi-Tenancy

Single config switch. Host, path, or custom resolvers with full isolation.

+
  • Enable with a single config toggle โ€“ disabled by default
  • Resolvers: hostname, subdomain, path segment, or custom callback
  • Tenant-aware paths for content, views, storage, and modules
  • Cache prefixing and session scoping prevent cross-tenant collisions
// config/tenancy.php
return [
    'enabled' => true,
    'resolver' => 'host', // or 'path', 'callback'
    
    'host' => [
        'map' => [
            'acme.example.com' => 'acme',
            'beta.example.com' => 'beta',
        ],
    ],
];

// Tenant-aware paths (automatic when enabled):
// Content: user/tenants/{tenant}/content
// Views:   user/tenants/{tenant}/views
// Storage: storage/tenants/{tenant}
โœจ

No Magic

No facades, no global state, no hidden behavior. Trace every part of your app.

+
  • Explicit over implicit โ€“ core services wired manually
  • No static proxies hiding your dependencies
  • Every part of the lifecycle is traceable and debuggable
  • Minimal dependencies โ€“ four runtime packages, all battle-tested

Scale with Your Content

Start with files. Evolve to a database. Never rewrite your frontend.

๐Ÿ“„

File Driver

Markdown files with frontmatter. Perfect for docs, blogs, and small sites.

+
  • Human-readable content you can edit in any text editor
  • Version control friendly โ€“ track changes with Git
  • Zero database setup required
  • Easy to migrate from other static site generators
โšก

Hybrid Driver

Files for editing, SQL for metadata. Best of both worlds.

+
  • Edit content as Markdown files โ€“ familiar workflow
  • Metadata indexed in SQL for fast queries and filtering
  • Scales to thousands of pages with instant lookups
  • Automatic sync between files and database
๐Ÿ—„๏ธ

Database Driver

Full SQL storage for high-volume content applications.

+
  • Content and metadata stored entirely in the database
  • Ideal for CMS with admin panels and user-generated content
  • Full query capabilities for complex filtering and search
  • Supports SQLite, MySQL, and PostgreSQL

Security by Default

Every layer is hardened. You have to opt out of protection.

๐Ÿ›ก๏ธ

CSRF Protection

Automatic token validation on all state-changing requests.

+
  • Tokens generated and validated automatically
  • Built into the middleware pipeline
  • Configurable exclusions for APIs and webhooks
๐Ÿ”’

XSS Auto-escaping

All output escaped by default. Opt-in for raw HTML when needed.

+
  • {{ $var }} automatically escapes HTML entities
  • {!! $var !!} for trusted raw HTML output
  • Safe mode available for rendering untrusted templates
๐Ÿ—ƒ๏ธ

SQL Injection Prevention

Parameterized queries everywhere. No exceptions.

+
  • Query builder uses prepared statements for all user input
  • No raw SQL interpolation in the framework
  • Even raw expressions are parameterized
๐Ÿ”

Secure Sessions

HTTP-only cookies, regeneration on auth, encrypted storage.

+
  • Cookies marked HTTP-only, Secure, and SameSite by default
  • Session ID regenerated on authentication events
  • Path traversal protection with realpath checks

Start Building

One command to get started.