Cloudsteading
SonicJS content management administration in the upstream demo

SonicJS

A Workers-native CMS toolkit with content administration and APIs

SonicJS is a self-hosted Ghost/WordPress alternative built on Cloudflare (D1, KV, R2, Workers). Free tier eligible within limits. Inspect the source and license in the linked repository.

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

@SonicJs-Org

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.
Check current pricing ↗
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.

Ghost logoGhost ↗

Managing structured content, media, editorial versions and APIs; no full WordPress plugin ecosystem, hosted Ghost business suite or automatic migration parity.

See supporting source ↗
WordPress logoWordPress ↗

Managing structured content, media, editorial versions and APIs; no full WordPress plugin ecosystem, hosted Ghost business suite or automatic migration parity.

See supporting source ↗
external SaaS target
varies
→ D1 + KV + R2
external SaaS target
varies
→ D1 + KV + R2

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 ↗
Public interface
Configured entry points3
my-sonicjs-app
my-sonicjs-app/wrangler.toml
sonicjs-stats
packages/stats/wrangler.toml
sonicjs-docs
www/wrangler.jsonc
↓
App
my-sonicjs-app
entry
Cloudflare Workers
Entrypoint: src/index.ts
sonicjs-stats
entry
Cloudflare Workers
Entrypoint: src/index.ts
sonicjs-docs
entry
Cloudflare Workers
Entrypoint: .open-next/worker.js
↓

Configuration and workflow sources

Reviewed commit 37df6afdcaea. Files were read as data; upstream applications and CI jobs were not executed.

Deployment configuration · 4 files
my-sonicjs-app/wrangler.toml ↗

Cloudflare Workers · compatibility 2025-05-05

my-sonicjs-app · default

Entrypoint: src/index.ts

  • DB → D1
  • CACHE_KV → KV
  • MEDIA_BUCKET → R2
  • EMAIL → Send Email
packages/create-app/templates/starter/wrangler.toml ↗

Cloudflare Workers · example/template, excluded from overview · compatibility 2024-09-23

my-sonicjs-app · default

