Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/release-doctor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
workflow_dispatch:

permissions:
actions: read
contents: read

jobs:
Expand All @@ -17,9 +18,22 @@ jobs:
with: { persist-credentials: false, fetch-depth: 0 }
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with: { node-version: '24.12.0', package-manager-cache: false }
- name: Mint an all-installation release App audit token
id: release-app-token
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
with:
app-id: ${{ vars.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
permission-checks: read
permission-contents: write
permission-pull-requests: write
- env:
GH_TOKEN: ${{ github.token }}
NPM_STAGED_PACKAGES_URL: ${{ vars.NPM_STAGED_PACKAGES_URL }}
RELEASE_APP_ID: ${{ vars.RELEASE_APP_ID }}
RELEASE_APP_PRIVATE_KEY_PRESENT: ${{ secrets.RELEASE_APP_PRIVATE_KEY != '' }}
RELEASE_APP_TOKEN: ${{ steps.release-app-token.outputs.token }}
RELEASE_REQUIRED_CHECKS: ${{ vars.RELEASE_REQUIRED_CHECKS }}
RELEASE_BRANCH_RULESET_ID: ${{ vars.RELEASE_BRANCH_RULESET_ID }}
RELEASE_TAG_RULESET_ID: ${{ vars.RELEASE_TAG_RULESET_ID }}
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release-qualify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ jobs:
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with: { name: '${{ env.CORE_ARTIFACT }}', path: .release/candidate }
- run: node scripts/release/release-candidate.mjs verify .release/candidate && node scripts/release/release-candidate.mjs hydrate .release/candidate
- name: Dry-run npm pack, publish, and staged publish over the exact tarballs
run: |
npm_bin=$(node scripts/release/install-pinned-npm.mjs "$RUNNER_TEMP/npm-cli-dry-run")
NPM_DRY_RUN_BIN="$npm_bin" node scripts/release/verify-npm-dry-runs.mjs .release/candidate
- name: Run package source and lifecycle properties
run: |
pnpm --filter @rxjs/observable-polyfill --filter @rxjs/test --filter @rxjs/migrate run test
Expand Down
21 changes: 16 additions & 5 deletions .github/workflows/release-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,23 @@ jobs:
permissions:
actions: read
attestations: read
contents: write
contents: read
id-token: write
pull-requests: write
pull-requests: read
steps:
- name: Mint guarded release App token for tag, release, and PR writes
id: release-app-token
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
with:
app-id: ${{ vars.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: { ref: '${{ needs.authorize.outputs.source-commit }}', fetch-depth: 0, persist-credentials: true }
with:
ref: '${{ needs.authorize.outputs.source-commit }}'
fetch-depth: 0
token: ${{ steps.release-app-token.outputs.token }}
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with: { node-version: '24.12.0', package-manager-cache: false }
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
Expand Down Expand Up @@ -119,7 +130,7 @@ jobs:
for tarball in .release/candidate/*.tgz; do gh attestation verify "$tarball" --repo "$GITHUB_REPOSITORY"; done
- name: Create protected candidate tag and draft evidence release
env:
GH_TOKEN: ${{ github.token }}
GH_TOKEN: ${{ steps.release-app-token.outputs.token }}
VERSION: ${{ inputs.version }}
run: |
git config user.name "github-actions[bot]"
Expand All @@ -132,7 +143,7 @@ jobs:
- name: Comment WebAuthn approval links, hashes, stage IDs, order, and CLI fallback
if: always()
env:
GH_TOKEN: ${{ github.token }}
GH_TOKEN: ${{ steps.release-app-token.outputs.token }}
NPM_STAGED_PACKAGES_URL: ${{ vars.NPM_STAGED_PACKAGES_URL }}
run: |
test -f .release/staged-release.json
Expand Down
8 changes: 5 additions & 3 deletions docs/RELEASE_PROCESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Self-merging the generated PR starts **Qualify RxJS 9 release**. Two separate fr

The canonical first build then passes every blocking Node, browser, Safari, Deno, Bun, Webpack, performance, package, and pinned Observable WPT gate. A scripts-disabled local installation of the exact tarballs produces a CycloneDX SBOM and release-only lockfile. A SHA-pinned OSV scan uses no monorepo exceptions. GitHub attests the exact tarballs.

The checked npm 11.18.0 CLI also runs `npm pack --dry-run`, `npm publish --dry-run`, and `npm stage publish --dry-run` over every exact tarball. It previews each trusted publisher with `npm trust github --allow-stage-publish --dry-run`, bound to `ReactiveX/rxjs`, `release-stage.yml`, and `npm-stage`, without granting direct-publish authority. These commands prove packaging, lifecycle, and trusted-configuration inputs without changing the registry. They do not prove npm OIDC or trusted-publisher authorization because dry-run does not submit a stage. The private staging of the first real beta is the live authorization proof; no public rehearsal package is created.

The retained 30-day artifact contains:

- `release-manifest.json`;
Expand Down Expand Up @@ -69,14 +71,14 @@ The finalizer has no npm credentials or publishing authority. It waits for all f
## One-time setup before beta

1. Protect `master`: require pull requests with zero approvals, require CI, CodeQL, dependency review, OSV, workflow validation, release coherence, WPT, and release readiness; require verified squash commits; prevent force-push and deletion.
2. Configure the release App with only checks read plus contents and pull-request write access. Store `RELEASE_APP_ID`, `RELEASE_APP_PRIVATE_KEY`, and the exact `RELEASE_REQUIRED_CHECKS` list.
2. Configure the release App with only checks read plus contents and pull-request write access. Store `RELEASE_APP_ID`, `RELEASE_APP_PRIVATE_KEY`, and `RELEASE_REQUIRED_CHECKS` as the repository-defined JSON array of exact master check names. Pull-request-only dependency review and Conventional Commit checks belong in branch protection, not this master wait list.
3. Restrict `release/rxjs-9` updates to the release App's guarded force-with-lease refresh.
4. Restrict the `npm-stage` environment to protected `master` with no reviewer and no secret.
5. Configure all four npm trusted publishers for the stage workflow and environment. Require WebAuthn and disallow publish-capable tokens; delete any reusable publication credential.
6. Verify the authenticated npm Staged Packages URL and store it as `NPM_STAGED_PACKAGES_URL`.
7. Protect `refs/tags/9.*` from update, deletion, and force-push; allow only the staging workflow to create a tag. Enable GitHub Release immutability.
8. Run the release doctor and rehearse qualification, typed digest authorization, stage inspection, rejection, restaging, WebAuthn approval, provenance, attestations, and finalization using existing disposable packages controlled only by Ben.
8. Run the release doctor and the complete local/CI dry-run ladder. Use private staging of `9.0.0-beta.0` as the first live OIDC proof, download and compare every stage, and pause before WebAuthn approval. A partial or mismatched stage is rejected in full and requires a freshly qualified version.

The repository cannot configure GitHub/npm account WebAuthn, rulesets, environments, trusted publishers, or perform the disposable npm rehearsal from source code. P6.10 remains active until that external evidence exists.
The repository cannot configure GitHub/npm account WebAuthn, rulesets, environments, or trusted publishers from source code. P6.10 remains active until those controls and the first real private stage are verified. Nothing becomes publicly installable until Ben separately approves the matching stages with WebAuthn.

Last reviewed: 2026-08-02.
5 changes: 5 additions & 0 deletions docs/rxjs-next/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ inventories, contents, and SHA-512 values are byte-identical. All package,
runtime, browser, Safari, alternate-runtime, Webpack, performance, WPT, SBOM,
OSV, and attestation evidence is bound to the canonical tarballs. The workflow
then stops and exposes its run ID, version, source commit, and manifest SHA-512.
The checked npm 11.18.0 CLI runs pack, publish, and staged-publish dry runs over
those exact tarballs. Dry-run does not submit to the registry and therefore
does not prove OIDC authorization. Private staging of the first real beta is
the live trusted-publisher proof; RxJS does not create a public rehearsal
package.

A separate manual dispatch by `benlesh` must reproduce the run ID, version, and
digest. It revalidates the protected branch/current commit, generated release
Expand Down
13 changes: 11 additions & 2 deletions docs/rxjs-next/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1485,9 +1485,18 @@ Status meanings:
bundle. Finalization requires registry integrity, `npm audit signatures`, and
GitHub attestation verification. OpenSSF remains secondary; Code-Review `0`
is accepted rather than manufacturing approvals.
- **Dry-run and first live proof:** The checked npm CLI runs pack, publish, and
staged-publish dry runs over the exact candidate tarballs. It also previews
stage-only GitHub trust configurations with the exact repository, workflow,
and environment inputs. Those commands do not contact the registry or prove
trusted-publisher authorization. Rather
than create a public rehearsal package, private staging of the first real
beta supplies the live OIDC proof; publication still requires Ben's separate
WebAuthn approval after the staged bytes are downloaded and matched.
- **Consequence:** Private Nx release imports, the token-based publisher,
reviewer requests, release-team ownership, and succession-role assumptions
are removed. Repository-owned policy, reproducibility, evidence, staging,
doctor, and finalizer scripts implement the accepted flow. GitHub/npm
WebAuthn, ruleset/environment/trusted-publisher setup, and the sole-account
disposable-package rehearsal remain external gates before publication.
WebAuthn and ruleset/environment/trusted-publisher setup remain external
gates before publication. P6.10 closes only after the first real beta is
privately staged, approved, and verified publicly.
71 changes: 48 additions & 23 deletions docs/rxjs-next/PROJECT_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1557,18 +1557,18 @@ names.

### Phase 6 — Release readiness

| Status | ID | Outcome |
| --------- | ----- | ------------------------------------------------------------------------------------- |
| `DONE` | P6.1 | Finalize version naming, supported environments, support policy, and release channels |
| `DONE` | P6.2 | Complete package, type, bundle, performance, and conformance gates |
| `DONE` | P6.3 | Publish package-local API, migration, and contributor documentation |
| `DONE` | P6.4 | Run pre-release adoption, resolve blockers, and approve the major release |
| `DONE` | P6.5 | Complete the terminal plan, verification, and documentation-site exclusion audit |
| `DONE` | P6.6 | Centralize eligible source subscriptions and record bundle-size evidence |
| `DONE` | P6.7 | Use direct `[create]` construction and record bundle-size evidence |
| `DONE` | P6.8 | Complete RxJS 9 CI coverage and validate the resulting pull-request workflow matrix |
| `BLOCKED` | P6.9 | Validate the first live dependency-review and Scorecard runs on GitHub |
| `NEXT` | P6.10 | Implement the secure release-PR and npm staged-approval process |
| Status | ID | Outcome |
| ------ | ----- | ------------------------------------------------------------------------------------- |
| `DONE` | P6.1 | Finalize version naming, supported environments, support policy, and release channels |
| `DONE` | P6.2 | Complete package, type, bundle, performance, and conformance gates |
| `DONE` | P6.3 | Publish package-local API, migration, and contributor documentation |
| `DONE` | P6.4 | Run pre-release adoption, resolve blockers, and approve the major release |
| `DONE` | P6.5 | Complete the terminal plan, verification, and documentation-site exclusion audit |
| `DONE` | P6.6 | Centralize eligible source subscriptions and record bundle-size evidence |
| `DONE` | P6.7 | Use direct `[create]` construction and record bundle-size evidence |
| `DONE` | P6.8 | Complete RxJS 9 CI coverage and validate the resulting pull-request workflow matrix |
| `DONE` | P6.9 | Validate the first live dependency-review and Scorecard runs on GitHub |
| `NEXT` | P6.10 | Implement the secure release-PR and npm staged-approval process |

#### P6.9 completion bar

Expand All @@ -1583,8 +1583,8 @@ names.
review blocks new moderate-or-higher runtime and development vulnerabilities.
- Active-workflow formatting, release coherence, documentation links, badge
targets, and the no-runtime/no-`rxjs.dev` scope boundary are verified. The
first default-branch Scorecard publication and required-check repository rule
remain explicit post-merge administrative follow-ups.
first default-branch Scorecard publication, code-scanning result, and
dependency-review required-check rule are verified live.

#### P6.9 implementation evidence

Expand All @@ -1601,10 +1601,11 @@ names.
Local YAML parsing, active-workflow formatting, documentation links, all 24
release-check tests, release coherence, and diff checks pass; `apps/rxjs.dev`
and runtime/package source are unchanged.
- P6.9 is `BLOCKED` until dependency review runs on the implementation PR.
After merge, the first `master` Scorecard publication, code-scanning result,
live badge population, and required dependency-review repository rule remain
administrative verification steps.
- Dependency review passed on merged PR #7613. After merge, `master` run
30919216705 published the first successful Scorecard result, CodeQL passed,
and the repository's protected-branch status list included `Dependency
review`. This completes P6.9; the later P6.10 ruleset migration must preserve
that required check rather than moving it into the master-only wait list.

#### P6.10 completion bar

Expand All @@ -1627,9 +1628,11 @@ names.
exceptions remain, while the isolated release train has no exceptions.
Bounded and scheduled properties cover release parsing/authorization and the
Observable lifecycle state machine.
- GitHub/npm WebAuthn, protected-branch/tag/environment/trusted-publisher setup,
and the sole-account disposable-package rehearsal remain explicit external
pre-publication gates. P6.10 remains active until they are verified.
- GitHub/npm WebAuthn and protected-branch/tag/environment/trusted-publisher
setup remain explicit external pre-publication gates. Exact npm pack,
publish, and staged-publish dry runs run before registry access; private
staging of the first real beta is the live OIDC proof. P6.10 remains active
through WebAuthn approval and public verification.

#### P6.10 implementation evidence

Expand All @@ -1650,8 +1653,8 @@ names.
every release-PR, qualification, authorization, and staging job individually,
with regression tests for both security-sensitive cases.
- Local verification is recorded in the P6.10 session entry. Live App,
ruleset, trusted-publisher, disposable-package, TFA, rejection, staged digest,
tag, and immutable-release evidence remain required before `DONE`.
ruleset, trusted-publisher, WebAuthn, staged-digest, tag, provenance, and
immutable-release evidence remain required before `DONE`.

#### P6.1 completion bar

Expand Down Expand Up @@ -3636,3 +3639,25 @@ conformance implementation depends on a runnable harness.
- Passed all 53 release/security tests, 177 OSV exception validations, release
coherence and doctor checks, workflow formatting, and diff hygiene. P6.10
remains the sole `NEXT` item pending its external setup and rehearsal gates.

### 2026-08-04 — P6.10 first-beta staging policy and dry-run evidence

- Replaced the public disposable-package rehearsal with exact-tarball
`npm pack --dry-run`, `npm publish --dry-run`, and
`npm stage publish --dry-run` checks plus stage-only GitHub trusted-publisher
previews. All sixteen dry runs passed over a local four-package
`9.0.0-beta.0` candidate with the pinned npm 11.18.0 CLI and no
registry credentials. The local manifest SHA-512 was
`d8977e21abb704df0c72ed2ba5663e282f81b6e4be24d3ff6a87f9922eadfc22c09de1b04adf998e4c8817f37ce15e669cb714b82c2f983400d9417430526585`;
it is diagnostic evidence, not the future canonical Ubuntu qualification
digest.
- Split the exact release policy into blocking `master` checks,
pull-request-only branch-protection checks, and advisory checks. Added
regression coverage that rejects PR-only or advisory checks in
`RELEASE_REQUIRED_CHECKS`, malformed or duplicate configuration, weak branch
protection, and drift from the exact check set.
- Verified PR #7613's successful Dependency review and the successful
post-merge Scorecard run 30919216705, completing P6.9. P6.10 remains the sole
`NEXT` item: GitHub App/ruleset/environment administration, npm trusted
publishing, canonical Ubuntu qualification, private staging, Ben's WebAuthn
approvals, and public registry verification still have to succeed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"analyze:bundles": "node scripts/analyze-bundles.mjs",
"prepare-packages": "pnpm nx run-many -t build,lint,test:circular,dtslint,copy_common_package_files --exclude rxjs.dev",
"release": "node scripts/release/release-doctor.mjs",
"release:check": "node --test scripts/analyze-bundles.test.mjs scripts/check-package-docs.test.mjs scripts/check-release-coherence.test.mjs scripts/finalize-esm-package.test.mjs scripts/prerelease-adoption-lib.test.mjs scripts/release/authorize-release-commit.test.mjs scripts/release/authorize-stage.test.mjs scripts/release/install-pinned-npm.test.mjs scripts/release/release-config.test.mjs scripts/release/release-policy.test.mjs scripts/release/release-doctor-policy.test.mjs scripts/release/release-candidate.test.mjs scripts/release/stage-release.test.mjs scripts/security/check-osv-exceptions.test.mjs && node scripts/security/check-osv-exceptions.mjs && node scripts/check-package-docs.mjs && node scripts/check-release-coherence.mjs && node scripts/release/release-doctor.mjs",
"release:check": "node --test scripts/analyze-bundles.test.mjs scripts/check-package-docs.test.mjs scripts/check-release-coherence.test.mjs scripts/finalize-esm-package.test.mjs scripts/prerelease-adoption-lib.test.mjs scripts/release/authorize-release-commit.test.mjs scripts/release/authorize-stage.test.mjs scripts/release/install-pinned-npm.test.mjs scripts/release/release-config.test.mjs scripts/release/release-policy.test.mjs scripts/release/release-doctor-policy.test.mjs scripts/release/release-candidate.test.mjs scripts/release/stage-release.test.mjs scripts/release/verify-npm-dry-runs.test.mjs scripts/security/check-osv-exceptions.test.mjs && node scripts/security/check-osv-exceptions.mjs && node scripts/check-package-docs.mjs && node scripts/check-release-coherence.mjs && node scripts/release/release-doctor.mjs",
"test:bundle-analysis": "node --test scripts/analyze-bundles.test.mjs",
"test:workflows": "prettier --check .github/workflows/*.yml .github/actions/install-dependencies/action.yml .github/dependabot.yml",
"test:kernel": "pnpm --filter rxjs run test:kernel",
Expand Down
Loading
Loading