Source & license
Upstream license: MIT
License TL;DR
You can use it, change it, self-host it and sell it. Keep the original copyright and license notice with copies of the code. You don’t have to publish your changes. The authors don’t promise it will work.
Explain MIT in plain English →Summary of the main license. Separate packages and assets can have different terms.
Inspect repository ↗Read this project’s actual license ↗Repository owner
See the upstream repository for the original creator and contributors.
Maintain this project? Maintainer verification →Cloudflare hosting
Free tier eligible within limits
A new small starter can fit Workers, D1, KV and standard R2 free allowances. Use the starter configuration, not the committed CI database/bucket or stats service. Optional email services, image transformation and external AI tools have separate limits and costs.
Hosting requirements
- Run the documented app creator, create your own D1/KV/R2 resources and auth secrets, and replace default/example admin credentials before exposure.
- Keep Worker CPU and daily DB/KV writes within Free limits. The diagram covers the starter, excluding the maintainer stats collector and CI-only Email Service binding.
- Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested.
Sources checked 01/10/2026
Repository snapshot: 37df6af. Hosting eligibility reflects the deployment documentation and listed assumptions.
- ghost ↗
No Cloudflare account? Run SonicJS on any server with Docker and SQLite:
- wordpress ↗
No Cloudflare account? Run SonicJS on any server with Docker and SQLite:
- workers ↗
name = "my-sonicjs-app" main = "src/index.ts" compatibility_date = "2024-09-23" compatibility_flags = ["nodejs_compat"] # Cloudflare Workers settings workers_dev = true # D1 Database [[d1_databases]] binding = "DB" database_name = "my-sonicjs-db" database_id = "YOUR_DATABASE_ID" # Run: wrangler d1 create my-sonicjs-db migrations_dir = "./node_modules/@sonicjs-cms/core/migrations" # R2 Bucket for media storage [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "my-
- d1 ↗
odejs_compat"] # Cloudflare Workers settings workers_dev = true # D1 Database [[d1_databases]] binding = "DB" database_name = "my-sonicjs-db" database_id = "YOUR_DATABASE_ID" # Run: wrangler d1 create my-sonicjs-db migrations_dir = "./node_modules/@sonicjs-cms/core/migrations" # R2 Bucket for media storage [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "my-sonicjs-media" # KV namespace for the cache plugin + cross-isolate bootstrap fast-path. # REQUIRED for good TTFB: without it every cold isolate re-runs the full
- kv ↗
t isolate per deploy pays # that cost and the rest skip it via a ~10ms KV read. [[kv_namespaces]] binding = "CACHE_KV" id = "YOUR_KV_NAMESPACE_ID" # Run: wrangler kv namespace create CACHE_KV # Environment variables [vars] ENVIRONMENT = "development" CORS_ORIGINS = "http://127.0.0.1:8787,http://127.0.0.1:4321" # Production environment [env.production] name = "my-sonicjs-app-production" vars = { ENVIRONMENT = "production" } # Observability [observability] enabled = true
- r2 ↗
= "./node_modules/@sonicjs-cms/core/migrations" # R2 Bucket for media storage [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "my-sonicjs-media" # KV namespace for the cache plugin + cross-isolate bootstrap fast-path. # REQUIRED for good TTFB: without it every cold isolate re-runs the full D1 # bootstrap (~10s+ first byte). With it, only the first isolate per deploy pays # that cost and the rest skip it via a ~10ms KV read. [[kv_namespaces]] binding = "CACHE_KV" id = "YOUR_KV_NAMESPACE_ID" # Run: wrangler kv namespa
- free-tier-eligible ↗
name = "my-sonicjs-app" main = "src/index.ts" compatibility_date = "2025-05-05" compatibility_flags = ["nodejs_compat"] # Cloudflare account account_id = "f9d6328dc3115e621758a741dda3d5c4" # Cloudflare Workers settings workers_dev = true # D1 Database # Note: database_name and database_id are automatically updated by GitHub Actions [[d1_databases]] binding = "DB" database_name = "sonicjs-worktree-lane711-graphql-integration-plan" database_id = "86a764d1-a166-4243-b8e0-c4a729e06610" migrations_dir = "./migrations" # R2 Bucket for media storage (using CI bucket) [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "sonicjs-ci-media" # KV Cache (using CI namespace) [[kv_namespaces]] binding = "CACHE_KV" id = "a16f8246fc294d809c90b0fb2df6d363" # Cloudflare Email Service binding (send_em
- free-tier-eligible ↗
| **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation |
- free-tier-eligible ↗
| Rows read | 5 million / day | First 25 billion / month included + $0.001 / million rows |
- free-tier-eligible ↗
| Keys read | 100,000 / day | 10 million/month, + $0.50/million |
- free-tier-eligible ↗
| Storage | 10 GB-month / month |
- free-tier-eligible ↗
| Class A Operations | 1 million requests / month |
- free-tier-eligible ↗
| Class B Operations | 10 million requests / month |
- free-tier-eligible ↗
| Rows written | 100,000 / day | First 50 million / month included + $1.00 / million rows |
- free-tier-eligible ↗
| Keys written | 1,000 / day | 1 million/month, + $5.00/million |
- MIT ↗
MIT License Copyright (c) 2024 Lane Campbell and SonicJS contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- architecture ↗
name = "my-sonicjs-app" main = "src/index.ts" compatibility_date = "2024-09-23" compatibility_flags = ["nodejs_compat"] # Cloudflare Workers settings workers_dev = true # D1 Database [[d1_databases]] binding = "DB" database_name = "my-sonicjs-db" database_id = "YOUR_DATABASE_ID" # Run: wrangler d1 create my-sonicjs-db migrations_dir = "./node_modules/@sonicjs-cms/core/migrations" # R2 Bucket for media storage [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "my-sonicjs-media" # KV namespace for the cache plugin + cross-isolate bootstrap fast-path. # REQUIRED for good TTFB: without it every cold isolate re-runs the full D1 # bootstrap (~10s+ first byte). With it, only the first isolate per deploy pays # that cost and the rest skip it via a ~10ms KV read. [[kv_namespaces]] binding = "CACHE_KV" id = "YOUR_KV_NAMESPACE_ID" # Run: wrangler kv namespace create CACHE_KV # Environment variables [vars] ENVIRONMENT = "development" CORS_ORIGINS = "http://127.0.0.1:8787,http://127.0.0.1:4321" # Production environment [env.production] name = "my-sonicjs-app-production" vars = { ENVIRONMENT = "production" } # Observability [observability] enabled = true
- architecture ↗
onicjs@latest my-app cd my-app npm run dev # Visit http://localhost:8787 ``` Your app includes: - ✅ SonicJS CMS pre-configured - ✅ Database migrations ready - ✅ Example content collections - ✅ Admin interface at `/admin` - ✅ Ready to deploy to Cloudflare ### For Package Developers (Contributing to SonicJS) ```bash # Clone this repository git clone https://github.com/lane711/sonicjs.git cd sonicjs # Install dependencies npm install # Build the core package npm run build:core # Create a test app to validate changes npx create-sonicjs@latest my-sonicjs-app # Run tests npm test ``` #### Setting Up a Fresh Database ```bash # Create a fresh D1 database for your branch (run from project root) npm run db:reset ``` This creates a new D1 database named `sonicjs-worktr
- architecture ↗
name = "my-sonicjs-app" main = "src/index.ts" compatibility_date = "2024-09-23" compatibility_flags = ["nodejs_compat"] # Cloudflare Workers settings workers_dev = true # D1 Database [[d1_databases]] binding = "DB" database_name = "my-sonicjs-db" database_id = "YOUR_DATABASE_ID" # Run: wrangler d1 create my-sonicjs-db migrations_dir = "./node_modules/@sonicjs-cms/core/migrations" # R2 Bucket for media storage [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "my-
- architecture ↗
odejs_compat"] # Cloudflare Workers settings workers_dev = true # D1 Database [[d1_databases]] binding = "DB" database_name = "my-sonicjs-db" database_id = "YOUR_DATABASE_ID" # Run: wrangler d1 create my-sonicjs-db migrations_dir = "./node_modules/@sonicjs-cms/core/migrations" # R2 Bucket for media storage [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "my-sonicjs-media" # KV namespace for the cache plugin + cross-isolate bootstrap fast-path. # REQUIRED for good TTFB: without it every cold isolate re-runs the full
- architecture ↗
t isolate per deploy pays # that cost and the rest skip it via a ~10ms KV read. [[kv_namespaces]] binding = "CACHE_KV" id = "YOUR_KV_NAMESPACE_ID" # Run: wrangler kv namespace create CACHE_KV # Environment variables [vars] ENVIRONMENT = "development" CORS_ORIGINS = "http://127.0.0.1:8787,http://127.0.0.1:4321" # Production environment [env.production] name = "my-sonicjs-app-production" vars = { ENVIRONMENT = "production" } # Observability [observability] enabled = true
- architecture ↗
= "./node_modules/@sonicjs-cms/core/migrations" # R2 Bucket for media storage [[r2_buckets]] binding = "MEDIA_BUCKET" bucket_name = "my-sonicjs-media" # KV namespace for the cache plugin + cross-isolate bootstrap fast-path. # REQUIRED for good TTFB: without it every cold isolate re-runs the full D1 # bootstrap (~10s+ first byte). With it, only the first isolate per deploy pays # that cost and the rest skip it via a ~10ms KV read. [[kv_namespaces]] binding = "CACHE_KV" id = "YOUR_KV_NAMESPACE_ID" # Run: wrangler kv namespa
Upstream screenshot · SonicJs-Org/sonicjs repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.
What it can replace
Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.
Managing structured content, media, editorial versions and APIs; no full WordPress plugin ecosystem, hosted Ghost business suite or automatic migration parity.
See supporting source ↗Managing structured content, media, editorial versions and APIs; no full WordPress plugin ecosystem, hosted Ghost business suite or automatic migration parity.
See supporting source ↗How it works
The shape of SonicJS on Cloudflare, and how it stacks up against the rented tools it replaces.
Architecture
Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.
View upstream source ↗Configuration and workflow sources
Reviewed commit 37df6afdcaea. Files were read as data; upstream applications and CI jobs were not executed.
Deployment configuration · 4 files
Cloudflare Workers · compatibility 2025-05-05
my-sonicjs-app · default
Entrypoint: src/index.ts
DB→ D1CACHE_KV→ KVMEDIA_BUCKET→ R2EMAIL→ Send Email
Cloudflare Workers · example/template, excluded from overview · compatibility 2024-09-23
my-sonicjs-app · default
Entrypoint: src/index.ts
DB→ D1CACHE_KV→ KVMEDIA_BUCKET→ R2
my-sonicjs-app-production · env.production
Inherited from default: main, compatibility_date, compatibility_flags
Entrypoint: src/index.ts
No resource bindings declared in this scope.
Cloudflare Workers · compatibility 2025-05-05
sonicjs-stats · default
Entrypoint: src/index.ts
DB→ D1CACHE_KV→ KV
sonicjs-stats-production · env.production
Inherited from default: main, compatibility_date, compatibility_flags
Entrypoint: src/index.ts
DB→ D1CACHE_KV→ KV
Cloudflare Workers · compatibility 2024-12-30
sonicjs-docs · default
Entrypoint: .open-next/worker.js
Static assets: .open-next/assets
EMAIL→ Send EmailASSETS→ Static assets
Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.
Runtime source · handlers, binding usage and workflow steps
Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.
Environment references: ctx.env.DB · ctx.env.EXAMPLE_GREETING
Environment references: ctx.env.DB · ctx.env.EXAMPLE_GREETING
- L68 · router.get("/")
- L88 · router.get("/moods")
- L107 · router.get("/:name")
- L23 · getPluginSettings calls (conditional paths may differ): svc.getPlugin
- L43 · getRandomMood calls (conditional paths may differ): repo.list, Math.floor, Math.random
- L62 · createExampleApiRoutes calls (conditional paths may differ): router.get, Promise.all, getRandomMood, Promise.resolve, getPluginSettings, c.json, trim, toISOString, repo.list, docs.map, c.req.param
- L68 · router.get("/")
- L88 · router.get("/moods")
- L107 · router.get("/:name")
- L23 · getPluginSettings calls (conditional paths may differ): svc.getPlugin
- L43 · getRandomMood calls (conditional paths may differ): repo.list, Math.floor, Math.random
- L62 · createExampleApiRoutes calls (conditional paths may differ): router.get, Promise.all, getRandomMood, Promise.resolve, getPluginSettings, c.json, trim, toISOString, repo.list, docs.map, c.req.param
- L8 · adminRoutes.use("*")
- L9 · adminRoutes.use("*")
- L48 · adminRoutes.get("/")
- L21 · esc calls (conditional paths may differ): replace, String
- L31 · jsonForScript calls (conditional paths may differ): replace, JSON.stringify
- L39 · fmtWeekDate calls (conditional paths may differ): parseInt
Environment references: c.env.DB
Build and deployment pipeline · 7 GitHub Actions workflows
Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.
Triggers: push, workflow_dispatch
deploy · no job dependencies declared
- Checkout code
actions/checkout@v4 - Setup Node.js
actions/setup-node@v4 - Install root dependencies (for SDK workspace)
npm ci - Build SDK
cd packages/sdk && npm run build - Install demo dependencies
cd demos/employee-directory && npm ci - Build demo
cd demos/employee-directory && npm run build - Deploy to Cloudflare Pages
npx wrangler pages deploy demos/employee-directory/dist --project-name sonicjs-sdk-demo --branch main
Triggers: push, workflow_dispatch
deploy · no job dependencies declared
- Checkout code
actions/checkout@v4 - Setup Node.js
actions/setup-node@v4 - Install dependencies
npm install - Build core package
npm run build:core - Type check stats
cd packages/stats && npx tsc --noEmit - Apply D1 migrations
cd packages/stats && npx wrangler d1 migrations apply DB --remote - Deploy stats to Cloudflare Workers
cd packages/stats && npx wrangler deploy
Triggers: push, workflow_dispatch
deploy · no job dependencies declared
- Checkout code
actions/checkout@v4 - Setup Node.js
actions/setup-node@v4 - Install dependencies
npm ci - Deploy WWW to Cloudflare
npm run deploy:www
Triggers: pull_request_target, push
authorize · no job dependencies declared
- Authorize
echo "✅ Workflow authorized" echo "Event: ${{ github.event_name }}" echo "Repository: ${{ github.repository }}" echo "PR Head Repo: ${{ github.event.pull_request.head.repo.full_name || 'N/A' }}" echo "Is Fork: ${{ github.event.pull_request.head.repo.full_name != github.repository }}"
test · after authorize
- Checkout code
actions/checkout@v4 - Setup Node.js
actions/setup-node@v4 - Install dependencies
npm install - Type check
npm run type-check - Run unit tests with coverage
npm run test:cov - Upload coverage to Codecov
codecov/codecov-action@v4 - Build core package
npm run build:core - Create fresh D1 database for PR
cd my-sonicjs-app BRANCH_NAME="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" # Limit to 43 chars to allow for "sonicjs-pr-" prefix (11 chars) = 54 total # Remove trailing hyphens to avoid invalid worker names SAFE_BRANCH=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9-]/-/g' | cut -c1-43 | sed 's/-*$//') DB_NAME="sonicjs-pr-${SAFE_BRANCH}" echo "Creating fresh D1 database: $DB_NAME" # Check if database already exists EXISTING_DB=$(npx wrangler d1 list --json | jq -r ".[] | select(.name == \"$DB_NAM…Condition: github.actor != 'dependabot[bot]' - Run D1 migrations
cd my-sonicjs-app echo "Applying migrations to database: ${{ steps.create-db.outputs.db_name }}" npx wrangler d1 migrations apply ${{ steps.create-db.outputs.db_name }} --remoteCondition: github.actor != 'dependabot[bot]' - Deploy to Cloudflare Workers Preview
cd my-sonicjs-app # Deploy to preview with unique name based on PR/branch # Cloudflare Workers has a 54 character limit for script names with previews # Prefix "sonicjs-pr-" is 11 chars, so max branch name is 43 chars BRANCH_NAME="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" # Limit to 43 chars to allow for "sonicjs-pr-" prefix (11 chars) = 54 total # Remove trailing hyphens to avoid invalid worker names SAFE_BRANCH=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9-]/-/g' | cut -c1-43 | sed 's/-*$//…Condition: github.actor != 'dependabot[bot]' - Wait for preview deployment
echo "Waiting for preview to be ready..." for i in {1..30}; do if curl -s -o /dev/null -w "%{http_code}" "${{ steps.deploy.outputs.preview_url }}" | grep -q "200\|301\|302"; then echo "Preview is ready!" exit 0 fi echo "Attempt $i/30: Preview not ready yet, waiting..." sleep 10 done echo "Preview failed to become ready" exit 1Condition: github.actor != 'dependabot[bot]' - Detect changed areas for E2E test selection
# Get changed files vs base branch (PR diff) or last commit (push) if [ "${{ github.event_name }}" = "pull_request_target" ]; then CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD 2>/dev/null || echo "") else CHANGED=$(git diff --name-only HEAD~1 2>/dev/null || echo "") fi echo "Changed files:" echo "$CHANGED" # Always include smoke TAGS="@smoke" # Strip test/spec files from consideration — only source changes should # trigger extra E2E feature tags; test file renames/updates…Condition: github.actor != 'dependabot[bot]' - Install Playwright browsers
npx playwright install --with-deps chromiumCondition: github.actor != 'dependabot[bot]' - Run E2E tests against preview
npx playwright test --config=tests/playwright.config.ts --grep "${{ steps.test-tags.outputs.grep_pattern }}"Condition: github.actor != 'dependabot[bot]' - Upload test results
actions/upload-artifact@v4Condition: always() && github.actor != 'dependabot[bot]' - Upload test videos
actions/upload-artifact@v4Condition: always() && github.actor != 'dependabot[bot]'
Triggers: release, workflow_dispatch
Build and Publish · no job dependencies declared
- Checkout repository
actions/checkout@v4 - Setup Node.js
actions/setup-node@v4 - Install dependencies
npm ci - Build core
npm run build:core - Build SDK
npm run build --workspace=@sonicjs-cms/sdk - Type check
npm run type-check - Publish @sonicjs-cms/core
if [ "${{ inputs.dry_run }}" = "true" ]; then npm publish --workspace=@sonicjs-cms/core --access public --dry-run else npm publish --workspace=@sonicjs-cms/core --access public fi - Publish @sonicjs-cms/sdk
if [ "${{ inputs.dry_run }}" = "true" ]; then npm publish --workspace=@sonicjs-cms/sdk --access public --dry-run else npm publish --workspace=@sonicjs-cms/sdk --access public fi - Publish create-sonicjs
if [ "${{ inputs.dry_run }}" = "true" ]; then npm publish --workspace=create-sonicjs --access public --dry-run else npm publish --workspace=create-sonicjs --access public fi - Tag beta dist-tag
VERSION=$(node -p "require('./packages/core/package.json').version") npm dist-tag add @sonicjs-cms/core@$VERSION beta || true npm dist-tag add create-sonicjs@$VERSION beta || trueCondition: inputs.dry_run != true
Triggers: release, workflow_dispatch
Announce Release · no job dependencies declared
- Checkout repository
actions/checkout@v4 - Setup Node.js
actions/setup-node@v4 - Install dependencies
npm ci - Run Release Announcements
ARGS="" if [ "${{ inputs.skip_discord }}" = "true" ]; then ARGS="$ARGS --skip-discord" fi if [ "${{ inputs.skip_twitter }}" = "true" ]; then ARGS="$ARGS --skip-twitter" fi if [ "${{ inputs.skip_www }}" = "true" ]; then ARGS="$ARGS --skip-www" fi node scripts/release/index.js $ARGS - Summary
echo "## Release Announcement Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Version:** ${{ github.event.release.tag_name || 'Manual trigger' }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Platforms" >> $GITHUB_STEP_SUMMARY echo "- Discord: ${{ inputs.skip_discord == 'true' && '⏭️ Skipped' || '✅ Attempted' }}" >> $GITHUB_STEP_SUMMARY echo "- Twitter: ${{ inputs.skip_twitter == 'true' && '⏭️ Skipped' || '✅ Attempted' }}" >> $GITHUB_STEP_SUMMARY ec…Condition: always()
Triggers: push, workflow_dispatch
update-test-count-badge · no job dependencies declared
- Checkout code
actions/checkout@v4 - Setup Node.js
actions/setup-node@v4 - Install dependencies
npm ci - Count tests
# Count unit tests (it/test calls in test files) UNIT_TESTS=$(grep -rE "(it|test)\(" --include="*.test.ts" --include="*.spec.ts" packages/ 2>/dev/null | wc -l | tr -d ' ') # Count e2e tests E2E_TESTS=$(grep -rE "(it|test)\(" --include="*.spec.ts" tests/e2e/ 2>/dev/null | wc -l | tr -d ' ') # Total tests TOTAL_TESTS=$((UNIT_TESTS + E2E_TESTS)) echo "Unit tests: $UNIT_TESTS" echo "E2E tests: $E2E_TESTS" echo "Total tests: $TOTAL_TESTS" echo "unit_tests=$UNIT_TESTS" >> $GITHUB_OUTPUT echo "e2e_tes… - Update test count gist
schneegans/dynamic-badges-action@v1.7.0
build: npm run build:core && npm run build --workspace=my-sonicjs-appbuild:www: npm run build --workspace=wwwbuild:core: npm run plugins:generate && npm run build --workspace=@sonicjs-cms/coredeploy: npm run deploy --workspace=my-sonicjs-appdeploy:www: npm run deploy --workspace=wwwdeploy:stats: npm run deploy:production --workspace=sonicjs-statsdeploy:demo: cd demos/employee-directory && npm run build && npx wrangler pages deploy dist --project-name sonicjs-sdk-demo --branch mainpublish:core: npm run build:core && npm publish --workspace=@sonicjs-cms/corepublish:create-app: npm publish --workspace=create-sonicjspublish:all: npm run publish:core && npm run publish:create-apprelease:patch: npm run version:patch && npm run publish:all && npm run release:announcerelease:minor: npm run version:minor && npm run publish:all && npm run release:announcerelease:major: npm run version:major && npm run publish:all && npm run release:announcerelease:draft: node scripts/release/generate-draft.jsrelease:announce: node scripts/release/index.jsrelease:announce:dry: node scripts/release/index.js --dry-run
build: tsc --noEmit && vite build
build: echo 'Worker build is handled by wrangler during deployment'deploy: wrangler deploy
prebuild: npm run generate:migrationsbuild: tsupprepublishOnly: npm run build
build: echo 'No build needed for CLI'
deploy: wrangler deploy
build: tsupprepublishOnly: npm run build
build: echo 'Worker build is handled by wrangler during deployment'deploy: wrangler deploydeploy:production: wrangler deploy --env production
build: tsup
prebuild: node scripts/generate-sections.mjsbuild: next build --webpackdeploy: opennextjs-cloudflare build && opennextjs-cloudflare deploy
Repository README
View original on GitHub ↗Full upstream document by @SonicJs-Org · README.md · snapshot 37df6af
SonicJS
The only headless CMS born on the edge. Zero cold starts. 15–50ms API responses. 300+ global locations. TypeScript-first. 100% MIT open source — every feature free, no Enterprise gate, ever.
📦 Get Started
npx create-sonicjs@latest my-app
$0 to start · No signup required · Runs anywhere SQLite runs
⚠️ Note: This repository is for developing the SonicJS core package. To build an application with SonicJS, use the command above to create a new project.
🐳 Self-Hosting with Docker
No Cloudflare account? Run SonicJS on any server with Docker and SQLite:
docker build -t sonicjs .
docker run -d --name sonicjs -p 3000:3000 \
-v $(pwd)/data:/app/data \
-e JWT_SECRET=$(openssl rand -base64 32) \
-e BETTER_AUTH_SECRET=$(openssl rand -base64 32) \
sonicjs
# Create the first admin user
docker exec sonicjs npm run reset
# → admin@sonicjs.com / sonicjs! (change after first login)
See the Self-Hosting guide for Docker Compose, Node.js, backup strategy, and production hardening.
🚀 Features
Edge Performance
- ⚡ Zero Cold Starts: 0–5ms cold start vs 500–2000ms on Node.js CMSs
- 🌍 Global by Default: 300+ edge locations — not one region, one continent
- 🚀 Sub-50ms APIs: 15–50ms API responses vs 1–4s with relations elsewhere
- 📈 Auto-Scaling: Cloudflare handles traffic spikes — no ops required
Developer Experience
- 🔧 Schema-as-Code: Define your content model in TypeScript; SonicJS generates the REST API and admin UI
- 📦
create-sonicjsCLI: From schema to global API in minutes - 🔥 Hot Reload: Fast local development with Wrangler
- 📱 Modern Stack: Hono.js, TypeScript, D1, R2, HTMX
AI-Native Content Layer
- 🤖 Native MCP Server: Auto-generated tools let Claude Code, Cursor, and VS Code read, create, and publish content
- ⚡ GraphQL API: Full GraphQL endpoint with GraphiQL playground — opt-in plugin, zero config
- 🔍 RAG-Powered Search: Semantic search with natural-language queries — zero extra infra
- 🛠 12 Specialized Claude Code Agents: Purpose-built agents for development (View all agents)
Content Management
- 📝 Rich Text Editor: TinyMCE integration with customizable toolbars
- 🎛️ Dynamic Fields: Text, number, date, boolean, select, media, slug, and more
- 📚 Content Versioning: Complete revision history with restore — free, no paywall
- ⏰ Content Scheduling: Publish/unpublish automation with date controls
- 🔄 Draft → Published Workflow: Role-based permissions throughout
- 💾 Auto-Save: Automatic content saving every 30 seconds
- 🛡️ XSS Protection: Comprehensive input validation and HTML escaping
No Lock-In, No Paywalls
- 100% MIT: Every feature in the core. No Growth tier. No Enterprise gate. Ever.
- No VC Clock: No license rug-pull. No infra lock-in.
- Runs Anywhere: Edge-native on Cloudflare Workers, or self-host on Docker/VPS — anywhere SQLite runs.
📊 How SonicJS Compares
| SonicJS | Strapi | Payload | |
|---|---|---|---|
| Edge-native | ✅ Yes | ❌ No | ❌ No |
| Cold start | 0–5ms | 500–2000ms | 500–2000ms |
| API response | 15–50ms | 1–4s | 1–4s |
| Global locations | 300+ | 1 region | 1 region |
| Version history | Free | Paywalled | Paywalled |
| SSO / Audit logs | Free | $99+/mo | $99+/mo |
| AI / MCP / GraphQL | Included | Upsold | Upsold |
| License | MIT (all features) | MIT (limited) | MIT (limited) |
SonicJS is the only production-ready CMS built specifically for edge computing.
🛠 Technology Stack
Core Framework
- Hono.js - Ultrafast web framework for Cloudflare Workers
- TypeScript - Strict type safety throughout
- HTMX - Enhanced HTML for dynamic interfaces
Cloudflare Services
- D1 - SQLite database at the edge
- R2 - Object storage for media
- Workers - Serverless compute runtime
- KV - Key-value storage for caching
- Images API - Image optimization and transformation
Development Tools
- Vitest - Fast unit testing
- Playwright - End-to-end testing
- Wrangler - Local development and deployment
- Drizzle ORM - Type-safe database queries
🏁 Quick Start
For Application Developers (Using SonicJS)
# Create a new SonicJS application
npx create-sonicjs@latest my-app
cd my-app
npm run dev
# Visit http://localhost:8787
Your app includes:
- ✅ SonicJS CMS pre-configured
- ✅ Database migrations ready
- ✅ Example content collections
- ✅ Admin interface at
/admin - ✅ Ready to deploy to Cloudflare
For Package Developers (Contributing to SonicJS)
# Clone this repository
git clone https://github.com/lane711/sonicjs.git
cd sonicjs
# Install dependencies
npm install
# Build the core package
npm run build:core
# Create a test app to validate changes
npx create-sonicjs@latest my-sonicjs-app
# Run tests
npm test
Setting Up a Fresh Database
# Create a fresh D1 database for your branch (run from project root)
npm run db:reset
This creates a new D1 database named sonicjs-worktree-<branch-name>, applies all migrations, and updates wrangler.toml.
Working with Database Migrations
Migrations live in packages/core/migrations/. Test apps reference them via npm workspace symlink.
From your test app directory (e.g., my-sonicjs-app/):
# Check migration status
wrangler d1 migrations list DB --local
# Apply pending migrations
wrangler d1 migrations apply DB --local
# Apply to production
wrangler d1 migrations apply DB --remote
Creating New Migrations:
SonicJS bundles migrations at build time (Workers can't access the filesystem at runtime).
- Create
packages/core/migrations/NNN_description.sql(useCREATE TABLE IF NOT EXISTSandINSERT OR IGNOREfor idempotency) - Regenerate bundle:
cd packages/core && npm run generate:migrations - Rebuild:
npm run build:core - Apply locally:
cd my-sonicjs-app && wrangler d1 migrations apply DB --local
Common Commands
npm run dev # Start dev server
npm run deploy # Deploy to Cloudflare
npm run db:migrate # Apply migrations
npm run db:studio # Open database studio
npm test # Run tests
📁 Project Structure
sonicjs/
├── packages/
│ ├── core/ # 📦 Main CMS package (@sonicjs-cms/core)
│ │ ├── src/
│ │ │ ├── routes/ # Route handlers (admin, API, auth)
│ │ │ ├── templates/ # HTML templates & components
│ │ │ ├── middleware/# Authentication & middleware
│ │ │ ├── utils/ # Utility functions
│ │ │ └── db/ # Database schemas & migrations
│ │ └── package.json
│ ├── templates/ # Template system package
│ └── scripts/ # Build scripts & generators
│
├── my-sonicjs-app/ # 🧪 Test application (gitignored)
│ # Created with: npx create-sonicjs@latest
│
├── www/ # 🌐 Marketing website
└── tests/e2e/ # End-to-end test suites
⚠️ This is NOT an application repository — it's for developing the @sonicjs-cms/core npm package.
🔧 Content Management
Creating Collections
Collections are TypeScript config objects registered at app startup — no database table required.
// src/collections/blog-posts.collection.ts
import type { CollectionConfig } from '@sonicjs-cms/core'
export default {
name: 'blog_post',
displayName: 'Blog Post',
slug: 'blog-posts',
description: 'Article content collection',
schema: {
type: 'object',
properties: {
title: { type: 'string', title: 'Title', required: true, maxLength: 200 },
content: { type: 'lexical', title: 'Content', required: true },
publishedAt: { type: 'datetime', title: 'Published Date' },
},
required: ['title', 'content'],
},
managed: true,
isActive: true,
} satisfies CollectionConfig
// src/index.ts — register before createSonicJSApp
import { registerCollections, createSonicJSApp } from '@sonicjs-cms/core'
import blogPostsCollection from './collections/blog-posts.collection'
registerCollections([blogPostsCollection])
export default createSonicJSApp({ plugins: { register: [] } })
Field Types
- string: Single-line text with validation
- lexical: Rich text editor
- number: Numeric input with min/max constraints
- boolean: Checkbox with custom labels
- datetime: Date/time picker
- select: Dropdown with single/multi-select
- slug: URL slug with auto-generation
- user: User reference picker
- media: File picker with preview
🌐 API Endpoints
Content Management
GET /admin/content/new?collection=id- Create new content formGET /admin/content/:id/edit- Edit content formPOST /admin/content/- Create contentPUT /admin/content/:id- Update content with versioningDELETE /admin/content/:id- Delete content
Advanced Features
POST /admin/content/preview- Preview before publishingPOST /admin/content/duplicate- Duplicate contentGET /admin/content/:id/versions- Version historyPOST /admin/content/:id/restore/:version- Restore version
Public API
GET /api/content- Get published content (paginated)GET /api/collections/:collection/content- Get content by collectionGET /api/collections- List all collections
GraphQL API
Opt-in plugin — add graphqlPlugin() to plugins.register:
import { graphqlPlugin, createSonicJSApp } from '@sonicjs-cms/core'
export default createSonicJSApp({
plugins: { register: [graphqlPlugin()] },
})
GET /graphql— GraphiQL playground (interactive browser IDE)POST /graphql— Execute queries and mutations (Bearer API key auth)
# Query published documents
query {
documents(typeId: "blog_posts", status: "published", limit: 10) {
items { id title slug status publishedAt data }
cursor
}
}
# Create a document (requires API key)
mutation {
createDocument(typeId: "blog_posts", title: "Hello", data: {}) {
id rootId isCurrentDraft
}
}
Supports: documents, document(id) queries · createDocument, updateDocument, publishDocument, unpublishDocument, deleteDocument mutations · JSON scalar · keyset pagination.
🚀 Deployment
# 1. Update wrangler.toml with your project settings
# 2. Create production database
wrangler d1 create my-app-db
# 3. Apply migrations
npm run db:migrate:prod
# 4. Deploy
npm run deploy
Your app will be live at https://your-app.workers.dev.
Environment Configuration
# wrangler.toml
name = "my-sonicjs-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "your-database-id"
[[r2_buckets]]
binding = "MEDIA_BUCKET"
bucket_name = "my-app-media"
🧪 Testing
npm test # Unit tests
npm run test:watch # Watch mode
npm run test:e2e # E2E tests
npm run test:e2e:ui # E2E with UI
🔌 Plugin Development
// src/plugins/my-plugin/index.ts
import type { Plugin, PluginContext } from '@sonicjs-cms/core'
export default {
name: 'my-plugin',
version: '1.0.0',
description: 'My custom plugin',
async activate(context: PluginContext) {
// Runs when the plugin is activated
},
async install(context: PluginContext) {
// Runs once on install — migrations, seed data, etc.
},
} satisfies Plugin
📚 Documentation
- sonicjs.com - Full documentation
- AI Agents - 12 specialized Claude Code agents
- Self-Hosting - Docker, VPS, production hardening
- Contributing - Contribution guidelines
❤️ Sponsor
SonicJS is 100% open source and free forever. If you find it useful, consider sponsoring:
100% of sponsorship funds go to marketing — spreading the word about SonicJS to grow the community.
SonicJS is a member of Open Source Collective, a 501(c)(3) nonprofit. Donations are tax-deductible for US contributors.
Thank You to Our Sponsors
📞 Support
Community (free)
Commercial Support (for teams that need an SLA)
Every feature stays 100% MIT and free — commercial support buys a guaranteed response time and a named contact, not features. If your company requires a support contract before adopting an open-source dependency, this is for you.
| Community | Priority Support | Enterprise | |
|---|---|---|---|
| All features (MIT, forever) | ✅ | ✅ | ✅ |
| GitHub Issues / Discord | ✅ | ✅ | ✅ |
| Guaranteed response time | — | Next business day | Same business day |
| Private support channel | — | Email + Slack | |
| Named support contact | — | — | ✅ |
| Security-issue priority triage | — | ✅ | ✅ |
| Upgrade & architecture guidance | — | ✅ | ✅ |
| License indemnification | — | — | ✅ |
| Price | Free | $499/mo | From $2,500/mo |
📄 Full terms, SLA, and procurement details: docs/commercial-support.md 📝 Request support: sonicjs.com/commercial-support
Built with ❤️ for the Cloudflare ecosystem · sonicjs.com
Frequently asked about SonicJS
What is SonicJS?+
SonicJS is a self-hosted Ghost/WordPress alternative built on the Cloudflare developer platform. A Workers-native CMS toolkit with content administration and APIs
What does SonicJS replace?+
SonicJS is listed as an alternative to Ghost, WordPress. Compare the features and tradeoffs before migrating.
What Cloudflare primitives does SonicJS use?+
SonicJS is built on D1, KV, R2, Workers.
How much does SonicJS cost to run?+
A new small starter can fit Workers, D1, KV and standard R2 free allowances. Use the starter configuration, not the committed CI database/bucket or stats service. Optional email services, image transformation and external AI tools have separate limits and costs. Run the documented app creator, create your own D1/KV/R2 resources and auth secrets, and replace default/example admin credentials before exposure. Keep Worker CPU and daily DB/KV writes within Free limits. The diagram covers the starter, excluding the maintainer stats collector and CI-only Email Service binding. Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested. Check current Cloudflare pricing before deploying.
Is SonicJS open source?+
The upstream repository declares the MIT license. Read its terms at https://raw.githubusercontent.com/SonicJs-Org/sonicjs/37df6afdcaead94beef8fd582ef7c2fe009bf831/LICENSE. Source code and contributor credit are available at https://github.com/SonicJs-Org/sonicjs.





Discussion · 0
sign in to comment →