jaimemartinez/wordjs: The CMS where a plugin can't take over your site: OS-sandboxed plugins, per-capability grants. SSR,

github.com · 2026-08-01 · by Long Nguyen ·github
The CMS where a plugin can't take over your site: OS-sandboxed plugins, per-capability grants. SSR, no PHP. - jaimemartinez/wordjs

jaimemartinez/wordjs

The CMS where a plugin can't take over your site: OS-sandboxed plugins, per-capability grants. SSR, no PHP. - jaimemartinez/wordjs

2 stars · 0 forks · 0 watchers

README

Build a website with drag-and-drop. Add plugins without trusting them. Try it in one command: npx create-wordjs@latest my-site No PHP, no MySQL, no build step — it downloads a ready-to-run bundle, starts a single process, and opens the setup wizard in your browser.

🧐 What is WordJS? WordJS is free, open-source software for building and running a website — a blog, a shop, a company site, a portfolio. If you've heard of WordPress, think "WordPress, rebuilt for today": you get a visual, drag-and-drop page builder, ready-made themes and plugins, and search-engine-friendly pages out of the box. The difference is what happens when you install a plugin.

On most website software, every add-on you install gets the keys to everything — your database, your passwords, your files. One bad or hacked plugin can quietly wreck the whole site. It's the single most common way websites get breached.

WordJS is built so that can't happen. 🔐 The big idea, in plain English Think about apps on your phone. A flashlight app can't read your bank messages unless you let it — each app is boxed off and has to ask permission for anything sensitive. WordJS does that for website plugins. Every plugin runs in its own locked box (a separate, operating-system-enforced process) and starts with nothing — no access to your files, your database, your visitors' data, or the internet. It only gets a capability when you, the admin, grant it — and you see exactly what each plugin is asking for. So a plugin that turns out to be buggy, greedy, or outright malicious stays in its box. It can't read your passwords, steal your config, or take the site down. That safety is enforced by the operating system itself — not by trusting the plugin to behave. Every other major CMS runs plugins with full trust. This is the one thing they can't copy without breaking their entire ecosystem — and it's what WordJS is built around.

Building a page by dragging blocks and editing text right on the canvas — the visual editor ships in the core, not as a paid add-on.

⚖️ How it compares

WordJS WordPress Ghost Strapi Payload

Plugins can't compromise the site ✅ OS-enforced sandbox ❌ full trust — no plugin runtime ❌ in-process ❌ in-process

You approve what each plugin can do ✅ per-plugin, default-deny ❌ — ❌ ❌

Drag-and-drop page builder in core ✅ built in partial (paid builders) ❌ ❌ headless ❌ headless

SEO-ready out of the box ✅ SSR + metadata + sitemap ✅ via plugins ✅ ❌ your job ❌ your job

Import your WordPress site ✅ built-in importer — ✅ ❌ ❌

What it takes to run 1 process, SQLite PHP + MySQL Node + MySQL Node + DB + frontend Node + DB

License MIT GPLv2 MIT MIT (paid tier) MIT (paid cloud)

Ecosystem maturity ⚠️ young, first-party only 60k+ plugins large large growing

The honest row is the last one: WordJS is young, and every plugin and theme in its marketplace today is first-party — there's no third-party author community yet. What it already has is the row at the top.

✨ What's inside

🎨 Build

Drag-and-drop visual editor (Puck) with 30 blocks & edit-in-place text Token-driven themes + a live customizer 64 themes & 31 plugins, one-click install Real SEO: server-rendered pages, sitemap, RSS, social cards Media library with automatic AVIF/WebP optimization

🧩 Extend — safely

Every plugin in its own OS process Ask-permission capabilities, default-deny Install-time code scanner + one-click sha256-verified marketplace WordPress-style hooks, filters & shortcodes Import from WordPress (posts, pages, comments, dates)

⚙️ Run

