zenixjs
Initial ZyroJS SSR CMS application on ZeruxJS with explicit host security.
Initial CMS architecture#
Zenix uses ZyroJS for server-rendered UI, ZeruxJS for server routing/response ownership and @zenixjs/core for authorized SQLite content/revision storage. It is not a second frontend runtime or complete CMS. Root metadata now reports initial-cms; the existing version/module format remains compatible. The separate create-zenix-js generator emits a SQLite integration scaffold; host authentication/CSRF must still be implemented.
zenixjs/application installs explicit login/setup/dashboard/editor/save/publish/ preview/public-page/logout routes under a configured base path. Native forms work without JavaScript. Host persistent authentication, session cookies, CSRF/admission policy and private setup secrets are mandatory; no administrator password is bundled. The package's examples/application.ts shows public imports and plugin disposal.
Setup and editorial boundaries#
The durable setup store claims a tenant/site once before writes. Interrupted claims require the trusted, non-HTTP operator recovery procedure; no automatic timeout reset or client role assertion unlocks setup. Core permissions remain authoritative.
The one title/body editor saves optimistic draft revisions; publication is a separate authorized POST. Public pages only read the published pointer through a fixed host read-only identity. Private previews require fresh auth plus an expiring session-bound random nonce and are no-store/noindex. Text is rendered inert through ZyroJS; no untrusted theme/plugin code or arbitrary HTML is executed.
Validation scope#
Fixtures use real SQLite files, synthetic host identity callbacks and an ephemeral HTTP request through framework Fetch registration. They cover concurrent setup, recovery, draft/public separation, stored XSS, CSRF/origin denial and cleanup. jsdom checks authored labels, not browser or screen-reader conformance. Live identity, proxy/TLS/cookie deployment, browser keyboard and backup qualification remain host prerequisites. See the package README for exact routes, limits and ownership.
Explicit owners and inert metadata#
import { version, status, capabilities } from 'zenixjs';
console.log({ version, status, capabilities });
// Importing metadata opens no database, session service or HTTP listener.Use the package's public examples/application.ts to install the application from a trusted host plugin. Its database/core/application cleanup order is explicit; private authentication and CSRF implementations must be supplied separately. createZenixApplication owns request admission, readers, previews and its renderer. createSqliteSetupStore borrows the qualified connector and has no invented close method. Close the application before shutting down the borrowed core/store/database.
The setup claim and CMS content writes are separate transactions. A failed or lost response after claiming setup does not authorize automatic retry or claim deletion. Use fresh operator proof and the original claim's recovery procedure. A preview nonce still requires fresh session/core authorization, expires locally and does not recall bytes already delivered. Keep private query tokens out of host access logs.
Authorization and parsing are separate: strict form schemas require the authored fields, but only host/session/core policy authorizes a write. Rendered text and fixed response messages do not include submitted passwords, setup secrets or private causes. Metadata flags describe the current baseline, not a certification of production identity, browser accessibility, distributed atomicity or complete CMS capability.
Public entry points
Manifest reference for zenixjs@0.0.4. Each target describes a published entry, not automatic framework registration.
| Import | Target |
|---|---|
zenixjs | {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
} |
zenixjs/application | {
"types": "./dist/application.d.mts",
"browser": null,
"import": "./dist/application.mjs"
} |
zenixjs/setup-store | {
"types": "./dist/setup-store.d.mts",
"browser": null,
"import": "./dist/setup-store.mjs"
} |
Runtime requirements and dependencies
{
"engines": {},
"dependencies": {
"@zenixjs/core": "0.0.4",
"@zeruxjs/accessibility": "0.0.4",
"@zeruxjs/db-sqlite": "0.0.4",
"@zeruxjs/security": "0.0.4",
"@zeruxjs/validator": "0.0.4",
"zeruxjs": "0.0.4",
"zyrojs": "0.0.4"
},
"peerDependencies": {
"@zeruxjs/asset-manager": "0.0.4"
},
"bin": {}
}Full package README
zenixjs#
Initial opt-in CMS application: ZyroJS UI + ZeruxJS server runtime + Zenix domain core. zenixjs/application renders actual ZyroJS static HTML through the ZeruxJS renderer registry and installs explicit framework Fetch routes. It does not implement another server, authentication protocol or frontend runtime. Native forms work with JavaScript disabled; hydration, browser bundles, dashboards beyond this one editor, plugin discovery and complete CMS parity are not supplied.
Root metadata keeps version = "0.0.4"; status is now "initial-cms" and capabilities lists the delivered scope. The metadata entry retains its CommonJS format; new Node-only application/setup-store entries use ESM. Importing them opens no connections or listeners. No automatic config key is added; the separate generator requires explicit host policy integration.
Explicit startup#
Public-import example creates a file-backed core and setup store, installs routes into a trusted ZeruxJS plugin API and registers cleanup. All identity, secrets, CSRF and provider settings must be private host configuration, not public build manifests or request-supplied objects. Supply these contracts:
- A borrowed
@zenixjs/core/cmsinstance and fixed tenant/site/entry ID. ItsresolvePrincipalverifies fresh host session proof and actual auth-owned permissions. - A durable
zenix-setup-cas-v1store.createSqliteSetupStoreuses the existing native synchronous SQLite transaction facade on a qualified file-backed borrowed connector.migrate:trueis explicit startup approval for its v1 table; otherwise an existing compatible table is required. Never use a volatile store in production. - Exact
origin, explicitbasePath(for example/cms, or/for root mounting), and a private least-privilegepublicPrincipalaccepted by the core for read-only published content in that site. Public requests cannot select this identity. - Host
authenticate,loginandlogoutcallbacks using persistent sessions, plus an action-specificauthorizecallback. No default account/password exists. - Host CSRF
issue/verify, login/setup admission limits, a constant-timeverifySetupSecret, and fresh privilegedauthorizeRecoveryproof verification.
authenticate returns {principal,key} only after verification; key is a nonsecret opaque session binding, not a raw cookie, email or client role. principal is passed to the core and revalidated there. login checks the submitted credentials with the configured host identity system; it returns one reviewed session cookie, never a browser role claim. logout returns an expiry cookie. Production cookies must use appropriate Secure/HttpOnly/SameSite/path policy. CSRF issuance can return a cookie for anonymous login/setup sessions; verification must bind the token to the actual request/session. The fixture's synthetic secrets and always-allowed callbacks are not production auth implementations.
This application does not create identity-provider accounts: the host provisions a privileged setup operator separately and supplies its login/session system, such as @zeruxjs/auth persistence. Roles alone are never authentication. Host rate limits must bound credential and setup-secret attempts across replicas. Request callbacks receive cancellation through the Request signal and must own dependency deadlines. No live identity provider or production session adapter is bundled or qualified.
Routes and editorial workflow#
For base /cms, installed exact routes are:
| Path | Behavior |
|---|---|
/cms/login | GET/HEAD host-backed login form; POST checks Origin, anonymous CSRF and admission |
/cms/setup | Authenticated privileged GET/POST setup; host secret, CSRF, Origin and durable unique claim |
/cms/admin | Authorized dashboard for the one configured entry |
/cms/editor | Authorized title/body editor, current optimistic revision and saved-preview action |
/cms/save | POST validated title/body draft with expected head; no remote executable functions |
/cms/publish | POST separate authorization and exact expected saved revision |
/cms/preview | POST creates private saved-revision preview; GET/HEAD requires fresh auth and session-bound nonce |
/cms/page | GET/HEAD only the published revision; drafts are never a fallback |
/cms/logout | CSRF/Origin-protected POST host session termination |
The initial immutable content type is article@1 with required title (200 characters) and plain-text body (8192 characters). This is one editor/theme, not arbitrary schema or rich HTML editing. Core permissions gate every read/write/publication. Draft saves retain the prior published pointer; stale expected revisions return 409. All state changes are POST. Body input is bounded URL-encoded form data, with duplicate/unknown fields and file/multipart uploads rejected. Invalid input/denials use fixed messages, not echoed passwords, secrets, database paths or private causes.
The basic theme renders only saved title/body as escaped ZyroJS text. Optional theme.brand pins an existing asset-manager BrandVersion reference (version/asset/ hash), and exposes only its version marker. It does not infer authorship, load brand notes/HTML, edit brand rules, fetch logos or grant binary access. Theme names remain inert. Media upload/editor controls are not supplied; core media references continue to require the core's host files ownership/version/hash authorization callback. There is no arbitrary theme/plugin code loading from content or browser settings; unknown application configuration rejects.
One-time setup and trusted recovery#
Setup initializes the configured site and article@1 using the already authenticated, privileged host operator. Before any content write it atomically claims the unique hashed tenant/site key. The durable record contains only scope/claim references, state, revision and time, never setup secrets or credentials. Parallel setup requests cannot both claim; a completed setup cannot run again, including after process restart. No application password or public administrator role is created by setup.
The SQLite implementation uses an immediate transaction for create-if-absent and claim/revision-bound completion. Core writes and setup completion are separate transactions. A crash or failure after claim leaves state claimed: retries and GET setup return conflict, not a guessed expired lock or automatic takeover.
Only the non-HTTP recoverSetup(session,evidence) API can continue. The host must verify fresh operator privilege and proof bound to that claim in authorizeRecovery, record its own restricted audit evidence and reconcile dependencies. Recovery checks existing site/schema against the expected configuration, creates only missing pieces and conditionally completes the original claim. Incompatible records require trusted manual investigation; no browser role string, old setup secret, timer or public route resets the record. Do not delete a live claim to retry after an uncertain outcome. The host owns secret rotation, filesystem permissions, storage retention and backups. Hashing references does not anonymize operator/tenant records.
Privacy, origin and output boundaries#
All responses use the existing production security-policy composer, no-store, no-referrer and private cache headers. Administrative/login/setup/preview responses also carry noindex/nofollow/noarchive. No inline scripts/styles are needed, no session or form state is serialized into a hydration payload, and CSP is never weakened from request headers. Published responses omit noindex but remain no-store in this initial implementation. There is no cache invalidation or CDN privacy promise.
The framework Fetch adapter intentionally gives handlers a neutral URL. install therefore checks the direct Host against the configured exact authority, then binds only that reviewed origin; forwarded identity/host/proto headers never establish trust. Every POST additionally requires the exact Origin and host CSRF validation. Reverse proxies must preserve/review that direct authority themselves; arbitrary cross-origin admin hosting and wildcard CORS are unavailable. Calling handle directly requires the embedding host to enforce the same transport boundary.
Previews are random 256-bit nonces bound to a verified session key and an immutable saved revision, expire after 60 seconds, and require fresh preview authorization and core access on every request. There are at most 128 live previews per application. A nonce alone or a public entry slug is insufficient. Preview state is private/process local and cleared on teardown/reload; it is not a public bearer link. Hosts must redact preview query values from external request logs. Expiry/revocation cannot recall bytes already delivered. Core/domain authorization remains the authority for storage access.
Accessibility, resource ownership and checks#
UI controls have authored labels/native form semantics, a skip link, headings, explicit buttons and bounded plain text. Accessibility prop contracts are used for control names and DOM fixtures validate the actual labels/relationships. No overlay, positive tabindex, manufactured label or conformance certification is claimed. Normal browser keyboard/form behavior is retained; live keyboard/screen-reader checks remain outstanding. Current tests use jsdom emulation, not a real browser.
Each application owns its renderer registry, HTTP reader slots, abort scope and preview map. Maximum pending handlers/readers is eight; body size is 20 KB, fields six, field bytes 10 KB, input deadline three seconds, rendered HTML 128 KiB and renderer deadline five seconds. Fully rendered HTML is sent through the framework Fetch response owner, which owns downstream streaming/backpressure/disconnect cleanup; this is not progressive component streaming or hydration. Host callbacks/core writes cooperate with cancellation; abort never proves rollback or permits automatic mutation replay.
Await application close before core/store/database shutdown. It rejects admission, cancels owned request/reader scopes, clears preview capabilities, drains actual handlers and closes the renderer. Borrowed CMS/auth/setup stores stay host-owned. No app/server is started, stopped or restarted on import. The public example registers this order through framework plugin disposal.
Build with npm run build --workspace=zenixjs; focused checks: node --experimental-strip-types --test packages/zenixjs/test/application.test.ts. Native SQLite fixtures check concurrent/repeated setup, privileged recovery, denied editor/CSRF/origin, duplicate fields, draft privacy, stored XSS, publication, labels, cleanup and an actual ephemeral HTTP request through the ZeruxJS Fetch registration. They use synthetic accounts/data. Production authentication, durable storage/backup qualification, real proxy/TLS/cookie integration, browser keyboard use and assistive technology remain host prerequisites.
Project generator and license#
create-zenix-js generates a JavaScript SQLite integration scaffold with explicit migration and unconfigured-host startup diagnostics. It does not supply a production identity provider or default credentials. Installing the application alone does not run generation. License: MIT OR Apache-2.0.
Explicit owners and inert metadata#
import { version, status, capabilities } from 'zenixjs';
console.log({ version, status, capabilities });
// Importing metadata opens no database, session service or HTTP listener.Use the package's public examples/application.ts to install the application from a trusted host plugin. Its database/core/application cleanup order is explicit; private authentication and CSRF implementations must be supplied separately. createZenixApplication owns request admission, readers, previews and its renderer. createSqliteSetupStore borrows the qualified connector and has no invented close method. Close the application before shutting down the borrowed core/store/database.
The setup claim and CMS content writes are separate transactions. A failed or lost response after claiming setup does not authorize automatic retry or claim deletion. Use fresh operator proof and the original claim's recovery procedure. A preview nonce still requires fresh session/core authorization, expires locally and does not recall bytes already delivered. Keep private query tokens out of host access logs.
Authorization and parsing are separate: strict form schemas require the authored fields, but only host/session/core policy authorizes a write. Rendered text and fixed response messages do not include submitted passwords, setup secrets or private causes. Metadata flags describe the current baseline, not a certification of production identity, browser accessibility, distributed atomicity or complete CMS capability.