diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index e8a63a0..fb3452d 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -16,7 +16,7 @@ body:
attributes:
label: Version or commit
description: Provide the package version, image tag, or commit SHA.
- placeholder: 0.1.0
+ placeholder: 0.2.0
validations:
required: true
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
index 683cfb1..e96df39 100644
--- a/.github/workflows/README.md
+++ b/.github/workflows/README.md
@@ -28,11 +28,11 @@ environment.
24, and 26. It also runs dependency review, dependency audit, full-history secret scanning and generates an
SPDX JSON source SBOM. Dependency review requires a public repository or the corresponding GitHub security
entitlement.
-- `alpha-acceptance.yml` verifies the seven synthetic migration goldens and generates one representative Node,
- Python, PHP and Go project. It installs each selected toolchain, verifies `monox.lock`, builds and tests the
- workspaces, then starts the Node, Python and Laravel APIs and probes their health endpoints. A separate job
- exercises the built-in local Cloudapter through doctor, deploy, an explicit health probe, status and
- owned-service destroy. This is representative coverage, not the complete catalog matrix.
+- `release-acceptance.yml` verifies the seven synthetic migration goldens and generates one representative
+ Node, Python, PHP and Go project. It installs each selected toolchain, verifies `monox.lock`, builds and
+ tests the workspaces, then starts the Node, Python and Laravel APIs and probes their health endpoints. A
+ separate job exercises the built-in local Cloudapter through doctor, deploy, an explicit health probe,
+ status and owned-service destroy. This is representative coverage, not the complete catalog matrix.
- `catalog-matrix.yml` is scheduled weekly and can be started manually. It generates every one of the 24
bundled workspace recipes in isolation, installs the selected toolchain, verifies the lock, runs tests and
builds. It then runs the shared acceptance helper, which probes services, checks workers, waits for jobs and
@@ -52,10 +52,11 @@ environment.
continue to block publication.
`create-monox` exists on npm under the `mosharush` maintainer account. Version 0.1.0 was the first release
-from this public repository and upgraded the historical 0.0.5 package in place. The public `latest` tag is
-currently 0.1.2; the 0.2 source stays on an alpha prerelease until the release gates are complete. Releases
-keep the `https://github.com/Mosharush/MonoX` metadata, include the MIT license in the tarball, and use
-trusted publishing with registry provenance.
+from this public repository and upgraded the historical 0.0.5 package in place. Stable releases publish under
+`latest`; prereleases publish under `next`. Verification waits for registry propagation, checks the expected
+dist-tag, signed provenance and exact Git commit, then runs a clean-cache consumer. Releases keep the
+`https://github.com/Mosharush/MonoX` metadata, include the MIT license in the tarball, and use trusted
+publishing without a long-lived npm token.
CodeQL uses GitHub default setup with JavaScript and TypeScript analysis. Keep that repository-level setup
instead of adding a duplicate advanced workflow.
diff --git a/.github/workflows/npm-release-verification.yml b/.github/workflows/npm-release-verification.yml
index 27223e8..324b408 100644
--- a/.github/workflows/npm-release-verification.yml
+++ b/.github/workflows/npm-release-verification.yml
@@ -93,6 +93,7 @@ jobs:
PACKAGE_VERSION: ${{ steps.release.outputs.package_version }}
RELEASE_TAG: ${{ inputs.release_tag }}
SOURCE_SHA: ${{ steps.release.outputs.source_sha }}
+ VERIFY_DIST_TAG: ${{ github.event_name != 'workflow_dispatch' }}
run: |
set -euo pipefail
@@ -145,6 +146,43 @@ jobs:
sleep 10
done
+ if [[ "${VERIFY_DIST_TAG}" == "true" ]]; then
+ if [[ "${PACKAGE_VERSION}" == *-* ]]; then
+ expected_dist_tag="next"
+ else
+ expected_dist_tag="latest"
+ fi
+ for attempt in {1..18}; do
+ tag_cache="$(mktemp -d)"
+ tag_record="$(
+ NPM_CONFIG_CACHE="${tag_cache}" \
+ npx --yes "npm@${NPM_CLI_VERSION}" view \
+ create-monox dist-tags \
+ --json \
+ --workspaces=false 2>/dev/null || true
+ )"
+ if DIST_TAG_RECORD="${tag_record}" \
+ EXPECTED_DIST_TAG="${expected_dist_tag}" \
+ PACKAGE_VERSION="${PACKAGE_VERSION}" \
+ node --input-type=module -e "
+ try {
+ const raw = JSON.parse(process.env.DIST_TAG_RECORD);
+ const tags = Array.isArray(raw) ? raw[0] : raw;
+ process.exit(tags?.[process.env.EXPECTED_DIST_TAG] === process.env.PACKAGE_VERSION ? 0 : 1);
+ } catch {
+ process.exit(1);
+ }
+ "; then
+ break
+ fi
+ if [[ "${attempt}" -eq 18 ]]; then
+ echo "npm dist-tag ${expected_dist_tag} does not point to ${PACKAGE_VERSION}." >&2
+ exit 1
+ fi
+ sleep 10
+ done
+ fi
+
package_integrity="$(
REGISTRY_RECORD="${registry_record}" node --input-type=module -e \
"const raw = JSON.parse(process.env.REGISTRY_RECORD); const data = Array.isArray(raw) ? raw[0] : raw; process.stdout.write(data?.['dist.integrity'] ?? data?.dist?.integrity ?? '')"
diff --git a/.github/workflows/alpha-acceptance.yml b/.github/workflows/release-acceptance.yml
similarity index 98%
rename from .github/workflows/alpha-acceptance.yml
rename to .github/workflows/release-acceptance.yml
index 60fcab0..4410f1b 100644
--- a/.github/workflows/alpha-acceptance.yml
+++ b/.github/workflows/release-acceptance.yml
@@ -1,4 +1,4 @@
-name: 0.2 alpha acceptance
+name: 0.2 release acceptance
on:
push:
@@ -11,7 +11,7 @@ permissions:
contents: read
concurrency:
- group: alpha-acceptance-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ group: release-acceptance-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1aa987f..7624691 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,13 @@ All notable changes will be documented here.
## Unreleased
+## 0.2.0 - 2026-07-26
+
+Version 0.2.0 stabilizes deterministic project generation, package-owned deployment contracts and the
+source-tested offline planning and rendering surface. Remote infrastructure execution is not promoted to
+stable support: PM2, SSH, Coolify and Kubernetes remain guarded behind injected transports, while AWS and
+Google Cloud providers remain plan-only.
+
### Added
- Package-owned deployment contract v2 with strict JSON Schema, generated TypeScript declarations and a
@@ -41,8 +48,8 @@ All notable changes will be documented here.
`applications[]` list.
- Replaced legacy zero-maximum-replica parking with explicit `suspended: true`.
- Replaced side deployments with deterministic variants and provider fields with target bindings.
-- Bumped source and `create-monox` metadata to `0.2.0-alpha.1`. A prerelease tag and npm publish remain gated
- by the complete acceptance suite and security prerequisites.
+- Promoted the deterministic generator and offline contracts to `0.2.0`, published through the protected npm
+ workflow under `latest` only when the exact release tag and source commit match.
- Aligned Nuxt 4 with its project-reference TypeScript layout and Angular 22 with the current `@angular/build`
builder.
- Namespaced generated Python distributions and modules so workspace names such as `fastapi` cannot shadow a
@@ -64,7 +71,9 @@ All notable changes will be documented here.
### Known limitations
-- AWS and Google Cloud packages are plan-only in this alpha and do not invoke Pulumi Automation API.
+- `create-monox@0.2.0` does not install the source-tree `@monox/cli`; generated projects receive deployment
+ contracts and fail-closed placeholders, while the delivery CLI remains npm-private.
+- AWS and Google Cloud packages are plan-only in 0.2.0 and do not invoke Pulumi Automation API.
- PM2, SSH, Coolify and Kubernetes adapters have not completed their live acceptance matrices.
- Kubernetes add-on chart coordinates and digests remain intentionally unverified and non-executable.
- The private production reference has not completed a MonoX 0.2 canary and proven rollback, so it is not yet
diff --git a/README.md b/README.md
index 82e189d..41944d3 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,10 @@ Every deployable package owns a versioned `package.json.deployment` block. The r
project boundaries, workload profiles, environments, targets and add-ons. There is no second application list
to keep in sync.
-> Status: `0.2.0-alpha.1` is a source prerelease. The protected npm workflow publishes prereleases under the
-> `next` dist-tag only after its release gate passes. The stable npm line remains available through `latest`.
+> `create-monox` 0.2.0 is the stable generator release and is published through the `latest` dist-tag. Stable
+> support covers deterministic project generation and the source-tested offline contracts described below.
+> Remote infrastructure execution remains guarded, acceptance-pending or plan-only as marked in the
+> [capability status](docs/capability-status.md).
[Project site](https://monox.dev) | [Architecture](docs/architecture.md) |
[Deployment contract](docs/deployment.md) | [create-monox on npm](https://www.npmjs.com/package/create-monox)
@@ -25,10 +27,10 @@ to keep in sync.
## Generate a product
-After the alpha is published under `next`:
+Create a project from the stable channel:
```bash
-npm create monox@next -- my-product \
+npm create monox@latest -- my-product \
--workspace api=node-fastify-api \
--workspace web=react-vite-web \
--workspace jobs=node-worker \
@@ -120,21 +122,25 @@ immutable. Every resolved base workload and variant must match exactly one targe
See [Deployment contract](docs/deployment.md) for the complete resolution order and safety rules.
-## CLI
+## Source-tree delivery CLI
+
+The delivery CLI below is tested from this repository with `yarn monox`. It is not installed by
+`create-monox@0.2.0`; generated projects receive the deployment contracts and fail-closed placeholders only.
+`@monox/cli` remains npm-private until its package scope and independent consumer contract are ready.
```text
-monox validate
-monox config explain
ה-build הנוכחי של 0.2 הוא 0.2.0-alpha.1, והוא זמין תחת next.
npm create monox@next -- my-product \
+ ה-release היציב הנוכחי הוא 0.2.0.
+ npm create monox@latest -- my-product \
--workspace api=node-fastify-api \
--workspace web=react-vite-web \
--workspace jobs=node-worker \
--addon redis --addon rabbitmq \
--delivery docker:local --yes
- מי שרוצה את ה-stable channel נשאר עם @latest. ל-alpha משתמשים ב-@next.
- בחירה שלא נתמכת נכשלת לפני ש-MonoX כותב קבצים או משנה infra.
+ ב-flow אינטראקטיבי משתמשים ב-@latest. ב-setup script שחייב להיות reproducible נועלים
+ @0.2.0. בחירה שלא נתמכת נכשלת לפני ש-MonoX כותב קבצים או משנה infra.
@@ -203,10 +203,16 @@ ה-contract נשאר קבוע, ה-target מתחלף
>רץ דרך ה-local executor המובנה או transport שמחובר במפורש, ודוחה plan לא עדכני.
- monox validate
-monox config explain @my-product/jobs --env staging
-monox plan --env staging --all --output .monox/plan.json
-monox apply --plan .monox/plan.json
+
+ הגבול של 0.2.0: create-monox כותב את ה-contracts האלה, אבל עדיין לא
+ מתקין monox delivery binary ציבורי. הפקודות למטה הן tooling מתוך ה-source repo, לא
+ פקודות שמגיעות בתוך הפרויקט שנוצר.
+
+ # MonoX source checkout
+yarn monox validate
+yarn monox config explain @monox/jobs --env staging
+yarn monox plan --env staging --all --output .monox/plan.json
+yarn monox apply --plan .monox/plan.json
@@ -225,7 +231,7 @@ MonoX מגדיר את הגבול. אתם אחרא
- הוכחות של 0.2 alpha
+ הוכחות של release 0.2
מה כבר ממומש, מה guarded ומה עדיין מחכה ל-production proof
הסטטוס מחובר לטסטים ב-repo הציבורי. Claims עתידיים נשארים בחוץ עד שיש להם הוכחה.
@@ -234,7 +240,7 @@ מה כבר ממומש, מה guarded ומה עדיין מ
Surface
- סטטוס alpha
+ סטטוס release
הוכחה
@@ -258,13 +264,13 @@ מה כבר ממומש, מה guarded ומה עדיין מ
PM2, SSH, Coolify ו-Kubernetes
- Guarded alpha
+ Guarded
Typed plans ו-fail-closed transport injection לפני שינוי חיצוני
AWS ו-GCP provisioning
Plan only
- Provider plans בגישת OIDC; apply אמיתי ב-sandbox עדיין release gate
+ ב-0.2.0 נוצרים provider plans בלבד; real sandbox apply לא כלול
Production reference
diff --git a/apps/web/public/index.html b/apps/web/public/index.html
index fc1b6aa..9b4c55a 100644
--- a/apps/web/public/index.html
+++ b/apps/web/public/index.html
@@ -19,8 +19,8 @@
content="Create workspaces, boundaries, local services, CI, containers and delivery contracts together."
/>
-
-
+
+
@@ -31,7 +31,7 @@
name="twitter:description"
content="Create workspaces, boundaries, local services, CI, containers and delivery contracts together."
/>
-
+
@@ -50,7 +50,7 @@
"url": "https://monox.dev/",
"codeRepository": "https://github.com/Mosharush/MonoX",
"description": "An open-source monorepo generator and delivery toolkit with package-level deployment contracts.",
- "version": "0.2.0-alpha.1",
+ "version": "0.2.0",
"programmingLanguage": ["JavaScript", "TypeScript", "Python", "PHP", "Go"],
"license": "https://spdx.org/licenses/MIT.html",
"isAccessibleForFree": true,
@@ -71,7 +71,7 @@
PM2, SSH, Coolify and Kubernetes
- Guarded alpha
+ Guarded
Typed plans and fail-closed transport injection before external state changes
AWS and GCP provisioning
Plan only
- OIDC-oriented provider plans; real sandbox apply remains a release gate
+ 0.2.0 renders provider plans only; real sandbox apply is not included
Production reference
diff --git a/apps/web/public/og-image.png b/apps/web/public/og-image.png
index 1d7b8b1..c7acf9c 100644
Binary files a/apps/web/public/og-image.png and b/apps/web/public/og-image.png differ
diff --git a/apps/web/src/server.mjs b/apps/web/src/server.mjs
index 4d6ff42..04496f1 100644
--- a/apps/web/src/server.mjs
+++ b/apps/web/src/server.mjs
@@ -21,7 +21,7 @@ function headers(contentType, options = {}) {
return {
'cache-control': contentType.startsWith('text/html') ? 'no-cache' : 'public, max-age=3600',
'content-security-policy':
- "default-src 'self'; style-src 'self'; script-src 'sha256-yobifisrUX5jmFbF2OJOXGbUsGsYDPdPdkH4ehe2Ll0='; frame-ancestors 'none'",
+ "default-src 'self'; style-src 'self'; script-src 'sha256-j5+huJAo2bEyExTIsvCL8uNR05Fe+DupuuQrek0PIb4='; frame-ancestors 'none'",
'cross-origin-opener-policy': 'same-origin',
'content-type': contentType,
'permissions-policy': 'camera=(), geolocation=(), microphone=()',
@@ -50,7 +50,7 @@ function preferredLanguage(request, url) {
}
export function createWebServer(options = {}) {
- const runtime = options.runtime ?? createAppRuntime({ name: '@monox/web', version: '0.2.0-alpha.1' });
+ const runtime = options.runtime ?? createAppRuntime({ name: '@monox/web', version: '0.2.0' });
const server = createServer(async (request, response) => {
const requestStartedAt = process.hrtime.bigint();
const url = new URL(request.url ?? '/', 'http://monox.local');
diff --git a/apps/web/test/server.test.mjs b/apps/web/test/server.test.mjs
index b052af2..e4e08c9 100644
--- a/apps/web/test/server.test.mjs
+++ b/apps/web/test/server.test.mjs
@@ -1,5 +1,6 @@
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
+import { readFile } from 'node:fs/promises';
import test from 'node:test';
import { startWebServer } from '../src/server.mjs';
@@ -18,20 +19,27 @@ test('serves the starter page and health endpoint', async () => {
const html = await page.text();
const structuredData = /