One process to deploy, SQLite by default Or split / multi-machine over secure mTLS SQLite · PostgreSQL · MySQL/MariaDB Automatic HTTPS (Let's Encrypt) 2FA, API tokens, webhooks, metrics

See the full feature list

Plugin system & security (the core differentiator)

OS-process plugin sandbox. Every plugin runs in a separate operating-system process (child_process.fork) — its own memory and event loop — so a crash, memory blow-up, or exploit is contained by the kernel and never takes down the host. Plugins reach the core only through a permission-checked wordjs bridge (structured-clone RPC, no live host references, an exact method allow-list). Install-time code scanner. Before a plugin can activate, its JavaScript is parsed and walked for dangerous constructs (eval, exec/spawn, dynamic require, sensitive modules, forbidden process access). It's fail-closed (an unparseable file is rejected) and runs on every plugin — no exemptions. Ask-permission model (default-deny). A plugin's manifest requests scoped capabilities (database, filesystem, settings, users:read, email:provider, notifications:provider, network, …); an admin grants each one per plugin. A call works only if the capability is BOTH declared AND granted. First-party plugins are pre-granted what they declare, but hold no special privilege — same sandbox, same rules. Kernel hardening on Linux (default-on). Each plugin child also runs as an unprivileged user with dropped capabilities, no-new-privs, PID/IPC/UTS namespaces, a read-only filesystem, and a seccomp syscall denylist. A plugin without the network grant is dropped into an empty network namespace, so it can't reach the internet or your internal services even at the kernel level. (Probe-gated, with a clean fallback where unavailable; Windows gets a Job Object memory cap; macOS relies on process isolation.) Per-plugin data isolation. Each plugin gets only its own wjp_<slug>_ database tables and a secret-scrubbed view of config — core users/options/sessions and secrets are unreachable. On PostgreSQL/MySQL the database itself enforces this via a per-plugin low-privilege role/user. Plugin & theme marketplace. A curated catalog of 31 first-party plugins and 64 first-party themes with one-click, sha256-verified installs through the same hardened, sandboxed pipeline as manual uploads. Sources are admin-configurable (point it at any HTTPS catalog); all catalog items are first-party today, with third-party submissions on the roadmap.

Authoring & content

Visual builder (Puck) — drag-and-drop editing, in-place rich text (bold/italic/links, a color picker with eyedropper, font family from your installed fonts, size, alignment), a searchable block inserter with reusable section patterns, and a device preview (desktop/tablet/mobile) that renders the canvas at true device width. Real server-side rendering — public pages are React Server Components, so crawlers and first paint get the real content, per-page metadata (generateMetadata), OpenGraph/Twitter cards, JSON-LD, real 404s, and a no-JS search form. SEO basics — semantic HTML, sitemap.xml, robots.txt, and an RSS feed. Themes — a shared, token-driven CSS framework (--wjs-* design tokens) that auto-styles pages, plus a live customizer at /admin/themes/customize. A theme's optional server-side functions.js runs in the same sandbox as plugins. Hooks, filters & shortcodes (WordPress-style), dynamic roles & permissions (database-driven), and UI in español / english / português. WordPress (WXR) importer — upload a .xml export to bring over authors, categories/tags, posts/pages (with meta, term relationships, and threaded comments), preserving publish dates. Idempotent and re-runnable. Backups — full-site export/restore with retention pruning, plus privacy-first analytics (no cookies, daily-rotated salted IP hashing) and a built-in cron.

Operations

Gateway — a clustered reverse proxy (one worker per CPU, auto-respawn), health-check eviction, an mTLS internal control channel, and SSE-aware proxying. It also acts as the cluster CA for multi-machine deployments. Automatic TLS — Let's Encrypt via HTTP-01 or DNS-01, manual upload, or a self-signed dev fallback; renewal works in single-process mode too. Databases — SQLite (default), PostgreSQL, or MySQL 8+/MariaDB, all behind one interface with a migration system to move between them. Headless-friendly — scoped API tokens (Authorization: Bearer wjt_…, per-resource scopes), HMAC-signed SSRF-safe webhooks, and token-gated Prometheus metrics. Native mail server — an optional, fully sandboxed first-party plugin: inbound SMTP, direct-MX delivery, and DKIM signing.

🚀 Deploy your way The same codebase runs three ways — switch anytime, no data migration:

Mode What it is Best for

🧱 Monolith Everything in one process, one port The simplest deploy — a small VM or container

🔀 Split (default) Gateway + backend + frontend on one host Scaling services independently

🌐 Separate The three services on different machines, joined over mTLS Larger, distributed setups

The whole thing, one process:

npx create-wordjs@latest my-site # then open the browser wizard

Or, from a source checkout:

npm run install:all npm run dev:mono # monolith on http://localhost:3000 npm run dev # or the 3-service split

Architecture & run-mode details

graph TD User((User)) --> Gateway[Gateway :3000] Gateway --> Frontend[Next.js Frontend :3001] Gateway --> Backend[TypeScript Backend :4000] Backend --> DB[(SQLite / PostgreSQL / MySQL)] Backend --> Plugins{Isolated Plugins}

Loading

Gateway — clustered Node/Express reverse proxy: routing, load-balancing, health-check eviction, mTLS internal channel, SSE-aware proxying, and the cluster CA. Backend — the core engine (content, users, roles, the plugin/theme system, mail, certificates, and the sandbox). TypeScript, compiled to JS for production. Frontend — the public site and the Next.js admin, including the Puck visual builder.

Monolith runs the backend (with its isolated plugins) and the Next.js frontend in one process on :3000 — no loopback proxy, no cluster — while still handling TLS, security headers, compression, and SEO rewrites locally. Split runs them as three cooperating processes behind the gateway. Separate spreads those three across machines: create-wordjs gateway --host <ip> makes the first box the cluster CA and prints ready-to-paste join commands with single-use, role-bound tokens; each other box enrolls over mutual TLS with no cert hand-copying. See the Separate-mode guide for the walkthrough.

📚 Documentation

🏗️ Architecture 🛠️ Development & Build ⌨️ CLI & Setup

🔌 Plugin Tutorial 🧩 Plugins Reference 🧱 Plugin Isolation

🎨 Themes 🖥️ Frontend 🛰️ Gateway

🗄️ Database 🗃️ Plugin Database Access ✉️ Mail Server

🚀 Deployment 🧩 Separate Mode 🌐 Multi-Node Ops

📥 Import from WordPress 🔔 Notifications 📡 REST API

🔐 Security Policy 🛡️ Security Architecture 🧭 Product Positioning

Live API reference (Swagger/OpenAPI) is served at http://localhost:4000/api/v1/docs (admin only).

🧭 Project status

Beta · pre-production · primarily solo-maintained. WordJS recently completed rounds of security hardening (fixing a CSRF bypass, committed secrets, and XSS sinks, among others). The plugin sandbox has had multiple internal red-team passes — but there is no independent third-party audit yet. An external audit is strongly recommended before any production or internet-facing deployment, and the residual risks are documented plainly in SECURITY.md and POSITIONING.md. The whole ecosystem is first-party: the marketplace ships 31 plugins and 64 themes, but there's no third-party author community or public review pipeline yet. Use it to build, learn, and experiment — do your own review before trusting it with real data or real users.

🔒 Security The plugin sandbox is designed to fail closed, with defense-in-depth layers (see What's inside and Security Architecture). Beyond plugins, WordJS ships TOTP two-factor auth with an admin-enforced MFA-by-role policy, JWT revocation, per-(IP + account) login throttling, constant-time secret comparison, origin-pinned CSRF protection, and SSRF-safe outgoing webhooks. Before exposing WordJS to the internet: have it independently audited, rotate every secret, and set a strong gatewaySecret (the gateway refuses management endpoints while the default is in place). Found a vulnerability? Please follow the disclosure process in SECURITY.md.

Tech stack

Runtime: Node.js (≥ 20.9; Node 20/22 LTS recommended) Backend: TypeScript — compiled with tsc for production, ts-node for dev Frontend: Next.js (React 19) Editor: Puck · Styling: vanilla CSS + Tailwind Communication: REST + JWT + scoped API tokens + WebSockets/SSE Gateway: Express + Node cluster, http-proxy, mTLS internal channel Sandbox: child_process OS-process isolation + acorn code scanning + runtime require proxies + layered memory caps (cgroup / Windows Job Object / RSS-poll) TLS: acme-client (Let's Encrypt HTTP-01 / DNS-01) Database: SQLite (better-sqlite3 / sql.js), PostgreSQL (pg), or MySQL/MariaDB (mysql2) Tooling: ESLint + Prettier, node:test, GitHub Actions CI

Run from source & backend scripts

npm run install:all # install root + sub-packages npm run dev # split: gateway + backend + frontend npm run dev:mono # or everything in one process, one port Public site at http://localhost:3000, admin at /admin. First-run setup (database + admin user) runs in the browser wizard, or via npm run setup. In production the backend is compiled (tsc → dist/) and run as plain JS. From backend/:

Script Purpose

npm run build Compile to dist/ (strict config)

npm run typecheck Strict type-check (tsc --noEmit), enforced in CI

npm test Test suite (node --test)

npm run lint / format ESLint / Prettier

CI (.github/workflows/ci.yml, Node 22) runs the strict type-check, the compiled build, a dependency license gate, unit tests, integration tests against real postgres:16 + redis:7 containers, and the frontend lint/type-check/tests/build.

🔮 Roadmap

🧩 Third-party marketplace — open the catalog to community authors, where "sandboxed & reviewed" is a verifiable trust badge. ☁️ Media CDN — S3-compatible object storage. 🌐 Multi-site — manage multiple domains from one install. 🛡️ More kernel hardening — building on the default-on Linux sandbox layer.

📜 License MIT — see LICENSE. Third-party dependency notices are in THIRD-PARTY-NOTICES.md.

Built with care by a small, independent project. Contributions, bug reports, and security disclosures are welcome.

source

github.com · 9 images · 156 links

Comments (0)

No comments yet