Entrypoint: src/index.ts

  • DB → D1
  • CACHE_KV → KV
  • MEDIA_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.

    packages/stats/wrangler.toml ↗

    Cloudflare Workers · compatibility 2025-05-05

    sonicjs-stats · default

    Entrypoint: src/index.ts

    • DB → D1
    • CACHE_KV → KV

    sonicjs-stats-production · env.production

    Inherited from default: main, compatibility_date, compatibility_flags

    Entrypoint: src/index.ts

    • DB → D1
    • CACHE_KV → KV
    www/wrangler.jsonc ↗

    Cloudflare Workers · compatibility 2024-12-30

    sonicjs-docs · default

    Entrypoint: .open-next/worker.js

    Static assets: .open-next/assets

    • EMAIL → Send Email
    • ASSETS → 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.

    my-sonicjs-app/src/index.ts ↗
    • L65 · fetch handler exported
    • L66 · scheduled handler exported · calls createScheduledHandler
    packages/stats/src/index.ts ↗

      Environment references: c.env.DB

      my-sonicjs-app/src/plugins/example/index.ts ↗

        Environment references: ctx.env.DB · ctx.env.EXAMPLE_GREETING

        packages/create-app/templates/starter/src/plugins/example/index.ts ↗

          Environment references: ctx.env.DB · ctx.env.EXAMPLE_GREETING

          my-sonicjs-app/src/plugins/example/routes/api.ts ↗
          • 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
          my-sonicjs-app/src/plugins/example/routes/admin.ts ↗
          • L34 · router.get("/")
          • L31 · createExampleAdminRoutes calls (conditional paths may differ): router.get, c.get, escapeHtml, c.html, renderAdminLayoutCatalyst
          packages/create-app/templates/starter/src/plugins/example/routes/api.ts ↗
          • 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
          packages/create-app/templates/starter/src/plugins/example/routes/admin.ts ↗
          • L34 · router.get("/")
          • L31 · createExampleAdminRoutes calls (conditional paths may differ): router.get, c.get, escapeHtml, c.html, renderAdminLayoutCatalyst
          packages/stats/src/plugins/stats-dashboard/routes/admin.ts ↗
          • 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.

          Deploy SDK Demo · .github/workflows/deploy-demo.yml ↗

          Triggers: push, workflow_dispatch

          deploy · no job dependencies declared

          1. Checkout codeactions/checkout@v4
          2. Setup Node.jsactions/setup-node@v4
          3. Install root dependencies (for SDK workspace)npm ci
          4. Build SDKcd packages/sdk && npm run build
          5. Install demo dependenciescd demos/employee-directory && npm ci
          6. Build democd demos/employee-directory && npm run build
          7. Deploy to Cloudflare Pagesnpx wrangler pages deploy demos/employee-directory/dist --project-name sonicjs-sdk-demo --branch main
          Deploy Stats · .github/workflows/deploy-stats.yml ↗

          Triggers: push, workflow_dispatch

          deploy · no job dependencies declared

          1. Checkout codeactions/checkout@v4
          2. Setup Node.jsactions/setup-node@v4
          3. Install dependenciesnpm install
          4. Build core packagenpm run build:core
          5. Type check statscd packages/stats && npx tsc --noEmit
          6. Apply D1 migrationscd packages/stats && npx wrangler d1 migrations apply DB --remote
          7. Deploy stats to Cloudflare Workerscd packages/stats && npx wrangler deploy
          Deploy WWW · .github/workflows/deploy-www.yml ↗

          Triggers: push, workflow_dispatch

          deploy · no job dependencies declared

          1. Checkout codeactions/checkout@v4
          2. Setup Node.jsactions/setup-node@v4
          3. Install dependenciesnpm ci
          4. Deploy WWW to Cloudflarenpm run deploy:www
          PR Tests · .github/workflows/pr-tests.yml ↗

          Triggers: pull_request_target, push

          authorize · no job dependencies declared

          1. Authorizeecho "✅ 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

          1. Checkout codeactions/checkout@v4
          2. Setup Node.jsactions/setup-node@v4
          3. Install dependenciesnpm install
          4. Type checknpm run type-check
          5. Run unit tests with coveragenpm run test:cov
          6. Upload coverage to Codecovcodecov/codecov-action@v4
          7. Build core packagenpm run build:core
          8. Create fresh D1 database for PRcd 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]'
          9. Run D1 migrationscd 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]'
          10. Deploy to Cloudflare Workers Previewcd 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]'
          11. Wait for preview deploymentecho "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]'
          12. 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]'
          13. Install Playwright browsersnpx playwright install --with-deps chromiumCondition: github.actor != 'dependabot[bot]'
          14. Run E2E tests against previewnpx playwright test --config=tests/playwright.config.ts --grep "${{ steps.test-tags.outputs.grep_pattern }}"Condition: github.actor != 'dependabot[bot]'
          15. Upload test resultsactions/upload-artifact@v4Condition: always() && github.actor != 'dependabot[bot]'
          16. Upload test videosactions/upload-artifact@v4Condition: always() && github.actor != 'dependabot[bot]'
          Publish to npm · .github/workflows/publish.yml ↗

          Triggers: release, workflow_dispatch

          Build and Publish · no job dependencies declared

          1. Checkout repositoryactions/checkout@v4
          2. Setup Node.jsactions/setup-node@v4
          3. Install dependenciesnpm ci
          4. Build corenpm run build:core
          5. Build SDKnpm run build --workspace=@sonicjs-cms/sdk
          6. Type checknpm run type-check
          7. Publish @sonicjs-cms/coreif [ "${{ 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
          8. Publish @sonicjs-cms/sdkif [ "${{ 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
          9. Publish create-sonicjsif [ "${{ inputs.dry_run }}" = "true" ]; then npm publish --workspace=create-sonicjs --access public --dry-run else npm publish --workspace=create-sonicjs --access public fi
          10. Tag beta dist-tagVERSION=$(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
          Release Announcements · .github/workflows/release-announce.yml ↗

          Triggers: release, workflow_dispatch

          Announce Release · no job dependencies declared

          1. Checkout repositoryactions/checkout@v4
          2. Setup Node.jsactions/setup-node@v4
          3. Install dependenciesnpm ci
          4. Run Release AnnouncementsARGS="" 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
          5. Summaryecho "## 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()
          Update Badges · .github/workflows/update-badges.yml ↗

          Triggers: push, workflow_dispatch

          update-test-count-badge · no job dependencies declared

          1. Checkout codeactions/checkout@v4
          2. Setup Node.jsactions/setup-node@v4
          3. Install dependenciesnpm ci
          4. 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…
          5. Update test count gistschneegans/dynamic-badges-action@v1.7.0
          package.json ↗
          • build: npm run build:core && npm run build --workspace=my-sonicjs-app
          • build:www: npm run build --workspace=www
          • build:core: npm run plugins:generate && npm run build --workspace=@sonicjs-cms/core
          • deploy: npm run deploy --workspace=my-sonicjs-app
          • deploy:www: npm run deploy --workspace=www
          • deploy:stats: npm run deploy:production --workspace=sonicjs-stats
          • deploy:demo: cd demos/employee-directory && npm run build && npx wrangler pages deploy dist --project-name sonicjs-sdk-demo --branch main
          • publish:core: npm run build:core && npm publish --workspace=@sonicjs-cms/core
          • publish:create-app: npm publish --workspace=create-sonicjs
          • publish:all: npm run publish:core && npm run publish:create-app
          • release:patch: npm run version:patch && npm run publish:all && npm run release:announce
          • release:minor: npm run version:minor && npm run publish:all && npm run release:announce
          • release:major: npm run version:major && npm run publish:all && npm run release:announce
          • release:draft: node scripts/release/generate-draft.js
          • release:announce: node scripts/release/index.js
          • release:announce:dry: node scripts/release/index.js --dry-run
          demos/employee-directory/package.json ↗
          • build: tsc --noEmit && vite build
          my-sonicjs-app/package.json ↗
          • build: echo 'Worker build is handled by wrangler during deployment'
          • deploy: wrangler deploy
          packages/core/package.json ↗
          • prebuild: npm run generate:migrations
          • build: tsup
          • prepublishOnly: npm run build
          packages/create-app/package.json ↗
          • build: echo 'No build needed for CLI'
          packages/sdk/package.json ↗
          • build: tsup
          • prepublishOnly: npm run build
          packages/stats/package.json ↗
          • build: echo 'Worker build is handled by wrangler during deployment'
          • deploy: wrangler deploy
          • deploy:production: wrangler deploy --env production
          www/package.json ↗
          • prebuild: node scripts/generate-sections.mjs
          • build: next build --webpack
          • deploy: opennextjs-cloudflare build && opennextjs-cloudflare deploy

          Full upstream document by @SonicJs-Org · README.md · snapshot 37df6af

          SonicJS

          GitHub stars npm downloads GitHub commit activity Discord TypeScript License: MIT

          PR Tests codecov Tests npm version

          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.

          sonicjs.com

          📦 Get Started

          npx create-sonicjs@latest my-app
          

          $0 to start · No signup required · Runs anywhere SQLite runs

          Sponsor Open Collective

          Deploy to Cloudflare

          ⚠️ 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-sonicjs CLI: 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).

          1. Create packages/core/migrations/NNN_description.sql (use CREATE TABLE IF NOT EXISTS and INSERT OR IGNORE for idempotency)
          2. Regenerate bundle: cd packages/core && npm run generate:migrations
          3. Rebuild: npm run build:core
          4. 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 form
          • GET /admin/content/:id/edit - Edit content form
          • POST /admin/content/ - Create content
          • PUT /admin/content/:id - Update content with versioning
          • DELETE /admin/content/:id - Delete content

          Advanced Features

          • POST /admin/content/preview - Preview before publishing
          • POST /admin/content/duplicate - Duplicate content
          • GET /admin/content/:id/versions - Version history
          • POST /admin/content/:id/restore/:version - Restore version

          Public API

          • GET /api/content - Get published content (paginated)
          • GET /api/collections/:collection/content - Get content by collection
          • GET /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

          ❤️ Sponsor

          SonicJS is 100% open source and free forever. If you find it useful, consider sponsoring:

          Sponsor on GitHub Support on Open Collective

          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

          @mmcintosh @nickgraynews

          📞 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 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 →
          No comments yet — be the first.