Compare commits

...
579 Commits
Author SHA1 Message Date
github-actions[bot] 89c2ecb341 Bump versions for v13.12 2026-11-14 19:39:17 +00:00
Lauri Ojansivu f4062a4708 Prepare v13.12 release 2026-11-14 21:38:09 +02:00
Lauri Ojansivu 28ed59e45f CHANGELOG: REST writes that change nothing answer 404, and the ListLockBleed follow-up for a card deleted through another list.
Thanks to xet7 !
2026-11-14 21:26:40 +02:00
Lauri Ojansivu d4c0d3e882 Security: a REST write that changes nothing answers 404, and a card deletion naming another list keeps the card's children.
DELETE /api/boards/:boardId/lists/:listId/cards/:cardId found the card by
board alone, removed its checklists, checklist items, comments and
attachments, and then removed the card by board AND list. With the wrong
list id the card stayed, emptied, and the reply was 200. Only the named list
was asked, so a member a list refuses could strip that list's cards by naming
a list they may write (ListLockBleed follow-up, GHSA-4734-8vrg-8j3w). The card
now has to be in the named list, and its own list is asked too, which records
the attempt in Admin Panel / Problems as authz.list-locked.

The siblings of #6900, #6905 and #6906 answered 200 for a change that changed
nothing: the swimlane, list, card, dependency and board-domain deletions; the
custom-field update, dropdown-item add and rename; the dependency update; the
board-member add and remove; and the token deletion of an unknown user. Each
looks the record up first and answers a JSON 404, and removing a board's last
admin answers 409. Member removal deactivates the user's active entry, not
the first one.

tests/restWriteRoutes404.test.cjs runs each handler with the record missing
and present, and fails for any id-addressed REST write in server/ without a
not-found answer. tests/playwright/specs/rest-write-routes-404.e2e.js replays
them against a running server. Three guards that pinned the old code shape
were updated, keeping what they protect.

Thanks to xet7 !
2026-11-14 21:26:30 +02:00
Lauri Ojansivu 485d74acba CHANGELOG: REST custom-field and dropdown-item deletion answer 404 for a missing one.
Thanks to AhmedLukman and xet7 !
2026-11-14 21:09:56 +02:00
Lauri Ojansivu 2ae82c9889 Merge pull request #6906 from AhmedLukman/codex/fix-dropdown-delete-not-found
Fix dropdown-item deletion acknowledging missing options
2026-11-14 21:07:01 +02:00
Lauri Ojansivu 08f963e3e5 Merge pull request #6905 from AhmedLukman/codex/fix-custom-field-delete-not-found
Fix custom-field deletion reporting success for missing records
2026-11-14 20:42:17 +02:00
Ahmed Lukman cefb6771cb Fix dropdown-item deletion acknowledging missing options 2026-11-14 21:10:46 +03:00
Lauri Ojansivu 809f6134d9 CHANGELOG: Move/Copy shortcuts, shared list row, Move/Copy remembered destination, swimlane resize line.
Thanks to mimZD and xet7 !
2026-11-14 18:44:20 +02:00
Lauri Ojansivu 67321dcb6c One shared list header row shows the same lists in every swimlane, scrolling together.
Board Settings / Swimlane / One shared list header row (#2805) only hid
the list headers of every swimlane but the first. Since lists belong to
a swimlane, the others kept lists of their own, so the one header row
did not match what was under it: a new swimlane showed no lists at all,
and each swimlane scrolled sideways on its own.

With the setting on, the Swimlanes view is now what swimlanes were
before lists belonged to one: every swimlane shows all of the board's
lists in their order, under the one header row, and the swimlanes
scroll sideways together (client/components/swimlanes/sharedListRow.js,
one capturing scroll listener that ignores the scroll events it causes,
so a shorter row cannot pull the others back). Each list still holds
the cards of the swimlane it is shown in. Nothing is written to a list
or a card, so on a board whose swimlanes have different lists, turning
the setting off shows each swimlane's own lists again. While it is on,
a list dropped under another swimlane is reordered, never moved there
with all its cards.

tests/sharedListRow6902.test.cjs drives the scrolling, including a row
that cannot scroll as far, and the Swimlanes view wiring, and
tests/playwright/specs/shared-list-row-6902.e2e.js checks the lists,
cards and scrolling of two swimlanes with the setting on and off.

Thanks to mimZD and xet7 !

Fixes #6902.
2026-11-14 18:43:43 +02:00
Lauri Ojansivu 8d5109b4b9 The swimlane resize line reaches the right edge of a swimlane scrolled to the right.
The swimlane-height resize handle is position: absolute across the
bottom of .js-swimlane, which is itself the horizontal scroll container
of its row of lists. An absolutely positioned child is as wide as the
visible box but moves with the scrolled content, so scrolling right by
N pixels left the last N pixels without a handle. The handle is now
shifted by the swimlane's scrollLeft on every scroll
(client/components/swimlanes/resizeHandleScroll.js), which is also
right for a right-to-left board, where scrollLeft is negative. Its
transition no longer animates transform, which made it trail behind.

tests/swimlaneResizeHandleScroll6903.test.cjs drives the shift in both
directions and that a re-run adds no second listener, and
tests/playwright/specs/swimlane-resize-handle-scroll-6903.e2e.js
scrolls a 14-list swimlane and measures the line against its edges.

Thanks to mimZD and xet7 !

Fixes #6903.
2026-11-14 18:40:06 +02:00
Lauri Ojansivu e95596c3b0 npmRetry test passes with a localized bash.
The test matched bash's English "unbound variable", and a Finnish bash
says "sitomaton muuttuja", so it failed on a Finnish system. bash runs
with LC_ALL=C there.

Thanks to xet7 !
2026-11-14 18:36:51 +02:00
Lauri Ojansivu 8508c15015 Keyboard shortcuts Shift+M for Move Card and Shift+C for Copy Card.
They act on the opened card, or on the hovered or selected minicard,
which is opened first, as the d due date shortcut does. Like d, they do
not open the popup themselves: client/lib/cardMenuHotkey.js clicks the
opened card's hamburger menu and then its Move Card or Copy Card item,
so the menu's permissions decide - Move Card for board admins, Copy
Card for everyone who is not a Worker. A menu without the item is
closed again. Shift keeps them apart from m (assign yourself) and c
(archive). The keyboard shortcuts list names them with the already
translated popup titles, and docs/Features/General/Keyboard-Shortcuts
lists the opened card's shortcuts.

findOpenedCardDetails() is shared from client/lib/dueDateHotkey.js.

tests/cardMenuKeyboardShortcut.test.cjs drives the DOM logic, including
a menu without the item, another opened card and a stray element, and
tests/playwright/specs/move-copy-keyboard-shortcut-6904.e2e.js presses
the keys, with shortcuts off, while typing, and as a non-admin member.

User-defined shortcuts, the question in the issue, are not part of this.

Thanks to mimZD and xet7 !

Fixes #6904.
2026-11-14 18:36:51 +02:00
Lauri Ojansivu 4b47ec81a7 Move and Copy card remember the swimlane and list chosen last time, not only the board.
BoardSwimlaneListDialog.setOption() checked the remembered swimlane and
list against the client's documents right after subscribing to the
remembered board. When that is another board than the open one, its
swimlanes and lists had not arrived yet, so both were replaced by the
first of nothing, and the popup opened on the remembered board's first
swimlane and list. The check is now left to the subscription's onReady,
which already falls back to the first swimlane and list when a
remembered one is really gone. The same picker serves Move, Copy, Link
to board, Copy many and Convert checklist item to card.

tests/moveCopyRemembersDestination6901.test.cjs runs the real dialog
classes with the board's documents arriving when it is ready, including
a remembered list that no longer exists, and
tests/playwright/specs/move-copy-remembers-destination-6901.e2e.js opens
the real popups.

Thanks to mimZD and xet7 !

Fixes #6901.
2026-11-14 18:29:02 +02:00
Lauri Ojansivu f47a8c4ebb CHANGELOG: Meteor 3.6.1, and OpenTelemetry and klauspost/compress in the embedded-engine benchmark.
Thanks to xet7 !
2026-11-14 18:26:38 +02:00
Lauri Ojansivu 58273d2bf8 Meteor skills: the npm fallback install works with npm 12.
The updated meteor-cli-installation skill wrote
npm install -g meteor --foreground-script, which npm 12 rejects, and
without --allow-scripts=meteor npm 12 skips the installer's script, so
no Meteor is installed. It now writes --foreground-scripts
--allow-scripts=meteor, as build.sh does. tests/npmMeteorInstall.test.cjs
passes again.

Thanks to xet7 !
2026-11-14 18:26:04 +02:00
Lauri Ojansivu 0566bf6ec8 Build the Docker image and the Bun build with Meteor 3.6.1.
The Dockerfile's METEOR_RELEASE is METEOR@3.6.1. The Bun build's
releases/bun/prepare-meteor.sh refused every release but 3.6; the 3.6
port (meteor-3.6.patch) applies unchanged to 3.6.1, checked with
git apply --check against 3.6.1's commit eaf13497, so 3.6.1 is accepted
pinned to that commit. The sandbox README names the current release.

Thanks to xet7 !
2026-11-14 18:26:04 +02:00
Lauri Ojansivu e0eb6c64db Updated OpenTelemetry to 1.47.0 and klauspost/compress to 1.20.2 in the embedded-engine benchmark.
Dependabot alert #189: go.opentelemetry.io/otel/trace >= 1.35.0,
< 1.47.0 lets attribute values made of valid U+FFFD runes bypass
AttributeValueLengthLimit. govulncheck then also reported GO-2026-6865
in github.com/klauspost/compress 1.18.7, fixed in 1.20.2. Both are
indirect dependencies of docs/Databases/FerretDB/1/SpeedTest/embedded.

tests/goModuleFloors.test.cjs raises the floors for every go.mod in the
repository, with every OpenTelemetry module at 1.47.0, and rejects the
versions that were reported.

Thanks to dependabot and xet7 !
2026-11-14 18:23:51 +02:00
Lauri Ojansivu ed5a534941 Updated to Meteor 3.6.1.
meteor update --release METEOR@3.6.1 failed: Meteor 3.6.1 pins
ddp-server@~3.6.1, and WeKan's local fork of ddp-server was 3.6.0_1.
Of WeKan's six Meteor package forks only ddp-server changed in 3.6.1,
so its 3.6.1 fixes (non-string unsub and ping ids are refused, a
protocol handler that throws synchronously is contained) and their
upstream tests are rebased onto the fork, now 3.6.1_1. The other
packages 3.6.1 bumps (accounts-base, accounts-express,
accounts-password, minimongo, mongo, webapp) are updated in
.meteor/packages and .meteor/versions.

tests/meteor353SecurityFixes.test.cjs now expects METEOR@3.6.1 and
checks that the fork carries the 3.6.1 fixes and not the older code.

Thanks to xet7 !
2026-11-14 18:23:50 +02:00
Ahmed Lukman 2733c8ba65 Fix custom-field deletion acknowledging missing records
Return JSON 404 when the board-scoped removal matches no custom field, including missing, wrong-board and repeated deletion requests. Preserve the existing success response, authorization and collection hooks.

Add handler regressions and live API/browser coverage for deletion results, access checks, card value cleanup and unaffected fields.
2026-11-14 15:35:11 +03:00
Lauri Ojansivu a99de56eba Merge remote-tracking branch 'origin/main' 2026-11-13 20:21:51 +02:00
Lauri Ojansivu 1ac1ed4bb8 Updated Meteor skills 2026-11-13 20:21:40 +02:00
github-actions[bot] 06e7db85dc Bump versions for v13.11 2026-11-13 18:11:28 +00:00
Lauri Ojansivu a97fde20c9 Prepare v13.11 release 2026-11-13 20:10:27 +02:00
Lauri Ojansivu a22dbc5b34 CHANGELOG: REST integration deletion answers 404 for a missing integration.
Thanks to AhmedLukman and xet7 !
2026-11-13 19:43:09 +02:00
Lauri Ojansivu cbe8a56d9d Merge pull request #6900 from AhmedLukman/codex/fix-integration-delete-not-found
Fix integration deletion acknowledging missing records
2026-11-13 19:41:28 +02:00
Ahmed Lukman ab685a7f6f Fix integration deletion acknowledging missing records
Reuse the board-scoped integration lookup before deleting, after board-admin
authorization. Missing, wrong-board and already deleted integrations now return
the existing JSON 404 response instead of acknowledging an unmatched selector.

Extend route regressions for deletion, authorization and database errors. Add
live API and browser coverage for rejected and successful deletions, persisted
state and retention of other webhooks in settings.
2026-11-13 19:01:54 +03:00
Lauri Ojansivu 24d5b2cdfd CHANGELOG: file-integrity baselines of deleted files, the checklist resetInterval index, REST custom-field and ownership fixes.
Thanks to Raymond-Voogt, AhmedLukman and xet7 !
2026-11-13 17:45:45 +02:00
Lauri Ojansivu ec05ff0089 File-integrity scan no longer reports files WeKan deleted itself as missing.
The daily scan keeps one baseline per stored file and reports every baseline
whose file is gone as a StorageBleed. Nothing ever removed a baseline, so
every attachment purged by retention and every avatar deleted was reported
missing on every run from then on - one finding folded 1,164 times.

The baseline is now forgotten where WeKan deletes or moves a local file:
- Attachments and Avatars onAfterRemove, for every version path. This is
  the retention purge's path: Meteor-Files unlinks the version paths itself
  after that hook, without going through the store strategy, so a fix in
  the strategy's unlink() alone would not have covered it;
- the filesystem strategy's unlink() (moving a file to GridFS or cloud);
- the filesystem strategy's rename(), for the old path.

A file that vanishes any other way keeps its baseline and is still reported.
The removal is fire-and-forget and can never break a delete.

Baselines already orphaned before this fix stay reported until removed, as
described in the issue.

Thanks to Raymond-Voogt and xet7 !

Fixes #6898.
2026-11-13 17:45:05 +02:00
Lauri Ojansivu c37575f1fa Checklists have a partial index on resetInterval for the recurring-reset cron.
The recurring-checklist reset cron queries
resetInterval: { $exists: true, $nin: [null, 'none'] } on every tick, and with
no index that read the whole checklists collection (about 45,000 documents on
the reporting instance), reported as db.collection-scan on Admin Panel ->
Problems. A partial index on resetInterval, like the one #6825 added for
recurring cards, holds only the recurring checklists.

Thanks to Raymond-Voogt and xet7 !

Fixes #6899.
2026-11-13 17:45:05 +02:00
Lauri Ojansivu a8e25952aa REST takeOwnership awaits the board update, and removeMember awaits its user lookup.
PUT /api/users/:userId with action takeOwnership called the async
board.changeOwnership() without await: the reply was sent before the board
was changed, and a failed update was an unhandled rejection behind a 200.

Boards.removeMember tested `!ReactiveCache.getUser(memberId)`. On the server
that lookup is async and returns a Promise, which is always truthy, so the
#5330 branch that hard-removes a member whose account was deleted never ran
on the server; such a member was only deactivated and lingered.

Tests pin both, and fail the tree for any un-awaited changeOwnership() or
negated un-awaited ReactiveCache lookup in server/ and models/.

Thanks to xet7 !
2026-11-13 17:45:05 +02:00
Lauri Ojansivu 22bf506ea1 Merge pull request #6896 from AhmedLukman/codex/fix-ownership-board-selection
Fix ownership transfer selecting ordinary board memberships
2026-11-13 17:23:36 +02:00
Lauri Ojansivu 41ff6da28d Merge pull request #6897 from AhmedLukman/codex/fix-custom-field-update-atomicity
Fix partial REST custom-field updates
2026-11-13 17:23:19 +02:00
Ahmed Lukman a81ec6ecdf Fix partial REST custom-field updates
Save supported custom-field properties in one board-scoped update so schema validation rejects the entire change before any accompanying fields persist. Preserve the existing authorization, field allowlist and response behavior.

Add handler regressions and live database/browser coverage for rejected settings and types, accepted updates, false values, omitted fields and board scoping.
2026-11-13 17:00:17 +03:00
Ahmed Lukman 4b4db92a1b Fix ownership transfer selecting ordinary board memberships
Match the target user and administrator flag within the same board member, following the existing membership query pattern. Leave boards where the target is only a normal member unchanged.

Cover selection, successful transfers, unchanged unrelated memberships, repeated requests, authorization, and retained browser access.
2026-11-13 11:51:40 +03:00
github-actions[bot] e2bf75b774 Bump versions for v13.10 2026-11-13 04:11:48 +00:00
Lauri Ojansivu d6c7dbcde2 Prepare v13.10 release 2026-11-13 06:10:46 +02:00
Lauri Ojansivu 8b44646314 CHANGELOG: every snapcraft install attempt is bounded, and snap-auth-check has room for all five.
Thanks to xet7 !
2026-11-13 06:06:16 +02:00
Lauri Ojansivu 560ff74a79 Release: every snapcraft install attempt is bounded, and snap-auth-check has room for all five.
In the v13.09 release the Snap Store failed snap-auth-check's first two
install attempts and then let the third hang with no output, until the job's
10-minute timeout cancelled it - the retry added for v13.07 never got its
remaining turns. Each `snap install snapcraft` in release-all.yml now runs
under `timeout 180`, and an attempt cut short is aborted (`snap abort
--last=install`) so the next one does not meet snapd's change still in
progress. snap-auth-check's job timeout is 25 minutes, enough for five
attempts 30 seconds apart and the whoami after them.

tests/snapAuthPreflight.test.cjs requires every install in the workflows to
be bounded and followed by the abort, and fails when snap-auth-check's job
timeout cannot hold all five attempts.

Thanks to xet7 !
2026-11-13 06:05:56 +02:00
github-actions[bot] 7347ae8f9d Bump versions for v13.09 2026-11-12 19:07:47 +00:00
Lauri Ojansivu 6cbb026f14 Prepare v13.09 release 2026-11-12 21:06:44 +02:00
Lauri Ojansivu 80253cf5e1 Merge remote-tracking branch 'origin/main' 2026-11-12 21:06:21 +02:00
Lauri Ojansivu d8fc23a4f4 CHANGELOG: snap-auth-check retries installing snapcraft.
Thanks to xet7 !
2026-11-12 21:02:13 +02:00
Lauri Ojansivu 534646347c Release: snap-auth-check retries installing snapcraft, as the build job does.
The v13.08 release stopped in snap-auth-check before it asked the Snap Store
anything: "cannot install system snap snapd: Post
https://api.snapcraft.io/v2/snaps/refresh: ... Client.Timeout exceeded while
awaiting headers". The job installed snapcraft once, while the snap build
job already retried five times, 30 seconds apart. It retries the same way now,
and when every attempt fails it says the store did not answer - not that
SNAP_AUTH is wrong.

tests/snapAuthPreflight.test.cjs fails on any snapcraft install in any
workflow without the five-attempt retry, and on the outage being reported as
a credential problem.

Thanks to xet7 !
2026-11-12 21:01:37 +02:00
github-actions[bot] 779ba553ce Bump versions for v13.08 2026-11-12 17:15:00 +00:00
Lauri Ojansivu 3144a578b5 Prepare v13.08 release 2026-11-12 19:13:48 +02:00
Lauri Ojansivu 82a30561d7 CHANGELOG: list-sync-estimate's checkbox test and index order.
Thanks to xet7 !
2026-11-12 17:03:34 +02:00
Lauri Ojansivu af76406d12 Tests: list-sync-estimate's checkbox test no longer depends on index order.
"dotted checkbox writes emit false once and removing a valued field emits an
unset" read the card's activities with an unsorted find and expected them in
insertion order. On FerretDB with the pushdown of plain equality, that find is
served by activities' { cardId: 1, createdAt: -1 } index and returns the newest
first, so it failed every time against Weddy, which creates WeKan's indexes,
with ['unsetCustomField', 'setCustomField']. The order of an unsorted find is
no contract on MongoDB either once an index serves it; what the test pins is
one set and one unset, so it now compares the activity types sorted.

Thanks to xet7 !
2026-11-12 17:03:33 +02:00
Lauri Ojansivu f57ea060fe CHANGELOG: CodeQL alert 604 in the security alert download script.
Thanks to GitHub CodeQL and xet7 !
2026-11-12 16:07:40 +02:00
Lauri Ojansivu ed19817433 releases/sec: SUMMARY.md carries no secret scanning field, and kept secrets are owner-only.
Code scanning alert #604 (py/clear-text-storage-sensitive-data, CWE-312):
download_github_security.py wrote a secret scanning alert's
secret_type_display_name into SUMMARY.md. That is the secret's type, not the
secret, but SUMMARY.md is the one file that is never redacted and the one most
likely to be pasted somewhere, so it now says only "Secret scanning alert" and
points at secret-scanning/, where the type still is. When
--include-secret-values keeps the secrets, the files holding them are created
readable by their owner only.

tests/securityDownloadScript.test.cjs checks the summary of a redacted and of a
kept download for the secret, its type and its type name, the 0600 mode of
kept secrets, and that describe() reads no field of a secret scanning alert
and every secret scanning write passes the private flag.

Thanks to GitHub CodeQL and xet7 !
2026-11-12 16:07:39 +02:00
Lauri Ojansivu 2b897dbf63 CHANGELOG: REST card parent, integration URL validation, and the single-file 32-bit host Node.js.
Thanks to AhmedLukman and xet7 !
2026-11-12 16:06:09 +02:00
Lauri Ojansivu c35c672ffb Single-file: the 32-bit host Node.js is the target bundle's exact version.
The v13.07 single files for armhf, armv6 and armv7 all stopped on "The
bundle's Node.js is v26.13.0 but this is v26.13.1: a single executable must
be built by the same Node.js version it embeds." A 32-bit target's SEA blob is
written by a 32-bit Node.js, and the workflow borrowed the i386 bundle's.
Every bundle takes the newest Node.js of the pinned major its platform has, so
i386 had v26.13.1 from wekan/node-patches while 32-bit ARM still had v26.13.0.

The host is now resolved with releases/resolve-node-source.sh at the target
bundle's exact version, checked against its published SHA256, and refused
unless it reports that version. libatomic1:i386, which the bundle used to
supply in node-runtime/, comes from apt, so HOST_LIBS is gone.

tests/singleFile.test.cjs pins the exact version, the checksum before the
first run, and that the host is never taken from another platform's bundle.

Thanks to xet7 !
2026-11-12 16:05:46 +02:00
Lauri Ojansivu 642ba6e876 Merge pull request #6895 from AhmedLukman/codex/fix-integration-update-validation
Validate integration URLs before updating other fields
2026-11-12 15:59:53 +02:00
Lauri Ojansivu e9c5aa2cae Merge pull request #6894 from AhmedLukman/codex/fix-rest-card-parent
Fix parent assignment when creating cards through REST
2026-11-12 15:59:37 +02:00
Ahmed Lukman 1418cb8480 Validate integration URLs before updating other fields 2026-11-12 13:21:00 +03:00
Ahmed Lukman 552950ed63 Fix parent assignment when creating cards through REST
Read parentId from the request body so single-card creation validates and persists the requested parent, matching the bulk endpoint. Cover persistence, subtask rendering, empty parents and rejected creation requests.
2026-11-12 12:48:09 +03:00
github-actions[bot] e1e0006a35 Bump versions for v13.07 2026-11-12 06:34:24 +00:00
Lauri Ojansivu 6f23e187ab Prepare v13.07 release 2026-11-12 07:15:19 +02:00
Lauri Ojansivu ab8c3452a8 CHANGELOG: Admin Panel Problems Clear button and event retention.
Thanks to Raymond-Voogt and xet7 !
2026-11-11 22:30:06 +02:00
Lauri Ojansivu fefabbf113 Admin Panel Problems: a Clear button, and resolved problems age out.
The problem streams were append-only and Acknowledge only reset the "new"
count, so a problem fixed long ago stayed in the table and buried the live
ones. Clear, beside Acknowledge, calls the admin-only clearEventLog, which
deletes the checked streams' events up to now and stamps the
acknowledgement; anything that happens after the click shows as new again,
and an unknown stream clears nothing. A daily sweep, claimed once per
installation, deletes events older than EVENTLOG_RETENTION_DAYS (default 90,
0 keeps them forever); a recurring problem's folded row keeps a recent date,
so it stays. The setting is listed on every platform, and clear-problems is
translated to every language.

Thanks to Raymond-Voogt and xet7 !

Fixes #6893,
2026-11-11 22:29:01 +02:00
Lauri Ojansivu 50ecd8944f CHANGELOG: ListLockAttachBleed, spent time as a duration, REST token deletion and the missing indexes.
Thanks to Furkan Derse, brlin-tw, AhmedLukman, Raymond-Voogt and xet7 !
2026-11-11 21:05:44 +02:00
Lauri Ojansivu 7bb3e13ba9 ListLockAttachBleed: a list's write refusal covers the attachment API.
A board admin can refuse a role write access in one list (#4527,
roleExceptions). The attachment API's upload, delete, copy and move, over
REST and DDP, asked only for board write access and mayWriteCardAttachment,
which allows every card to a member who is not assigned-only, so a normal
member the list refused still added and removed files on that list's cards.

Every attachment write that names a card now also asks mayWriteInCardList
with that card, and a move asks both the card it takes the file from and the
card it puts it on. The card's own board decides. As for the REST card
routes, only a list's refusal counts. A refusal is recorded in Admin Panel ->
Problems as ListLockAttachBleed (authz.list-locked-attachment, medium).

tests/listLockAttachments.test.cjs runs the guard on the reported case and
the negative cases, and fails on any mayWriteCardAttachment call in the tree
without the list check beside it; list-lock-attachments.e2e.js replays the
writes against a running server.

Thanks to Furkan Derse and xet7 !
2026-11-11 20:58:01 +02:00
Lauri Ojansivu 8dc77a89d8 Indexes for the LDAP id, cards' assignees and members, and History by entity and hash.
On a large installation the database-problems page filled with collection
scans of queries WeKan makes constantly but never indexed: a user looked
up by services.ldap.id at every LDAP login and sync pass (13,000+ users,
~430 ms per login), cards by assignees or members across every board for
My Cards and the filters (1.1M cards, 1-3.7 s, dozens of times a minute),
and changeHistory by entity and by integrity hash (120,000+ rows, and
growing). Each now has an index, created at startup with ensureIndex like
every other one: services.ldap.id sparse, assignees and members both so
their $or uses an index on each branch, and changeHistory's
{ entityType, entityId, createdAt } and { integrityHash }.

Thanks to Raymond-Voogt and xet7 !

Fixes #6892.
2026-11-11 20:29:31 +02:00
Lauri Ojansivu 9f5ab97a3c Spent time: the formatter imported as cardTime.js's other dependencies are.
A require() line stayed in the script tests/acceptedFixesLogic.test.cjs
evaluates, which strips imports, and failed it.

Thanks to brlin-tw and xet7 !
2026-11-11 20:29:31 +02:00
Lauri Ojansivu 3e8e8039ff Spent time is shown as a duration, not decimal hours.
A card's Spent time is stored in decimal hours, and a Pomodoro or Flowtime
session adds its duration to it, so a short session left a badge such as
0.034722222222222224. The badge shows days, hours, minutes and seconds now,
the leading zero units left out (12h34m56s, 2m5s), and the tooltip that
names hours rounds them to two decimals. The stored value and the edit
popup keep their hours. models/lib/spentTimeFormat.js, with a unit test
and a UI test.

Thanks to brlin-tw and xet7 !

Fixes #6890.
2026-11-11 20:20:55 +02:00
Lauri Ojansivu 4bdbded5d9 Merge remote-tracking branch 'origin/main' 2026-11-11 20:13:29 +02:00
Lauri Ojansivu dce58125c9 Merge pull request #6891 from AhmedLukman/codex/fix-rest-token-revocation
Await REST token deletion before reporting success
2026-11-11 19:54:56 +02:00
Ahmed Lukman fd59a8ff70 Await REST token deletion before reporting success
Wait for Accounts.destroyToken to finish so deletion completes before a success response and rejected database writes reach the existing error handler.

Add regression coverage for delayed and failed deletion, authorization, existing token input behavior, session isolation and browser logout.
2026-11-11 14:43:56 +03:00
Lauri Ojansivu 14a2fa9f43 CHANGELOG: releases/sec's GitHub security alert download script.
Thanks to xet7 !
2026-11-11 04:26:44 +02:00
Lauri Ojansivu 801f8956fe releases/sec: download a GitHub organization's Security and quality alerts with gh.
releases/sec/download-github-security.sh ORGANIZATION saves, for every
repository of the organization (or the ones named with --repo), its
Dependabot, code scanning, secret scanning and security advisory alerts
to .tools/log/sec/<organization>/<repository>/<YYYY-MM-DD_HH-MM-SS>/,
a directory per kind: alerts.json, a file per alert, an open code
scanning alert's instances, and SUMMARY.md with every open alert and its
link. It runs the GitHub CLI outside the editor's sandbox, where gh and
its login are, so the alerts can then be read and fixed from the files
inside it. A kind GitHub does not answer - not enabled, or not visible
to the account - gets status.txt with the reason and the run goes on;
archived repositories and forks are skipped unless asked for; the secret
a secret scanning alert found is redacted unless --include-secret-values
is given. A name that is not a GitHub name is refused before it becomes
a path or an API address. .tools/ keeps the downloads out of git.

tests/securityDownloadScript.test.cjs runs it against a stand-in gh: the
layout, both pages of a paginated answer, the summary, a disabled kind,
the redaction everywhere in the download and, as negatives, bad names,
no gh and gh signed out writing nothing. buildScriptParity knows why
build.sh has no menu entry for it.

Thanks to xet7 !
2026-11-11 04:25:18 +02:00
Lauri Ojansivu 4335614dcb CHANGELOG: CodeQL alerts #602 and #603 in the tests, and fast-xml-parser 5.12.0 for Dependabot alert #188.
Thanks to GitHub CodeQL, dependabot and xet7 !
2026-11-11 03:44:57 +02:00
Lauri Ojansivu a76a19058a Updated fast-xml-parser to 5.12.0, Dependabot alert #188.
fast-xml-parser <= 5.11.2, with its stopNodes option, rescanned the rest
of the input from every "<" of a stop node's body when no ">" followed,
so n such fragments cost O(n^2) CPU before it gave up (CWE-407). The
Azure and Google Cloud storage clients bring it in; each allows 5.x, so
the lock moves to 5.12.0 - which stops at the first failed scan - with
is-unsafe 2.0.3 and strnum 2.5.1, and no override.

tests/dependencySecurityUpdates.test.cjs pins that no tracked lockfile
resolves a version below 5.12.0, with a negative that the finder flags
5.11.2 at any depth, and runs the alert's input: 40,000 unterminated
fragments in a stop node are refused within two seconds. On 5.11.2 that
input grows quadratically (20,000 fragments took 1.2 s here, so 40,000
take about 5 s); on 5.12.0 it takes about a millisecond. A dependency
inside a storage client is not an attempt anybody makes, so there is
nothing to record in Admin Panel -> Problems.

Thanks to dependabot and xet7 !
2026-11-11 03:44:13 +02:00
Lauri Ojansivu 2b65b713a0 Tests: no path handed to a shell at all, CodeQL alerts #602 and #603.
The fix for #601 passed the paths to `bash -c` as arguments ("$1",
"$2") instead of pasting them into the script. That is safe in itself,
but it still hands a temporary or checkout path to a shell, and CodeQL
flagged js/shell-command-injection-from-environment again on the two
umask tests in tests/releaseDownloads.test.cjs. Those tests no longer
use a shell: fetch.sh runs as bash's program with its arguments, the
umask is set on the test's own process around the call and put back,
and `chmod +x` is fs.chmodSync. tests/macosSandbox.test.cjs, fixed the
same way for #601, now runs ls and touch as programs of sandbox-exec,
each path an argument.

The guard (tests/testShellPathsFromEnv.test.cjs) now also refuses any
value given to a shell's -c script as an argument - spawned or as a
wrapper's program - and the one other test that did so,
tests/dbConformanceWiring.test.cjs, passes its value in the environment.
Run against the files as they were, it names all three; its pattern
takes a backslash only as an escape, so a long file cannot make it
backtrack.

The findings were in tests, not in WeKan, so there is no attempt to
show in Admin Panel -> Problems.

Thanks to GitHub CodeQL and xet7 !
2026-11-11 03:44:13 +02:00
github-actions[bot] 77239ee100 Bump versions for v13.06 2026-11-10 22:50:32 +00:00
Lauri Ojansivu 3913fbd507 Prepare v13.06 release 2026-11-11 00:49:29 +02:00
Lauri Ojansivu c45359a759 CHANGELOG: CodeQL alert #601 and its siblings in the tests; the oldest October release archived to stay under GitHub's limit.
Thanks to GitHub CodeQL and xet7 !
2026-11-11 00:38:03 +02:00
Lauri Ojansivu d482856e03 Tests: no path is quoted into a bash -c script, CodeQL alert #601 and its siblings.
CodeQL flagged js/shell-command-injection-from-environment (CWE-78/88) in
tests/npmRetry.test.cjs: its bash -c script set
node_bin=${JSON.stringify(prog)} and ran bash ${JSON.stringify(script)},
pasting a path under the temporary directory and one under the checkout
into the script text. JSON's quotes are not the shell's: a path with $(,
a backtick or $ would change the command. The paths now go in through
the environment ("$NODE_BIN", "$NPM_RETRY_SCRIPT"), so the script is a
constant - and NODE_BIN is not node_bin, so the test still shows the
unbound variable it exists to show.

The guard written for alerts #549 and #550 (tests/testShellPathsFromEnv
.test.cjs) only knew `source "${path}"`. It now refuses any value quoted
into a -c script - "${...}" or ${JSON.stringify(...)} - across the tests,
while a script built from the test's own snippets stays allowed; with
the fixes taken away it names every offender. It found the same fault in
two more places, fixed as arguments ("$1", "$2"):
tests/releaseDownloads.test.cjs, which quoted fetch.sh and a temporary
file into its umask tests, and tests/macosSandbox.test.cjs, which quoted
the home and temporary paths into its sandbox probe.

The finding was in tests, not in WeKan, so there is no attempt to show
in Admin Panel -> Problems.

Thanks to GitHub CodeQL and xet7 !
2026-11-11 00:37:39 +02:00
Lauri Ojansivu 72ee10a3ed CHANGELOG: the merged REST overtime and card member fixes, their follow-ups, and cleared card dates.
Thanks to AhmedLukman and xet7 !
2026-11-11 00:08:21 +02:00
Lauri Ojansivu 32590ee7ab Card members and assignees: adding one whose account is gone no longer throws.
#6889 guarded the removal halves of the cardMembers and cardAssignees
hooks, which read user.username after a lookup that finds nobody for a
deleted account. The joining halves had the same read: the REST route
checks the user before adding, but a client $addToSet, a copied or
imported card, or a rule can still add such an id, and the hook threw.
Both now record the join with the id and no username, as a removal does.

tests/cardMemberRemoval.test.cjs runs the real hooks: adding a member
and an assignee whose account is gone records the join without
throwing; and, tree-wide, no `const x = user.field;` follows a
ReactiveCache.getUser() lookup without a guard.

Thanks to AhmedLukman and xet7 !
2026-11-11 00:07:53 +02:00
Lauri Ojansivu 569756b66c REST card overtime: an isOverTime stored before #6888 becomes isOvertime.
#6888 makes the REST card PUT write its isOverTime parameter to the
schema's isOvertime field. Every overtime set through the API before it
- since b895e61237 - was stored under isOverTime, which the UI and every
reader ignore: those cards still show no overtime.

A schema upgrade step, card-overtime-field, carries that over on the
next start: isOverTime becomes isOvertime where the card has none, the
value the UI already shows wins where it has both, and the stray field
is removed. Two updateMany batches and an $unset, version-gated and
idempotent like the steps beside it.

tests/schemaUpgradeSteps.test.cjs pins the fold, the precedence and a
second pass finding nothing; as negatives, no work on cards without the
field, and - tree-wide - no server code writing isOverTime into a card.

Thanks to AhmedLukman and xet7 !
2026-11-11 00:07:26 +02:00
Lauri Ojansivu 50334816ca Card dates: clearing one no longer goes through the timing hook as a set.
The Cards.before.update timing hook read modifier.$set[field] for any
update whose first field was a date, but unsetDue/unsetStart/unsetEnd/
unsetReceived send only a $unset. Without a $set the hook threw a
TypeError; with one beside the $unset (a stamped modifiedAt) it wrote an
a-dueAt with timeValue: undefined, which counted as a due date change
(#6081), matched the "date set or changed" rule triggers (#2474), and
showed in the activity feed as an empty date.

server/lib/timingChangeActivity.js now decides it: a date set records
timeValue, a date cleared records timeOldValue alone - the same record a
rule's removeDate writes, and the one models/lib/boardTimeline.js rewinds
- and clearing a date the card does not have records nothing. The rules
skip a cleared date before matching triggers, as server/triggersDef.js
already said they should, and the feed shows only the date it had.

tests/cardTimingActivity.test.cjs runs the real hook: a set, a $unset
alone and a $unset beside a $set (both used to fail), and clearing
nothing; and, tree-wide, no modifier.$set[...] read that assumes the $set
is there. The two guards that pinned the old inline list and its
modifier.$set[action] read now pin the helper instead.

Thanks to xet7 !
2026-11-11 00:06:31 +02:00
Lauri Ojansivu 9821478329 Merge pull request #6889 from AhmedLukman/codex/card-member-removal
Fix removal of card members and assignees with missing user accounts
2026-11-10 23:49:48 +02:00
Lauri Ojansivu ad5c35e9f3 Merge pull request #6888 from AhmedLukman/codex/rest-card-overtime
Fix REST card overtime persistence
2026-11-10 23:49:23 +02:00
Ahmed Lukman 416c69dfe2 Revert "Use optional chaining for removed assignment users"
This reverts commit 0acc9955d0.

Thanks to AhmedLukman and xet7 !
2026-11-10 16:59:39 +03:00
Ahmed Lukman 0acc9955d0 Use optional chaining for removed assignment users
Read the optional username safely when removing a card member or assignee whose account is missing.

Thanks to AhmedLukman and xet7 !
2026-11-10 16:52:39 +03:00
Ahmed Lukman 5e9d1cb630 Fix removal of missing card members and assignees
Allow card assignment removal when the target user no longer exists. Keep normal membership activity and no-op behavior intact, with unit and REST/browser regression coverage.

Thanks to AhmedLukman and xet7 !
2026-11-10 16:27:40 +03:00
github-actions[bot] 4f6832845d Bump versions for v13.05 2026-11-10 09:55:03 +00:00
Lauri Ojansivu 1ca199b607 Prepare v13.05 release 2026-11-10 11:54:10 +02:00
Lauri Ojansivu 63c6f42495 CHANGELOG: updated to Meteor 3.6.
Thanks to xet7 !
2026-11-10 09:20:28 +02:00
Lauri Ojansivu 0fbef8936f Updated to Meteor 3.6.
From 3.6-rc.1. `meteor update --release METEOR@3.6` refused it: WeKan's
forks of six Meteor packages in packages/ (allow-deny, ddp-server,
dynamic-import, facebook-oauth, google-oauth, shell-server) declared
3.6-rc.1's versions with a _1 wrap number, and a local package
overrides the release's, so ~3.6.0 could not be met. Meteor 3.6 changed
those six packages only in their version line (compared on GitHub,
release/METEOR@3.6-rc.1...release/METEOR@3.6), so each fork now
declares 3.6's version with its _1 wrap number; .meteor/packages drops
the rc pins, and .meteor/versions, @meteorjs/rspack 3.0.0 and the
lockfile are what meteor update wrote. webapp is 2.4.0.

The Dockerfile's METEOR_RELEASE is METEOR@3.6. The Bun build's patch
applies unchanged to Meteor 3.6, which still has no Bun ESM loader of
its own: it is meteor-3.6.patch, checked against 3.6's commit
e730097c. tests/meteor353SecurityFixes.test.cjs is moved to 3.6 by
decision, after that check. WeKan builds with Meteor 3.6, and the node
suites pass (2162 run, 0 failed).

Thanks to xet7 !
2026-11-10 09:20:28 +02:00
Lauri Ojansivu 0905de097b CHANGELOG: the Super Productivity fix's link names its commit after the rebase.
Thanks to xet7 !
2026-11-10 08:48:52 +02:00
Lauri Ojansivu 679ea9e63a CHANGELOG: the Android bundles build again.
Thanks to xet7 !
2026-11-10 08:48:37 +02:00
Lauri Ojansivu d08f658dea Android bundles build again: npm-retry.sh is given Android's node and npm, not a shell function.
v13.04's four Android bundle jobs all died with "environment: node:
unbound variable". v13.04 sent the Android npm installs through
releases/npm-retry.sh as `npm-retry.sh npm install`, and npm there is
build-bundle.sh's function npm() { "$node" "$npm_cli" ...; }.
npm-retry.sh is a new bash with set -u: export -f carried the function
into it, but not $node or $npm_cli, so the function failed before npm
ran - and the retry wrapper rightly called it not a network error.

The two installs now give npm-retry.sh the program itself, "$node"
"$npm_cli" install, still retried on a network error as before.
tests/npmRetry.test.cjs fails when any releases/ script gives
npm-retry.sh one of its own shell functions (it fails on the old two
lines), and reproduces the failure: an exported function without its
variable fails in npm-retry.sh, the program it runs works.

Thanks to xet7 !
2026-11-10 08:48:31 +02:00
Lauri Ojansivu dcd9fec925 CHANGELOG: the Super Productivity export no longer fails on a card whose list is archived.
Opens the Upcoming section. The fix is covered by a positive and a
negative check in tests/superProductivityFormat.test.cjs, which fails
on the old formatter; it is a pure formatter behind the REST export,
so no browser test was added, and WeKan's own server was not run here.

Thanks to xet7 !
2026-11-10 08:18:49 +02:00
Lauri Ojansivu 8b4fe0e737 Super Productivity export no longer fails on a card whose list is archived.
A card whose list is archived is collected with listTitle '', and the
Super Productivity formatter tagged such a card with tagFor(''), which
is undefined - the export then threw "Cannot read properties of
undefined (reading 'taskIds')" and the whole board's export failed.
The list with no title is now "Untitled", its tag and its panel alike,
so the panel shows its cards and every task's tags exist. The test
fails with that error on the old formatter.

Found while checking Weddy's Super Productivity export against
WeKan's formatter.

Thanks to xet7 !
2026-11-10 08:18:49 +02:00
github-actions[bot] bc698806b2 Bump versions for v13.04 2026-11-09 18:28:55 +00:00
Lauri Ojansivu 0556f01030 Prepare v13.04 release 2026-11-09 20:24:26 +02:00
Lauri Ojansivu b379a02631 CHANGELOG: the Sandstorm and Android bundles retry their npm install through a GitHub outage.
Thanks to xet7 !
2026-11-09 20:02:11 +02:00
Lauri Ojansivu c3f12b5659 Sandstorm and Android bundles retry their npm install through a GitHub outage.
v13.03's build-sandstorm died in releases/sandstorm-slim-spk.sh with
"npm error 504 Gateway Time-out - GET https://github.com/meteor/
node-source-map-support/tarball/81bce1f9..." - the same github.com outage
releases/npm-retry.sh was written for in v10.86. That install, and the
Android bundle's programs/server and sharp installs, ran npm bare, because
tests/npmRetry.test.cjs read only release-all.yml and never the scripts it
runs. All three now go through npm-retry.sh: a transient 5xx or socket
error is retried with backoff, a real npm error still fails at once.

tests/npmRetry.test.cjs now follows every releases/ script a workflow runs,
and the scripts those run, and fails on a bare npm install or ci in any of
them; it fails on the old lines. tests/androidBundles.test.cjs pins the same
installs in the same order, now through the retry.

Thanks to xet7 !
2026-11-09 20:01:57 +02:00
Lauri Ojansivu ea53de4985 CHANGELOG: ListLockBleed; the security regression inventory guards it.
The Upcoming release fixes the CRITICAL SECURITY ISSUE of ListLockBleed
(GHSA-4734-8vrg-8j3w): a list that refuses a role write access refuses that
role's checklist and checklist-item REST writes too.
tests/securityRegressionCoverage.test.cjs names its guards
(tests/listPermissions.test.cjs and
tests/playwright/specs/list-lock-checklists.e2e.js) and counts 147.

Thanks to tsurutanmen and xet7 !
2026-11-09 19:44:17 +02:00
Lauri Ojansivu 214b20e7ad ListLockBleed: a list's write refusal holds for the checklist REST routes too.
GHSA-4734-8vrg-8j3w. A board admin can refuse a role write access in one
list (#4527, list.roleExceptions). The card and comment REST routes and the
DDP checklist writes applied that refusal; the six checklist and
checklist-item REST routes asked only the board, so a member the list
refused still created, renamed, ticked and deleted its cards' checklists
and items with an API token.

They now call assertListNotRefused with the card's list, as the card routes
do, and answer 403. The refusal is recorded in Admin Panel -> Problems as
ListLockBleed (authz.list-locked, medium: recorded, never disables the
account): no screen offers such a write, so it is the lock being stepped
round. The card routes' refusals are recorded through the same gate.

tests/listPermissions.test.cjs scans every REST write route of the tree that
names a card and fails on one that does not ask the list - not only the
per-list card routes it checked before - naming the three that follow their
own rule (a comment's edit and deletion, dependency lines, a bot run's
report), and checks the refusal is recorded. It fails on the old routes.
tests/playwright/specs/list-lock-checklists.e2e.js refuses all six writes in
a locked list with 403, leaves the checklist and item untouched, records the
attempt, and still lets the same member write in an open list and the board
admin in the locked one.

Thanks to tsurutanmen and xet7 !
2026-11-09 19:41:49 +02:00
github-actions[bot] 0f89ad07b8 Bump versions for v13.03 2026-11-09 07:49:50 +00:00
Lauri Ojansivu 3fb9e4d1be Prepare v13.03 release 2026-11-09 09:47:39 +02:00
Lauri Ojansivu 5ca5b00a98 CHANGELOG: amd64 snaps wait out an archive outage; UnassignedArchiveBleed inventory.
Thanks to xet7 !
2026-11-09 09:08:09 +02:00
Lauri Ojansivu 6cc84a07cd Security regression inventory: UnassignedArchiveBleed is guarded.
The Hall of Fame published UnassignedArchiveBleed (GHSA-6xqf-jh8x-4wxc), so
tests/securityRegressionCoverage.test.cjs failed: the companion list had a
name the inventory did not. It is in GUARDED now, with the two suites that
pin the fix, and those suites name it.

Thanks to tsurutanmen and xet7 !
2026-11-09 09:07:51 +02:00
Lauri Ojansivu a7ace18af8 Snap: wait out an Ubuntu archive outage instead of failing the amd64 snaps.
v13.02's snap-native amd64 and both amd64 snap-variants failed within eight
minutes of each other with "Failed to refresh package list: failed to run
apt update" while snapcraft installed build-packages, and the arm64 snaps,
served by ports.ubuntu.com, built. releases/snap-refresh-managed-apt.py
recovered only a stale .deb 404 and refused this, so the one guarded retry
never ran.

It now also recognises a failed apt update in the managed project instance,
retries apt-get update there with a 30/60/120/240 s backoff until the archive
answers, and only then lets the Snapcraft retry run; an outage that outlasts
the backoff stays fatal. It prints apt's own E:/W:/Err: lines from the
Snapcraft log first, so a failing job says why apt failed. Anything else is
still refused.

Thanks to xet7 !
2026-11-09 09:07:51 +02:00
github-actions[bot] 7e0fd84c3c Bump versions for v13.02 2026-11-09 02:14:45 +00:00
Lauri Ojansivu 4023654d84 Prepare v13.02 release 2026-11-09 04:13:46 +02:00
Lauri Ojansivu d213e7b674 Merge remote-tracking branch 'origin/main' 2026-11-09 04:08:15 +02:00
Lauri Ojansivu 423ac8a145 German: "Due next week" filter says "Nächste Woche fällig", not "Diese Woche fällig".
The filter sidebar's due date options "Due this week" and "Due next week"
had the same German label, so two options returning different cards could
not be told apart. Fixed in de, de-AT, de_DE and de-CH, and the same copied
label in lb, sv and ts. In ak, "Due today" and "Due next week" both said
"search this"; both now say what they filter (low confidence for ak, lb
and ts).

tests/dueFilterDistinctLabels.test.cjs pins the German labels and fails
when any language gives two due date options the same label.

Thanks to d0g2342 and xet7 !

Fixes #6887.
2026-11-08 21:16:56 +02:00
Lauri Ojansivu 913acde0ef CHANGELOG: UnassignedArchiveBleed, GHSA-6xqf-jh8x-4wxc.
Thanks to tsurutanmen and xet7 !
2026-11-08 14:52:11 +02:00
Lauri Ojansivu 1513b2e98c UnassignedArchiveBleed: an assigned-only member archives and converts only the cards assigned to them.
GHSA-6xqf-jh8x-4wxc. The DDP methods archiveSelectedCards and
createBoardFromCard asked only for the board's write capability. The
Normal, assigned only role has it, but sees and changes only its own
cards, so such a member who knew a card's id archived any card of the
board - and its subtasks - and turned any card into a linked-board
card. Both methods now also ask assignedOnlyAllowsCard, as copyCard
does; archiveSelectedCards checks every card of the selection before it
archives the first, so a refused selection changes nothing.

A refusal is recorded in Admin Panel -> Problems as
UnassignedArchiveBleed (authz.assigned-card-write, medium, never
disabling the account). tests/assignedCardMethods.test.cjs drives the
reported case and fails for any server method that takes a card id and
decides by the board's write access alone;
tests/playwright/specs/assigned-card-methods.e2e.js replays the
reporter's calls against a running server.

Thanks to tsurutanmen and xet7 !
2026-11-08 14:50:53 +02:00
github-actions[bot] 246ba44838 Bump versions for v13.01 2026-11-07 23:51:55 +00:00
Lauri Ojansivu 1eda4f256e Prepare v13.01 release 2026-11-08 01:49:05 +02:00
Lauri Ojansivu f83ee4d320 CHANGELOG: Dependabot's updates #6879-#6886, checked and kept.
Thanks to xet7 !
2026-11-08 01:48:20 +02:00
Lauri Ojansivu e43dc0070f CHANGELOG: translations and the S3 client; the Cloud Panel is Weddy's.
Thanks to xet7 !
2026-11-08 00:38:40 +02:00
Lauri Ojansivu 579de7d6af The Cloud Panel is Weddy's, not WeKan's: WeKan stays Meteor 3.
Admin Panel / People / Cloud and the Cloud Panel (73db2a28c3) are removed from
WeKan, with their methods, route, menu item, docs and tests. Many domains from
one Weddy is a Weddy feature: there every domain's WeKan is in one process,
where Node.js WeKan containers would have to work together, which gets
complicated. It is written in Go in caddy-wekan-plugin.

The translations of its strings stay: WeKan, Weddy and the rest use the same
WeKan translations.

Thanks to xet7 !
2026-11-08 00:38:40 +02:00
Lauri Ojansivu fb8021ba96 Cloud Panel docs: Weddy runs WeKan in Go, data instead of the Caddy v1-looking root.
The Caddyfile example used `root /path`, which tests/caddyfileExamplesV2.test.cjs
rightly reads as Caddy v1. Weddy's wekan option is now `data`, and WeKan runs
inside Weddy in Go - no bundle, no WEDDY_TENANT - so the page says so, and that
the Cloud Panel's files are written by hand or by a Meteor WeKan until its Go
pages are done.

Thanks to xet7 !
2026-11-08 00:21:56 +02:00
Lauri Ojansivu fe0e187fa0 Translations: Moroccan Arabic is Darija, not Persian.
374 values of ary.i18n.json were Persian - "افزودن مسیر شنا" for add-swimlane,
"برد" for board, "دوشنبه" for Monday - or Persian words inside Arabic text, or
English left in ("And __count__ other بطاقة"). The locale tag is authoritative,
so they are Darija in Arabic script now, with the file's own vocabulary: لوحة,
بطاقة, مسار, قائمة, زيد, حيّد, عدّل. The outdated Todoist and Planner import
instructions and two flow notes follow the current English.

tests/moroccanArabicNotPersian.test.cjs scans every value for words only
Persian has, pins repaired values, proves the scan catches the old strings and
checks every placeholder against English.

Low confidence, for a speaker to correct: the colour names (lime, orange,
magenta, plum, saddlebrown, navy), prefix-with-parent and subtext-with-*,
bidirectional-webhooks, impersonate-user, flow-active, predicate-quarter, r-is
and operator-has.

Thanks to xet7 !
2026-11-08 00:21:29 +02:00
Lauri Ojansivu d1d3beb12d Translations: Sardinian is Sardinian, not Italian.
1772 values of sc.i18n.json were Italian or Italian/Sardinian mixtures
("Il monitoraggio di tàulas e cartas è disattivadu dall'amministradore"); the
locale tag is authoritative, so they are now Sardinian (LSC), from the file's
own correct values and dictionaries. Stray "Elementu: " prefixes are gone, and
values that translated an older English string follow the current one.

tests/sardinianTranslationProgress.test.cjs pins repaired values and scans every
value for words that exist only in Italian; a negative check proves the scan
still catches the old strings.

Low confidence, for a speaker to correct: smtp-port/s3-port, owner, next and
previous, stickers, welcome-swimlane, impersonate-user, speedReportTitle and
the one-word operator-/predicate- search terms.

Thanks to xet7 !
2026-11-08 00:19:58 +02:00
Lauri Ojansivu e33b3f26d3 Bump @aws-sdk/client-s3 to 3.1161.0, which @aws-sdk/lib-storage 3.1161.0 requires.
The lib-storage update of #6884 needs client-s3 ^3.1161.0 as a peer, and the
lock still had 3.1159.0; tests/dependencySecurityUpdates.test.cjs caught it.
Only AWS SDK packages change version: no new packages, no install scripts.

Thanks to xet7 !
2026-11-08 00:14:38 +02:00
Lauri Ojansivu 1211221a0f Translations: fill the remaining English placeholders in 33 languages.
Aragonese, Moroccan Arabic, Bislama, Kashubian, Welsh (cy, cy-GB), Friulian,
Upper Sorbian, Armenian, Javanese, Georgian, Kazakh, Khmer (km, km_KH),
Konkani, Luxembourgish, Ladin, Maithili, Mongolian, Occitan, Odia, Punjabi,
Romansh, Sardinian, Shona, Somali, Silesian, Telugu, Tajik, Turkmen, Tagalog,
Tok Pisin and Tatar: every string still equal to English, including the Cloud
Panel strings, written directly in the language from its existing translations
and kanban terms. Only English placeholders were filled; human translations
were not touched, and placeholders are copied from en.i18n.json.

Low confidence, for a speaker to correct: Bislama, Kashubian, Silesian, Ladin,
Friulian, Sardinian, Moroccan Arabic, Konkani, Maithili, Shona, Somali, Tok
Pisin, Turkmen, Tajik and Odia.

Thanks to xet7 !
2026-11-08 00:09:47 +02:00
Lauri Ojansivu c00c602441 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-11-08 00:07:09 +02:00
Lauri Ojansivu 47a19e3842 CHANGELOG: the Cloud Panel, and companion repositories in .tools/w.
Thanks to xet7 !
2026-11-07 23:32:43 +02:00
Lauri Ojansivu 73db2a28c3 Cloud Panel: many WeKan domains from one Weddy, for the SuperAdmin at the Cloud Panel Domain.
Weddy (wekan/caddy-wekan-plugin) runs a WeKan for every domain, each in its own
directory - files/domains/<domain>/files/db/ for its database - and starts
the first domain's WeKan with WEDDY_CLOUD_DIR, every customer's with
WEDDY_TENANT=true.

- Admin Panel / People / Cloud, only in that first WeKan and only for a site
  admin (a per-tenant admin's whitelisted menu never has it): Cloud Panel
  Domain, e.g. cloud.wekan.team, and SuperAdmin Username, a username or
  e-mail address.
- /cloud, the Cloud Panel, where the SuperAdmin is taken at the Cloud Panel
  Domain: Settings with the left menus Domains and CloudFlare, in the Admin
  Panel's layout. Domains is a table page - Domain, Customer name, Customer
  email address, Billing email address, Start Date, End Date and the TLS
  Weddy got - with a form to add and edit. CloudFlare is a write-only API
  key field.
- server/methods/cloudPanel.js checks in every method again that the caller is
  the SuperAdmin and the request's Host is the Cloud Panel Domain - not
  X-Forwarded-Host, which a client could send itself. The collections are not
  published. Every change writes WEDDY_CLOUD_DIR/domains.json for Weddy; the
  key goes to cloudflare.json, mode 0600, and is never sent back.
- models/lib/cloudPanel.js has the rules, shared by client and server; its
  domain rule is the same as Weddy's, since the name becomes a directory.
- Customers' WeKans show none of it.

The 15 new strings are added as pending Transifex, written in Finnish, and
translated into every other locale. Low-confidence translations: ace ak an ay
ba bho bi bm bo bua chr co csb cv dz ee ff fj fo fur gn gv haw hsb ht ig iu
kl kok ks kw lg lld mai mi nah nap nd nso oc om pap qu rm rn rup rw sah sc scn
se sm sn so ss st szl tig tlh tn to tpi ts uz-AR ve ve-CC ve-PP vo wa wa-RR
wal wo wuu-Hans yi zgh. Transifex translations replace them when they arrive.

tests/cloudPanel.test.cjs: the rules with their negatives, and the security
properties the source keeps. The node suites pass, 2160 of 2160. The
templates are not compiled here (no Jade compiler in this sandbox); a Meteor
build checks them. docs/Platforms/FOSS/Weddy/Cloud-Panel.md is the design.

Thanks to xet7 !
2026-11-07 23:32:43 +02:00
Lauri Ojansivu 11bed26788 Companion repositories are found in .tools/w, .tools/xet7 and .tools/Secretchronicles.
The repositories under .tools/ are now grouped by their GitHub owner:
github.com/wekan/* in .tools/w/, github.com/xet7/* in .tools/xet7/ and
github.com/Secretchronicles/* in .tools/Secretchronicles/. build.sh,
build.bat, the release and debug scripts, the tests and the docs looked
for .tools/FerretDB, .tools/wekan.fi, .tools/charts, .tools/node-patches,
.tools/mongo-tools-patches and .tools/TSC, and now use the new places.

ensure_tool_repo takes the owner from the clone URL through a new
tool_repo_dir, so a missing companion repository is cloned into the same
place the scripts read it from. tests/toolsDirectoryRepos.test.cjs runs the
mapping and checks that nothing lands flat in .tools/ any more.

Finding the website checkout again let securityRegressionCoverage compare
the Hall of Fame, which it had been skipping: CreateBleed is published and
is now in the inventory with its unit and UI tests. The exceljs fork test
reads npm-packages/exceljs, the copy WeKan builds with, instead of a clone
under .tools that is not part of the build.

Thanks to xet7 !
2026-11-07 23:32:43 +02:00
Lauri Ojansivu 6a64d374c6 Merge pull request #6886 from wekan/dependabot/npm_and_yarn/mailparser-3.14.2
Bump mailparser from 3.12.4 to 3.14.2
2026-11-07 23:32:07 +02:00
Lauri Ojansivu 8f15879816 Merge pull request #6885 from wekan/dependabot/npm_and_yarn/isomorphic-git-1.44.10
Bump isomorphic-git from 1.44.8 to 1.44.10
2026-11-07 23:31:40 +02:00
Lauri Ojansivu 74339a8a82 Merge pull request #6884 from wekan/dependabot/npm_and_yarn/aws-sdk/lib-storage-3.1161.0
Bump @aws-sdk/lib-storage from 3.1155.0 to 3.1161.0
2026-11-07 23:31:15 +02:00
Lauri Ojansivu 36a1939653 Merge pull request #6883 from wekan/dependabot/npm_and_yarn/filesize-11.0.30
Bump filesize from 11.0.28 to 11.0.30
2026-11-07 23:30:52 +02:00
Lauri Ojansivu a61a40a5db Merge pull request #6882 from wekan/dependabot/npm_and_yarn/pdfjs-dist-6.5.438
Bump pdfjs-dist from 6.4.299 to 6.5.438
2026-11-07 23:30:33 +02:00
Lauri Ojansivu 4fd3217aee Merge pull request #6881 from wekan/dependabot/github_actions/helm/chart-releaser-action-1.9.0
Bump helm/chart-releaser-action from 1.8.0 to 1.9.0
2026-11-07 23:30:18 +02:00
Lauri Ojansivu 5b436a18dc Merge pull request #6880 from wekan/dependabot/npm_and_yarn/tests/playwright/mongodb-7.8.0
Bump mongodb from 7.7.0 to 7.8.0 in /tests/playwright
2026-11-07 23:30:03 +02:00
Lauri Ojansivu 2e87a775ae Merge pull request #6879 from wekan/dependabot/github_actions/vmactions/netbsd-vm-1.5.3
Bump vmactions/netbsd-vm from 1.5.2 to 1.5.3
2026-11-07 23:29:45 +02:00
dependabot[bot] f653d619f5 Bump mailparser from 3.12.4 to 3.14.2
Bumps [mailparser](https://github.com/nodemailer/mailparser) from 3.12.4 to 3.14.2.
- [Release notes](https://github.com/nodemailer/mailparser/releases)
- [Changelog](https://github.com/nodemailer/mailparser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/mailparser/compare/v3.12.4...v3.14.2)

---
updated-dependencies:
- dependency-name: mailparser
  dependency-version: 3.14.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:33:40 +00:00
dependabot[bot] da731aa2fe Bump isomorphic-git from 1.44.8 to 1.44.10
Bumps [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) from 1.44.8 to 1.44.10.
- [Release notes](https://github.com/isomorphic-git/isomorphic-git/releases)
- [Commits](https://github.com/isomorphic-git/isomorphic-git/compare/v1.44.8...v1.44.10)

---
updated-dependencies:
- dependency-name: isomorphic-git
  dependency-version: 1.44.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:33:31 +00:00
dependabot[bot] a42b63d711 Bump @aws-sdk/lib-storage from 3.1155.0 to 3.1161.0
Bumps [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) from 3.1155.0 to 3.1161.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1161.0/lib/lib-storage)

---
updated-dependencies:
- dependency-name: "@aws-sdk/lib-storage"
  dependency-version: 3.1161.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:33:22 +00:00
dependabot[bot] a14923cac3 Bump filesize from 11.0.28 to 11.0.30
Bumps [filesize](https://github.com/avoidwork/filesize.js) from 11.0.28 to 11.0.30.
- [Changelog](https://github.com/avoidwork/filesize.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/avoidwork/filesize.js/compare/11.0.28...11.0.30)

---
updated-dependencies:
- dependency-name: filesize
  dependency-version: 11.0.30
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:33:03 +00:00
dependabot[bot] fdcb26096b Bump pdfjs-dist from 6.4.299 to 6.5.438
Bumps [pdfjs-dist](https://github.com/mozilla/pdf.js) from 6.4.299 to 6.5.438.
- [Release notes](https://github.com/mozilla/pdf.js/releases)
- [Commits](https://github.com/mozilla/pdf.js/compare/v6.4.299...v6.5.438)

---
updated-dependencies:
- dependency-name: pdfjs-dist
  dependency-version: 6.5.438
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:32:55 +00:00
dependabot[bot] b59152a079 Bump helm/chart-releaser-action from 1.8.0 to 1.9.0
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/helm/chart-releaser-action/releases)
- [Commits](https://github.com/helm/chart-releaser-action/compare/df287104677278f869ca5480bd260358397cc3f9...92c6b97fa9f664492a2592e0d1d80bc6bf378957)

---
updated-dependencies:
- dependency-name: helm/chart-releaser-action
  dependency-version: 1.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:32:46 +00:00
dependabot[bot] 1595155af2 Bump mongodb from 7.7.0 to 7.8.0 in /tests/playwright
Bumps [mongodb](https://github.com/mongodb/node-mongodb-native) from 7.7.0 to 7.8.0.
- [Release notes](https://github.com/mongodb/node-mongodb-native/releases)
- [Changelog](https://github.com/mongodb/node-mongodb-native/blob/main/HISTORY.md)
- [Commits](https://github.com/mongodb/node-mongodb-native/compare/v7.7.0...v7.8.0)

---
updated-dependencies:
- dependency-name: mongodb
  dependency-version: 7.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:32:44 +00:00
dependabot[bot] f4f4c7f2cb Bump vmactions/netbsd-vm from 1.5.2 to 1.5.3
Bumps [vmactions/netbsd-vm](https://github.com/vmactions/netbsd-vm) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/vmactions/netbsd-vm/releases)
- [Commits](https://github.com/vmactions/netbsd-vm/compare/c8a0d7ddb84619a7a8cc4e652efa7950b3fd9f92...5957cf330aef2e6ddc63c604364c99ce2e02b91d)

---
updated-dependencies:
- dependency-name: vmactions/netbsd-vm
  dependency-version: 1.5.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-11-07 20:32:39 +00:00
github-actions[bot] 97cc91cd67 Bump versions for v13.00 2026-11-06 23:05:04 +00:00
Lauri Ojansivu 33c2ad8fb0 Prepare v13.00 release 2026-11-07 01:03:53 +02:00
Lauri Ojansivu c936e7708d CHANGELOG: CreateBleed, only admins create boards on every path.
Thanks to tsurutanmen and xet7 !
2026-11-07 00:53:47 +02:00
Lauri Ojansivu 5c1f6088f4 Fix CreateBleed: "Only admins can create boards" holds on every path.
GHSA-qcfm-2g4w-wqcc. Admin Panel / Settings / Visibility / "Only admins
can create boards" (#4475) was checked only inside
createBoardWithInitialSwimlanes, the Add Board form. A non-admin still
created working boards by a client Boards.insert (Boards.allow.insert
approves any signed-in user), POST /api/boards and both board import
routes, as reported, and also by importBoard, cloneBoard, copyBoard,
POST /api/boards/:boardId/copy and createBoardFromCard.

One decision (models/lib/boardCreationPolicy.js) and one guard
(server/lib/boardCreationGuard.js) are asked by every one of those paths
before they write, and by a new Boards.deny insert rule. A board template
stays every member's own; boards WeKan makes itself (a Templates container,
a board's subtasks board) are not created by the user. A REST refusal is
403. A refusal is recorded in Admin Panel / Problems as CreateBleed
(authz.board-creation, low, CWE-863, blocks no account), and Duplicate and
"Create board from card" are hidden like Add Board, so only a client
bypassing the interface reaches it.

tests/createBleed.test.cjs drives the decision, the guard and the real deny
rule against the reporter's console insert, and fails if any path that
creates a board for a user does not ask the guard, or if anything else
reads the setting. tests/playwright/specs/createbleed-board-creation-admin-only.e2e.js
replays the report against a running server. The guards that pinned the
old inline check now pin the shared one.

Thanks to tsurutanmen and xet7 !
2026-11-07 00:51:53 +02:00
Lauri Ojansivu ceb20a81fc Tests: keyboard-nonlatin-once skips outside Chromium, and every CDP test must.
keyboard-nonlatin-once sends its key events through the DevTools protocol
and had no skip, so in the full Firefox run it failed with "CDP session is
only available in Chromium" - the same error as card-window-touch-drag's
mouse test (2128e6e02f). It now skips outside Chromium like the other CDP
tests. tests/playwrightCdpChromiumOnly.test.cjs requires that skip on every
test that opens a CDP session, itself or through a helper of its file (a
`touch: false` call opens none, a describe-level skip covers its file), and
finds both tests as they were.

Thanks to xet7 !
2026-11-07 00:14:50 +02:00
Lauri Ojansivu 2128e6e02f Tests: the mouse test of card-window-touch-drag runs in every browser.
openCardWindow always opened a DevTools (CDP) session to emulate touch, and
the mouse test, which needs no touch, called it too: in Firefox it failed
with "CDP session is only available in Chromium" before it had moved
anything. The touch is now optional, and the mouse test asks for none; the
touch tests still skip outside Chromium. It passes in Firefox and in
Chromium.

Thanks to xet7 !
2026-11-06 23:53:09 +02:00
Lauri Ojansivu 49ee37a3a8 Tests: Card's settings menu is opened again until its panel shows.
In the full two-worker Firefox run, board-settings-columns' non-admin test
clicked Card's hamburger right after Board Settings / Card was drawn and
the panel never opened; alone it passes 4 of 4. The button toggles the
panel, so the helper now clicks again only while the panel is still closed,
up to 15 seconds. The whole spec passes in Firefox.

Thanks to xet7 !
2026-11-06 23:27:52 +02:00
Lauri Ojansivu 19350f2611 A code block in a description reads in the dark theme.
.viewer pre and code have their own light background (#dedede) in
layouts.css but inherited the theme's text colour. In the dark theme's
opened card that is #bbb, 1.42:1, and axe-core's color-contrast rule
failed the dark theme in the full Firefox run (46-axe-theme-contrast,
".viewer > pre #bbbbbb/#dedede 1.42"). The block now names its text colour,
#222, 11.8:1 on its background - the background codeHighlight.css's token
colours are chosen for. tests/viewerCodeContrast.test.cjs requires an
explicit text colour of at least 4.5:1 on the block's own background, and
fails on the old rule.

Thanks to xet7 !
2026-11-06 23:22:54 +02:00
Lauri Ojansivu 2306a36814 A long popup menu is laid out before it is drawn, not a frame later.
The column layout of a long menu (Board View, the member menu, the card's
menu) was applied on the animation frame after the menu's contents arrived,
so for a frame the popup was drawn - and could be clicked or dragged - at
its narrow size, then jumped to 960px under the pointer. In the full Firefox
run a click on Board View's "Lists" landed where "Calendar" ends up, the
view stayed Swimlanes and 06-views-layout's #6659/#6660 test failed; the
same jump was behind the popup drag of card-title-edit-and-move-6839. The
MutationObserver callback, which already runs before the next paint, now
applies the layout itself. tests/popupMenuLayout.test.cjs drives the
observer and fails if the layout is deferred to an animation frame again.

Thanks to xet7 !
2026-11-06 23:11:29 +02:00
Lauri Ojansivu a4d2313a68 Password policy specs no longer run side by side or leave a minimum enforced.
In the full Chromium run (two workers) password-length-report.e2e.js and
password-min-length-signup-login.e2e.js ran at the same time. Both write
settings.loginOptions.passwordMinLength and passwordMinLengthEnforce, one
document for every worker, so each turned enforcement on or off under the
other and one test of each failed. Both also restored the loginOptions they
had seen before each test, so once a test was interrupted the snapshot
already held an enforced minimum, and every later test put it back: the
test database was left with a minimum of 10 enforced.

tests/playwright/helpers/sharedSettingLock.js is a lock across workers (an
atomic mkdir under TMPDIR, counted per process, and taken over from a
worker that died); both specs hold 'password-policy' for the whole file,
and put the policy back to its default with $unset after each test. Run
together on two workers both files pass, 6 of 6.
tests/playwrightSharedSettingLock.test.cjs requires the lock and the
$unset in every spec that writes the policy, refuses a restored snapshot,
and drives the lock: shared within a process, released by the last holder,
taken over from a dead one, and waited for from a live one.

Thanks to xet7 !
2026-11-06 22:12:49 +02:00
Lauri Ojansivu 570cc34552 Stopping a timer adds its time to the total, never replaces it.
Flowtime's Stop and Pomodoro's completed or stopped work interval read the
total on the client (getSpentTime()) and $set the sum. For a card that
links to a board or a card, that total is the linked document's, which can
still be on its way to the client: getSpentTime() then read null, and the
Stop replaced a linked board's 4 logged hours with the 1 hour of the
session (linked-timer-sessions.e2e.js failed 4 of 6 repeats). The session
length is now added on the server with addSpentTime(), an $inc on the same
spentTime field of the linked board, the linked card or the card. The
Flowtime and Pomodoro guards check addSpentTime and no longer pin
setSpentTime, and tests/linkedTimerSessions.test.cjs credits correctly
with no total on the client, pins the $inc targets, refuses a zero,
negative or non-number amount, and fails if a timer helper computes a total
from getSpentTime() again.

Thanks to xet7 !
2026-11-06 21:35:36 +02:00
Lauri Ojansivu 7ae45651dc A popup that grows while it is dragged stays inside the window.
A long menu, such as the member menu, gets its 960px column layout a frame
after it is drawn (client/lib/popupMenuLayout.js). A drag begun in that
frame clamped every move with the 380px the popup started at, so the grown
popup was put at left 850, mostly outside a 1280px window - and each move
undid the ResizeObserver that had just put it back inside. The drag now
clamps with the popup's current size. tests/calendarPopupResize.test.cjs
grows the popup mid-drag and fails on the old clamp; the Playwright spec
card-title-edit-and-move-6839 measures a popup only after its box has
settled, and passes 24 of 24 repeats where it failed 3 of 20.

Thanks to xet7 !
2026-11-06 21:00:30 +02:00
Lauri Ojansivu db52764592 Docker Playwright browsers open WeKan at ROOT_URL, so uploads work in tests.
build.sh starts the test server with ROOT_URL=http://localhost:3000 but sent
the Docker Playwright browsers and the Node E2E container to
http://127.0.0.1:3000. Meteor-Files sends every upload to ROOT_URL +
/cdn/storage/<collection>/__upload and answers no CORS preflight for another
origin, so the browser blocked it ("Access to fetch at
'http://localhost:3000/cdn/storage/attachments/__upload' from origin
'http://127.0.0.1:3000' has been blocked by CORS policy") and the four drawing
specs whose Save uploads an attachment timed out in the full Chromium run.
The default is now http://localhost:3000, like playwright.config.js and the
macOS path; with it the eight drawing tests pass in the same container.
tests/playwrightBaseUrlMatchesRootUrl.test.cjs checks that every browser
default in build.sh and the Playwright config is the test server's ROOT_URL
origin, and fails on the old default.

Thanks to xet7 !
2026-11-06 20:56:02 +02:00
Lauri Ojansivu 2b99ffe288 Source-scanning tests skip every _build* directory, not only _build.
After the Mocha stage of a full test run had compiled _build-local-test/,
tests/publishFilesCursor.test.cjs read server-rspack.js there and failed
on nine lines of bundled code. 62 tests walked the tree skipping _build by
exact name, so each of them read rspack's second copy of every source
file whenever a test build was present. They now skip by the _build
prefix, as tests/noIdentityReplacement.test.cjs already did, and
tests/sourceWalkersSkipBuildOutput.test.cjs fails on any walker that
skips _build by exact name only.

Thanks to xet7 !
2026-11-06 20:20:10 +02:00
Lauri Ojansivu b4b130858f CHANGELOG: built API page spells the name WeKan.
Thanks to xet7 !
2026-11-06 19:43:28 +02:00
Lauri Ojansivu 0275363043 The built API page public/api/wekan.html spells the name WeKan.
The v12.99 version bump rebuilt public/api/wekan.html with the generator
from before the spelling fix, and releases/release-website.sh copies it
to wekan.fi as api/v12.99/index.html, so that page would have said
Wekan again. Its visible text and the Redoc JSON now say WeKan, and
tests/openapiBrandSpelling.test.cjs checks the built page too.

Thanks to xet7 !
2026-11-06 19:43:20 +02:00
Lauri Ojansivu 42d8341d47 Merge remote-tracking branch 'origin/main' 2026-11-06 19:42:55 +02:00
Lauri Ojansivu a9aaa22592 CHANGELOG: REST API documentation spells the name WeKan.
Thanks to xet7 !
2026-11-06 19:25:20 +02:00
Lauri Ojansivu 4d5fe63fc4 The name is WeKan in the REST API documentation.
openapi/generate_openapi.py's header and the models' JSDoc said
"Wekan REST API", "extend Wekan", "a production Wekan server" and
"in the Wekan board", so every release's API pages on wekan.fi failed
its brand-spelling check (v12.95-v12.97 did). The generator, the model
comments and public/api/wekan.yml now say WeKan, and
tests/openapiBrandSpelling.test.cjs checks the generated file, the
generator header and every model JSDoc block.

Thanks to xet7 !
2026-11-06 19:24:04 +02:00
github-actions[bot] 1f12b20291 Bump versions for v12.99 2026-11-06 17:05:44 +00:00
Lauri Ojansivu bf8ecbbc19 Prepare v12.99 release 2026-11-06 19:04:35 +02:00
Lauri Ojansivu e89a3450cd CHANGELOG: mac-arm64 on macos-15, weba instructions moved.
Thanks to xet7 !
2026-11-06 18:57:40 +02:00
Lauri Ojansivu 7e4483b1a9 Build mac-arm64 release bundle on macos-15, macos-14 runner is retired.
The v12.98 release run's build-mac-arm64 job was cancelled with
"The configuration 'macos-14-arm64-us-default' is not supported",
so no mac-arm64 bundle was built. macos-15 is Apple Silicon too and
is what mac.yml already uses. tests/workflowRunnerLabels.test.cjs now
lists macos-14 as retired and checks that build-mac-arm64 asks for a
current Apple Silicon runner.

Thanks to xet7 !
2026-11-06 18:52:56 +02:00
Ahmed Lukman f74ef4c40d Fix REST card overtime persistence
Keep accepting the isOverTime request parameter and write its validated value to the isOvertime schema field used by card readers and the UI.

Correct the handler regression and cover JSON/form edits, persistence, API reads, reactive display, reloads and rejected requests.
2026-11-06 19:04:30 +03:00
Lauri Ojansivu a4eacfd9bf Merge remote-tracking branch 'origin/main' 2026-11-06 17:02:46 +02:00
Lauri Ojansivu b4cb989391 Move weba instructions into its own repository
Remove the weba-specific section from the instruction twins now that the companion repository maintains its own scoped AGENTS.md and CLAUDE.md.
2026-11-06 17:01:28 +02:00
github-actions[bot] 60377a4dc2 Bump versions for v12.98 2026-11-06 14:54:56 +00:00
Lauri Ojansivu 5c45d55d70 Prepare v12.98 release 2026-11-06 16:53:41 +02:00
Lauri Ojansivu e5a84cbcef CHANGELOG: weba in AGENTS.md and CLAUDE.md.
Thanks to xet7 !
2026-11-06 16:51:08 +02:00
Lauri Ojansivu 1ef7a57ce4 Added weba info to AGENTS.md and CLAUDE.md.
Thanks to xet7 !
2026-11-06 16:49:47 +02:00
Lauri Ojansivu 9caf0c8653 CHANGELOG: notificationAttachments publication.
Thanks to xet7 !
2026-11-06 16:42:30 +02:00
Lauri Ojansivu adb73fd8ee Notifications drawer: notificationAttachments publishes a cursor again.
Every subscription failed on the server with "Exception from sub
notificationAttachments ... Publish function can only return a Cursor or an
array of Cursors". Attachments is an ostrio:files FilesCollection, so
ReactiveCache.getAttachments(..., true) answers with a FilesCursor; the
publication returned that instead of the Mongo cursor it wraps, and the
drawer never received the attachments its activities name. It now returns
`ret.cursor || ret`, as every other attachment publication does.

tests/publishFilesCursor.test.cjs runs the publication with a FilesCursor
and requires the Mongo cursor back, and scans every publishing file so no
getAttachments/getAvatars cursor query is published without its .cursor.

Thanks to xet7 !
2026-11-06 16:42:21 +02:00
Lauri Ojansivu ea5724babd CHANGELOG: REST card update sort, isOverTime and description.
Also points the Upcoming entries at their commits after the pull rebased them.

Thanks to AhmedLukman and xet7 !
2026-11-06 16:24:40 +02:00
Lauri Ojansivu b895e61237 REST card update: sort is a number, isOverTime can be turned off, a description can be cleared.
#6877 made the card PUT write `sort` whenever it is present, so 0 works -
and so would null, '' or 'abc', written as the card's position. The same
handler read `isOverTime` and `description` by truthiness, so false could
not be set and an empty description could not clear the old one.

parseCardScalarFields() in server/lib/restCardHelpers.js now decides all
three by presence and checks them before the first write: sort must be a
finite number (a form-encoded '0' becomes 0), isOverTime true/false or
'true'/'false', description a string (null clears it). Anything else is a
400 and the card is left as it was, rather than half of the edit applied.

tests/restCardSort.test.cjs drives the handler for the accepted values,
refuses the invalid ones with no writes, and fails if any of the three is
read by truthiness again.

Thanks to AhmedLukman and xet7 !
2026-11-06 16:24:31 +02:00
Lauri Ojansivu 3108e017fc CHANGELOG: public CSV export fields, REST card 404s, test guards.
Also points the GroupLinkBleed entry at its commit after the pull rebased it.

Thanks to AhmedLukman and xet7 !
2026-11-06 16:15:34 +02:00
Lauri Ojansivu 081690875d Tests: the real single executable test says why it cannot run on a kernel with large pages.
On Linux, --build-sea injects the SEA blob with LIEF, which adds PT_LOAD
segments aligned to 4 KiB. A kernel with 16 KiB pages (Apple-silicon Linux)
or 64 KiB pages (some ARM servers) cannot map them, and the executable dies
with SIGSEGV before it runs a line. A one-line console.log SEA built with
Node.js v26.13.0 crashes the same way, so it is the injection, not WeKan.
The test failed there as `null !== 0`, which said nothing; it is now skipped
when the page size is over 4 KiB, with that reason. Release builders have
4 KiB pages, so they still run it.

Thanks to xet7 !
2026-11-06 16:15:34 +02:00
Lauri Ojansivu 68b575637d Tests: three client guards follow the code they pin again.
The behaviour did not change; each guard read source that later commits
moved, and failed on what it could no longer find:

- sidebarBelowHeader: Template.header.onRendered also wires the full
  screen button since f25957dcf3, and the slice now ran into the new
  onDestroyed. The slice ends at onDestroyed, and the fake environment has
  an inert Fullscreen API.
- maximizedCardPosition: since efaf94242e the maximized selector is also in
  a narrow-screen @media rule that only moves `top` below the header bar on
  a phone. The regex found that rule first; it now reads the top-level one.
- titleClickToEdit: since 1b05673200 the opened card moves with Pointer
  Events, and what a header press does is cardHeaderPress() in
  models/lib/windowDrag.js. The guards read the pointerdown handler and call
  that decision, keeping every assertion: the title edits, links stay links,
  the drag handle wins when drag handles are on, the editor is never dragged.

Each still fails when the behaviour it pins is removed (checked by deleting
onTitleEditZone and by making the maximized card absolute).

Thanks to xet7 !
2026-11-06 16:15:34 +02:00
Lauri Ojansivu 6d5f660c27 REST custom-field update answers JSON 404 for an unmatched card instead of 500.
POST /api/boards/:boardId/lists/:listId/cards/:cardId/customFields/:customFieldId
threw Meteor.Error(404) when no unarchived card matched the board, list and
card id. A WebApp handler is a plain connect handler, so that reached the
client as HTTP 500. It now answers through sendJsonResult with a JSON 404,
as the card archive/restore endpoints do since #6876.

tests/restHandlerNotFound.test.cjs drives the handler for a matching card,
a missing card, the wrong list, the wrong board and an archived card, and
scans every WebApp.handlers route in the source tree so no REST handler
can answer not-found by throwing Meteor.Error(404) again.

Thanks to AhmedLukman and xet7 !
2026-11-06 16:15:34 +02:00
Lauri Ojansivu f54aa93dc1 CHANGELOG: GroupLinkBleed.
Thanks to bhaswanthc and xet7 !
2026-11-06 16:15:34 +02:00
Lauri Ojansivu e3458004ec Fix GroupLinkBleed: board admins can no longer attach foreign teams or add members by direct board writes.
GHSA-v544-rmr5-38gx. Boards.allow lets a board admin update any field of
their board, and two checks existed only inside Meteor methods: a board
admin may add only the organizations and teams they belong to (#5141,
setBoardOrgs/setBoardTeams), and inviteUserToBoard's checks (who may
invite, disabled accounts, the #6116 same organization/team policy). A
client calling /boards/update or /boards/insert directly skipped both.

New Boards.deny rules refuse any client insert or update naming orgs,
teams or domains - the UI changes them only through the board methods,
which write server-side - and any client update that adds a member or
switches a removed member back on (models/lib/boardWriteGuards.js). Role
changes and removals, which the client writes itself, still pass. A
refusal is recorded in Admin Panel / Problems as GroupLinkBleed
(authz.group-link, authz.member-add, medium).

tests/groupLinkBleed.test.cjs runs the real deny rules in a vm against
the reported writes and every legitimate client write, and fails if a
method-guarded board field is client-writable; it fails without the fix.
tests/playwright/specs/group-link-bleed.e2e.js replays the attack over DDP.

Thanks to bhaswanthc and xet7 !
2026-11-06 16:15:34 +02:00
Lauri Ojansivu 6ae4ad3545 Merge pull request #6877 from AhmedLukman/codex/rest-card-sort-zero
Allow zero sort values in REST card updates
2026-11-06 16:15:10 +02:00
Ahmed Lukman b0a71ba57b Allow zero sort values in REST card updates
Check whether sort is supplied so JSON updates can persist a zero position. Keep the existing scoped update and history handling, and add handler and browser regressions for ordering, persistence, omitted values and rejected requests.
2026-11-06 16:54:07 +03:00
Lauri Ojansivu f3d013339c Merge pull request #6876 from AhmedLukman/codex/rest-card-archive-not-found
Fix 404 responses for card archive and restore
2026-11-06 15:42:23 +02:00
Lauri Ojansivu 95ea04b049 Merge pull request #6875 from AhmedLukman/codex/public-csv-export-fields
Honor field selections in public CSV and TSV exports
2026-11-06 15:42:08 +02:00
Ahmed Lukman 3b33c7d7ba Return JSON 404 for unmatched card archive and restore requests
Use the existing JSON response helper when the board, list, card and archive-state selector finds no card. Preserve authorization checks and the existing archive and restore methods.

Add handler and browser regressions for missing cards, incorrect scopes, wrong states, repeated requests, successful transitions and unauthorized callers.

Thanks to AhmedLukman and xet7 !
2026-11-06 14:34:33 +03:00
Ahmed Lukman 4762bed8c1 Honor field selections in public CSV and TSV exports
Share validated export options between public and private CSV routes. Add handler regressions and live export-menu coverage for comma, semicolon and tab delimiters, default columns and private-board access.
2026-11-06 12:20:48 +03:00
github-actions[bot] 029ecb6b39 Bump versions for v12.97 2026-11-06 02:01:17 +00:00
Lauri Ojansivu 49c309b324 Prepare v12.97 release 2026-11-06 04:00:13 +02:00
Lauri Ojansivu e09267cc90 CHANGELOG: BSD smoke test cleanup and snap Launchpad retries.
Thanks to xet7 !
2026-11-06 03:52:17 +02:00
Lauri Ojansivu b46f155fa3 Fix v12.96 release builds: BSD smoke test cleanup and snap Launchpad retries.
openbsd-arm64 bundle: releases/bsd/smoke.sh returned OK with WeKan and
FerretDB still running, because start-wekan.sh runs its TERM trap only
after the foreground node exits. FerretDB then removed wekan.sqlite-wal
and -shm while the VM copyback rsync walked the workspace, and rsync exit
24 failed a bundle that had built and started. smoke.sh now stops node
and FerretDB, waits until both are gone (SIGKILL after 30 s) and removes
its work directory before returning.

s390x snap: all three attempts met Launchpad "HTTP Error 503: Service
Unavailable" / "Error: Timeout", but snapcraft printed only its last
exception, "Could not find repository at path ...", and the 503 was only
in snapcraft's execution log, which infra_failure() did not read. The
attempts were counted as WeKan failures instead of earning the extra
retries. infra_failure() now reads this attempt's execution logs, and
also counts the push refusal "You do not have permission to create
refs/..." from the repository those timed-out requests half-created.

Tests reproduce both from the v12.96 logs, with negatives: an earlier
attempt's execution log and a log with no outage do not count, and no
process or smoke-test file is left behind when smoke.sh returns.

Thanks to xet7 !
2026-11-06 03:52:04 +02:00
Lauri Ojansivu 0c0288a6c5 CHANGELOG: build.sh and releases scripts work on Arch Linux and Omarchy.
Thanks to xet7 !
2026-11-06 01:57:16 +02:00
Lauri Ojansivu c61a1b7f41 build.sh and releases/*.sh work on Arch Linux and Omarchy.
Omarchy (https://omarchy.org) is Arch with Hyprland: pacman, the Arch
repositories and yay for the AUR. The shared detector in
releases/ensure-tools.sh now names Omarchy and the other Arch derivatives
(EndeavourOS, CachyOS, Manjaro) explicitly, and what the scripts did there
is fixed:

- No more `pacman -Sy <package>`: that is a partial upgrade, which Arch does
  not support. _et_pacman installs with `-S --needed`, does a full `-Syu`
  only when pacman has never synced (a fresh archlinux container) or when
  WEKAN_PACMAN_SYU=1 asks for it, and otherwise says to update first, naming
  omarchy-update on Omarchy.
- 7zip instead of p7zip, which Arch has replaced.
- snapcraft: snapd from the AUR with yay or paru (never as root), then
  snapd.socket, /snap and the snapcraft snap, instead of only an error.
- The SSH server menu entries use sshd.service outside Debian/Ubuntu, where
  the unit is not called ssh.
- Playwright: `install --with-deps` runs apt-get, so outside Debian/Ubuntu
  the browser libraries come from pacman (or dnf) and the browsers are
  installed without --with-deps. WebKit, which Playwright builds for
  Debian/Ubuntu only, runs in the Playwright Docker image on Arch when
  Docker is there.
- The en_US.UTF-8 locale note gives the Arch/Omarchy steps too.

tests/archOmarchyScripts.test.cjs drives the helpers offline with stubbed
sudo/pacman/yay, and its negative test fails if any tracked shell script
does a `pacman -Sy` partial upgrade or installs p7zip.

Thanks to xet7 !
2026-11-06 01:57:00 +02:00
github-actions[bot] 9ce35869a7 Bump versions for v12.96 2026-11-05 19:41:40 +00:00
Lauri Ojansivu 2017d07e37 Prepare v12.96 release 2026-11-05 21:40:23 +02:00
Lauri Ojansivu 9ea5c81932 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-11-05 21:39:54 +02:00
Lauri Ojansivu 45ccdd9c3c CHANGELOG: the bundle starts again, and v12.95 records that its bundle did not.
Thanks to xet7 !
2026-11-05 21:39:02 +02:00
Lauri Ojansivu 5a21b074d4 The bundle starts again: nodemailer 10 in meteor/email gets back the lib/ paths it was built against.
releases/bump-bundle-npm-deps.mjs raises the nodemailer inside Meteor's email
package from 8.0.3 to 10.3.1 (dad03959d9), and nodemailer 10 moved lib/ to
dist/cjs/. packages/email.js, compiled against 8.0.3, links
lib/nodemailer.js and lib/well-known/index.js and Npm.requires
lib/mail-composer by path, so v12.95's build-amd64 failed its smoke boot:
"Cannot find module .../meteor/email/node_modules/nodemailer/lib/nodemailer.js".

releases/bundle-npm-security-bumps.json now shims nodemailer the way it
already shims uuid 11: lib/nodemailer.js and every lib/<name> directory the
exports map lists are written back, each forwarding to dist/cjs, and
lib/package.json marks lib/ as CommonJS because nodemailer is "type":
"module". A fresh local bundle taken through build-amd64's install, bump,
trim and prune steps now passes releases/bundle-smoke-boot.sh.

tests/bundleNpmSecurityBumps.test.cjs applies the shim to node_modules'
nodemailer 10, requires the recorded paths, checks that every exported
lib/<name> has a forward to the same module, and fails without the CommonJS
marker.

Thanks to xet7 !
2026-11-05 21:39:02 +02:00
github-actions[bot] e9c5888485 Bump versions for v12.95 2026-11-05 19:04:13 +00:00
Lauri Ojansivu 09c9d72398 Prepare v12.95 release 2026-11-05 21:03:04 +02:00
Lauri Ojansivu 4f3d45f3de CHANGELOG: the attachment upload policy method is loaded.
Thanks to Raymond-Voogt and xet7 !
2026-11-05 20:57:07 +02:00
Lauri Ojansivu 3aaa46fdae The attachment upload policy method is loaded, so a card hides the "+" when uploads are blocked.
d8b3579117 added server/methods/attachmentUploadPolicy.js but nothing
imported it, so the method did not exist: the card's
Meteor.callAsync('attachmentUploadPolicy') failed, the client treated that
as "not blocked", and the "+" stayed. server/imports.js now imports it with
the other server methods. tests/serverModulesLoaded.test.cjs, which failed
on it, passes.

Thanks to Raymond-Voogt and xet7 !
2026-11-05 20:57:01 +02:00
Lauri Ojansivu 59f1b705e0 CHANGELOG: missing users, organizations and teams return 404, Requested by on the minicard, and the pull request rules.
Thanks to AhmedLukman, mueschel and xet7 !
2026-11-05 20:56:19 +02:00
Lauri Ojansivu 22e5df55d7 Requested by and Assigned by show on the minicard when a member was picked, with the same icon everywhere.
The opened card's + button picks board members into card.requesters /
card.assigners; the free-text requestedBy / assignedBy is optional. The
minicard only drew the free text, so a requester picked the usual way never
showed, although Show on Minicard was on. It now draws the picked members
as avatars beside the typed name, as the opened card does.

Card Settings showed Requested by with a user-plus icon and Assigned by with
a shopping cart, the other way round from the opened card and the minicard.
The settings rows now use the cart for Requested by and user-plus for
Assigned by.

tests/minicardRequestedAssignedBy6872.test.cjs pins the avatars, the
either-or condition and one icon per field across all three places, and
fails on a minicard person block gated on the free text alone.
tests/playwright/specs/minicard-requested-by-6872.e2e.js drives it.

Thanks to mueschel and xet7 !

Fixes #6872.
2026-11-05 20:55:28 +02:00
Lauri Ojansivu 016b4bf3bd CLAUDE.md, AGENTS.md: only maintainer mode commits directly and updates changelogs; one pull request per feature or fix.
A pull request contributor, in wekan/wekan and in the maintainer's
repositories under .tools/, sends each feature or fix as its own pull
request, with only related changes in it, and changes no changelog: the
maintainer writes the entry after merging, when there is a commit to link.
The CHANGELOG section and the security CRITICAL entry say they are
maintainer mode only, and "good practice for everyone" no longer includes
the CHANGELOG structure. tests/changelogFormat.test.cjs checks both files.

Thanks to xet7 !
2026-11-05 20:55:28 +02:00
Lauri Ojansivu 4d44349ab6 Merge pull request #6874 from AhmedLukman/codex/rest-people-features-not-found
Return 404 for missing organization and team feature updates.
2026-11-05 20:53:48 +02:00
Lauri Ojansivu d3edc56170 Merge pull request #6873 from AhmedLukman/codex/user-update-not-found
Return 404 for updates to missing users
2026-11-05 20:53:12 +02:00
Ahmed Lukman 5134e3d2a8 Return 404 for missing organization and team feature updates.
Return a JSON not-found response when the feature update lookup finds no organization or team. Cover missing records, successful updates, authorization, and reactive People-table checkboxes.

Thanks to AhmedLukman and xet7 !
2026-11-05 21:28:38 +03:00
Ahmed Lukman f686b76023 Keep the user update fix limited to code and tests.
Thanks to AhmedLukman and xet7 !
2026-11-05 20:54:17 +03:00
Ahmed Lukman cd2adcad02 Document user update not-found responses.
Thanks to AhmedLukman and xet7 !
2026-11-05 20:48:43 +03:00
Ahmed Lukman 0766a65fef Return 404 for updates to missing users.
Thanks to AhmedLukman and xet7 !
2026-11-05 20:47:06 +03:00
Lauri Ojansivu 58ad81e301 continuousBackup integration suite: stop each engine before its directory is removed.
Three tests registered the hook that removes their temp directory before the hook
that stops their engine. node:test runs after-hooks in the order they were
registered, not last-in first-out, so the directory was gone when stop() sealed
into it, and the ENOENT (files/index.ndjson) surfaced after the test as
"generated asynchronous activity after the test ended" - which failed the whole
suite although all 8 tests passed. One hook now stops the engine first and then
removes the directory. Five runs in a row, and the suite runner, pass.

Thanks to xet7 !
2026-11-04 19:20:58 +02:00
Lauri Ojansivu 2912f4b080 Close #6870: attachments are already off site-wide in Admin Panel / Attachments / Transfer limits.
How: Admin Panel / Attachments / Transfer limits, set "Attachments upload" and
"API upload" to Blocked. That is the site-wide "Disable attachments" the issue
asks for, as two settings instead of one checkbox.

Comment for the issue:

  A site-wide switch for this already exists, so a new "Disable attachments"
  setting is not needed. Open Admin Panel / Attachments / Transfer limits and set
  "Attachments upload" and "API upload" to Blocked. While they are on, the server
  refuses an upload on every path - the upload pipeline, the api.attachment.upload
  and api.attachment.copy methods, and the /api/attachment upload,
  upload-background and copy routes - on every board, including boards created
  later, and no board's allowsAttachments flag has to be touched. Download, move
  and delete are not affected, so files uploaded earlier stay reachable. What was
  missing is that a card still showed the "+" button and accepted a drop for an
  upload that would then be refused; since d8b3579117 the card asks the server
  once a minute and hides both. There is no second setting, so nothing can
  disagree with what the server enforces. Two settings instead of one checkbox
  is the one difference from the request.

Thanks to Raymond-Voogt and xet7 !

Fixes #6870,
2026-11-04 19:17:53 +02:00
Lauri Ojansivu 97d1eca09d CHANGELOG: In short for the upcoming release. 2026-11-04 19:12:14 +02:00
Lauri Ojansivu 95b75177b8 CHANGELOG: continuous backup runs on one server at a time, with a lease. 2026-11-04 19:12:06 +02:00
Lauri Ojansivu 880ec2d707 Continuous backup runs on one server at a time, with a lease.
With several WeKan servers on one database, every server applied the continuous
backup setting, so each of them tailed the oplog and wrote its own archives into
the same target directory (#6861, item 3).

The leader holds a lease: one continuousBackupLease document with its id and an
expiry. Taking it is one atomic operation (an insert while there is none, else
an update that matches only an unheld, own or expired lease), the leader renews
it every 15 seconds and it lapses after 45, so another server takes over within
a minute when the leader stops. A server that loses the lease stops its stream.
Startup, saving the settings and a change saved on another server all converge
through that loop, and a restore or a fetch from the cloud takes the lease and
waits one renewal so it is the only writer. One server is always its own leader.

Checked against the real driver on FerretDB (SQLite): 24 clients at once give
exactly one leader; renewal, expiry, loss of the old leader and release behave.
tests/continuousBackupLease.test.cjs pins the same and the wiring in
server/continuousBackup.js. docs/Backup/Continuous-Backup.md no longer says
single-server only.

Thanks to Raymond-Voogt and xet7 !

Refs #6861, not closed: the remaining items of the issue were fixed in 4e28465964.
2026-11-04 19:11:32 +02:00
Lauri Ojansivu dbc7cccea3 CHANGELOG: a card no longer offers the attachment plus when uploads are blocked for the whole site. 2026-11-04 19:07:11 +02:00
Lauri Ojansivu d8b3579117 A card no longer offers the attachment "+" when uploads are blocked for the whole site.
#6870 asks for a site-wide switch that turns attachments off, because the
per-board allowsAttachments flag has to be set on every board. That switch is
there already: Admin Panel / Attachments / Transfer limits, "Attachments upload"
and "API upload" set to Blocked, and the server refuses on every path - the
upload pipeline, api.attachment.upload and .copy, and the /api/attachment
upload, upload-background and copy routes. What was missing is that the card
still showed the "+" button and accepted a drop for an upload that would be
refused.

attachmentUploadPolicy tells a signed-in client the one boolean (nothing about
the limits), asked at most once a minute, and the "+" and the drop/paste upload
follow it. No second setting is added, so there is nothing that could disagree
with what the server enforces. tests/attachmentUploadPolicy.test.cjs pins the
client, the method, and the server enforcement it relies on, and that no
"disableAttachments" setting exists.

Thanks to Raymond-Voogt and xet7 !

Refs #6870, not closed: the reporter asked for a single Features checkbox, and the
two Blocked limits are two settings.
2026-11-04 19:07:09 +02:00
Lauri Ojansivu c7e53e9f38 CHANGELOG: an integration update for a missing or wrong-board integration returns 404. 2026-11-04 19:04:14 +02:00
Lauri Ojansivu 6012521ccc An integration update for a missing or wrong-board integration returns 404.
PUT /api/boards/:boardId/integrations/:intId answered 200 with the requested id
when no integration matched that id and board (PR #6871). The two activities
routes (POST and DELETE .../activities) had the same fault: they updated
nothing and answered 200 with `data: null`. All three now look the integration
up on that board after board-admin authorization and answer the JSON 404 the GET
route already uses (#6862), before validating or writing anything.

tests/restIntegrationUpdate.test.cjs and the Playwright spec are the PR's.
tests/restIntegrationWriteRoutes404.test.cjs adds the activities routes and
pins, for the whole file, that every handler that updates an integration looks
it up first and only after authorization.

Thanks to AhmedLukman and xet7 !
2026-11-04 19:04:12 +02:00
Lauri Ojansivu 0195a5b3d4 Merge remote-tracking branch 'origin/main' 2026-11-04 15:02:36 +02:00
Lauri Ojansivu b12e761afb CHANGELOG: missing-board exports return 404 and an over-long export token 400. 2026-11-04 14:50:49 +02:00
Lauri Ojansivu 0c0d666ffa Missing-board exports return 404, and an over-long export token returns 400.
The board Excel export answered a missing board with HTTP 200 and the text
"Board not found" (PR #6869). The two PDF export routes had the same fault, and
the Excel and PDF routes answered an over-long auth token with 200 too, while
export.js already used 404 and 400. They now choose their status the same way.

tests/boardExcelNotFound.test.cjs and the Playwright spec are the PR's.
tests/exportErrorStatuses.test.cjs scans every models/export*.js route for a
text answer without a status, so the fault cannot return anywhere there.

Thanks to AhmedLukman and xet7 !
2026-11-04 14:50:33 +02:00
Lauri Ojansivu fc2ad0a48b CHANGELOG: nodemailer, body-parser, qs, uuid and postcss-selector-parser raised in the bundle. 2026-11-04 14:45:57 +02:00
Lauri Ojansivu dad03959d9 Raise nodemailer, body-parser, qs, uuid and postcss-selector-parser in the bundle.
A Grype scan of the image found advisories in packages that Meteor's own
packages carry in programs/server/npm/node_modules/meteor/<package>, which
package.json cannot reach.

nodemailer 8.0.11 -> 10.3.1: meteor/email only calls createTransport, the
'stream' plugin hook, nodemailer/lib/well-known and nodemailer/lib/mail-composer,
and all four work in 10.3.1 (checked with the bundle's own package specifiers,
through its exports map). body-parser 1.20.6/1.20.8 -> 1.20.9 and 2.3.0 ->
2.4.0: a minimum may now be "name@MAJOR", so each major is raised inside
itself. qs -> 6.16.1, postcss-selector-parser 6.1.4 -> 6.1.5.

uuid 8.3.2 and 9.0.1 -> 11.1.1: 11 moved its entry point, which is why it was
refused before. The bumper now writes the recorded dist/index.js back as an
ESM re-export of dist/esm/index.js, which require() loads and which leaves
uuid's own ESM and CJS entry points as they are.

glob 14 and minimatch 11 are already in package.json. Run against a copy of
the published image's bundle, the bumper raises all of these and every raised
copy loads.

Thanks to the Grype scan reporter and xet7 !
2026-11-04 14:45:04 +02:00
github-actions[bot] ac960e7d81 Bump versions for v12.94 2026-11-04 01:07:15 +00:00
Lauri Ojansivu 3213d7fa1d Prepare v12.94 release 2026-11-04 03:06:07 +02:00
Lauri Ojansivu 4757676abf CHANGELOG: on a phone in desktop mode the opened card starts below the header bar. 2026-11-04 03:04:05 +02:00
Lauri Ojansivu efaf94242e On a phone in desktop mode the opened card starts below the header bar.
The header wraps to several rows on a phone, far taller than the 8px the
floating card docks at, so the card's top row sat among the header's rows and
was only seen by dragging. Below 801px a card without an inline top, popup
cards included, starts at --wekan-header-height + 8px; a dragged place is an
inline top and is never overridden.

Thanks to the iPhone 12 mini reporter and xet7 !
2026-11-04 03:03:59 +02:00
Lauri Ojansivu d87baa0244 CHANGELOG: the opened card covers the header bar on an iPad. 2026-11-04 02:56:44 +02:00
Lauri Ojansivu 006d24502c The opened card covers the header bar on an iPad: the header drops to the content's level.
On iPad Chrome and Safari (both WebKit) the opened card's top row - the collapse
caret, the card number, the menu, maximize and X - was behind the header bar,
although the card has z-index 2001 and the header 1000. iOS WebKit makes every
scrolling element (#content, the board wrapper) a stacking context of its own, so
the card's z-index is compared only inside it while the header bar, a flex item of
body and a stacking context too, is painted over all of it. While a card is open
the header bar (#header-quick-access) is now at z-index 0, the level of the
content, which comes after it in the page and so paints over it; with no card open
it keeps 1000.

A browser spec makes #content and the board wrapper stacking contexts with
isolation: isolate, which is the same trap, and shows the card over the header open
and collapsed, its close button reachable, in Chromium and WebKit; without the rule
it fails with the card's top at 8 behind a header 88 high, as in the screenshot.

Thanks to xet7 !
2026-11-04 02:56:33 +02:00
Lauri Ojansivu 8f4efbca57 Merge remote-tracking branch 'origin/main' 2026-11-04 01:35:33 +02:00
Lauri Ojansivu dde09dead5 CHANGELOG: where the time goes when a WeKan board is opened. 2026-11-04 01:21:48 +02:00
Lauri Ojansivu 3621413d6f Speed test: where the time goes when a WeKan board is opened, on FerretDB and on MongoDB.
app/board-open.cjs seeds a board of 200, 2000 or 5000 cards, opens it in Chromium and
records the wall clock to the first card and until the DDP WebSocket is quiet, what
crossed it, the Meteor server's CPU by who spends it (Node inspector) and FerretDB's
CPU (pprof). Two runs on each database: the Meteor server's CPU is the same on both
(0.1-0.5 s) and the DDP server is 1% of it; a 5000-card board takes FerretDB (SQLite)
400-600 ms longer to open, the data sent to the browser being the same ~210 KB, a
small board 70-100 ms. FerretDB uses about 4 s of CPU for the 5000-card open: SQLite
39%, filtering in Go 34%, find/getMore/aggregate 0.7-0.8 s each. The README says what
was measured and what was not: the development build, one machine, the browser's own
time not separated, and the aggregate's source not identified.

Thanks to Raymond-Voogt and xet7 !
2026-11-04 01:21:16 +02:00
github-actions[bot] 3c3fc07b3c Bump versions for v12.93 2026-11-03 23:04:07 +00:00
Lauri Ojansivu c1e8976b28 Prepare v12.93 release 2026-11-04 01:02:51 +02:00
Lauri Ojansivu dcce935310 CHANGELOG: the opened card and popups move under a finger. 2026-11-04 01:00:21 +02:00
Lauri Ojansivu 1b05673200 The opened card and every popup move under a finger: Pointer Events and touch-action.
On an iPad the opened card did not move when dragged by a finger from the collapse
caret, the space beside it, the card number, or the drag handle, while the page
behind it scrolled. The card was moved by mousedown / mousemove / mouseup, and a
finger sends none of them while it moves; the drag areas also lacked
touch-action: none, so the browser took the gesture for a scroll. The card window
is now moved by Pointer Events: a press on the header row or the handle moves it, a
press on a link or button in the row (the caret, close, the menu) waits for a real
drag so a tap still does its job and a drag does not also collapse or close, the
title still edits, and with drag handles on the handle alone moves the window. The
card number is above the title's edit zone so it drags too, the window is kept
within reach, and touch-action: none is on the card header, its handle and every
popup's title bar and handle. Blaze hands over a jQuery event without isPrimary, so
the native event is read.

A browser spec drags with a real touch (DevTools protocol, Pointer Events) from the
caret, the number and the space beside the title, with handles on and off, a tap
that still collapses, a mouse that still drags and a popup; without the fix the
finger tests fail.

Thanks to xet7 !
2026-11-04 01:00:19 +02:00
Lauri Ojansivu dafb63299f CHANGELOG: a popup card covers the header bars. 2026-11-04 00:42:59 +02:00
Lauri Ojansivu 84b0480aa8 An opened card opened as a popup covers the header bars, in every browser.
A card opened as a popup (a search result, a table row, a Roadmap/Gantt bar) got
the generic wide-layout rule top: 97px and z-index 30, because the desktop-mode
rules that dock an opened card at the top (top: 8px, z-index 2001) skip a popup.
So it opened BELOW the header, and collapsed it was a bar floating over the
swimlane, while the same file's popup wrapper rule says it must cover the header
bars. The popup card now docks at top: 8px with z-index 2001 in the wide layout;
mobile mode keeps its own full-window rule. A browser spec shows the card over
the header, open and collapsed, in Chromium and WebKit, and fails without the fix
(top 97 against a header 88 high).

Thanks to xet7 !
2026-11-04 00:42:57 +02:00
Lauri Ojansivu f6fc97e77a The full screen button says Minimize while the page is in full screen.
The same button leaves full screen; its title and name were still Maximize, which
does not say so. The existing Minimize translation is used, so no language file
changes.
2026-11-04 00:29:00 +02:00
Lauri Ojansivu d7105e531d CHANGELOG: a full screen button in the header bar. 2026-11-04 00:27:30 +02:00
Lauri Ojansivu f25957dcf3 A full screen button in the header bar, to the right of the logo.
It puts the whole page in full screen with the Fullscreen API (the unprefixed one
and the webkit-prefixed one) and takes it out again; the icon follows the
browser's own events, so Esc is understood. The label is the existing Maximize
translation, so no language file changes. It is drawn only where the browser
offers the API and the page may use it: not on iPhone, not in an iframe without
allowfullscreen. Unit tests drive the pure part with fake documents, including a
refusal; a browser spec checks the position, the label, entering, leaving, Esc,
and that nothing is drawn where the API is not enabled, in Chromium and WebKit.

Thanks to xet7 !
2026-11-04 00:27:28 +02:00
Lauri Ojansivu 984ea93aa8 CHANGELOG: Dependabot alerts 180 and 183-187, and the SQLite membership test fix. 2026-11-04 00:13:58 +02:00
Lauri Ojansivu 2441563008 Speed test: SQLite after the membership test fix (68.3 ms, 4.1x), the Due Cards profile and what was tried.
Thanks to Raymond-Voogt and xet7 !
2026-11-04 00:13:34 +02:00
Lauri Ojansivu f3950b9adc Dependabot alerts 180 and 183-187: minimatch in the other lockfiles, and the Go modules of the embedded benchmark.
minimatch before 11.0.1 (GHSA-g3hv-75wg-m249): npm-packages/exceljs still had
10.2.6 through readdir-glob 3, and the vendored jade lockfile under
meteor-jade-loader had three copies through glob 13 and test-exclude; both get
an override to minimatch 11, as the root lockfile has. A test now reads every
package-lock.json of the repository, not only the reported ones.

The benchmark module of embedded engines pulled klauspost/compress 1.18.0,
golang.org/x/net 0.23.0 and golang/glog 1.0.0 (GHSA-259r-337f-4rfw,
GHSA-5cv4-jp36-h3mw, GHSA-qxp5-gwg8-xv66, GHSA-vvgc-356p-c3xw,
GHSA-6wxm-mpqj-6jpf). They are raised to 1.18.7, 0.62.0 and 1.2.4, and
govulncheck then found more in x/net, x/text and OpenTelemetry, which are raised
too (x/net 0.62.0, x/text 0.43.0, otel 1.42.0): it now reports none. A test
holds every go.mod of the repository at or above these floors.

Thanks to dependabot and xet7 !
2026-11-04 00:12:45 +02:00
Lauri Ojansivu 5e07b8db6c Docs: the speed test, version history and embedded engines READMEs link directories, not README.md files. 2026-11-03 23:59:03 +02:00
Lauri Ojansivu eeeb0dbec9 CHANGELOG: Go databases compared with FerretDB's SQLite. 2026-11-03 23:47:33 +02:00
Lauri Ojansivu 96d56ecc6c Go databases that could be linked into FerretDB, against its SQLite: none is faster where it counts.
A benchmark of its own (docs/Databases/FerretDB/1/SpeedTest/embedded, a Go module,
no cgo) loads the speed test's cards into SQLite set up as FerretDB sets it and
into Pebble, Badger, bbolt, BuntDB, Nutsdb and Clover, and asks each the six
questions WeKan asks of cards, at 15,377 and 153,768 cards. A board's cards take
the same time on every engine that is not slower, because decoding the JSON
dominates; the key-value stores win single reads and writes by microseconds that
a FerretDB call hides; Pebble is 12x slower on fetching a board, Clover 90-190x
slower in total, and bbolt with its default fsync 100-160x slower on writes. The
README accounts for every entry of the awesome-go section.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 23:47:13 +02:00
Lauri Ojansivu aadfd9b6b3 CHANGELOG: Dolt in the speed test and the version history experiment. 2026-11-03 23:19:00 +02:00
Lauri Ojansivu 1c44876beb Speed test: Dolt, and what a database with version history would give WeKan's undo and redo.
Dolt runs under the existing MySQL handler with no new handler and gives the same
answers, but is by far the slowest: 20,470 ms in total, every card query a
2.8 s whole-table scan. The VersionHistory scripts drive WeKan-shaped changes
through FerretDB onto Dolt (a commit per write, undo and redo with DOLT_REVERT,
AS OF, +0.4 ms a write, a conflict when the same document is restored again) and
through the REST API of TerminusDB (author and message per commit, field-level
restore). ClickHouse and StarRocks were tried with the MySQL handler and need a
handler of their own; the README says where each fails.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 23:18:51 +02:00
Lauri Ojansivu bd86b518ea CHANGELOG and tests: the small-scale speed test, pgrust, TiDB and the databases that cannot run FerretDB. 2026-11-03 22:47:16 +02:00
Lauri Ojansivu f576a0cca9 Speed test: TiDB added, and the README says what was tried: pgrust, TiDB, Noria, YugabyteDB, CockroachDB.
TiDB (MySQL protocol, Apache-2.0) runs FerretDB after its metadata table's
generated columns became VIRTUAL, and gives the same answers as MongoDB; it is
a speed test row and opt-in for the conformance run (WEKAN_CONFORMANCE_TIDB=1).
Noria has no JSON type, triggers or generated columns, which the MySQL handler
needs; YugabyteDB refuses a CREATE INDEX on a jsonb expression, which every
FerretDB v1 PostgreSQL index is; CockroachDB is not open source. The README
also records that analyzing a PostgreSQL table after its indexes are created
took the PostgreSQL row from 454 to 145 ms, and that the same on MySQL and
MariaDB did nothing.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 22:45:56 +02:00
Lauri Ojansivu 46c0ad43e8 Speed test: small-scale results ordered fastest to slowest, with times slower, and pgrust.
Results.md now opens with an At a glance table (rank, total, how many times
slower than the fastest, the same for the slowest user), every table is ordered
by its own numbers, the header says the real data size and the real operating
system, and the load table is ordered by the whole load time. pgrust
(PostgreSQL in Rust, malisper/pgrust:v0.4-beta) is one more row through the
same PostgreSQL handler; in the conformance run it is opt-in with
WEKAN_CONFORMANCE_PGRUST=1. The run is 1/20 of a production WeKan so it takes
minutes.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 22:36:56 +02:00
Lauri Ojansivu 499772e4c2 Speed test README: sorting in PostgreSQL was tried and was not faster.
The ten newest of 30,116 users took 52 ms with an ORDER BY in the database and
38-49 ms decoded and sorted in Go, so the change was not kept; the README says
what was measured so it is not repeated.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 21:53:31 +02:00
Lauri Ojansivu 90fe133363 Merge remote-tracking branch 'origin/main' 2026-11-03 21:37:36 +02:00
Lauri Ojansivu b812d36871 CHANGELOG: the speed test README. 2026-11-03 21:24:06 +02:00
Lauri Ojansivu d05a42a5af Speed test README: the FerretDB v1.100-v1.105 and WeKan improvements since the last run.
SQLite sorts and stores JSONB, PostgreSQL uses a GIN index for membership and
pushes down $or, $elemMatch and $and, MySQL and MariaDB read through their
indexes with multikey side tables, and WeKan stopped scanning whole collections
every hour or at every start. Results.md still shows v1.99.0, which the README
now says, and the open items are updated to match.

Thanks to Raymond-Voogt and xet7 !
2026-11-03 21:24:03 +02:00
github-actions[bot] 42507de1d0 Bump versions for v12.92 2026-11-03 18:51:08 +00:00
Lauri Ojansivu 131b0cc509 Prepare v12.92 release 2026-11-03 20:48:47 +02:00
Lauri Ojansivu 93ae5ac6ee Merge remote-tracking branch 'origin/main' 2026-11-03 20:46:48 +02:00
Lauri Ojansivu 34bb91d1f5 CHANGELOG: a missing REST checklist answers a JSON 404. 2026-11-03 20:43:21 +02:00
Lauri Ojansivu d231df56fc REST API: a missing checklist, or one under another card, answers a JSON 404, not an empty 500.
GET /api/boards/:boardId/cards/:cardId/checklists/:checklistId answered 500 with
no body when the checklist did not exist or belonged to another card. It now
answers 404 with the same error text the checklist update and delete routes
use; board access and the parent-card check are unchanged. Route tests drive
the registered handler (found, missing, other card, other board, denied, lookup
failure) and a browser spec reads the checklist over REST and after a reload.

Thanks to AhmedLukman and xet7 !
2026-11-03 20:43:06 +02:00
github-actions[bot] e422ab691e Bump versions for v12.91 2026-11-03 18:01:08 +00:00
Lauri Ojansivu 3f712a0445 Prepare v12.91 release 2026-11-03 19:59:16 +02:00
Lauri Ojansivu d99dadadee Merge remote-tracking branch 'origin/main' 2026-11-03 19:42:04 +02:00
Lauri Ojansivu a58b7bee2c CHANGELOG: code scanning alert 600, a test replacing getLabel with itself. 2026-11-03 19:17:12 +02:00
Lauri Ojansivu 62da564a1e Code scanning #600: a test no longer replaces getLabel with itself, and the guard knows anchored patterns.
tests/boardLabelLookup.test.cjs sliced the getLabel method out of models/boards.js
and then ran .replace(/^getLabel/, 'getLabel') on it, which does nothing: the
slice already starts at getLabel. tests/noIdentityReplacement.test.cjs skipped
every regular expression with an anchor, so it could not see it; a leading ^ or
a trailing $ over plain characters is now compared like any other.

Thanks to GitHub CodeQL and xet7 !
2026-11-03 19:16:44 +02:00
github-actions[bot] 2ada2e2d61 Bump versions for v12.90 2026-11-03 15:58:18 +00:00
Lauri Ojansivu 5de1acf496 Prepare v12.90 release 2026-11-03 17:57:04 +02:00
Lauri Ojansivu 3008bb5606 Merge remote-tracking branch 'origin/main' 2026-11-03 17:55:41 +02:00
Lauri Ojansivu 4f7521ae3c Tigre: the login page background label uses the Tigre word for color, not the Tigrinya one.
Thanks to Raymond-Voogt and xet7 !
2026-11-03 17:31:40 +02:00
Lauri Ojansivu 1c63049913 CHANGELOG: login page background color and a readable support page. 2026-11-03 17:20:07 +02:00
Lauri Ojansivu 6b27764452 Admin Panel: a login page background colour, and a readable layout for the login text and the support page.
Lay-out has a new Login page background color (loginBackgroundColor). Only a
hex colour is accepted, so the value in the CSS custom property can never carry
CSS; the text under the login logo turns white on a dark colour and black on a
light one. The colour is on body only while the sign-in layout shows. The
support page and the text under the login logo get a centred column with sized,
bold headings and space between paragraphs. The label is translated into every
language file.

Thanks to Raymond-Voogt and xet7 !

Fixes #6866,
2026-11-03 17:19:22 +02:00
Lauri Ojansivu ae49937b44 Tests: the Playwright specs expect the accessibility-audit colours and drop below the last checklist row.
The belize and nephritis buttons, the limegreen header, the red-string and
typed-relation lines are now the darker shades that keep their contrast, and
the checklist drag released on the sortable's own bottom edge.
2026-11-03 16:48:44 +02:00
Lauri Ojansivu 7f27ea1f26 CHANGELOG: Show focus outline, and Escape closes the add-list composer. 2026-11-03 16:04:58 +02:00
Lauri Ojansivu 144777ae6b Member Settings: a per-member switch to hide the focus outline.
Change Settings has Show focus outline, on by default: the blue ring round the
focused control is an accessibility feature (WCAG 2.4.7), and it also shows
after a click on a minicard. A member who turns it off - profile.hideFocusOutline,
a toggle method that takes the signed-in user only - gets a class on body that
hides the outline and its halo for them and nobody else. The label is translated
into every language file (best effort; the low-confidence ones are listed in the
changelog).

Thanks to mimZD and xet7 !

Refs #6755,
2026-11-03 16:04:44 +02:00
Lauri Ojansivu f4b5025a7b Escape closes the inline add-list composer.
The composer opened after a list (or in an empty swimlane) stays open after a
list is created, so several can be added, and only its X closed it. Escape now
closes it at the level of an inlined form, with the same reset as the X, and not
on a click elsewhere.

Thanks to mimZD and xet7 !

Fixes #6865,
2026-11-03 15:39:24 +02:00
github-actions[bot] cdcf8ec39d Bump versions for v12.89 2026-11-03 13:35:18 +00:00
Lauri Ojansivu 739f9ed7a0 Prepare v12.89 release 2026-11-03 15:33:40 +02:00
Lauri Ojansivu f06d082e34 Merge remote-tracking branch 'origin/main' 2026-11-03 15:33:13 +02:00
Lauri Ojansivu 4036b16774 CHANGELOG: shortcuts run once on a non-Latin keyboard. 2026-11-03 15:30:41 +02:00
Lauri Ojansivu 0110e84ce5 Keyboard shortcuts run once on a non-Latin layout.
On a Farsi (or Cyrillic) keyboard the d shortcut opened the Due date popup and
closed it at a glance. keyboard.js re-dispatched every key typed on another
script as its Latin key, a leftover from Mousetrap; hotkeys-js resolves the key
from event.code itself, so the shortcut ran twice and the repeat clicked the
same control again, closing it. The re-dispatch is removed, and a test drives
real key events for Latin, Farsi and Cyrillic at the library.

Thanks to mimZD and xet7 !

Fixes #6840,
2026-11-03 15:30:39 +02:00
github-actions[bot] 00a50ae139 Bump versions for v12.88 2026-11-03 12:58:20 +00:00
Lauri Ojansivu c67a67bc9a Prepare v12.88 release 2026-11-03 14:57:20 +02:00
Lauri Ojansivu 0b8a7face3 CHANGELOG: every theme and chosen color is readable; padded REST labels. 2026-11-03 12:37:32 +02:00
Lauri Ojansivu 8b2114cd0f Themes: every board theme and every color a member chooses is readable.
Eight themes (nephritis, pumpkin, moderatepink, strongcyan, limegreen, relax,
corteza, modern) had white text on an accent at 2.1 to 4.3:1: their accents and
hover, selected and emphasis shades are darkened until it reads at 4.5:1. The
filter sidebar's headings, the clear themes' quiet text and cleanlight's
add-dependency link are darker. A color a member chooses is made legible: a
custom theme accent or slide end is darkened under the header's white text, the
text color is moved to read on the theme's surfaces and no longer reaches the
header, labels and primary buttons, a dependency line is drawn at 3:1, Kanri's
tag text is the better of dark and light, and a custom label, card, list or
swimlane color already derives its text. All 25 themes show no colour contrast
failure on the board, the sidebars, Card Settings, the card and its popups.

Thanks to Samunosuke and xet7 !
2026-11-03 12:37:10 +02:00
Lauri Ojansivu 0426c7741a Board labels: a padded name finds the stored label, in every way a label is added.
A label is looked up by name and color before one is added, and the lookup
compared the name as sent while the schema stores it trimmed: a padded ' Bug '
missed the stored 'Bug', and each repeat created another label with the same
name and color. The REST builder now trims, as the pull request did, and so
does the lookup itself, which the card's label form, the importers and the list
sync share. A legacy label stored with padding is still found.

Thanks to AhmedLukman and xet7 !
2026-11-03 12:37:10 +02:00
Lauri Ojansivu c2a28a9233 CHANGELOG: the dark board themes are readable again. 2026-11-03 11:42:09 +02:00
Lauri Ojansivu a6b10e3592 Dark themes: the quiet greys, Card Settings, popups and sidebars are readable again.
The accessibility audit made the quiet text greys dark for every theme, and
old rules never followed the dark themes: Card Settings drew white text on
light grey blocks, popup items, the sidebar's shortcuts row, quiet buttons and
sidebar labels were black on dark, and an !important list header beat each
theme's own. The quiet text is a variable with a light value on the dark
surfaces; Card Settings' sections, popup items and the other rules follow the
theme. axe-core finds no colour contrast failure in cleandark, moderndark, dark
and exodark on the board, sidebar, Card Settings, an opened card and the custom
fields popup.

Thanks to Samunosuke and xet7 !

Fixes #6863,
2026-11-03 11:41:58 +02:00
github-actions[bot] 1ac4d1b1f9 Bump versions for v12.87 2026-11-02 22:34:10 +00:00
Lauri Ojansivu 222492f313 Prepare v12.87 release 2026-11-03 00:33:10 +02:00
Lauri Ojansivu 4f95b10f0d Merge remote-tracking branch 'origin/main' 2026-11-03 00:26:43 +02:00
Lauri Ojansivu 86a18665f0 CHANGELOG: REST single-item GETs answer 404 when nothing is found. 2026-11-03 00:22:26 +02:00
Lauri Ojansivu 1ded34dfdd REST API: a single-item GET that finds nothing answers 404, not 200 with an empty body.
The scoped card GET (/api/boards/:boardId/lists/:listId/cards/:cardId) sent
200 with no body for a missing, wrong-list, wrong-board or archived card. The
list, swimlane, custom field, comment and integration GETs did the same, and
the user GET threw a TypeError for an unknown name. Each now answers 404 after
the board check, so a stranger still learns nothing about which ids exist.

Thanks to AhmedLukman and xet7 !
2026-11-03 00:22:24 +02:00
github-actions[bot] db791e1d40 Bump versions for v12.86 2026-11-02 17:03:48 +00:00
Lauri Ojansivu e8e22dc25e Prepare v12.86 release 2026-11-02 19:02:52 +02:00
Lauri Ojansivu 08d20930de Merge remote-tracking branch 'origin/main' 2026-11-02 18:56:24 +02:00
Lauri Ojansivu 10f244de77 CHANGELOG: daily jobs run once per installation. 2026-11-02 18:49:22 +02:00
Lauri Ojansivu 4e28465964 Daily jobs run once per installation: notification cleanup and the file integrity audit.
The notification cleanup ran at every start and daily on every server, reading
and rewriting the same users 24 times at a rollout; a notification added by
another server between a read and its write was lost. A run now claims a
document per job and day in dailyJobRuns, and the timer looks hourly for an
unclaimed day. The file integrity audit read 'lastRun', checked it, then wrote
it, so two servers could both scan; one atomic update now claims the run.

Continuous backup has no leader: that is documented as single-server, and the
lease it needs is not built, so #6861 item 3 stays open and this commit does
not close it.

Thanks to Raymond-Voogt and xet7 !
2026-11-02 18:49:19 +02:00
github-actions[bot] 2936a682ce Bump versions for v12.85 2026-11-02 15:27:08 +00:00
Lauri Ojansivu ab80aaf9a3 Prepare v12.85 release 2026-11-02 17:26:05 +02:00
Lauri Ojansivu 0fb943b6fb Merge remote-tracking branch 'origin/main' 2026-11-02 17:25:22 +02:00
Lauri Ojansivu 6d8b0c15ec CHANGELOG: a cleared connection pool is a lost connection; the board key backfill uses the key index. 2026-11-02 17:18:17 +02:00
Lauri Ojansivu abc23a7eda Database problems: a cleared connection pool is a lost connection; the board key backfill uses the key index.
collection2 wraps a failed write as 'ValidationError: Failed validation <driver
text>', and the driver's 'Connection pool for <host> was cleared' names a
replica set member that MONGO_URL does not list, so the schema rule matched.
The text is now a lost connection. The board key backfill looked for boards
without a key with an $or including an exists-false branch, a scan of every
board at every start of every server; one $in on the key index finds the same
boards.

Thanks to Raymond-Voogt and xet7 !

Fixes #6860,
2026-11-02 17:17:58 +02:00
Lauri Ojansivu d9aa210012 Accessibility: the results of the fixes in Audit.md, page by page. 2026-11-02 17:17:07 +02:00
Lauri Ojansivu 2e8e1e2ce2 CHANGELOG: the accessibility audit findings are fixed in the default theme. 2026-11-02 16:08:47 +02:00
Lauri Ojansivu c8bcc4817d Accessibility: fix the findings of the axe-core audit in the default theme.
The default theme's blue is #2573a7, so the header's white and #f2f2f2 text
passes WCAG AA; label colours, quiet greys and grey tiles have text that
passes too. The Starred menu, the user menu and initials avatars carry their
visible text in their accessible name. The card's Pomodoro field and the
comment and description editors have a name. A card section heading is an h3
holding a real button with aria-expanded, and its menu and layout toggle sit
beside it. The sign-in form is the main landmark with a level-one title, every
page has a level-one heading, and the shortcuts list is reachable by keyboard.

After the fixes axe-core finds no violation on 22 pages. Spec 45 runs the scan
and tests/accessibilityAuditFixes.test.cjs pins each fault by its shape.

Thanks to xet7 !
2026-11-02 16:08:32 +02:00
Lauri Ojansivu 2dd25c733e Accessibility: the result of an automated axe-core audit of seven pages.
Thanks to xet7 !
2026-11-02 15:03:54 +02:00
github-actions[bot] cb6c6533c5 Bump versions for v12.84 2026-11-02 12:45:02 +00:00
Lauri Ojansivu 292b804474 Prepare v12.84 release 2026-11-02 14:43:46 +02:00
Lauri Ojansivu 623fccf06e Merge remote-tracking branch 'origin/main' 2026-11-02 14:39:48 +02:00
Lauri Ojansivu cfd7ad2e5f Catch two unhandled rejections found by the wider promise lint.
The instance-editing hook looked up the board of an edit without a catch,
and the service worker registration had none.

Thanks to xet7 !
2026-11-02 14:37:12 +02:00
Lauri Ojansivu 762e5a9432 CHANGELOG: npm run lint:promises. 2026-11-02 14:30:59 +02:00
Lauri Ojansivu f1d2a497bc Lint for promises that are not awaited, handled or marked void.
npm run lint:promises runs typescript-eslint's no-floating-promises and
no-misused-promises over models/lib, server/lib and imports/lib, with type
information read from tsconfig.lint.json (nothing is type-checked). The
eleven findings were promises that never reject (their own catch) and now
say so with void, and three checks that used a promise as a boolean.

Thanks to xet7 !
2026-11-02 14:30:49 +02:00
Lauri Ojansivu 2f9c972d46 CHANGELOG: the code-quality findings. 2026-11-02 14:15:17 +02:00
Lauri Ojansivu 582a22c291 Fix GitHub code scanning code-quality findings.
Real faults: a card's original-position description formatted a promise
because it did not await the history; the attachment swipe distances and
two drag-scroll locals were undeclared globals; a file that cannot be read
during a GridFS upload left the upload open; api.py called exit() instead of
sys.exit(); a rules test redefined a property and a security-log test kept
an unreachable branch. Also 31 missing semicolons, unused imports in the
card details, exporters and attachment API, unused Python imports, and
comments on empty except clauses. The list-sync scanner test cut its source
at the last Meteor.startup, which the new index startup of #6855 had moved.

Thanks to GitHub CodeQL and xet7 !
2026-11-02 14:15:02 +02:00
github-actions[bot] d200234535 Bump versions for v12.83 2026-11-02 11:40:29 +00:00
Lauri Ojansivu 8548bae527 Prepare v12.83 release 2026-11-02 13:38:58 +02:00
Lauri Ojansivu d6b2d55c36 Merge remote-tracking branch 'origin/main' 2026-11-02 13:35:16 +02:00
Lauri Ojansivu f7e3533b13 CHANGELOG: LDAP account binding. 2026-11-02 13:32:03 +02:00
Lauri Ojansivu e91c14cd9a LDAP: a reused login name no longer opens the previous owner's account.
An account found by username or e-mail that records another directory
entry's identifier (same attribute, other value) is refused at login and
skipped by the import. An existing account now records its identifier and
its attribute, so LDAP_UNIQUE_IDENTIFIER_FIELD takes effect for it.
LDAP_EMAIL_MATCH_REQUIRE looks users up by username instead of _id, which
never matched. The background sync skips an account whose username matches
several entries instead of binding it to the first.

Thanks to Raymond-Voogt and xet7 !

Fixes #6859,
2026-11-02 13:31:40 +02:00
github-actions[bot] e7439e1d45 Bump versions for v12.82 2026-11-02 11:00:45 +00:00
Lauri Ojansivu 4a34b91ecf Prepare v12.82 release 2026-11-02 12:59:36 +02:00
Lauri Ojansivu d9984d2069 Merge remote-tracking branch 'origin/main' 2026-11-02 12:54:11 +02:00
Lauri Ojansivu e982282954 CHANGELOG: card numbers on a move, a restore and a set number. 2026-11-02 12:42:43 +02:00
Lauri Ojansivu e68938698c Card numbers: a card moved to another board, restored or given a number cannot take one another card has.
#6815 checked the number of every new card, but a card also enters a board's
numbering by an update: a move from the browser, which computed the
destination number from the cards it had loaded (often none, so 1), a restore
from the archive, and a number set by the REST API or an import. A before-update
hook now does what the insert hook does: a taken number is replaced by a fresh
one from the board's counter, a free one is kept and the counter raised past it.

Thanks to Raymond-Voogt and xet7 !

Fixes #6858,
2026-11-02 12:42:33 +02:00
github-actions[bot] 55299a5172 Bump versions for v12.81 2026-11-02 10:22:59 +00:00
Lauri Ojansivu 8bbc6fcda1 Prepare v12.81 release 2026-11-02 12:20:06 +02:00
Lauri Ojansivu 71463fb445 Merge remote-tracking branch 'origin/main' 2026-11-02 12:17:05 +02:00
Lauri Ojansivu b60a1e5a85 CHANGELOG: the database health probe reports a finding once. 2026-11-02 12:15:29 +02:00
Lauri Ojansivu 114e9ecd5a Database health: one server probes the database per period, so a finding is reported once.
Every server read the same MongoDB log and counters and recorded each
finding itself: one collection scan became up to 24 identical rows in Admin
Panel / Problems on 24 replicas. A probe now claims the period in a shared
document, and the state that compares one probe with the next lives there.
A claim that cannot be made at all does not stop the probe.

Thanks to Raymond-Voogt and xet7 !

Fixes #6857,
2026-11-02 12:15:09 +02:00
github-actions[bot] c6739fe452 Bump versions for v12.80 2026-11-02 10:01:31 +00:00
Lauri Ojansivu efd45c651e Prepare v12.80 release 2026-11-02 12:00:25 +02:00
Lauri Ojansivu fdc2c57b8f Merge remote-tracking branch 'origin/main' 2026-11-02 11:52:22 +02:00
Lauri Ojansivu 2c75b95908 CHANGELOG: minimatch 11.0.4 for GHSA-g3hv-75wg-m249. 2026-11-02 11:42:44 +02:00
Lauri Ojansivu a3630d96ce Update minimatch to 11.0.4, GHSA-g3hv-75wg-m249 (Dependabot alerts #180 and #182).
minimatch before 11.0.1 grows its glob parts without bound while it
preprocesses a pattern, then compares them pairwise. archiver (readdir-glob),
@typescript-eslint/typescript-estree and @babel/cli's glob used 10.2.6; they
now use the root minimatch 11.0.4.

Thanks to dependabot and xet7 !
2026-11-02 11:41:52 +02:00
github-actions[bot] db2aafe16c Bump versions for v12.79 2026-11-02 09:35:57 +00:00
Lauri Ojansivu c4b90d08a0 Prepare v12.79 release 2026-11-02 11:34:58 +02:00
Lauri Ojansivu cb5278ddb3 CHANGELOG: a collection-scan row says which query it is. 2026-11-02 11:31:54 +02:00
Lauri Ojansivu bc79de8c57 Database health: a collection scan row names the command, the operators per field and the queryHash.
A row said only which fields a slow query filtered on, which is not enough
to find the query in the code or to run explain(). It now shows the command
(for a slow getMore, the query it continues, instead of 'filter on nothing'),
the operators used on each field - never values - and MongoDB's queryHash.

Thanks to Raymond-Voogt and xet7 !

Fixes #6856,
2026-11-02 11:31:47 +02:00
Lauri Ojansivu 81714cd695 CHANGELOG: the schema re-check and due-date reminders run once. 2026-11-02 11:28:59 +02:00
Lauri Ojansivu d42369e96a Full collection scans run once, not at every start of every server; due-date reminders are sent once.
The startup schema re-check claims a run in _wekan_migration so one server
re-checks while the others skip (a claim older than 2 hours is taken over),
and a run that leaves work unresolved is retried once a day instead of at
every start. The daily due-date reminder scan is claimed per day in
dueReminderRuns, so 24 replicas no longer send 24 reminders per card.
A partial index on lists serves the 15-minute list sync scan, and a sparse
index on boards.dueReminderDays serves the due-date lookup.

Thanks to Raymond-Voogt and xet7 !

Fixes #6855,
2026-11-02 11:28:44 +02:00
Lauri Ojansivu efff590f74 CHANGELOG: the announcement shows on Home and All Boards. 2026-11-02 11:26:12 +02:00
Lauri Ojansivu 64b5762020 Announcement: shown on Home and every All Boards list, not only on a board.
The banner waited for membership of the current board, and Home, Remaining,
Starred, Templates and the other All Boards pages have none. It now shows to
every signed-in user there, and to the members of the open board on a board.
Signed-out visitors still do not see it.

Thanks to titver968 and xet7 !

Fixes #6854,
2026-11-02 11:25:57 +02:00
Lauri Ojansivu c29a59eb95 CHANGELOG: any year by number in the date picker. 2026-11-02 11:17:04 +02:00
Lauri Ojansivu dcaa316a01 Date picker popup: the year is a number field, any year by typing or stepping.
A list of years had to end somewhere. The year is now a whole number
(0 is 1 BC) found by jumping through the calendar in use, so thousands of
years back or ahead are one entry away, up to the range a date holds.

Thanks to brlin-tw and xet7 !

Fixes #6853,
2026-11-02 11:17:02 +02:00
Lauri Ojansivu 7721c5f655 CHANGELOG: year, month and day drop-downs in the date picker. 2026-11-02 11:10:08 +02:00
Lauri Ojansivu 492895200b Date picker popup: year, month and day drop-downs under the calendar.
The heading above the calendar shows the month and year only, not the
YYYY-MM-DD of the selected day. Under the calendar, year, month and day
drop-downs pick any past, present or future date, in every calendar
system.

Thanks to brlin-tw and xet7 !

Fixes #6853,
2026-11-02 11:09:34 +02:00
Lauri Ojansivu 1b346dc465 CHANGELOG: the date picker heading names the year. 2026-11-02 10:50:22 +02:00
Lauri Ojansivu 45c291b406 Date picker popup: the heading names the year as well as the month.
Thanks to brlin-tw and xet7 !

Fixes #6853,
2026-11-02 10:50:13 +02:00
Lauri Ojansivu c3af2a08a9 CHANGELOG: the macOS sandbox keychain and Claude Code token. 2026-11-02 10:16:03 +02:00
Lauri Ojansivu 21c94ce098 Fix macOS Sandbox for VSCodium.
Thanks to xet7 !
2026-11-02 10:14:47 +02:00
Lauri Ojansivu 2d8a28c2f6 Merge remote-tracking branch 'origin/main' 2026-11-02 09:44:15 +02:00
Lauri Ojansivu c58b2896d1 Fix macOS Sandbox for VSCodium.
Thanks to xet7 !
2026-11-02 09:43:32 +02:00
github-actions[bot] 6b98272272 Bump versions for v12.78 2026-11-02 07:14:42 +00:00
Lauri Ojansivu ee22fb1a3b Prepare v12.78 release 2026-11-02 09:13:43 +02:00
Lauri Ojansivu 154c9a6e52 Updated Changelog. 2026-11-02 09:11:57 +02:00
Lauri Ojansivu 121f37be91 Tests: drawing refit, Due Cards board scope, dependency import counts, and SAML's one Issuer.
Changes since October whose commits lacked a unit, negative or browser
test:
- 2929b61c85: a new drawing refits when its window changes, and is left
  alone once drawn on, edited, or zoomed or resized by hand.
- c64b3a3b1d: Due Cards, in "me" and "all users", shows an assigned-only
  member only their assigned cards; the owner sees them all.
- 8a6aeba7d1: the All Boards dependency import says how many lines were
  added, already there and unmatched.
- 5c50f38e25: a signed SAML Response with two Issuers is refused, which
  node-saml 5.1.0 accepted; the same Response with one Issuer logs in.
The browser specs pass in Chromium.

Thanks to xet7 !
2026-11-02 00:54:59 +02:00
Lauri Ojansivu 79c40407d1 Tests: Admin Panel pane titles, audit log alignment, GCS help, Data health "Never", card reports.
October Admin Panel changes whose commits had no test of their own:
- 85af731c1b and 2017f2871f: the Password length and Audit log panes
  name themselves in the title bar, the audit log has its own URL, and
  its cells align to the start - right in Hebrew - with no left/right
  rule left in its CSS.
- e6f7d226ca: no help line of a storage field is shown twice.
- 77956c98ed: Data health's "Never" is its own key, also when an
  administrator translates the List Sync one differently.
- 69f3358154: the card reports stay instance-wide and admin-only.
tests/playwright/specs/admin-pane-titles-and-help.e2e.js passes in
Chromium.

Thanks to xet7 !
2026-11-02 00:31:24 +02:00
Lauri Ojansivu 148d18d456 Tests: OpenSSL 4 decrypt refusal, the Scrum rollover lease, and the backup catch-up scan.
October changes whose commits had no node test of their own:
- ffe49dd2fd: decryptFile() refuses both the OpenSSL 4 and the OpenSSL 3
  wording of a failed authentication as "changed or the key is wrong",
  on any OpenSSL; an unrelated error (ENOSPC) is passed through.
- 0218cd823f: the rollover and batch-job leases are claimed before every
  write, another server's live lease is refused, and each lease field is
  written in one place in the whole tree.
- 8cbe2605fe: on macOS, a stopped engine and one with watching off run no
  catch-up scan.

Thanks to xet7 !
2026-11-02 00:30:57 +02:00
Lauri Ojansivu 15877dd1bc Source server: a local package fork that a prebuilt package implies is loaded first.
ecmascript implies the local dynamic-import fork, so compiling ddp-server,
listed before it in .meteor/versions, stopped the source-loaded server
with "isopack dynamic-import not yet loaded". The loader now loads the
local package asked for and retries. The source server still stops later,
on "Cannot find module 'meteor/accounts-base'"; the bundle server is what
the browser tests ran against.

Thanks to xet7 !
2026-11-02 00:24:55 +02:00
Lauri Ojansivu 3ccb465c3f Tests: the #6755 browser test focuses the minicard link itself.
Chromium does not leave the focus on the minicard after its menu popup
is closed with Escape - the menu's opener is a link without href, which
cannot take the focus back - so the test asserted a state the browser
had not reached. It now focuses the minicard's link, the state of the
report, and checks that d still opens the Due date editor from there.
Passes in Chromium and WebKit.

Thanks to mimZD and xet7 !
2026-11-02 00:24:55 +02:00
Lauri Ojansivu 71d9776b61 CHANGELOG: the Rules page has a Back to the board.
Thanks to mimZD and xet7 !
2026-11-02 00:07:50 +02:00
Lauri Ojansivu 34d9817110 Rules page: Back to the board.
The Rules page lost its Back with its own sidebar; only the trigger, action
and rule details pages kept one, to the rules list, so the way back to the
board was the Board View menu. The rules list and its Workflow, Bots,
Blocks and History views now have a Back link to the board at the top. The
three sub-pages keep their own Back and show no second one.

Thanks to mimZD and xet7 !

Fixes #6852.
2026-11-02 00:07:42 +02:00
Lauri Ojansivu aa939d7bec Tests: CrossBoardExportBleed and UnassignedAttachBleed are in the security regression inventory.
Both are published on the Hall of Fame, so the inventory counts 143 and
names each one's unit and browser regression tests. CrossBoardExportBleed
was published without being added, which failed the whole node-suite run.

Thanks to xet7 !
2026-11-02 00:05:44 +02:00
Lauri Ojansivu 194e5b0a51 CHANGELOG: UnassignedAttachBleed, and shortcuts after a click on a minicard or a closed popup.
Thanks to Javier García Herrera, mimZD and xet7 !
2026-11-02 00:05:10 +02:00
Lauri Ojansivu 532a96da11 Security: UnassignedAttachBleed - attachment API writes obey assigned-only card scope.
An assigned-only member whose card assignment was removed kept the card and
attachment ids and, through POST /api/attachment/upload and
DELETE /api/attachment/delete/:id, still added a file to that card and
soft-deleted its existing one: both asked only for board write access. The
DDP methods api.attachment.upload/delete, and copy and move onto a target
card over REST and DDP, had the same shape.

mayWriteCardAttachment (server/lib/assignedOnlyAttachments.js) applies the
same card rule the ordinary card Delete already used to all eight handlers.
A refusal is recorded in Admin Panel / Problems as UnassignedAttachBleed
(authz.assigned-attachment-write, medium: never disables the account).
tests/unassignedAttachBleed.test.cjs reproduces the reported sequence and
fails if any attachment API write lacks the card check;
tests/playwright/specs/unassigned-attach-bleed.e2e.js replays it over REST.

GHSA-w26g-rc43-q6cm.

Thanks to Javier García Herrera and xet7 !
2026-11-02 00:04:03 +02:00
Lauri Ojansivu ba0de93f79 Keyboard shortcuts keep working after a click on a minicard or a closed popup.
The shortcut filter refused every key while a link or button had the focus.
The minicard is itself a link, so a click on its date badge or its menu left
that link focused, and closing a popup returns focus to the button that
opened it: hovering a card and pressing d then did nothing until a reload.

A focused control now keeps only the keys it uses itself - Enter for any of
them, Space for a button, summary or checkbox, End/Home/arrows for a tab -
so Space still activates a focused button (audit A01) and every other key
is a shortcut again.

Thanks to mimZD and xet7 !

Fixes #6755.
2026-11-02 00:03:54 +02:00
Lauri Ojansivu 9a15491985 CHANGELOG: sign-in errors kept through a redraw, and the Firefox full-run test fixes.
Thanks to xet7 !
2026-11-01 20:35:06 +02:00
Lauri Ojansivu c8ed3e0fd0 Tests: the bulk export popup is opened until it stays, in all five Haitian Creole tests.
Each test navigates to All Boards and at once opens Export all boards.
All Boards is still drawing just after the route changes, and in
EVERYTHING's full Firefox run the popup opened then was closed again: no
header title to read (the first test; it passed in the run before).

All five tests open the popup again until its title is there. The spec in
Firefox, three times beside the running suite: 15 of 15.

Thanks to xet7 !
2026-11-01 18:56:50 +02:00
Lauri Ojansivu 4a9c3f21d2 Tests: the drawing surface's width is waited for, not read once after its viewBox.
The test waited until the drawing's viewBox had grown past the old 800,
then read the surface's box once. The element's layout follows the
viewBox a frame later, and in EVERYTHING's full Firefox run it was still
800 px against more than 1088 expected (it passed in the run before).

It polls the surface's width as well. The spec in Firefox, three times
beside the running suite: 9 of 9.

Thanks to xet7 !
2026-11-01 18:23:13 +02:00
Lauri Ojansivu 3a2fef4dc7 Sign-in: login errors are rendered from template state, and the SAML error test clicks a finished form.
saml-popup-error's first test failed in EVERYTHING's full Firefox run and
2 of 3 times alone: #login-error-message stayed empty after the SAML
button was clicked with a stub that fails at once.

The test clicked #at-saml (force) as soon as Meteor connected, while the
sign-in form was still drawing: the click landed on a button being
replaced, or - in another attempt - below the window ("Element is
outside of the viewport"), and no handler ran. It now waits for the form,
scrolls the button into view and clicks until the error shows. With one
worker both tests pass 6 of 6 in Firefox against a rebuilt bundle.

Found on the way: #login-error-message sits inside `if isLoading`, and
showLoginError wrote into the element - when that block is drawn again
the element is new and empty, so an error shown just before was lost. The
template now renders the message from a ReactiveVar (loginErrorText) as
escaped text, so a redraw keeps it and it can still never be HTML.
oauthProviders.test.cjs pins the same region, the translated codes and
text-only output in that form.

The other sign-in specs (44 files) pass in Firefox with it, apart from
runs where two copies of a settings-changing spec ran at once.

Thanks to xet7 !
2026-11-01 17:49:21 +02:00
Lauri Ojansivu f241681e80 Tests: background notification recovery checks its own mail, not the first one the sink caught.
The SMTP sink captures every mail the shared test server sends while it
listens. The test waited until user2 had exactly one mail, then read
sink.messages[0] - in EVERYTHING's full Firefox run another worker's
comment notification ("Please look", to another user) had arrived first,
so "Frozen recovery body 0" was looked for in the wrong mail.

It reads the mail addressed to user2. The whole spec in Firefox, twice:
24 passed, 2 conditionally skipped.

Thanks to xet7 !
2026-11-01 17:32:09 +02:00
Lauri Ojansivu c71c875649 Tests: the custom field settings popup is opened until its Create button shows.
The String Template help test opened a card and at once its custom field
settings popup. The opened card was still drawing, and in Firefox under
the whole suite the popup sometimes closed again before Create could be
clicked: ve-CC and rup failed in EVERYTHING's full Firefox run, 42
others passed.

The test opens the popup again until its Create button is there. ve-CC,
rup and en, twice each in Firefox beside the running suite: 6 of 6.

Thanks to xet7 !
2026-11-01 17:06:02 +02:00
Lauri Ojansivu 0da86171e9 CHANGELOG: TODO Later - the whole EVERYTHING run has passed in the browsers, conformance and FerretDB.
Thanks to xet7 !
2026-11-01 14:15:06 +02:00
Lauri Ojansivu b80e21fd48 CHANGELOG: Android bundles start again.
Thanks to xet7 !
2026-11-01 14:11:20 +02:00
Lauri Ojansivu 24a7270b7f Android bundles start again: npm 12 runs argon2's and bcrypt's install scripts.
All four Android bundle jobs failed: WeKan crash-looped on

  No native build was found for platform=android arch=x64 runtime=node
  abi=147 ... loaded from: .../meteor/accounts-password/node_modules/argon2

until the 240 s smoke test gave up. Meteor's npm rebuild had printed
"install scripts blocked because they are not covered by allowScripts",
though prepare-bundle-npm.mjs writes argon2 and bcrypt into every
rebuild directory's allowScripts.

npm 12 applies allowScripts to a project's DECLARED dependencies only.
The package.json that prepare-bundle-npm writes there declared none, so
argon2 and bcrypt were "extraneous" and their scripts stayed blocked -
reproduced with npm 12.2.1 on this release's bundle, and with the two
declared the same rebuild runs both install scripts. Linux, macOS and
Windows never noticed: prebuilds load there, and the Windows jobs compile
the two themselves. Android has no prebuild, and check-addon-arch passed
the Linux ones it cannot load.

prepare-bundle-npm.mjs now declares each reviewed installer a rebuild
directory has installed, at its installed version, and leaves a declared
one as it is. The Android build runs it itself first - the Linux bundle
it rebuilds may predate this - and loads argon2 and bcrypt with Android's
Node.js right after the install, so a missing addon stops the build with
its name instead of a smoke-test timeout.

prepareBundleNpm.test.cjs and androidBundles.test.cjs pin both; they fail
on the code before. Not run on Android here: the build needs Termux on
an Android-CPU runner.

Thanks to xet7 !
2026-11-01 14:11:10 +02:00
Lauri Ojansivu 73ea323da1 CHANGELOG: CrossBoardExportBleed, GHSA-8885-xrq4-j47h.
Thanks to ccsimplyspolit and xet7 !
2026-11-01 14:01:07 +02:00
Lauri Ojansivu be6727607c CrossBoardExportBleed: a checklist scope stays on its board, and every export obeys Disable all export and Anonymize export users.
GHSA-8885-xrq4-j47h, reported by ccsimplyspolit, both findings confirmed
in the current code:

1. GET /api/boards/<board>/export?checklistId=<id> (and exportZip)
   selected the scoped checklist and its items by id alone. A checklist
   of another board - a private one too - was streamed with every item
   and its creator, whose profile the anonymization map did not cover.
   Both selectors are now bound to the exported board's cards, and a
   checklist of another board is recorded in Admin Panel / Problems
   (authz.export-checklist-scope).

2. The streaming CSV/TSV export asked neither Disable all export nor
   Anonymize export users. Neither did, it turned out, Excel (board,
   card, all boards), PDF (board and card), the chart exports, the
   calendar feed or the external formats: only the WeKan JSON, ZIP and
   Trello exports did, though the setting promises that "the server
   rejects any export request" and names CSV and Excel.
   - refuseDisabledExport() answers 403 before anything is written, and
     all 13 HTTP export routes call it first; assertExportEnabled() still
     guards the builders. Both record the attempt (authz.export-disabled,
     never blocking the account: a page opened before the switch still
     shows the menus).
   - anonymizeExportIdentities() anonymizes what one exporter writes -
     users, @mentions, requested-by / assigned-by, comments - with the map
     build() uses, board members included. The CSV/TSV stream, Excel,
     PDF, chart exports (forExport: the chart views keep names), the
     calendar feed and every external format call it.

tests/crossBoardExportBleed.test.cjs drives the gate and the anonymizer
and scans the tree: no scoped checklist selected by id alone, no export
route without the gate, no exporter naming people without anonymizing.
All 7 fail on the code before. The new spec
security-cross-board-export.e2e.js replays the reporter's requests and
checks every export route while export is disabled and the CSV and
Kanboard exports while anonymizing; it passes with export-access and
scrum-export-anonymization against a rebuilt bundle.

Thanks to ccsimplyspolit and xet7 !
2026-11-01 14:00:19 +02:00
Lauri Ojansivu 9c5fa760c3 CHANGELOG: glob 14.0.3, checked: no WeKan code loads it, no telemetry, tarballs match the lockfile.
Thanks to dependabot and xet7 !
2026-11-01 13:41:40 +02:00
Lauri Ojansivu 27040c6f2f Merge pull request #6851 from wekan/dependabot/npm_and_yarn/npm_and_yarn-0e36e05768
Bump glob from 13.0.6 to 14.0.3 in the npm_and_yarn group across 1 directory
2026-11-01 13:36:11 +02:00
Lauri Ojansivu c15e1ab4b2 Tests: the Card Settings splitter is dragged once the popup holds still.
The splitter test measured the splitter straight after opening Card
Settings, while the popup was still placing itself. In WebKit the press
landed where the splitter had been and dragged nothing: the pane stayed
526 px against more than 606 expected (the first attempt in EVERYTHING's
WebKit stage; the retry passed).

It now measures once two reads of the splitter and the panes 150 ms apart
agree. WebKit 5 of 5, Chromium 3 of 3.

Thanks to xet7 !
2026-11-01 11:58:06 +02:00
Lauri Ojansivu b676cdc169 CHANGELOG: the WebKit date-range refusal and the language and push fixes of the browser tests.
Thanks to xet7 !
2026-11-01 11:25:20 +02:00
Lauri Ojansivu 4ad13db5ef Filters: the date-range forms are checked by WeKan, not refused by WebKit after Clear.
After Clear emptied a refused range, WebKit kept a half-edited date
segment in the From field and fired `invalid` on it at the next submit -
though its validity read valid - so the form never submitted and the new
range never applied, with no message (card-date-range.e2e.js in WebKit,
also on retry: 0 submit events, `invalid` on .js-card-date-from). The old
Clear handler emptied the fields the same way, so this was there before.

Both range forms are novalidate now. The submit handler already decides
whether a range is valid (rangeRefused), marks the end date invalid with
the reason and says it in the form, so nothing is lost.

filterDateRangeRefusal.test.cjs pins novalidate on both forms. Against a
rebuilt bundle card-date-range and card-movement-filter pass twice each
in WebKit, Firefox and Chromium.

Thanks to xet7 !
2026-11-01 11:24:59 +02:00
Lauri Ojansivu efe0e9287c Tests: a language switch is waited for, and WebKit does not freeze on asking for a push subscription.
Two causes behind most of EVERYTHING's WebKit failures so far, both in the
test pages rather than in WeKan:

- 94 specs switch language with setLanguage and then click at once. The
  client loads that language afterwards and draws everything again, which
  closes a popup opened before that: the board view popup of the flow and
  report help specs (Latin monte-carlo in WebKit; Corsican and Scottish
  Gaelic in earlier Firefox runs) had no entry left to click. The
  fixture's context now wraps Meteor.callAsync in each page so that a
  setLanguage call resolves once <html lang> - which client/lib/i18n.js
  sets in the same flush that redraws - names the language. It waits at
  most 10 s and then resolves anyway, so it can only make a spec patient,
  never fail one. A page of a context a spec makes itself uses
  untilLanguageShown().
- Playwright's WebKit freezes the page's main thread on
  PushManager.getSubscription(): after it not even a timer runs. Member
  Notification Settings asks it on opening, to show whether this device
  gets push (client/lib/webPush.js), so every WebKit spec that opened it
  hung in its next click - notification-activity-mute and the member
  scope of notification-delivery-settings, in every language. In WebKit
  the page now answers as a device with no subscription does. Chromium,
  Firefox and Safari answer the real call; no spec subscribes.

WebKit: those notification specs 10 of 10, and the Latin, Corsican and
Product Backlog specs 13 of 13; Chromium and Firefox 11 of 11 each with
the same subset. playwrightPageInit.test.cjs runs both page scripts on a
fake page: setLanguage waits for the language and keeps its result,
other methods are not held, a page whose Meteor arrives later is
wrapped, the wait is capped; WebKit's getSubscription answers null and
Chromium's and Firefox's are left alone; the fixture installs both.

Thanks to xet7 !
2026-11-01 11:21:45 +02:00
Lauri Ojansivu cfcead6a1a CHANGELOG: the Firefox fixes - date range refusal, history Retry, whiteboard line width - and the browser test fixes.
Thanks to xet7 !
2026-11-01 09:23:34 +02:00
Lauri Ojansivu 40f60582b7 Whiteboard: a line is drawn at its own width, not the whiteboard's 800.
Inside `each strokes` the template drew stroke-width=width, and Spacebars
looks a name up among the template's helpers before the line's own
fields: the width helper is the drawing's width, so every line was drawn
800 wide - one black shape over the whole whiteboard, in every browser.
The lines are mapped with strokeWidth now.

whiteboard.test.cjs: every name the line's path uses is a field of the
line and none is a helper of the template, which would win - it fails on
the code before. whiteboard.e2e.js checks each drawn path is the pen's
width (4) in both browsers.

The same spec drew the second person in another context of the same
browser. In Firefox two contexts share the pointer capture the first pen
takes: one line zig-zagged between both paths and the other page drew
nothing, 4 of 4. The second person now has a browser of their own, as
two people do; both pens are still down at once. Firefox 6 of 6 and
Chromium 4 of 4 against a rebuilt bundle.

Thanks to xet7 !
2026-11-01 09:22:05 +02:00
Lauri Ojansivu 96845f6f4b History recovery: Retry is enabled only once the board lets this person write on it.
After a reload the notice for an unanswered keyboard undo or redo shows
as soon as its board is open - before the board and its members have
arrived. undoRedoLast does nothing while Utils.canModifyBoard() is false,
so a Retry clicked in that moment was dropped without a word and the
notice stayed (history-recovery-notice.e2e.js, Firefox).

Retry is disabled until the retry can act; Forget needs no right and
stays usable. For someone who may only read the board Retry stays
disabled, as it could never do anything for them.

historyRecoveryNotice.test.cjs pins the binding and the guard it mirrors,
and that Forget is not gated - it fails on the code before. The spec
waits for Retry to be enabled before it clicks; the spec passes in
Firefox against a rebuilt bundle.

Thanks to xet7 !
2026-11-01 09:22:05 +02:00
Lauri Ojansivu c5f5ad2338 Filters: a refused date range says why in the form, so Clear still works in Firefox.
The filter sidebar's two date-range forms (card dates, card movement)
reported a reversed range with reportValidity(). Firefox opens that
bubble just under the end date - over the Apply and Clear buttons - and
while the field has focus it takes every click on them: no mousedown and
no click reached the page, twice in a row, so a refused range could not
be cleared at all (card-date-range.e2e.js, 4 of 4 in Firefox; with the
bubble suppressed it passed).

The end date is still marked invalid with the reason (setCustomValidity,
for :invalid and screen readers), and the reason is shown in the form as
a role="alert" line under the buttons. Typing in the range or Clear takes
it back.

filterDateRangeRefusal.test.cjs: both forms report through rangeRefused,
show the line and take it back on input and Clear, and nothing in the
filter sidebar calls reportValidity() - it fails on the code before. The
card-date-range and card-movement-filter specs check the line appears
with the reason, is gone after Clear or an edit, and is absent for an
accepted range; both pass in Firefox against a rebuilt bundle.

Thanks to xet7 !
2026-11-01 09:22:05 +02:00
Lauri Ojansivu 0c8c254dc5 Tests: the viewport matrix cleans up only what it seeded.
viewport-matrix is Chromium-only (device emulation), so in Firefox and
WebKit every test of a viewport is skipped and its beforeAll never runs -
but its afterAll still does, and read board.boardId of a board that was
never seeded: each of the twelve viewports failed in EVERYTHING's Firefox
stage, on the last test of the group, in 0 ms.

afterAll returns when nothing was seeded. Firefox: 132 skipped, none
failed. Chromium: phone-portrait 11 of 11, and its board is cleaned up.

Thanks to xet7 !
2026-11-01 09:09:14 +02:00
Lauri Ojansivu 0a1f8584ee Tests: the board view popup is opened until it stays open for the Product Backlog.
The test switches language and then opens the board, and when the language
arrives the board header draws again and closes a popup opened before
that. In Firefox the click on the board view toggle often came first: no
popup at all, and no Product Backlog entry to open - 2 of 6 runs alone,
the st variant in EVERYTHING's Firefox stage.

The test opens the popup again until its Product Backlog entry is there.
9 of 9 in Firefox (st, en, haw, three times each).

Thanks to xet7 !
2026-11-01 09:01:09 +02:00
Lauri Ojansivu 2042fd5102 Tests: the Blocks drag test grabs the block's body, not its edge.
In Firefox a Khmer label overflows its Blocks block and widens the box the
test measures, so the first point of the block it found, scanning from the
left, was the block's outline - and a press there dragged nothing: km and
km-KH failed every time (4 of 4), every other language passed.

The grab point is now one with the block's own body on both sides of it.
The whole spec in Firefox: 104 of 104, Khmer, Georgian, Fijian,
Kinyarwanda (the long labels the search was written for) and Arabic
right-to-left among them.

Thanks to xet7 !
2026-11-01 08:23:41 +02:00
Lauri Ojansivu 0403e27c62 Tests: the notification delivery sections are opened until they stay open.
The member-scope test switches language just before it opens Notification
Settings, and when the language arrives the popup draws again with every
section closed. Under a whole-suite run in Firefox that came after the
click on the section's summary, so the text checks passed and the select
the test then used was hidden: vi and zh_SG failed, 101 others passed,
and vi passed 3 of 3 alone.

expand() opens the section when it is closed and waits until its content
shows, again if a redraw closes it. vi, zh_SG, en and fi twice each, and
the board-scope, webhook-form and tray tests: all pass.

Thanks to xet7 !
2026-11-01 08:04:55 +02:00
Lauri Ojansivu d9e48a909d Tests: the list-sync specs' mock servers are reachable from WeKan when the browsers run in Docker on macOS.
The list-sync specs answer as Jira or GitLab from a server in the test
process, and WeKan's server fetches from it. With the browsers in Docker
on macOS that server was on the container's loopback, which Docker
Desktop keeps from the host: "fetch failed", and every test of
list-sync-estimate, -planning, -source and -time-estimates failed in
EVERYTHING's Firefox stage.

build.sh already forwarded the test SMTP port from the host into the
container through docker exec; the same forwarder carries any TCP. On
macOS it now also forwards a small pool, 39101-39104 by default
(WEKAN_TEST_REVERSE_PORTS), and names it to the specs, which listen
through helpers/reachable-server.cjs: the first free port of the pool,
or any port when there is none, as before. Linux forwards nothing.

30 of 30 of those specs pass in Firefox in Docker now.
playwrightReachableServer.test.cjs: the pool is read strictly, a port in
use is skipped, no pool listens anywhere, and when every forwarded port
is taken it fails clearly instead of listening where WeKan cannot reach.
playwrightDockerHost.test.cjs: macOS forwards the SMTP port and the pool
(or the one configured), names it to the container and removes its ready
files; Linux forwards nothing.

Thanks to xet7 !
2026-11-01 07:50:40 +02:00
Lauri Ojansivu 0cea141702 Tests: the duplicate relation picker is opened once the card has opened.
The test clicked Add dependency straight after the minicard. Under a
whole-suite run in Firefox that click landed while the card was still
opening, and the picker it opened was closed again when the card's route
and details settled - no popup, and the wait for its type select ran out
(the ve-PP variant; 59 of 60 passed, and it passed 3 of 3 alone).

The test waits for the opened card, then opens the picker again until its
type select shows. 6 of 6 (en, ve-PP, tlh, twice each) beside the suite.

Thanks to xet7 !
2026-11-01 07:21:43 +02:00
Lauri Ojansivu 9188843369 Tests: the checklist drag tests drag once the opened card's rows hold still.
The opened card draws the sections above its checklist for a moment after
it opens, and they push the rows down: in Firefox they moved 50 px between
the test measuring them and releasing the mouse. With handles off the
release landed on item 1's own title and opened its editor, so the titles
read 2,3 (EVERYTHING's Firefox stage); with handles on the enabled drag
dropped item 1 between 2 and 3. 2 of 6 in a row failed that way.

drag() waits until two reads of the rows' positions 150 ms apart agree,
then measures and drags. 10 of 10, both variants five times, while the
whole suite ran beside it.

Thanks to xet7 !
2026-11-01 06:57:18 +02:00
Lauri Ojansivu dbf07aadf9 Tests: the activity recovery report is searched again until no worker lease covers it.
The report is a snapshot taken when the search runs, and the live
one-second recovery worker leases each pending intent while it looks at
it. A search made during that lease shows the seeded orphan as "Delivery
reservation active" - correct - and the page keeps showing it, so the
wait for "Original activity is missing" ran out in EVERYTHING's Firefox
stage (the en variant).

The test searches again until a snapshot falls between leases, up to 30 s,
and then asserts the same status and the disabled Retry. 10 of 10 in
Firefox, every test twice, while the whole suite ran beside it.

Thanks to xet7 !
2026-11-01 06:40:42 +02:00
Lauri Ojansivu e001c4f9c2 CHANGELOG: the Docker test proxy redials an unanswered connect.
Thanks to xet7 !
2026-11-01 06:23:55 +02:00
Lauri Ojansivu ffe207e191 Tests: the Docker test proxy dials the host again when a connect is never answered.
Even on the bridge network, EVERYTHING's Firefox stage lost a test now and
then to a page whose document never came (RuleBleed's cross-board test,
Red Strings, shared templates). Inside the container the cause was in
plain sight: a connect to host.docker.internal:3000 in SYN_SENT after nine
retransmits, while the next connection went straight through. Through the
container's proxy 9 of 1000 requests went unanswered; from the Mac itself
none did.

The proxy no longer waits on such a connect. It reads nothing from the
browser until a connection to the host is up (pauseOnConnect), drops a
dial that has not connected in 1.5 s - a normal one takes milliseconds -
and dials again, five times, so no byte is lost or sent twice. If none
connects the browser's connection is closed rather than left open.

playwrightDockerHostProxy.test.cjs: two unanswered dials, then one that
connects - the request arrives once and is answered, and the abandoned
dials are closed; when every dial is unanswered the client is closed after
exactly the attempts, in well under the old 30-60 s wait.

Thanks to xet7 !
2026-11-01 06:23:34 +02:00
Lauri Ojansivu b2198cb61b CHANGELOG: the Firefox stage's Docker network, host paths and phone invitation test.
Thanks to xet7 !
2026-11-01 06:10:30 +02:00
Lauri Ojansivu a6bec88266 Tests: the phone invitation tile is measured in one layout with its buttons.
43-mobile-allboards read the invitation tile's box, then each control's
box, one round trip at a time. Firefox draws the row above the board list
later than Chromium, and in between it moved the whole list down 29.4 px:
the Decline button (1102-1162) was compared with where the tile had been
(853-1139) and failed, 2 of 2, in EVERYTHING's Firefox stage. The tile was
never clipped - settled, it runs 882-1168 around all three controls.

The tile and its message and two buttons are measured in one layout now,
polled until they hold, and a missing or zero-height control fails too.
Firefox: 3 of 3. With the original #6488 bug put back (the tile forced to
4rem) it still fails, naming the message and both buttons.

Thanks to xet7 !
2026-11-01 06:10:03 +02:00
Lauri Ojansivu 8a1f7a14a9 Tests: the Playwright container sees the repo and the files root at their own host paths.
A spec and the server share files by path. admin-continuous-backup gives
the server a key file it wrote under .tools/tmp, and reads the stream.json
the server wrote to its target - and with the repository mounted at /repo
and the files root at /wekan-files, every such path named a file only one
side had: "The encryption key file cannot be read", and no stream.json, in
both of its tests in EVERYTHING's Firefox stage (the only browser there that
runs in Docker on this Mac, and on Linux arm64 every browser does).

Both are mounted at the path they have on the host now, the specs are told
that path in WEKAN_FILES_PATH, and the in-container script finds the proxy
and the Playwright binaries from its working directory. Both tests pass in
the container now.

playwrightDockerHost.test.cjs drives run_playwright_docker on macOS and
Linux: the repo and the files root are mounted at their own paths,
WEKAN_FILES_PATH and HOME follow, and the function names no container-only
path. It fails on the code before.

Thanks to xet7 !
2026-11-01 06:08:05 +02:00
Lauri Ojansivu 7f948bf438 Tests: the macOS Playwright container uses Docker's bridge network, not the host network.
Three Firefox tests of one EVERYTHING run waited the full 60 s in
waitForMeteor: their page's own document never arrived. Measured from
containers during that run, on Docker Desktop for macOS:

- host network, through the WeKan proxy:          3 of 600 requests lost
- host network, straight to host.docker.internal: 8 of 600 connects never
  completed
- bridge network, through the same proxy:          0 of 1000
- from the Mac itself:                             0 of 300

So it is Docker Desktop's host network, not WeKan and not the proxy. On
macOS nothing needs that network - WeKan and MongoDB are reached through
host.docker.internal and SMTP through docker exec - so the container runs
on the bridge network, where the proxy still serves localhost:3000 and the
browser keeps ROOT_URL's origin. Linux keeps the host network, which is
the real one there.

playwrightDockerHost.test.cjs drives run_playwright_docker with uname
stubbed: Darwin gets bridge and the proxy, Linux gets host and none, and
the function names no network of its own. It fails on the code before.

Thanks to xet7 !
2026-11-01 06:06:04 +02:00
Lauri Ojansivu 6d0ab6cf11 CHANGELOG: continuous backup, single-file builds and the EVERYTHING test fixes.
The seven commits since the Dependabot entries: two bug fixes (continuous
backup watching before its first scan; single-file Mach-O signing and
start/backup waiting for WeKan) and five test-run fixes, grouped by topic.
The short changelog guard is a group of its own now, so the developer-
tooling subsection is grouped throughout. CHANGELOG.md regenerated.

Thanks to xet7 !
2026-11-01 05:47:52 +02:00
Lauri Ojansivu 89b64966c6 Tests: the password minimum specs wait until the server has the policy they wrote.
password-min-length-signup-login.e2e.js writes the password policy straight
into the settings document. The server learns it from its settings observer,
which polls on the standalone test MongoDB EVERYTHING runs: for up to ten
seconds it still answered the old policy. A sign-up form loaded meanwhile kept
the old minimum (6 instead of 10), and an administrator's Set password was
refused under the minimum the test before had left enforced - two failures in
EVERYTHING's Chromium stage that never showed on a dev server, whose MongoDB
is a replica set.

untilServerHas() asks the server (getPasswordPolicy) until it has the policy
the test wrote; the sign-up form is loaded again after it. 6 of 6 with two
workers, each test twice.

Thanks to xet7 !
2026-11-01 05:01:18 +02:00
Lauri Ojansivu d5e3325e87 Tests: Card Settings section order waits for the published board before reading the card.
"with every field allowed, the card draws exactly the Card pane's
sections" writes the board's Scrum visibility straight to the database and
reloads. The board publication's cursor has limit: 1, which Meteor's
change-stream driver does not follow, so it polls: a reload could still be
served the board as it was for up to ten seconds, and the card drew no
Scrum section - failing every time in EVERYTHING's Chromium stage. Live in
the browser the update arrived after 9.8 s; in the app itself a write
through a method re-polls at once, so only this direct write waited. The
test waits for the published board (up to 15 s) before it reads the card.

Thanks to xet7 !
2026-11-01 03:27:46 +02:00
Lauri Ojansivu 897376ba54 Tests: the Board Settings button theme test checks the settings that still have action buttons.
button-theme-foreground.e2e.js opened Board Settings / Swimlane, List and
Card and asked each for its column-actions buttons. Card Settings has had
none since 3e5bc58105 ("Card Settings: a checkbox per section, ...":
card actions live in the card's own section hamburgers), so every theme
failed on Card, in EVERYTHING's Chromium stage. Swimlane and List are
checked, in all four themes, and the test says why Card is not.

Thanks to xet7 !
2026-11-01 03:17:38 +02:00
Lauri Ojansivu 16103f9ad2 Tests: the whole-suite run keeps its test database in .tools/, not beside the checkout.
build.sh and build.bat started the test mongod on :3001 with its dbpath
in the checkout's parent directory (../mongodb-test-3001). That is the
one thing in a test run that was written outside the repository, against
the rule that everything WeKan's scripts write stays in .tools/ - and a
sandboxed editor, which may write only the repository, could not create
it, so EVERYTHING's browser stages had no database to run against. It is
.tools/mongodb-test-<port> now, which git and Meteor already ignore.

toolsDirOnly.test.cjs pins the new path in both scripts and fails on a
test database beside the checkout.

Thanks to xet7 !
2026-11-01 02:51:41 +02:00
Lauri Ojansivu 8cbe2605fe Continuous backup: files are watched before the first scan, and a macOS start catches up once.
The file engine scanned its sources first and started watching them
after, so a change made between the two - after the scan had passed a
file, before the watcher began - waited for the next scan, five minutes
by default. And macOS reports file events through FSEvents, which
delivers nothing for a moment after a watcher is created: an EVERYTHING
run on a loaded Mac lost a log line appended right after the start
(tests/integration/continuousBackup.test.cjs, "the appended log line was
streamed by watching", 4 !== 5).

The watchers start first now; a change seen by the scan and a watcher
too is recorded once, as they share one queue. On macOS one catch-up
scan runs two seconds after the start. Not elsewhere: inotify is
watching when fs.watch returns, and a second full scan of a large store
would double the start-up cost.

A new test, on macOS and Linux, with a watcher that never reports:
watching starts before the first scan; on macOS the catch-up scan
records a change made right after the start; on Linux no second scan
runs. It fails on the code before this change.

Thanks to xet7 !
2026-11-01 02:42:03 +02:00
Lauri Ojansivu f5eb4ecf61 Mac smoke test: a process group's live members come from libproc, not ps.
When macOS refuses to signal a process group (EPERM, which it returns for
a group of zombies), the smoke test checked with `ps` whether any member
was still alive. A Mac that will not let the build run `ps` - "Operation
not permitted", as here, where EVERYTHING's node suites run - failed the
check itself, and with it a correct app: macAppPackaging.test.cjs, every
EVERYTHING run on this machine.

On macOS it asks the kernel through libproc now (proc_listpids for the
group, proc_pidinfo for each member: a zombie's info cannot be read, a
live one's status is not SZOMB), with no process started. Elsewhere it
uses ps exactly as before.

tests/mac-smoke-app.py: the ps cases run as the non-macOS path; on macOS
an EPERM asks libproc and never ps; and a real group - a live child, then
a zombie leader alone - is told apart. macAppPackaging.test.cjs passes,
three runs in a row.

Thanks to xet7 !
2026-11-01 02:32:31 +02:00
Lauri Ojansivu 8d4f4caa2f Single file: a macOS build is signed whatever its target is called, and start/backup return once WeKan runs.
EVERYTHING's node suites run under Meteor's Node.js, which has single
executable applications, so singleFile.test.cjs built and ran a real
single executable - and it failed twice:

- The builder ad-hoc signed only a target named mac*/darwin*. Built from
  this Mac's Node.js under another label (the test builds 'amd64'), the
  file is still a Mach-O binary, the injected archive had invalidated its
  signature, and macOS killed it (SIGKILL) before it printed anything:
  status null. It signs every Mach-O output now, read from the file's
  magic number.
- `start --background` and `backup` returned as soon as they had spawned
  WeKan, before it wrote its pid file - which it does only after reading
  its settings and extracting FerretDB. A `stop` right after them said
  "WeKan was not running" and left it running. Both wait now
  (status.waitUntilStarted) until the pid file names the process they
  started, give up at once if it ended, and say so if it did not start.

singleFile.test.cjs: 24 of 24 under Meteor's Node.js, the real single
executable included; a new test pins waitUntilStarted, and that both
commands use it.

Thanks to xet7 !
2026-11-01 02:23:36 +02:00
dependabot[bot] 9a74138a84 Bump glob
Bumps the npm_and_yarn group with 1 update in the / directory: [glob](https://github.com/isaacs/node-glob).


Updates `glob` from 13.0.6 to 14.0.3
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v13.0.6...v14.0.3)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 14.0.3
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 23:51:54 +00:00
Lauri Ojansivu fa7e447536 CHANGELOG: ten Dependabot updates, checked; node-saml 5.2.0 where logins run; @smithy/core 3.38.1.
Thanks to dependabot and xet7 !
2026-10-31 23:06:40 +02:00
Lauri Ojansivu 05cc2460ea Tests: the short changelog guard holds between releases too.
changelogShort.test.cjs asserted an Upcoming section always, and one
Upcoming in CHANGELOG.md - but right after a release there is none until
the next real entry (CLAUDE.md, Making a release), so it failed after
every release, v12.77 included. With no Upcoming it checks that the
newest release is first and that nothing links an Upcoming that does
not exist; with one, what it checked before.

Thanks to xet7 !
2026-10-31 23:06:08 +02:00
Lauri Ojansivu 5c50f38e25 SAML logins are verified by @node-saml/node-saml 5.2.0, as package.json says.
Dependabot's 5.1.0 -> 5.2.0 bump (#6850) changed the app's package.json,
but a SAML login is verified by the wekan-accounts-saml package's own
copy, pinned by its Npm.depends to 5.1.0 - so nothing that runs changed.
5.2.0 is a hardening release a login relies on: it rejects a message
with more than one Issuer, checks idpIssuer on the Response and its
assertion, rejects a parsed query whose RelayState is not the signed
one, and checks a POST LogoutResponse's status and issuer.

The package pins 5.2.0 now. samlLogin.test.cjs required '5.1.0' in one
place and '^5.1.0' in the other; it requires the package and package.json
to name the SAME version, and at least 5.2.0, so a bump of one alone
fails.

A login roundtrip with the package's own copy (node-saml 5.2.0,
xml-crypto 6.4.0) and WeKan's options: a signed Response and a signed
Assertion log in; a NameID changed after signing, a signature by another
key, an unsigned response, and an answer to a request WeKan never made
are refused. saml-popup-error.e2e.js completes a real signed login in
the browser.

Thanks to xet7 !
2026-10-31 23:06:08 +02:00
Lauri Ojansivu 338afc15f8 @smithy/core 3.38.1: the AWS SDK's logger redaction fix, and the telemetry gate passes again.
The @aws-sdk/client-s3 3.1159.0 bump (#6844) resolved @smithy/core to
3.37.0, which npm marks deprecated: "a bug in custom logger data
redaction" (aws/aws-sdk-js-v3#8393). With a custom logger passed to an
AWS client, sensitive fields nested in a list or map reached the logger
unredacted. @smithy/core 3.24.0 to 3.38.0 have it, so 3.36.2 before the
bump did too; 3.38.1 is the fix.

WeKan's only AWS client is the S3 backup (server/methods/backup.js), and
it passes no logger, so nothing was logged. Updated all the same, in the
lockfile only and within the ranges the SDK already allows - one package,
nothing else moved. Its tarball matches the lockfile's integrity hash,
and compared with 3.37.0 it adds no network host, telemetry, process
spawning or install script. A multipart upload to a local S3 stand-in
still arrives signed AWS4-HMAC-SHA256, whole.

releases/check-telemetry.py --source failed on it: the deprecation text
links github.com, a URL origin new to package-lock.json. It passes now.

Thanks to xet7 !
2026-10-31 23:06:08 +02:00
Lauri Ojansivu dd2082b0ea Merge pull request #6846 from wekan/dependabot/npm_and_yarn/isomorphic-git-1.44.8
Bump isomorphic-git from 1.44.3 to 1.44.8
2026-10-31 22:39:27 +02:00
Lauri Ojansivu 4a86e9c19e Merge pull request #6850 from wekan/dependabot/npm_and_yarn/node-saml/node-saml-5.2.0
Bump @node-saml/node-saml from 5.1.0 to 5.2.0
2026-10-31 22:38:24 +02:00
dependabot[bot] 568cd9b6ec Bump isomorphic-git from 1.44.3 to 1.44.8
Bumps [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) from 1.44.3 to 1.44.8.
- [Release notes](https://github.com/isomorphic-git/isomorphic-git/releases)
- [Commits](https://github.com/isomorphic-git/isomorphic-git/compare/v1.44.3...v1.44.8)

---
updated-dependencies:
- dependency-name: isomorphic-git
  dependency-version: 1.44.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:37:49 +00:00
Lauri Ojansivu fea468471d Merge pull request #6849 from wekan/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-8.72.1
Bump @typescript-eslint/eslint-plugin from 8.72.0 to 8.72.1
2026-10-31 22:37:36 +02:00
Lauri Ojansivu 83199b9af7 Merge pull request #6848 from wekan/dependabot/npm_and_yarn/rspack/dev-server-2.2.2
Bump @rspack/dev-server from 2.2.1 to 2.2.2
2026-10-31 22:37:10 +02:00
Lauri Ojansivu 3839ee698d Merge pull request #6845 from wekan/dependabot/npm_and_yarn/mailparser-3.12.4
Bump mailparser from 3.12.2 to 3.12.4
2026-10-31 22:36:48 +02:00
dependabot[bot] a60b996c92 Bump @node-saml/node-saml from 5.1.0 to 5.2.0
Bumps [@node-saml/node-saml](https://github.com/node-saml/node-saml) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/node-saml/node-saml/releases)
- [Changelog](https://github.com/node-saml/node-saml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/node-saml/node-saml/compare/v5.1.0...v5.2.0)

---
updated-dependencies:
- dependency-name: "@node-saml/node-saml"
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:36:39 +00:00
Lauri Ojansivu 1230164c16 Merge pull request #6847 from wekan/dependabot/npm_and_yarn/i18next-26.5.11
Bump i18next from 26.5.4 to 26.5.11
2026-10-31 22:36:29 +02:00
dependabot[bot] 8008f410fe Bump @typescript-eslint/eslint-plugin from 8.72.0 to 8.72.1
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.72.0 to 8.72.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.72.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.72.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:36:27 +00:00
Lauri Ojansivu bd90782da2 Merge pull request #6844 from wekan/dependabot/npm_and_yarn/aws-sdk/client-s3-3.1159.0
Bump @aws-sdk/client-s3 from 3.1155.0 to 3.1159.0
2026-10-31 22:35:45 +02:00
dependabot[bot] 5eadc45563 Bump @rspack/dev-server from 2.2.1 to 2.2.2
Bumps [@rspack/dev-server](https://github.com/rstackjs/rspack-dev-server) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/rstackjs/rspack-dev-server/releases)
- [Commits](https://github.com/rstackjs/rspack-dev-server/compare/v2.2.1...v2.2.2)

---
updated-dependencies:
- dependency-name: "@rspack/dev-server"
  dependency-version: 2.2.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:35:25 +00:00
Lauri Ojansivu 3785a13f6a Merge pull request #6842 from wekan/dependabot/npm_and_yarn/imapflow-2.9.1
Bump imapflow from 2.8.0 to 2.9.1
2026-10-31 22:35:18 +02:00
Lauri Ojansivu 97cabdeca4 Merge pull request #6843 from wekan/dependabot/github_actions/helm/chart-releaser-action-1.8.0
Bump helm/chart-releaser-action from 1.7.0 to 1.8.0
2026-10-31 22:34:58 +02:00
Lauri Ojansivu 2802326888 Merge pull request #6841 from wekan/dependabot/github_actions/docker/login-action-4.7.0
Bump docker/login-action from 4.6.0 to 4.7.0
2026-10-31 22:34:17 +02:00
dependabot[bot] 1f46af0639 Bump i18next from 26.5.4 to 26.5.11
Bumps [i18next](https://github.com/i18next/i18next) from 26.5.4 to 26.5.11.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.5.4...v26.5.11)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 26.5.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:33:51 +00:00
dependabot[bot] 1bcb1da92f Bump mailparser from 3.12.2 to 3.12.4
Bumps [mailparser](https://github.com/nodemailer/mailparser) from 3.12.2 to 3.12.4.
- [Release notes](https://github.com/nodemailer/mailparser/releases)
- [Changelog](https://github.com/nodemailer/mailparser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/mailparser/compare/v3.12.2...v3.12.4)

---
updated-dependencies:
- dependency-name: mailparser
  dependency-version: 3.12.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:33:31 +00:00
dependabot[bot] 067efac7f8 Bump @aws-sdk/client-s3 from 3.1155.0 to 3.1159.0
Bumps [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) from 3.1155.0 to 3.1159.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1159.0/clients/client-s3)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1159.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:33:21 +00:00
dependabot[bot] 2bdd16060e Bump imapflow from 2.8.0 to 2.9.1
Bumps [imapflow](https://github.com/postalsys/imapflow) from 2.8.0 to 2.9.1.
- [Release notes](https://github.com/postalsys/imapflow/releases)
- [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md)
- [Commits](https://github.com/postalsys/imapflow/compare/v2.8.0...v2.9.1)

---
updated-dependencies:
- dependency-name: imapflow
  dependency-version: 2.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:32:54 +00:00
dependabot[bot] 433bc62211 Bump helm/chart-releaser-action from 1.7.0 to 1.8.0
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/helm/chart-releaser-action/releases)
- [Commits](https://github.com/helm/chart-releaser-action/compare/cae68fefc6b5f367a0275617c9f83181ba54714f...df287104677278f869ca5480bd260358397cc3f9)

---
updated-dependencies:
- dependency-name: helm/chart-releaser-action
  dependency-version: 1.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:32:54 +00:00
dependabot[bot] 29f9c62a64 Bump docker/login-action from 4.6.0 to 4.7.0
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.6.0 to 4.7.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/dbcb813823bdd20940b903addbd779551569679f...81c6f93d4bc991e510b342a00b419b3bb4efc5c6)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-31 20:32:36 +00:00
github-actions[bot] 21766cf38d Bump versions for v12.77 2026-10-31 19:47:43 +00:00
Lauri Ojansivu 101cdb7579 Prepare v12.77 release 2026-10-31 21:46:40 +02:00
Lauri Ojansivu 54cf951d9c Merge remote-tracking branch 'origin/main' 2026-10-31 21:46:38 +02:00
Lauri Ojansivu 57a43ee8e1 CHANGELOG: the date-popup guards follow movable popups and the date picker's empty cells.
Thanks to xet7 !
2026-10-31 21:44:13 +02:00
Lauri Ojansivu ff2c0ed7f6 Tests: the date-popup guards follow movable popups and the date picker's empty cells.
Two node guards still described v12.76's behaviour before it changed on
purpose. calendarPopupResize asserted that a popup without a date editor
does not drag; every popup drags by its title bar now (#6839), and with
drag handles on only by its handle - both pinned instead, its mock no
longer answering every selector as if a drag handle were there.
calendarTodayMarker picked a month's days with filter(Boolean), which an
empty cell passes now that it is an object with its own _id (#6838); it
skips the cells marked empty.

Thanks to xet7 !
2026-10-31 21:44:11 +02:00
github-actions[bot] 63d022fe5f Bump versions for v12.76 2026-10-31 19:44:06 +00:00
Lauri Ojansivu e9cf825b22 Prepare v12.76 release 2026-10-31 21:43:03 +02:00
Lauri Ojansivu 4ce8514e36 CHANGELOG: open issues #6834-#6840 - Today in the date picker, the card title and movable popups, Change Settings, the d shortcut, minicard dates.
#6834 is fixed in part and #6837 needs a decision: both in TODO Later.

Thanks to mimZD and xet7 !
2026-10-31 21:32:28 +02:00
Lauri Ojansivu 429fdc5319 Tests: a card-button rule is a button on the minicard (#6836 was already there).
"Show rule button on minicard" is Board Settings / Card, Card buttons,
Show on Minicard (allowsCardButtonsOnMinicard), in WeKan since v12.16:
the board's card-button rules are drawn under each minicard, and a click
runs the rule on that card without opening it. Off by default, which is
why it was not seen. It had no test.

minicard-rule-button-6836.e2e.js (Chromium and WebKit): with it on, a
card-button rule's button is on the minicard and a click archives the
card without opening it; with it off, no minicard has one.

Thanks to mimZD and xet7 !

Fixes #6836,
2026-10-31 21:31:36 +02:00
Lauri Ojansivu ffd5cbd2ad Change Settings: icons in a column on the first line, compact wrapped lines, rows divided.
Member Settings / Change Settings used the shared popup list as it is:
a flex row whose every line is 33px high with its icon centred. Its
settings are sentences, and most wrap - in Persian, in German - so a
setting became two tall lines with its icon floating between them and
the next setting right under it, with nothing to tell where one ended.
Each row is now an icon column, the words and the check mark at the
end; the icon sits on the first line, a wrapped line is an ordinary
line high, a rule divides the rows, and the Card settings checkbox has
the same space beside its words as an icon has.

Scoped to the popup's own list (change-settings-list): a popup keeps
the screens before it - the member menu - in the same .pop-over, so its
data-popup would have restyled those too.

change-settings-popup-6835.e2e.js, in Persian and English, Chromium and
WebKit: each row's icon is on its first line, a line is under 28px high
and a row only as tall as its lines, rows are divided, and the member
menu beside it is untouched.

Thanks to mimZD and xet7 !

Fixes #6835,
2026-10-31 21:29:31 +02:00
Lauri Ojansivu cf4b48df09 Minicard: a due date uses the card's width instead of three lines beside an empty strip.
The dates row of a minicard reserved 84px at its trailing edge - a third
of a 250px card - for nothing: the avatars float after it, and clear:
both starts it below the menu. A due date with its "1 day left" was
broken over three lines beside that empty strip, in a Persian board of
the report. It reserves the drag handle's room now, 24px, and the same
badge takes two lines at the default list width.

The board header bar wrapping to a second row, and the list header icons
under the card count, in the same report, did not reproduce on the
current code at the same window width, in Persian, with the card count
on; they are in CHANGELOG TODO Later until the reporter can say which
WeKan and which browser zoom showed them.

minicard-dates-width-6834.e2e.js (Chromium and WebKit, English and
Persian): the dates row reserves at most 24px, and the due date badge is
at most two lines high.

Thanks to mimZD and xet7 !
2026-10-31 21:26:15 +02:00
Lauri Ojansivu 73c21dfe3e Tests: typing d in the card title types at the end of the title in WebKit too.
WebKit puts the caret at the start of a field on focus and Chromium at
the end, so the d typed into the title landed at its start in WebKit and
the check for a title ending in dd failed there - while what the test is
for, that d typed in a field opens no Due date editor, held in both.
The test puts the caret at the end itself now.

Thanks to xet7 !
2026-10-31 21:23:04 +02:00
Lauri Ojansivu 22a12dc7f8 Opened card: the whole title edits; the window and every popup move by their bar or handle.
The opened card's title was split like a minicard's: the leading half
opened the editor and the trailing half dragged the window, so clicking
the right of the title, or beside a short last line, did not edit. Now
all of the title edits. With drag handles off, the window is moved from
the empty space of its header row above the title - between the collapse
caret and the menu; with drag handles on, only by the drag handle. A
reader who may not edit has no edit zone and still drags by the title.

Every popup now moves the same way - by its title bar with drag handles
off, by the handle before its title alone with them on - where only the
Drawing, Board View and Board Card settings popups and the date popups
moved before. A dragged popup is kept inside the window as it grows. The
drag pins a popup where it is before moving it: one placed from the
right edge (the member menu) jumped across the window, and one with a
margin by that margin, as the drag began.

card-title-edit-and-move-6839.e2e.js, in Chromium and WebKit: a two-line
title edits from its right, middle and left; the empty header row moves
the window by exactly the drag with handles off and not at all with them
on, when the handle does; and the card menu, Labels and the member menu
move by their title bar, or with handles on by the handle alone.
titleClickToEdit, popupMoveMaximize and popupBounds pin it.

Thanks to mimZD and xet7 !

Fixes #6839,
2026-10-31 21:23:04 +02:00
Lauri Ojansivu b1403ca7af The d shortcut opens the Due date editor of a card that is still being opened.
On a slower server a card's details render a few hundred milliseconds
after the click that opens it. `d` pressed in that moment found no Due
date control yet and did nothing - only the hovered-minicard path (#6755)
waited for it. Both paths now wait for THAT card's control, stop if
another card was opened meanwhile, click it once, and give up after 3
seconds.

The card details also closed any popup of their card when they were
destroyed (#6686), which a re-render of the card still open does too: a
popup could open and shut again at once. They close it now only when
the card is no longer the open one - closed, or another card opened.

The Playwright test emulates a 300 ms connection in Chromium, clicks
each card open and presses d at once: the editor opens and is still
open 1.5 s later, and closing the card still closes it. It fails on the
code before this change. dueDateKeyboardShortcut and
labelsPopupStaleCardContext pin both halves.

Thanks to mimZD and xet7 !

Fixes #6840,
2026-10-31 21:06:53 +02:00
Lauri Ojansivu bc21c901f4 Date picker: a Today button, and a month navigated back to is drawn whole.
The board Calendar view already has Today (and Calendar mode too), but
the date picker of every date popup - Due, Start, End, Received and the
rest - had only « ‹ › », so a user who paged to another month had to
page back. Today, between the month and ›, shows today's month and
focuses today; it changes nothing until a day is clicked and saved.

Testing it found a picker bug that was already there: going back to a
month drew it wrong - October came back as "29, empty, 30, empty", with
no 31st. Blaze matches list items by _id, and the weeks and days had
none; the empty cells, all alike, were moved around. Every week and
cell, empty ones too, now has an _id that names its month.

calendarPickerControls.test.cjs checks the ids, that Today shows today's
month and focuses today, and that it selects nothing; the Playwright test
pages two months ahead, presses Today, and checks every day of the month
in order, today focused, and the card's due date unchanged.

Thanks to mimZD and xet7 !

Fixes #6838,
2026-10-31 21:06:53 +02:00
Lauri Ojansivu 88fef5dc08 CHANGELOG: the Password length strings in every language, and its title bar.
Thanks to xet7 !
2026-10-31 15:18:23 +02:00
Lauri Ojansivu 3f9379ac86 Translations: the Password length page and Change your password, in 234 languages.
The 18 new strings of Admin Panel / Problems / Password length and of the
"Change your password" dialog, written in the language and script each
locale tag names, with the terms each file already uses for password,
account, save and sign in, and __min__ kept exactly. Only those 18 keys
changed in each file, written with fill-translations.mjs --apply, which
fills English placeholders only.

Low confidence, for a speaker to correct on Transifex: ace, ak, an, ary,
ast-ES, ay, ba, bho, bi, bm, bo, br, bua, chr, co, csb, cv, cy, dz, ee,
ff, fj, fo, fur, ga, gd, gn, gv, ha, haw, hsb, ig, iu, jv, kl, kok, ks,
kw, lg, lld, mg, mi, mt, nah, nap, nd, nso, ny, om, pap, qu, rm, rn,
rup, rw, sah, sc, scn, sd, se, si, sm, sn, so, ss, st, szl, te-IN, tg,
ti, tig, tk_TM, tlh, tn, to, tpi, ts, tt, uz-AR, ve, ve-CC, ve-PP, vo,
wa, wa-RR, wal, wo, wuu-Hans, xh, yi, zgh.

Thanks to xet7 !
2026-10-31 15:18:13 +02:00
Lauri Ojansivu 85af731c1b Admin Panel / Problems / Password length names itself in the title bar.
The title bar keeps its own copy of the menu labels, and the new pane
had none: tests/adminUrls.test.cjs failed on problems/password-length.

Thanks to xet7 !
2026-10-31 15:18:11 +02:00
Lauri Ojansivu bb32a50550 CHANGELOG: sign-up works again, Admin Panel / Problems / Password length, and a changed password signs every session out.
Thanks to xet7 !
2026-10-31 14:56:22 +02:00
Lauri Ojansivu c99aa7f845 Admin Panel / Problems / Password length, and a changed password signs every session out.
PASSWORD_MIN_LENGTH (default 8) is now the length below which a sign-in
is REPORTED, in the new Admin Panel / Problems / Password length, and
PASSWORD_MIN_LENGTH_ENFORCE (off by default) is what makes it a rule: a
shorter new password is refused, and an account that signs in with an
older shorter one sees only "Change your password" until it has a long
enough one. Signing in is never refused, enforced or not, so nobody is
locked out by a minimum set after their password. LDAP, OAuth2, OIDC,
SAML, CAS and Sandstorm accounts are neither reported nor asked: their
password is their provider's. An administrator's Set password no longer
applies WeKan's policy to such an account.

Meteor sends a password hashed, so only the browser knows its length: it
notes it on the sign-in form and, after a sign-in that worked, says "it
was short" - never the password or its length.

The page has both settings and a table, one row per account: username,
first and last short-password login, the latest IPv4 and IPv6 address
with the country's flag and city when a CDN says where it is, the count,
and when it was fixed, with the Too short (default) / Fixed / All filter.
Fixed is a changed password not seen short since; a short one seen
again reopens the row.

Changing one's own password - Member Settings, the forced change, a
reset link - now signs EVERY session of the account out, the one that
changed it too, and only the new password signs in again. The browser
goes to the sign-in page as soon as it is signed out, since the server
closes the connection before the answer can arrive.

Documented in docs/Features/Admin-Panel/Problems/Password-length.md, the
settings on every platform. The new strings are in every locale in
English for now. Tests: passwordPolicy, passwordChangeRequired and
shortPasswordLogins (node); password-min-length-signup-login,
password-length-report and password-change-other-sessions (Playwright,
Chromium and WebKit), which change each setting on the page and check
what it does, and that sign-up with no mail server registers and signs
the user in.

Thanks to xet7 !
2026-10-31 14:54:22 +02:00
Lauri Ojansivu 508484ef1e Sign-up, and every method and publication, pass audit-argument-checks again.
Registering answered "Internal server error" - and created the account
anyway, so the next try said it already existed - and the
starredPublicBoards and translation publications failed for everybody:
"Did not check() all arguments". audit-argument-checks asks,
synchronously, the moment a handler returns, whether check() saw every
argument. The REQUIRE_2FA wrapper of every method and publication, and
the Audit log's wrapper of the administrators' methods, were async and
awaited before they called the handler - even with REQUIRE_2FA off - so
the handler's own check() came after the audit had already failed the
call, and the handler went on running.

A wrapper now calls the handler at once when it needs no lookup
(REQUIRE_2FA off, nobody signed in, a cached answer). When it must await
first, it hands the audit over (server/lib/argumentChecks.js): the
arguments count as seen for the outer audit, and the handler runs under
an audit of its own that it must satisfy exactly as unwrapped.

tests/wrappedHandlerArgumentChecks.test.cjs runs both wrappers under a
model of the check package's audit: a checking handler passes, off and
on, a refusal stays a refusal, a handler that does not check still fails
on every path, and no wrapper in server/ awaits before it has checked.

Thanks to xet7 !
2026-10-31 14:13:40 +02:00
Lauri Ojansivu 548196c9a9 CHANGELOG: Meteor in .tools/.meteor no longer springboards forever.
Thanks to xet7 !
2026-10-31 13:23:36 +02:00
Lauri Ojansivu 9a118c83b2 Meteor in .tools/.meteor no longer springboards forever.
A build from the menu printed uname -p and sysctl hw.cpu64bit_capable
over and over and never reached its first package. With
METEOR_WAREHOUSE_DIR set and no SANDBOX, Meteor takes the running tool
to be wherever the warehouse's "meteor" link points - 3.5.4, the release
it was installed with - so with .meteor/release at 3.6-rc.1 every start
springboarded to 3.6.0-rc.1, found itself the wrong tool again and
springboarded again: 28 times in 20 seconds of meteor --version. The
earlier 1.1 GB build log was the same loop, its module trace printed
anew on every exec.

tools-env.sh and tools-env.bat now set SANDBOX=true beside the
warehouse, as Meteor's own self-test does; that one check is all it
changes. The Nextcloud test scripts and the dev-source runner, which set
the warehouse themselves, do the same. meteor --version now springboards
once, from the linked 3.5.4 to 3.6.0-rc.1.

tests/meteorSpringboardLoop.test.cjs checks both env scripts, that
nothing sets the warehouse without SANDBOX, and runs the installed
Meteor to count its springboards.

Thanks to xet7 !
2026-10-31 13:23:24 +02:00
Lauri Ojansivu d442bd8a7e CHANGELOG: builds trace module resolution only with WEKAN_BUILD_TRACE_MODULES=1.
Thanks to xet7 !
2026-10-31 13:02:19 +02:00
Lauri Ojansivu 5a13e927bc Builds trace module resolution only with WEKAN_BUILD_TRACE_MODULES=1.
Turning the NODE_DEBUG=module trace off for EVERYTHING alone was not
enough: a dev build from the build.sh menu wrote 1.1 GB, three million
lines, in its first three minutes, before it was interrupted. The trace
is now opt-in in build.sh and build.bat alike, and the diagnostics line
says whether it is on and how to turn it on. The other build diagnostics
(resolver commands, npm verbose, METEOR_PROFILE) are unchanged.

Thanks to xet7 !
2026-10-31 13:01:57 +02:00
Lauri Ojansivu c78af24a36 CHANGELOG: EVERYTHING builds without the per-require module trace.
Thanks to xet7 !
2026-10-31 12:46:14 +02:00
Lauri Ojansivu 6551062492 EVERYTHING builds without the per-require module trace.
The build's NODE_DEBUG=module trace wrote over 12 GB in the first half
hour of an EVERYTHING run on macOS - twice, through tee - and slowed the
build it traced, so the run would fill the disk before reaching the tests.
A build from the menu still traces by default; WEKAN_BUILD_TRACE_MODULES=0
turns it off, and EVERYTHING sets that unless the caller asked for 1. The
other build diagnostics (resolver commands, npm verbose, METEOR_PROFILE)
are unchanged.

Thanks to xet7 !
2026-10-31 04:15:01 +02:00
Lauri Ojansivu a0ed5de6f1 CHANGELOG: the breached-password check's lookup digest (CodeQL #599).
Thanks to GitHub CodeQL and xet7 !
2026-10-31 03:52:47 +02:00
Lauri Ojansivu f406d46fe7 Breached-password check: one Web Crypto lookup digest for the browser and the server.
CodeQL #599 (js/insufficient-password-hash, CWE-916) flagged the SHA-1
the server computed for PASSWORD_CHECK_PWNED. That digest is not password
storage - accounts-password keeps passwords with bcrypt - and it is never
stored or logged: Have I Been Pwned's range API is keyed by SHA-1, its
first 5 hex digits are the question and the rest is compared in memory.
A slow hash cannot be used, because the service could not match it.

The browser and the server now compute it with one implementation,
breachLookupDigest in models/lib/passwordPolicy.js, which says what it is
and is not; the server no longer hashes a password with a hash of its
own, and a digest that cannot be computed lets the password through.
Tests pin that the digest is SHA-1 for ASCII and UTF-8, that it reaches
only the lookup and the comparison, and that nothing in WeKan hashes a
password-named value with a fast hash. Unreleased hardening, so no Hall
of Fame entry: docs/Security/Password-Breach-Check-Digest-2026-10-31.md.

Thanks to GitHub CodeQL and xet7 !
2026-10-31 03:52:32 +02:00
Lauri Ojansivu 4b0aa715eb Merge remote-tracking branch 'origin/main' 2026-10-31 03:36:43 +02:00
Lauri Ojansivu f5af33ac38 CHANGELOG: Audit log documentation.
Thanks to xet7 !
2026-10-31 00:44:17 +02:00
Lauri Ojansivu f165a24192 docs: Admin Panel / People / Audit log.
Thanks to xet7 !
2026-10-31 00:44:14 +02:00
Lauri Ojansivu e6ea524541 CHANGELOG: password policy, compulsory 2FA, idle sign-out, audit log, HSTS, cross-site guard and S3 encryption.
Thanks to xet7 !
2026-10-31 00:33:31 +02:00
Lauri Ojansivu de23dece66 Translations: the password policy, two-factor, audit log and S3 encryption strings in 234 languages.
The 16 strings the security features added were English placeholders in
every locale; each is now in the language and script its locale tag
names, with the terms the file already uses for password, account,
attachment, backup and two-factor authentication. __min__ is kept
character for character. Where the right word is spelled like the
English ("Administrator"), the column header says "Admin" or "Who"
instead, because a value equal to English is not a translation.

fy and fy-NL: the existing two-factor strings were Dutch in a Frisian
file, and are Frisian now; tests/frisianTwoFactorLanguage.test.cjs.
tig: Tigre spells storage መክዘን, not Tigrinya's መኽዘን.

Low confidence, for a speaker to correct on Transifex: ace, ak, an, ary,
as, ast-ES, ay, ba, bho, bi, bm, bo, br, bua, chr, ckb, co, csb, cv, dz,
ee, ff, fj, fy, gn, gv, haw, hsb, ht, ig, iu, kl, kok, ks, kw, la, lg,
lld, mg, mi, nah, nd, nso, ny, om, pap, ps, qu, rm, rn, rup, rw, sah, sc,
scn, sd, se, si, sm, sn, so, ss, st, szl, tig, tlh, tn, to, tpi, ts,
uz-AR, ve, ve-CC, ve-PP, vo, wa, wa-RR, wal, wo, xh, yi, zgh.

Thanks to xet7 !
2026-10-31 00:32:43 +02:00
Lauri Ojansivu 2017f2871f Audit log: its own Admin Panel URL and title, and right-to-left table alignment.
/admin/people/audit-log opens the pane and names it in the title bar like
every other People pane, and the table aligns to the start of the line,
so it reads right to left in Arabic, Hebrew, Persian and the rest.

Thanks to xet7 !
2026-10-31 00:32:33 +02:00
Lauri Ojansivu 302b47db6f docs: Security settings, every hardening option in one page.
Thanks to xet7 !
2026-10-31 00:15:13 +02:00
Lauri Ojansivu 482c2c29f4 Attachments, avatars and backups in S3 can be encrypted at rest.
Admin Panel / Attachments / S3 / Server-side encryption: SSE-S3 (AES256)
or SSE-KMS (aws:kms, with a KMS key or the account's default). S3
encrypts each object as it is written and decrypts it for WeKan on read,
so reads, range requests and moves are unchanged. Every attachment and
avatar upload, every renamed object and every backup written to S3 now
carries the choice; a value that is not one of them, or a malformed key,
adds nothing.

On the filesystem, encryption at rest stays with the disk or volume
(LUKS, an encrypted cloud volume): exports, PDF, thumbnails, virus scans,
backups and WebDAV all read the stored files directly, and encrypting
them inside WeKan would change every one of those and convert every file
already stored.

The new interface strings are pending translation.

Thanks to xet7 !
2026-10-31 00:12:09 +02:00
Lauri Ojansivu 4d5b832499 Admin Panel / People / Audit log: what site administrators did.
Nothing recorded who set somebody's password, made somebody an admin,
changed a login setting or restored a backup. Every call of those
administrative methods by a site administrator is now kept, one document
each, for a year: when, who, which action, the outcome - a refused call
too - its arguments and the address. A password or a secret never
reaches the trail: values under a credential-named key, the arguments
that are a password by position, and a password inside a URL are
replaced by [redacted]. An administrator editing cards is not in it,
the trail is shown to site administrators only, and a trail that cannot
be written never changes what the method did. It and the REQUIRE_2FA
wrapper keep each other's marks, so neither wraps the other twice.

The new interface strings are pending translation.

Thanks to xet7 !
2026-10-31 00:09:20 +02:00
Lauri Ojansivu 41173f0f6b Sign a browser session out after it has been idle for LOGOUT_IDLE_MINUTES.
LOGOUT_WITH_TIMER ends a login at a deadline however busy its user is;
nothing ended a session somebody walked away from. LOGOUT_IDLE_MINUTES
(Admin Panel / People / Login, every platform; empty or 0 is off) does:
the browser reports a key press, click, touch or scroll at most once a
minute, the server keeps the time per login token in idleSessions, and
once a minute every token idle past the limit is removed, which signs its
connections out. A tab that reconnects and resumes its login by itself -
a laptop waking up - is not activity and does not extend the session.
REST API tokens have no session record and are never removed. The time
is kept beside the user rather than on the token, because updating one
element of the token array needs the positional $ operator, which not
every database WeKan runs on supports.

Thanks to xet7 !
2026-10-31 00:08:20 +02:00
Lauri Ojansivu d7abe9910a Password policy for new passwords, and compulsory two-factor authentication.
New passwords: PASSWORD_MIN_LENGTH (default 8) and PASSWORD_CHECK_PWNED
(off by default), in Admin Panel / People / Login and on every platform.
The browser sends a password to Meteor hashed, so the server checks every
password that reaches it as text - Admin Panel Set password and Add user,
POST /users/register - and the sign-up, Change Password, reset and
enrolment forms check in the browser. Signing in is never checked, so an
existing shorter password keeps working until it is changed. A breached
password is found with Have I Been Pwned's k-anonymity range API: only 5
hex digits of the SHA-1 leave WeKan, and an unreachable service never
refuses a password.

REQUIRE_2FA: off (default), admins or all. It covers accounts that sign in
with a WeKan password; LDAP, OAuth2, SAML, CAS and Sandstorm accounts sign
in through an identity provider and are not asked. Such an account can
still sign in, sees only the 2FA set-up until it has 2FA, and until then
the server refuses every method and publication but signing in and
enrolling, HTTP routes do not accept its login and the REST API gives it
no token. Once required, 2FA cannot be switched off.

The new interface strings are pending translation.

Thanks to xet7 !
2026-10-31 00:07:58 +02:00
Lauri Ojansivu 48a2e97ea3 A state-changing request that logs in only with the cookie must come from WeKan itself.
POST /import-trello and other HTTP routes accept the login cookie as the
login, so a page on another site could make a signed-in browser send one.
The cookie is SameSite=Strict, which keeps it off such a request in
today's browsers; this is the second line, for older browsers, same-site
sibling hosts and routes added later. A POST, PUT, PATCH or DELETE whose
only login is the cookie must carry an Origin, or Referer, naming
ROOT_URL's host or the Host / X-Forwarded-Host it was sent to, which on
Sandstorm is the grain's own host. Token-authenticated API calls, Meteor's
cookie routes and DDP are not affected. A refusal is recorded in Admin
Panel / Problems as csrf.cross-site-cookie and never blocks the account
whose cookie it was.

Thanks to xet7 !
2026-10-30 23:52:09 +02:00
Lauri Ojansivu 4993242c5f Strict-Transport-Security on every response of an HTTPS site.
WeKan sent no HSTS header, so a link typed as plain HTTP or a downgrade on
a hostile network could strip TLS from the next visit unless the reverse
proxy added it. It is now sent whenever ROOT_URL is https: 180 days, this
host only. HSTS_MAX_AGE, HSTS_INCLUDE_SUBDOMAINS and HSTS_PRELOAD (Admin
Panel / People / Login, every platform) change it; 0 stops it, and preload
is sent only with includeSubDomains and at least a year. Every response
also carries X-Content-Type-Options: nosniff and
Referrer-Policy: strict-origin-when-cross-origin.

Thanks to xet7 !
2026-10-30 23:50:02 +02:00
Lauri Ojansivu 12eef31162 CHANGELOG: snap backup and restore instructions work on AlmaLinux, Rocky Linux and Fedora.
Thanks to Group-Deed and xet7 !
2026-10-30 23:14:01 +02:00
Lauri Ojansivu 0b5c855820 Snap backup and restore instructions work on AlmaLinux, Rocky Linux and Fedora.
snapd keeps the snaps in /var/lib/snapd/snap on Fedora, RHEL, AlmaLinux,
Rocky Linux, CentOS Stream and Arch Linux, which have no /snap, so the
instructions that exported /snap/wekan/current/bin and its lib directory
did not find mongodump and mongorestore there. They now pick the directory
with one S= line and the CPU's library directory with $(uname -m), and the
scripts that can be run from a host shell find the snap in either place.

Thanks to Group-Deed and xet7 !
2026-10-30 23:13:50 +02:00
Lauri Ojansivu 0c53ef6960 CHANGELOG: boards from before the templates feature are shown on All Boards again.
Thanks to xet7 !
2026-10-30 22:56:38 +02:00
Lauri Ojansivu 2ab0afdc93 Old boards without a type are shown on All Boards again.
Boards, swimlanes, lists and cards from before the templates feature have
no `type`: the schemas' defaultValue fills it on insert only. All Boards
publishes `type: 'board'` and the card queries ask for 'cardType-card', so
a Sandstorm grain migrated from MongoDB 3 opened onto an empty All Boards
page although its board was intact in the database. The new
type-defaults-backfill startup step sets the schema default on every
document whose type is missing, null or empty, and never touches an
explicit type.

Thanks to xet7 !
2026-10-30 22:56:24 +02:00
github-actions[bot] f2a1d7b9ba Bump versions for v12.75 2026-10-30 20:26:45 +00:00
Lauri Ojansivu cd1f1b8f05 Prepare v12.75 release 2026-10-30 22:25:30 +02:00
Lauri Ojansivu b941f36e4c Merge remote-tracking branch 'origin/main' 2026-10-30 22:25:01 +02:00
Lauri Ojansivu 487a528806 CHANGELOG: a changed password signs the account out in every other browser.
Thanks to xet7 !
2026-10-30 21:39:35 +02:00
Lauri Ojansivu dc9a7bb1ac Tests: a changed password signs the account out in every other browser.
A report described "failure to invalidate session on password change": the
same account signed in on two browsers, the password changed in one, and
the other's session "updated" rather than ended. WeKan does not do that.
Member Settings / Change Password is Meteor's changePassword, which removes
every login token but the changing session's; the admin's Set password is
setPasswordAsync, which removes them all, as resetPassword does; and a
session whose token is gone is closed, while WeKan's HTTP routes accept only
a token still in the list.

password-change-other-sessions.e2e.js runs the report in two browser
contexts: both signed in with the password, the password changed through
the form in one, and the other is signed out within seconds, stays signed
out after a reload, and only the new password signs in. It passes three
times in a row in Chromium and in WebKit. passwordChangeSessions.test.cjs
pins those paths and fails on a password change with logout: false, the
LDAP sign-in's password sync being the one stated exception.

Thanks to xet7 !
2026-10-30 21:39:20 +02:00
github-actions[bot] 696a1a2c8a Bump versions for v12.74 2026-10-30 08:15:31 +00:00
Lauri Ojansivu ef1e683cdd Prepare v12.74 release 2026-10-30 10:14:18 +02:00
Lauri Ojansivu 55163d0ce6 Merge remote-tracking branch 'origin/main' 2026-10-30 10:14:15 +02:00
Lauri Ojansivu b974ba426d CHANGELOG: Caddy and FerretDB run with no MPTCP listener.
Thanks to xet7 !
2026-10-30 04:31:14 +02:00
Lauri Ojansivu e9751b65d4 Caddy and FerretDB run with no MPTCP listener (CVE-2026-98432).
CVE-2026-98432 is a Linux kernel use-after-free in IPv4 TCP connection
handling, shown to give remote code execution through an MPTCP listener
(golang-announce, 2026-10). Since Go 1.24 every Linux listen socket of a
Go program is MPTCP by default, and WeKan ships two Go servers: Caddy, the
snap's web server on ports 80 and 443, and FerretDB, the default database.

Every place WeKan starts one sets GODEBUG=multipathtcp=0: the snap's
environment (every app, Caddy and FerretDB among them), the Docker
entrypoint and the bundle launcher - which keep an operator's own
multipathtcp setting - every docker-compose FerretDB service (the commented
FerretDB 2 example too), and the FerretDB the Sandstorm grain starts. Caddy
is an upstream binary, so no rebuild is needed: Go reads GODEBUG at start.
wekan/FerretDB builds with `godebug multipathtcp=0` as well. The real fix
is a patched host kernel.

Checked on Linux: a Go server's default listener reports MPTCP true;
GODEBUG=multipathtcp=0, or the go.mod setting, makes it false.
tests/goMptcpOff.test.cjs pins every launcher, runs the launcher line, and
fails when something turns MPTCP back on.

Thanks to xet7 !
2026-10-30 04:30:39 +02:00
github-actions[bot] 5abc2809b9 Bump versions for v12.73 2026-10-30 02:18:18 +00:00
Lauri Ojansivu 03f3a197cf Prepare v12.73 release 2026-10-30 04:17:07 +02:00
Lauri Ojansivu 6ed3f5cfd0 CHANGELOG: v12.73 was not released, so its section is Upcoming again.
"Prepare v12.73 release" renamed Upcoming, but its push was refused and
the release workflow was never dispatched: there is no v12.73 tag and no
v12.73 GitHub Release. As CLAUDE.md says for a release that published
nothing, the heading goes back to "# Upcoming WeKan ® release", so the next
release-all.sh takes v12.73 again. It also gains the entry for the fix that
keeps this from happening again.

Thanks to xet7 !
2026-10-30 04:16:04 +02:00
Lauri Ojansivu e372016826 Release All pulls before releasing, and pulls and retries when its push is refused.
v12.73 was prepared on a main that origin had moved past: the release
bot's v12.72 version bump had landed meanwhile. release-all.sh renamed
Upcoming and committed "Prepare v12.73 release", its bare git push was
refused, and set -e stopped it before gh workflow run - so no release ran,
and no tag was made, while the CHANGELOG already said v12.73.

release-all.sh now runs build.sh's git pull right after its checks, before
it chooses the version or renames anything, and checks the Upcoming section
again after it; and it pushes with build.sh's git push, which repairs moved
CHANGELOG links and pulls and pushes once more when origin moved meanwhile.
If the push still does not finish, it says that the workflow was NOT
dispatched, and the command to dispatch it.

releaseUpcomingPreflight pins the order - the pull before the version and
the rename, the push before the dispatch - and fails on a bare git push;
releaseTelemetry and the version-selection fixture follow.

Thanks to xet7 !
2026-10-30 04:14:48 +02:00
Lauri Ojansivu fd699b67e9 Merge remote-tracking branch 'origin/main' 2026-10-30 04:07:50 +02:00
Lauri Ojansivu 827641193f Prepare v12.73 release 2026-10-30 04:07:36 +02:00
Lauri Ojansivu fb7bd9c6de CHANGELOG: CodeQL alert #598, the Sandstorm host test.
Thanks to xet7 !
2026-10-30 04:02:02 +02:00
Lauri Ojansivu fcd995095d Tests: the Sandstorm host check names no host but sandstorm.org.
CodeQL alert #598 (incomplete URL substring sanitization, CWE-20) flagged
tests/sandstormOrgHosts.test.cjs, which skipped files that did not include
the old Sandstorm host name as a substring. The file no longer names that
host at all. It finds every Sandstorm host on a top-level domain other than
org by its shape, written as a URL - a scheme or // before it, or a port or
path after it - so property paths such as services.sandstorm.id, file names
such as sandstorm.js and the domains reserved for tests are not hosts. The
demo and the store workflow's older-market publish step stay allowed by
their subdomain and place.

A new self-test checks what it finds and what it does not; run against the
docs and workflows from before the sandstorm.org switch it finds 13 old
hosts, the installer address of release-all.yml among them.

Thanks to xet7 !
2026-10-30 04:01:36 +02:00
Lauri Ojansivu b7b5ec3711 CHANGELOG: the short CHANGELOG.md starts with the upcoming release.
Thanks to xet7 !
2026-10-30 03:56:32 +02:00
Lauri Ojansivu 1af642069c The short CHANGELOG.md starts with the upcoming release's In short.
CHANGELOG.md showed only the five newest releases, so what was about to be
released was only in CHANGELOG-big.md. changelog-short.mjs now puts the
unreleased # Upcoming WeKan ® release first - its In short and a link to
its full section - and the five newest releases below it; right after a
release, when there is no Upcoming yet, none is invented.

CLAUDE.md and AGENTS.md say what "update wekan changelog" means: find the
commits not in the changelog yet, add an Upcoming section when a release
was cut meanwhile, write the entries and the In short into
CHANGELOG-big.md, run changelog-short.mjs, run the changelog tests, and
commit both files.

tests/changelogShort.test.cjs: Upcoming first, whole, above the releases,
its link resolves, and no Upcoming appears after a release.

Thanks to xet7 !
2026-10-30 03:56:11 +02:00
Lauri Ojansivu 0343e6614b CHANGELOG: the short CHANGELOG.md, written from CHANGELOG-big.md.
Thanks to xet7 !
2026-10-30 03:30:23 +02:00
Lauri Ojansivu f1c0db247a CHANGELOG.md is the short changelog, written from CHANGELOG-big.md.
CHANGELOG-big.md is the changelog as it was: # Status, # Upcoming and every
release in full, with its <details> entries. Everything that writes or edits
the changelog uses it - release-all.sh, release-notes.sh and the release
workflow, the commit-link repair in build.sh and fix-changelog-hashes.sh,
the archive, the wrapper, the version scripts - and the release notes'
"More details at ChangeLog" link goes to its section. remote-release.py,
shared with FerretDB and the patch repositories, takes the file from its
config, and only WeKan's names CHANGELOG-big.md.

CHANGELOG.md is new: no Status and no Upcoming, only the In short paragraph
of each of the five newest releases and a link to its full section, in plain
Markdown - no <details>, <summary>, </summary> or </details>, and HTML links
written as Markdown links. releases/changelog-short.mjs writes it;
release-all.sh runs it right after naming the release, so it is in the
release commit, and changelog.sh does too. The Sandstorm App Market keeps
embedding CHANGELOG.md, now the short one.

old-CHANGELOG/'s headers say they moved out of CHANGELOG-big.md. References
to Upcoming or TODO Later in code comments and docs name CHANGELOG-big.md.
CLAUDE.md and AGENTS.md describe both files: CHANGELOG-big.md is written,
CHANGELOG.md is generated and never edited by hand.

tests/changelogShort.test.cjs: CHANGELOG.md is exactly what the generator
writes, the five newest releases with their whole In short, links that
resolve, and none of the four tags or an HTML link; the changelog tests
read CHANGELOG-big.md.

Thanks to xet7 !
2026-10-30 03:29:48 +02:00
Lauri Ojansivu 61e838df46 Rename CHANGELOG.md to CHANGELOG-big.md.
Only the rename, in a commit of its own, so that git log --follow and blame
keep the changelog's history; the next commit adds the new short
CHANGELOG.md and points the release tooling at CHANGELOG-big.md.

Thanks to xet7 !
2026-10-30 03:29:34 +02:00
github-actions[bot] 057cbc26d9 Bump versions for v12.72 2026-10-30 00:37:33 +00:00
Lauri Ojansivu 22e8b9bc86 Prepare v12.72 release 2026-10-30 02:36:31 +02:00
Lauri Ojansivu 602b47f06a CHANGELOG: new Sandstorm app market screenshots.
Thanks to xet7 !
2026-10-30 02:35:31 +02:00
Lauri Ojansivu a086323a1f Updated Sandstorm WeKan screenshots.
Thanks to xet7 !
2026-10-30 02:33:12 +02:00
Lauri Ojansivu 22140739ab CHANGELOG: card links never launch an OS handler, the Apache guide and Sandstorm publishing.
Thanks to ocdtrekkie and xet7 !
2026-10-30 01:26:40 +02:00
Lauri Ojansivu 55b71ce19e Tests: the Sandstorm store workflow may publish to apps.sandstorm.io too.
#6833 publishes WeKan to both Sandstorm app markets, apps.sandstorm.org
and the older apps.sandstorm.io, whose API is alpha-api.sandstorm.io.
sandstormOrgHosts.test.cjs refused every sandstorm.io host; it allows
that publish step in sandstorm-store.yml only, and checks that both
markets are published to.

Thanks to ocdtrekkie and xet7 !
2026-10-30 01:25:31 +02:00
Lauri Ojansivu 1a97c8bd86 Apache guide: no request captures in proxy targets, and httpd 2.4.60 or newer.
Checked against the 2024 Confusion Attacks on Apache httpd
(blog.orange.tw, CVE-2024-38472 to -38477 and CVE-2024-39573, fixed in
2.4.60). The guide's examples were reverse proxies only, with no
RewriteRule, but each passed the WebSocket with ProxyPassMatch and $1 - a
request capture in a proxy target, the shape those attacks abuse - and in
the sub-URL example that rule looked for ^/sockjs while the requests come
to /wekan/sockjs, so its WebSocket never connected.

- Both examples pass the WebSocket with "ProxyPass ... upgrade=websocket"
  on their fixed prefix (2.4.47+); no ProxyPassMatch remains.
- The guide asks for httpd 2.4.60 or newer, drops NameVirtualHost (a no-op
  in 2.4) and the path in the sub-URL ServerName, which is not valid.
- A "What not to add" section: no RewriteRule or ProxyPassMatch with a
  request capture in the target, no <Files>/<Location> ACL in front of
  WeKan, AllowEncodedSlashes Off and MergeSlashes On, <Directory /> denied.

Verified with Apache 2.4.67: both virtual hosts parse, and in front of a
WebSocket server both pass HTTP and the WebSocket, at / and at /wekan;
the old sub-URL example fails its WebSocket ("Unexpected server response:
200"). tests/apacheReverseProxy.test.cjs pins it and fails on the old guide.

WeKan's own redirects stay on its origin (CAS checks the callback's origin
against ROOT_URL; the file and avatar redirects build their own paths), so
a backend-chosen Location cannot be steered elsewhere.

Thanks to xet7 !
2026-10-30 01:24:48 +02:00
Lauri Ojansivu b4e5f90afb Card links never launch an OS handler with a code-execution history.
Custom URL schemes in card text link only when Admin Panel -> Features ->
URL lists them, and never javascript:, data: or another scheme that runs
code in the page. A listed operating-system handler was linked, though, and
some have a public code-execution history: ms-msdt (Follina,
CVE-2022-30190), search-ms and search, ms-officecmd, ms-appinstaller,
ms-cxh and ms-cxh-full, and telnet - the last step of the 2026 Edge full
chain, where Windows ran telnet.exe from a user-writable PATH directory.
An administrator listing one would let any board member plant a link that
runs something on whoever clicks it.

- They are never linked, listed or not, by the app's parser and by the
  markdown package's copy of the rule (the two lists must be identical),
  and a click on one does nothing should such a link reach the page.
- A card title or description, comment, checklist or checklist item that
  ADDS a link to one is shown in Admin Panel -> Problems (HandlerLink,
  CWE-939), with who wrote it; the text is saved as written and the writer
  is told nothing. The word in prose, or a link the text already had, is
  not recorded.
- docs/Features/Admin-Panel/Settings/Visibility.md lists both kinds of
  never-linked scheme, and says what a listed file: does on Windows (an SMB
  link outside the network sends the clicker's NTLM hash).

Tests: tests/urlSchemeAllowlist.test.cjs (positive and negative, through
the real DOMPurify, both sanitizer passes and the detector; fails on the
old code), and custom-url-schemes.e2e.js, which passes in Chromium and
WebKit: listed handlers do not link, a description saved with one through
the editor is recorded for its writer, and a kept link or a comment that
only mentions the word is not. Firefox does not start in this
environment.

Thanks to xet7 !
2026-10-30 01:19:16 +02:00
Lauri Ojansivu 2c1fc39f61 Fix Sandstorm store spk publish syntax.
Thanks to ocdtrekkie and xet7 !
2026-10-30 01:14:55 +02:00
Lauri Ojansivu ee82c21718 Merge pull request #6833 from ocdtrekkie/patch-1
Sandstorm: Publish to both app markets
2026-10-30 01:08:19 +02:00
Jacob Weisz 10851cd3f4 Sandstorm: Publish to both app markets 2026-10-29 18:04:45 -05:00
github-actions[bot] a937ed2180 Bump versions for v12.71 2026-10-29 22:39:44 +00:00
Lauri Ojansivu 22be2f2dc0 Prepare v12.71 release 2026-10-30 00:38:34 +02:00
Lauri Ojansivu 833c0bc021 CHANGELOG: two entries made after v12.70 go to Upcoming, not into v12.70.
The v12.70 release renamed Upcoming to v12.70 at 23:59, and the entries for
the Hall of Fame guards (43c4d41877) and the Sandstorm install address
(ca2dc5fae5), committed after it, were added to its Tests and Releases
groups - inside a released section, for commits that release does not
contain, and with no Upcoming section left for release-all.sh to take the
next version from. They move to a new Upcoming section; the v12.70 section is
again exactly what v12.70 shipped. changelogEntriesBelongToTheirRelease
passes.

Thanks to xet7 !
2026-10-30 00:36:01 +02:00
Lauri Ojansivu 1251c1c8b3 Merge remote-tracking branch 'origin/main' 2026-10-30 00:33:58 +02:00
Lauri Ojansivu 5b428faea6 CHANGELOG: the Sandstorm build installs from install.sandstorm.org.
Thanks to xet7 !
2026-10-30 00:28:08 +02:00
Lauri Ojansivu ca2dc5fae5 Sandstorm: build from install.sandstorm.org, and link sandstorm.org everywhere.
v12.69's build-sandstorm in release-all.yml stopped because the
certificate of install.sandstorm.io had expired. It fetches the installer
from install.sandstorm.org, as sandstorm.yml and sandstorm-store.yml
already did, and so does releases/install-sandstorm.sh. That installer
downloads Sandstorm from dl.sandstorm.org and configures a development
server at local.sandstorm.org, so the docs follow: docs, apps, dl, install
and local.sandstorm.io links and addresses become sandstorm.org in the docs,
SECURITY.md, code comments and the accounts-sandstorm README, each URL
checked to answer there. demo.sandstorm.io stays, as it has no sandstorm.org
name; released CHANGELOG sections are records and stay as they are. The
release audit's baseline allows the two code comments' docs.sandstorm.org.

tests/sandstormOrgHosts.test.cjs: every Sandstorm build installs from
install.sandstorm.org, and no tracked file names a sandstorm.io host
outside those kept on purpose; it fails on the old release-all.yml.

Thanks to xet7 !
2026-10-30 00:27:54 +02:00
Lauri Ojansivu b9f6eff3d9 CHANGELOG: the Hall of Fame guards read linked vulnerability names.
Thanks to xet7 !
2026-10-30 00:20:59 +02:00
Lauri Ojansivu 43c4d41877 Tests: the Hall of Fame guards read a linked vulnerability name too.
hallOfFameProblemsCoverage and securityRegressionCoverage collected the
names from <td valign="top"><b>Name</b></td>, so a name that was a link to
its page was never seen. Now that every name is one, the match takes the
link; the four it had missed - ImportPlacementBleed, SamlSubjectBleed,
StampBleed and UploadBleed - are listed as guarded by the suites that name
them, and the inventory count goes from 137 to 141.

Thanks to xet7 !
2026-10-30 00:20:47 +02:00
github-actions[bot] 609c6d3d64 Bump versions for v12.70 2026-10-29 22:00:25 +00:00
Lauri Ojansivu 8097935f5d Prepare v12.70 release 2026-10-29 23:59:10 +02:00
Lauri Ojansivu 4f7222f541 CHANGELOG: AppImages, Launchpad snap source, certificate errors and three test fixes.
Thanks to xet7 !
2026-10-29 23:50:04 +02:00
Lauri Ojansivu 6bbb4d39bc Downloads: an unverifiable TLS certificate is named as that, not as an outage.
v12.69's build-sandstorm stopped because install.sandstorm.io's certificate
expired that day (Let's Encrypt, notAfter 2026-10-29 19:02 GMT), and
fetch.sh reported "The server is having an outage" for curl's exit 60. It
now says the certificate could not be verified and that only that server's
owner can fix it. dl.sandstorm.io, where Sandstorm itself is downloaded
from, is valid.

Thanks to xet7 !
2026-10-29 23:49:36 +02:00
Lauri Ojansivu a1f2c006b4 Snap on Launchpad: fetch.sh is uploaded with the tools-env.sh it sources.
releases/prepare-launchpad-source.sh uploads only the files the snap recipe
reads, and listed releases/fetch.sh without releases/tools-env.sh, which
fetch.sh sources for its TMPDIR. In v12.69's armhf build the caddy part's
fetch.sh had no TMPDIR, mktemp made nothing, and curl got an empty -o seven
times ("option -o: is badly used here"), with pauses of up to 480s, before
it was reported as a server outage.

- tools-env.sh is uploaded, and it makes .tools/tmp in a tree that has only
  snapcraft.yaml and releases/, as the Launchpad source does.
- fetch.sh stops at once when it has no temporary file, and does not retry
  curl's exit 2 (it could not start) or call it an outage.
- tests: every releases/ file a listed script sources or runs must be
  listed; tools-env.sh and fetch.sh work in such a tree with no .git and no
  TMPDIR; both fail on the old list. fetch.sh's two new refusals are tested.

s390x failed earlier, on Launchpad: even a new build answered "Could not
find repository at path" three times.

Thanks to xet7 !
2026-10-29 23:48:30 +02:00
Lauri Ojansivu dfdadaa8ca AppImages: FerretDB and mongo-tools are readable by every user again.
releases/fetch.sh downloaded into a mktemp file and moved it into place, so
every download kept mktemp's 0600, and the bundled ferretdb and mongo-tools
were 0711 after chmod +x. A user other than the owner gets "Permission
denied" from such a file, and since today's appimagetool checks for it, all
six v12.69 AppImages (i686, x86_64, aarch64, armhf and both musl ones)
failed: "has mode 0711, but it must be readable for all users".

A download now gets the mode its umask gives a new file, and the AppDir is
made a+rX before it is packed. tests/releaseDownloads.test.cjs fails on the
old fetch.sh with 711 instead of 755 and checks that a stricter umask is
still followed; tests/appImageRuntime.test.cjs pins both.

Thanks to xet7 !
2026-10-29 23:44:34 +02:00
Lauri Ojansivu 856a32968b Tests: board drag settings open Card's Draggable table in its section hamburger.
Since Board Settings / Card became the card's Card Settings (52de57c844),
the Card Draggable table is in the Card section's hamburger, not in the
popup, so board-drag-settings.e2e.js failed in every language waiting for a
table that is no longer there. It opens Card the way the cog menu does and
then that hamburger, as board-settings-columns.e2e.js already does.

Thanks to xet7 !
2026-10-29 23:42:36 +02:00
Lauri Ojansivu e4b83d2274 Tests: the keyboard card move waits for the reset sorts to reach the client.
F02 F06 reset three cards to sort 0 in the database and waited only for the
minicards to show in id order. gamma 0, alpha .125, beta .25 already is id
order one time in six, so the wait passed while the client still had the old
sorts, the move was computed from them, and the card landed last instead of
second (1 in 3 runs here). The test waits for the client's own copy of all
three cards to read sort 0; 8 repeats pass.

Thanks to xet7 !
2026-10-29 23:28:31 +02:00
Lauri Ojansivu d06b468629 Tests: ReplyBleed's mocha test counts the attempt in every ReplyBleed row.
ReplyBleed is one Problems row per source, and the webhook's source is the
client address, so the bounce and board-address tests leave rows of their
own in the test database, which persists between runs. The test's findOne
picked one of those (source "ses"), whose count the spoofed reply never
moves, and failed although the attempt was recorded under 127.0.0.1. It sums
the count over all ReplyBleed rows, the way emailBounces.tests.js already
does.

Thanks to xet7 !
2026-10-29 23:07:04 +02:00
Lauri Ojansivu 379058bc9d CHANGELOG: OpenSSL 4 backup errors, Bun assets on Python 3.9, snap build logs, macOS test fixes.
Thanks to xet7 !
2026-10-29 22:52:42 +02:00
Lauri Ojansivu 5534c8830c Tests: the macOS suites run inside the sandboxed editor too.
mac-smoke-app.py checked that a killed descendant was gone with ps, which a
sandboxed macOS app may not run; it waits for os.kill(pid, 0) to find the
process gone and uses ps only to tell a zombie from a live one. macosSandbox
skips its live sandbox-exec check only when even an (allow default) profile
cannot be applied, which is the case inside the sandbox it is testing.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu 48a8937ff0 Snap on Launchpad: this attempt's build log is found by name, not only by mtime.
Whether a Launchpad build log belonged to the current attempt was decided by
[ "$log" -nt "$attempt_marker" ] alone. bash 3.2 compares whole seconds, so a
log written in the marker's second was not "newer": it was neither printed
nor checked for an infrastructure failure. this_attempts_log() counts a log
whose name did not exist when the attempt started, or one rewritten after the
marker; an earlier attempt's untouched log still does not count. Used by
infra_failure() and by the log printing, and tested for all three cases.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu 2dcccf0d4a Bun release assets: runs on Python 3.9, which macOS ships as python3.
hashlib.file_digest is Python 3.11+ and Path.write_text(newline=) is 3.10+,
so releases/bun/assets.py failed with AttributeError/TypeError on macOS's
python3 3.9. It hashes in chunks and writes the LF-only .sha256sum through
open(newline='\n'), and the test still pins LF on every platform.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu bf266fb4da DB conformance: a live run is still recognised where ps is not allowed.
In a sandboxed macOS app ps fails with "operation not permitted", so every
live conformance run looked dead and a second run would remove its
containers. command_line_of() falls back to pgrep -lf, which still works
there. The test pins the fallback.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
Lauri Ojansivu ffe49dd2fd Continuous backup: a changed encrypted file is reported as changed, also with OpenSSL 4.
A Node.js linked to OpenSSL 4 reports an AES-GCM tag mismatch as
ERR_OSSL_BAD_DECRYPT ("bad decrypt") instead of OpenSSL 3's "Unsupported
state or unable to authenticate data", so decryptFile() let the raw OpenSSL
error through instead of "An encrypted backup file has changed or the key is
wrong". Both are recognised now; tests/continuousBackupEncryption.test.cjs
passes with OpenSSL 4.1.0 and 3.5.9.

Thanks to xet7 !
2026-10-29 22:51:29 +02:00
github-actions[bot] ee16bdce6b Bump versions for v12.69 2026-10-29 20:03:10 +00:00
Lauri Ojansivu 44bae91f8e Prepare v12.69 release 2026-10-29 22:02:00 +02:00
Lauri Ojansivu ae13eab23e SECURITY.md, CLAUDE.md, AGENTS.md: a report without a GHSA goes from 3 to 5 stars when its GHSA arrives.
A researcher who sent a report to security@wekan.fi, or some other way, and
is later able to add it as a GitHub security advisory: the maintainer
requests the CVE through that GHSA, and the Hall of Fame row's 3 stars
become 5. SECURITY.md says so to researchers. CLAUDE.md and AGENTS.md tell
an AI updating or auditing the Hall of Fame to check each such 3-star row
against the advisories, match by the report's subject and reporter, and
upgrade the index row, its own page, the CVE cell, the Process cell and the
star audit together.

Thanks to xet7 !
2026-10-29 21:52:15 +02:00
Lauri Ojansivu f5f6ad4d4d CHANGELOG: ImportPlacementBleed, and Release all missing builds after a failed rerun.
Thanks to Zachariah Mithani (AploSec) and xet7 !
2026-10-29 20:27:59 +02:00
Lauri Ojansivu 0d6d481fcb ImportPlacementBleed: an import into a board is placed only beside that board's own list, swimlane or card.
The scoped importer, reached by POST /api/import/zip and the importScoped
method, authorized the import on boardId alone. The listId and swimlaneId
beside it were written onto the new cards as given, and a cardId was looked
up with no board filter, so a writer of board A could create cards on A
whose list and swimlane were board B's, and could probe whether a card id
existed on a board they cannot see.

Before anything is written, the list, swimlane and card are now looked up
together with the board's id; an id of another board is refused (400 over
REST, error-import-placement from the method), with the same answer for a
foreign card and a missing one. Cards are placed only by what was found on
the board. A refusal is recorded in Admin Panel / Problems as
ImportPlacementBleed (authz.import-placement), without blocking the account.

Tests: the decision with the reporter's ids, the check before every write,
a negative scan for a target id written or looked up without the board, and
a browser test of the reporter's attack by both routes, which fails on the
previous code.

Thanks to Zachariah Mithani (AploSec) and xet7 !
2026-10-29 20:27:14 +02:00
Lauri Ojansivu 99d9958e39 Release all missing: the missing assets are built even when the rerun of failed jobs fails again.
The planner needed recover, so one job failing again in the rerun of the
full release skipped the planner and every build after it, and assets that
could be filled were not. For v12.68 the rerun failed on four Launchpad
snaps (503 / timeout) and on the OpenBSD bundle, whose start-wekan.sh fault
is in the tagged code and so in every rerun of it. The planner now runs
unless the run is cancelled, and each job after it checks the planner's own
result, since an implicit success() also counts recover. recover stays red
and now names the jobs that failed again.

Thanks to xet7 !
2026-10-29 20:15:32 +02:00
Lauri Ojansivu daae36b313 CHANGELOG: Invite via Email adds the invited new user, not the first user of the database.
Thanks to momosari and xet7 !
2026-10-29 17:36:01 +02:00
Lauri Ojansivu 8556459f97 Invite via Email adds the invited new user, not the first user of the database.
Meteor 3's server Accounts.createUser is createUserAsync. inviteUserToBoard
called it without await, so the new user's id was a Promise; findOneAsync
read the Promise as the empty selector {} and returned the FIRST user,
usually the site administrator. That user was added to the board and got
the invitee's language and invitedBoards, while the invitee was created but
not added. It was not FerretDB: any database did the same.

createUser and sendEnrollmentEmail are awaited (an enrollment email that
fails is logged, as the invitation email is), and the user found must be
the one just created. Every server ReactiveCache getter now refuses a
Promise as its id or selector, so a missing await fails loudly instead of
picking the first document. Tests: the guard and the reporter's case in
memory, a scan that no server file calls an async Accounts API without
await, and a browser test that inviting a new address adds exactly that new
account - it fails on the previous code.

Thanks to momosari and xet7 !

Fixes #6831.
2026-10-29 17:35:21 +02:00
Lauri Ojansivu 7f9e8ea7a3 CHANGELOG: History on FerretDB, date paths as strings, migration reports refused documents.
Thanks to vijlautens and xet7 !
2026-10-29 16:58:42 +02:00
Lauri Ojansivu 01968fc637 History on FerretDB: date paths are stored as strings, and the migration reports refused documents.
A History row of a date-valued custom field held datePaths as an array of
path arrays, [["3","value"]]. FerretDB v1 does not store an array inside an
array, so on FerretDB the change was not recorded, and migrateTextDatabase
to FerretDB dropped those rows while it swallowed the per-document error and
still reported success.

Each path is now a JSON string, '["3","value"]'. Rows already written in
the array form on MongoDB still restore, and the migration to FerretDB
rewrites them to strings. The migration counts every document the target
refuses and ends as failed with the count per collection and the first
error, instead of success: true.

Thanks to vijlautens and xet7 !

Fixes #6832.
2026-10-29 16:58:21 +02:00
Lauri Ojansivu 04c2598cf4 Merge remote-tracking branch 'origin/main' 2026-10-29 10:55:23 +02:00
Lauri Ojansivu 77ded27994 CHANGELOG: OpenBSD bundle starts again.
Thanks to xet7 !
2026-10-29 08:25:49 +02:00
Lauri Ojansivu ec6cf66bff OpenBSD bundle starts again: start-wekan.sh takes only a number from sysctl.
OpenBSD's sysctl prints nothing and exits 0 for a name it does not have, so
`sysctl -n hw.memsize || sysctl -n hw.physmem` stopped at the empty
hw.memsize and $(( / 1048576 )) ended start-wekan.sh before WeKan answered.
Both start-wekan.sh copies now try hw.memsize, hw.physmem64 (NetBSD, whose
hw.physmem is 32-bit) and hw.physmem in turn and take the first numeric one.
The test runs the lines against fake OpenBSD, NetBSD and macOS sysctls, and
shows the old chain failing on the OpenBSD one.

Thanks to xet7 !
2026-10-29 08:25:38 +02:00
github-actions[bot] 8561f49ed9 Bump versions for v12.68 2026-10-29 05:16:53 +00:00
Lauri Ojansivu 749e9b76a7 Prepare v12.68 release 2026-10-29 07:15:43 +02:00
Lauri Ojansivu ad43de3c01 CHANGELOG: Card Settings, two panes, the card and the minicard follow its order, menus inline.
Thanks to xet7 !
2026-10-29 02:03:11 +02:00
Lauri Ojansivu 2bd5d1180a Card Settings: two panes, the card and the minicard follow their order, menus inline.
The opened card and the minicard now draw Card Settings' sections, in
Card Settings' order, each section's fields in the order of its rows:
- Section order is per side: cardSettingsSectionOrder for the card,
  the new minicardSettingsSectionOrder for the minicard. A board that
  stores none: the card starts with Labels, Date, Members, Card, ...
  (the card's historical order as far as the grouping allows), the
  minicard with what it drew first before.
- CARD_LAYOUT's and MINICARD_LAYOUT's reorderable sections are Card
  Settings' sections, so dragging a section or a row in Card Settings
  moves it on the card or the minicard. Stored field orders map: a
  field keeps its place relative to the others of its new section.
- On the card every section has the shared heading (its icon, title,
  caret and hamburger) - Voting and Planning Poker too, which had a
  heading of their own. The minicard stays compact: no headings.

Card Settings shows both sides at once: the minicard's pane first (on
the left), the card's second, each with a caret that folds it, each
scrolling on its own, stacked when the popup is too narrow for two.
The Show radio is gone. A splitter between the panes changes their
widths by dragging, like spreadsheet columns (clamped, kept per user
as a fraction, reset by a double-click, moved by the arrow keys). The
popup is wider again for two panes and its corner grip resizes it,
the panes following.

A hamburger's content opens under its line inside the pane, not in a
popup: the hamburger shows it is open (aria-expanded, highlighted, the
panel attached to it); its button, the panel's X or Escape closes it.
A custom field's edit form and the + create form open inline the same
way and close only their panel when saved. The popup layer added in
9c9742e09d is removed, as nothing uses it now.

Docs: Card-Settings.md, Card-Fields.md and Board Settings / Card.md.
New tests cardSectionsOnCard, minicardSections, cardSettingsSplit,
cardSettingsInlinePanels and card-settings-sections-order.e2e.js; the
guards of the old layouts and orders say why they changed.

Thanks to xet7 !
2026-10-29 02:02:54 +02:00
Lauri Ojansivu eddfd515ea CHANGELOG: Card Settings, hamburger menus open on top of it, next to their button.
Thanks to xet7 !
2026-10-28 23:23:18 +02:00
Lauri Ojansivu 9c9742e09d Card Settings: hamburger menus open on top of it, next to their button.
A hamburger of Card Settings opened its menu in Card Settings' place:
the popup slid to the menu and Card Settings disappeared until its back
arrow. Its menus, and the forms it opens (a custom field's edit form,
the + under Custom Fields, Subtask settings), now open as a popup
LAYER: on top of Card Settings, next to the button that opened them,
with Card Settings visible under it and scrolled where it was.

Popup.open(name, { layer: true }), from inside a popup, pushes the
layer on the stack as before, so Popup.back() - the layer's X, Escape,
a form's Save - closes the layer alone. The popup template draws the
popup under it with the layer on top, outside the scrolling content;
depth counts the sliding popups only, so a confirmation opened from a
layer still slides. One layer at a time: another replaces it, its own
button closes it, a press elsewhere in the popup closes it.

A layer opens toward the middle of the popup: a hamburger at the end of
a line gets its right edge, the + on the left its left edge, so the
create form no longer ran out of the popup's left side where its Save
could not be clicked; below the button, or above when there is more
room, its height and width capped to the room
(models/lib/popupLayerPosition.js). A menu's checkboxes start at its
edge rather than .check-div's form indent.

tests/popupLayer.test.cjs runs the placement and pins the Popup wiring
and that every Card Settings menu opens as a layer;
card-add-fields.e2e.js checks Card Settings stays visible under the
menu, the menu sits by its button, toggles, closes on a press elsewhere
and by its X alone, and that the + form opens inside Card Settings.

Thanks to xet7 !
2026-10-28 23:23:08 +02:00
Lauri Ojansivu 0250e54a07 CHANGELOG: Card Settings, a checkbox per section, every field always shown, only in Board Settings.
Thanks to xet7 !
2026-10-28 22:44:02 +02:00
Lauri Ojansivu 3e5bc58105 Card Settings: a checkbox per section, always every field, only in Board Settings.
Card Settings is now only Board Settings / Card. The opened card has no
Card Settings button any more: it simply shows every field its board
shows on cards, empty or not.

- On this card, Show empty and Show empty fields are removed, with the
  hamburgers' Select all / Select none rows, their handlers, helpers,
  board methods (setCardFieldAlwaysShown, setShowEmptyCardFields), CSS
  and the three translation keys. The card no longer reads its own
  shownFields / hiddenFields, nor the board's cardFieldsAlwaysShown /
  showEmptyCardFields; the schema keeps them so old documents validate.
- Every section has a checkbox before its title: whether the whole
  section is shown on the chosen side, whatever its rows are ticked.
  Stored per side in cardSettingsHiddenOnCard / cardSettingsHiddenOnMinicard
  (setCardSettingsSectionShown, board admins). The opened card's view
  of the board and a new minicard view read false for every row of a
  hidden section, Scrum fields included; Card Settings itself reads the
  raw board, so the rows keep their ticks.
- A section with one row on the chosen side is one line: one checkbox,
  the name, one hamburger.
- A hamburger is drawn only when it holds something.
- Custom Fields: its items are the board's custom fields, in their sort
  order, reordered by drag and drop within the section, with a + under
  them that creates one. Show on Minicard sets a field's showOnCard,
  Show on Card its alwaysOnCard; its hamburger edits the field. The
  section's checkbox is the board's Custom Fields flag of that side.
- Card actions (Filter: Subtasks, Edit voting, Card kind, ...) live in
  the card's own section hamburgers; Card Settings keeps only board
  actions.

Docs: docs/Features/Cards/Card-Settings.md replaces
Card-Settings-On-Card.md; Card-Fields.md and Board Settings / Card.md
rewritten. New tests cardSettingsSectionShown and
cardSettingsCustomFields; the guards of the removed behaviour say why
they changed; card-add-fields.e2e.js covers section hiding per side, an
empty card showing every field, empty hamburgers, the one-row line, the
Custom Fields items and +, and that the opened card has no Card
Settings.

Thanks to xet7 !
2026-10-28 22:43:29 +02:00
Lauri Ojansivu 039935dd0b CHANGELOG: Card Settings, a space between a checkbox and its label.
Thanks to xet7 !
2026-10-28 20:46:50 +02:00
Lauri Ojansivu 5adfc1b9db Card Settings: a space between a checkbox and its label.
On the rows of Card Settings - on the opened card and in Board Settings /
Card - and in their hamburgers' popups, a checkbox touched its label:
the rows' grid had lost its column gap with the old pane's CSS, and a
hamburger's option put the box and the text side by side with none.
Both now have a space's width (0.3em) between them.

A ticked box still touched the label, because a tick is drawn rotated
and reaches about 3px past its box. Left to right it is drawn 3px
further left, so a ticked box keeps the same space and the label does
not move when it is ticked; right to left the tick leans away from the
label and stays as it was.

tests/cardSettingsOnCard.test.cjs pins the gaps and the tick rule, and
card-add-fields.e2e.js measures at least 3px for ticked and unticked
boxes on the rows and in Card's hamburger, with every label in the same
place.

Thanks to xet7 !
2026-10-28 20:46:42 +02:00
Lauri Ojansivu f269b1ef16 CHANGELOG: Board Settings / Card shows the same checklists as the opened card, by the same templates.
Thanks to xet7 !
2026-10-28 20:23:42 +02:00
Lauri Ojansivu 52de57c844 Board Settings / Card is the opened card's Card Settings, same templates.
Board Settings / Card had its own layout: an Admin Panel left menu of
sections and a pane with two lists, column headings and per-column Select
all. It now renders the same template as the opened card's Card Settings
(cardSettingsInline), with no card: Show on Minicard / Show on Card, the
sections as checklists, their rows as the items, and the same section and
row hamburgers. The popup's header is the title and the close button, so
the inline's own title and X are not drawn there, and a row's hamburger
has no On this card. The popup is 600px wide, about an opened card,
instead of 1200px.

The popup no longer registers the Card Settings handlers itself: the
template inside it does, and registering them on both would handle every
click twice. The left menu, the pane, the column Select all, the
in-place action forms and the personal-only mode are removed with their
helpers and CSS. A non-admin sees what a card shows them: the board's
rows disabled, their own rows (Labels text, List title) usable.

Dragging, on the card and in Board Settings / Card:
- With drag handles on, a row or a section is dragged only by its
  handle; the rest of the line scrolls the page. With them off, by its
  icon and name.
- A row dropped on another section goes back where it was and nothing
  is saved: the sections are unrelated kinds of fields. jQuery UI fires
  a delayed "out" just before "stop", so where it was dropped is read
  in beforeStop.

Found while testing both open at once:
- Each Card Settings has its own Show radio group name; one name made
  the card's and the popup's radios one group.
- A section hamburger's Select all acts on the Card Settings it was
  opened from.
- Rows are keyed, so switching Show moves each row with its side instead
  of relabelling rows in place a flush before they reorder, when a
  click could land on the neighbouring field.

Docs: Card-Settings-On-Card.md, Card-Fields.md and Board Settings /
Card.md. The node guards that pinned the old layout now pin the shared
template, with negatives that the old layout and the double handler
registration are gone; card-add-fields.e2e.js compares the two views
section by section and row by row on both sides, and tests the drag
rules, and the specs that drove the old pane drive the new one.

Thanks to xet7 !
2026-10-28 20:23:05 +02:00
Lauri Ojansivu 81e6988491 Southern Sotho: "dikarolo", not "masimo", for data fields.
Three Southern Sotho strings called a data field "masimo", which means
farm fields: Show empty fields in Card Settings, the five fields of a
cron schedule, and the webhook's optional fields. They now use
"dikarolo", the word the file already uses for card and custom fields.
sesotho-popup-titles.e2e.js, which checks that Card Settings names data
fields as such, now reads Card's "Show empty fields" in Board Settings /
Card and caught the first. The cron and webhook wording is low
confidence; a Southern Sotho speaker's correction on Transifex replaces
it.

Thanks to xet7 !
2026-10-28 20:22:56 +02:00
Lauri Ojansivu 8b4987d5bd CHANGELOG: the Data health entry no longer repeats its group's name.
Thanks to xet7 !
2026-10-28 18:50:35 +02:00
Lauri Ojansivu e12106ce7a CHANGELOG: Meteor 3.6-rc.1, with WeKan's six Meteor package forks rebased onto it.
Thanks to xet7 !
2026-10-28 18:50:00 +02:00
Lauri Ojansivu 120f484a30 Meteor 3.6-rc.1, with WeKan's six Meteor package forks rebased onto it.
meteor update --release 3.6-rc.1 failed with "No version of
shell-server satisfies all constraints" (and the same for
dynamic-import, allow-deny, ddp-server, facebook-oauth and
google-oauth): those six are WeKan's forks in packages/, made because
Meteor 3.6-rc.0 lacked the 3.5.3 security fixes, and pinned to rc.0's
versions.

Meteor 3.6-rc.1 carries the security fixes of 3.5.3 and 3.5.4, but not
what is WeKan's own in the forks: the Admin Panel / Problems reports of
refused attempts, and the crypto-RNG keys of dynamic-import and meteor
shell (rc.1 still makes them with Math.random()). So the forks are kept
and rebased: each is rc.1's package with WeKan's changes over rc.0's
package merged three-way onto it. The 3.5.3 hunks merged away, as rc.1
has them; what remains over rc.1 is only the hooks and the keys. The
versions are rc.1's with a _1 wrap number.

@meteorjs/rspack moves to 3.0.0-rc.2 with the release. The Dockerfile's
METEOR_RELEASE and the snap description follow. The experimental Bun
build's Meteor patch applies unchanged to 3.6-rc.1; prepare-meteor.sh
now checks for rc.1's tag commit and applies meteor-3.6-rc.1.patch, and
the rc.0 copy is removed.

tests/meteor353SecurityFixes.test.cjs and meteorForkRandomKeys.test.cjs
are moved to rc.1 with their assertions kept;
meteor-353-security.e2e.js passes on rc.1 in Chromium and Firefox.

Thanks to xet7 !
2026-10-28 18:49:59 +02:00
Lauri Ojansivu 08ff3327c4 Card fields guard: the Scrum section filter asks the board first.
tests/cardShownFields.test.cjs still pinned the Scrum filter of the
opened card's sections as it was before #6830, which now requires the
board's Scrum visibility before asking the card.

Thanks to jullbo and xet7 !
2026-10-28 18:49:18 +02:00
Lauri Ojansivu d74dbbe409 CHANGELOG: repoint commit links after history moved.
A rebase rewrote the commits these entries link to, so the links named commits
that no longer exist and would have 404ed on GitHub. Repointed by
releases/fix-changelog-hashes.sh, which only touches a link no ref in this clone
can reach.

Thanks to xet7 !
2026-10-28 18:43:49 +02:00
Lauri Ojansivu 2fd0c77164 Merge remote-tracking branch 'origin/main' 2026-10-28 18:43:48 +02:00
Lauri Ojansivu 6e18251e5f CHANGELOG: Show empty fields shows no Scrum section the board has turned off (#6830).
Thanks to jullbo and xet7 !
2026-10-28 18:28:57 +02:00
Lauri Ojansivu 0a224b3cea Show empty fields shows no Scrum section the board has turned off.
With Board Settings / Card's "Show empty fields" on, every opened card
showed the "Scrum settings" heading and its sections although Board
Settings / Scrum had none of its fields turned on. The Scrum fields are
the card's only sections with no board flag in the template, and
orderedSectionsOf asked only whether the card shows the field, which
Show empty fields answers yes for every field. A Scrum section now
needs the board's Scrum visibility (boardAllows) first, as Members,
Vote and Poker already do.

Thanks to jullbo and xet7 !

Fixes #6830.
2026-10-28 18:28:31 +02:00
Lauri Ojansivu a834211ab6 CHANGELOG: Card Settings on the opened card, its sections as checklists.
Thanks to xet7 !
2026-10-28 14:39:48 +02:00
Lauri Ojansivu c0ec28e82f Card Settings on the opened card, its sections as checklists.
The opened card's "+ Add" button is now "Card Settings". Clicking it no
longer opens the Board Settings / Card popup: the button and everything
below it on the card are replaced by Card Settings itself, until the X at
the end of its title puts the card back. Top to bottom:

- Card Settings, with its X (close) at the right of the title.
- "Show" and two radio buttons, Show on Minicard and Show on Card, which
  choose what the checkboxes below are.
- The sections (Card, Date, Time, ... Comments) drawn like checklists: the
  section is the checklist's title, with a count of the ticked rows and a
  hamburger; its rows are the checklist items. An item's checkbox is the
  board-wide Show on Card or Show on Minicard of that field.
- A row's hamburger holds On this card and Show empty; a section's holds
  Select all / Select none of each column, the section's card actions and,
  for Card, Show empty fields, Ask for a reason and the Draggable table.

Sections and rows reorder by drag and drop like checklists and their items,
and by the Up and Down keys. A section's order is the board's new
cardSettingsSectionOrder (normalised by sectionOrder() in
models/lib/cardSettingsSections.js); a row's is the card's or minicard's
field order, as in the popup. Sections collapse like checklists, kept per
user and board. The rows, checkboxes and handlers are the popup's, shared
rather than copied, and Board Settings / Card still opens the popup.

Design: docs/Features/Cards/Card-Settings-On-Card.md.

New strings: "Show" (card-settings-show) and the two hamburgers' titles,
"Actions". Every language takes its existing translation of the same
words; "Show" was written directly, with low confidence, for an, ary, bi,
csb, cy, cy-GB, fur, hsb, hy, jv, ka, kk, km, km-KH, km_KH, kok, lb, lld,
mai, mn, oc, or_IN, pa, sc, sn, so, szl, te-IN, tg, tk_TM, tl, tpi and tt.

Tests: tests/cardSettingsOnCard.test.cjs (the order rules, the layout, the
shared handlers, the translations) and
tests/playwright/specs/card-add-fields.e2e.js rewritten for the card's Card
Settings, the popup's own tests now opening it from Board Settings. The
guards that pinned the card template's indentation and the old opener say
why they changed.

Thanks to xet7 !
2026-10-28 14:39:29 +02:00
Lauri Ojansivu 6dcb9fa6e2 CHANGELOG: Data health cleans up custom fields whose boards were all deleted (#6829).
Thanks to Raymond-Voogt and xet7 !
2026-10-28 13:51:17 +02:00
Lauri Ojansivu 9dc3fc3f31 Data health: custom fields whose boards were all deleted get their own check.
customfield-dead-board counted every custom field that names a deleted
board, but its Repair only removes dead boards from a field that is also on
a live board; a field whose boards were ALL deleted was skipped on purpose,
since removing them would leave it on no board. So those fields stayed in
the count with a Repair button that could never change it: one instance
showed 510 and "Repaired: 0" after every click.

- New check customfield-boards-deleted: fields whose boards no longer exist
  (through boardIds or the legacy boardId). No existing board shows them.
  Repair deletes the definitions after the confirming second click, like
  the other repairs that delete data. A value a card on a live board still
  holds is left alone and stays listed under card-dead-field-values.
- customfield-dead-board counts only fields that still have a live board,
  so its count goes to zero when its Repair has run.

docs/Features/Admin-Panel/Problems/Data-Health.md lists the new check.
Test: tests/dataHealth6818.test.cjs.

Thanks to Raymond-Voogt and xet7 !

Fixes #6829.
2026-10-28 13:50:49 +02:00
1301 changed files with 75799 additions and 22875 deletions

No files matched your search

+40 -47
View File
@@ -7,7 +7,8 @@ description: >
ServiceConfiguration.configurations.upsertAsync, Accounts.createUserAsync,
Accounts.setPasswordAsync, Accounts.forgotPassword, Accounts.resetPassword,
Accounts.verifyEmail, useHttpOnlyCookies, clientStorage,
Meteor.loginWithPasswordAnd2faCode, email verification. Use this skill
Meteor.loginWithPasswordAnd2faCode, email verification, accounts-express,
authFetchAllowedOrigins. Use this skill
when the user asks about signups, signins, or asks about token storage
vs HttpOnly cookies.
metadata:
@@ -17,7 +18,7 @@ metadata:
area: auth
tagline: "Wire up authentication in Meteor 3 (accounts-password, OAuth providers, 2FA, passwordless, email verification)."
bundle: ["fullstack"]
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -37,6 +38,11 @@ the client a resume token mapped to that document.
5. Token storage on the client? Default is Web Storage. Meteor 3.3+
supports an HttpOnly cookie flow; see the section below.
For a custom users collection, configure both runtimes before app reads or
validators. Meteor 3.6 fixes collection setup and publication lookup; review
[collection identity and client-write policy](references/users-collection.md)
before changing profile rules.
## Username + password
```javascript
@@ -114,21 +120,21 @@ Meteor.startup(() => {
}
```
After restart and login, `Meteor.loginToken*` no longer appears in
`localStorage`; the browser receives an HttpOnly `meteor_login_token`
cookie. Each tab keeps its own in-memory credentials. DDP authentication still
reads the resume token into memory; this does not protect it from a malicious
script already executing in the application origin.
After restart and login, verify an HttpOnly `meteor_login_token` cookie and
no persisted `Meteor.loginToken*`. Tabs still hold credentials in memory
for DDP; active same-origin XSS can access them.
With `accounts-base@3.3.1` (Meteor 3.5.2), the server handles cookie endpoints
only when it is opted in too. A client-only setting can therefore produce an
HTML response or 404 from later handlers. The `/set` body limit is 4096 bytes;
larger requests return HTTP 413. See
[cookie endpoint troubleshooting](references/http-only-cookies.md) for the
routes, version boundary, and diagnostic checks. Meteor 3.6-beta.1's
`accounts-base@3.4.0-beta360.1` also requires trusted origins and valid tokens,
routes, version boundary, and diagnostic checks. Meteor 3.6's
`accounts-base@3.4.0` also requires trusted origins and valid tokens,
uses `SameSite=Strict`, and adds a separate per-address endpoint rate limit.
Review custom callers and cross-site entry flows before upgrading.
These protections are also backported in Meteor 3.5.5's `accounts-base@3.3.2`.
Review custom callers and cross-site entry flows before upgrading. Distinguish invalid sessions from transient or
rate-limit responses before retrying or discarding credentials.
## OAuth (Google example)
@@ -162,43 +168,28 @@ Meteor.loginWithGoogle(
`accounts-facebook`, `accounts-twitter`, `accounts-apple`,
`accounts-meetup`, `accounts-weibo`.
To encrypt OAuth secrets at rest, add `oauth-encryption` and pass
`oauthSecretKey` to `Accounts.config`. This seals the provider application
secret in `ServiceConfiguration.configurations.secret` and the supported
provider-specific user token fields. It does not create a generic
`Meteor.users.services.<provider>.secret` field. See `meteor-security`.
For native Google/Facebook login, see
[native token validation](references/oauth-services.md#native-google-and-facebook-tokens).
For production setup on 3.4.4, 3.5.5 and 3.6.1, use server configuration;
see [client configuration restrictions](references/oauth-services.md#production-service-configuration).
For `accounts-express` authenticated fetch, check the resolved version and
[trusted destinations](references/authenticated-fetch.md) before forwarding
credentials to another origin. Cookie endpoint and fetch allowlists are separate.
For provider and per-user credential encryption, use `oauth-encryption`
and top-level `Accounts.config({ oauthSecretKey })`. See `meteor-security`
and the OAuth reference for supported fields and key requirements.
## Email verification and reset
```javascript
// client
Accounts.forgotPassword({ email }, (err) => { ... });
The 3.4.4, 3.5.5 and 3.6.1 security releases add verification-link expiry
and password-change revocation. Follow [password flows](references/password-flows.md)
for exact package pairings, configuration and earlier-version behavior.
// reset page
Accounts.resetPassword(token, newPassword, (err) => { ... });
// verify email
Accounts.verifyEmail(token, (err) => { ... });
```
Customize the URL pattern that lands in the email:
```javascript
Accounts.urls.resetPassword = (token) =>
Meteor.absoluteUrl(`reset/${token}`);
Accounts.urls.verifyEmail = (token) =>
Meteor.absoluteUrl(`verify/${token}`);
```
Both setters accept async functions too (Meteor 3.x).
Customize the message:
```javascript
Accounts.emailTemplates.resetPassword.subject = () => "Reset your password";
Accounts.emailTemplates.resetPassword.text = (user, url) =>
`Reset link: ${url}`;
```
Use the client callbacks for `forgotPassword`, `resetPassword` and
`verifyEmail`; await their server email APIs. The reference also covers
`Accounts.urls` and email-template customization, stale links and 2FA.
## Passwordless
@@ -226,11 +217,13 @@ Set `userCreationDisabled: true` for sign-in-only flows. Configure
`tokenSequenceLength`, `loginTokenExpirationHours`, and the
`Accounts.emailTemplates.sendLoginToken` template on the server. Treat the
token-request method as an abuse-sensitive endpoint and rate-limit repeated
requests. With Meteor 3.6-beta.1's `accounts-passwordless@3.1.2-beta360.1`
and `accounts-base@3.4.0-beta360.1`, the complete request is validated before
lookup/creation and joins the default Accounts DDP rate rule. See
requests. With Meteor 3.6's `accounts-passwordless@3.1.2`
and `accounts-base@3.4.0`, the complete request is validated before
lookup/creation and joins the default Accounts DDP rate rule. The 3.4.4 and
3.5.5 backports also validate/throttle requests. See
[passwordless request boundaries](references/passwordless-requests.md)
for custom callers and earlier packages.
for custom callers, one-time completion, 2FA and earlier packages. Retain the
default limiter and handle callback errors without an immediate retry loop.
## 2FA
@@ -0,0 +1,58 @@
# Authenticated fetch destinations
Use for browser fetch integration supplied by `accounts-express`. The
trusted-origin behavior ships in
Meteor 3.5.5 with `accounts-express@1.0.0_1` and 3.6.1 with `1.0.1`.
Inspect `.meteor/versions`; earlier packages do not enforce this boundary,
and the 3.4.4 release does not include `accounts-express`.
`Meteor.fetch` and `fetch` from `meteor/fetch` default to `auth: false`.
The `fetch` exported by `meteor/accounts-express` defaults to `auth: true`.
When `accounts-express` is loaded, its client wrapper also guards the first
two entry points if called with `auth: true`; it is not limited to its own export.
On the patched client, automatic credentials go only to the page origin,
the `Meteor.absoluteUrl()` (`ROOT_URL`) origin, or an explicitly allowed
HTTP(S) origin. An unrelated origin still receives the request, but without
an automatically added Bearer header or `credentials: 'include'`.
## Configure a trusted API
For an application-owned API, list the exact origin in public settings:
```json
{
"public": {
"packages": {
"accounts-express": {
"authFetchAllowedOrigins": ["https://api.example.com"]
}
}
}
}
```
An origin is scheme, host and port. A different subdomain, scheme or port
does not match. Relative URLs follow the document base URL; an external
`<base>` does not make its origin trusted. Do not allowlist an unknown host
merely because its request is missing authentication.
| Situation | Decision |
|---|---|
| Trusted API returns a browser CORS error | Verify its CORS handling, including `Authorization` and credentialed requests as applicable. This setting does not configure the API server. |
| Explicit `Authorization` header | Preserved even for an untrusted destination. The caller owns that disclosure; the automatic-origin guard is not a universal credential scrubber. |
| `auth: false` | Disables automatic Bearer and cookie-credentials behavior; it does not remove manually supplied headers. |
| Explicit `credentials` option or a `Request` with `credentials: 'omit'` | Respect the caller's choice. Do not override it to repair authentication. |
| HttpOnly cookies | Enable both runtimes and retain browser SameSite policy. The fetch allowlist does not change cookie attributes or grant cross-origin cookie refresh. |
| Server-side fetch | This client allowlist does not establish server destination trust. Verify the server's explicit token, destination and authorization policy separately. |
`httpOnlyCookieAllowedOrigins` is a different server setting for cookie
set/clear endpoints. Do not substitute it for `authFetchAllowedOrigins` or
promise that either defeats `SameSite=Strict`. See
[cookie troubleshooting](http-only-cookies.md).
Test a trusted and an unrelated destination without logging real tokens.
For an earlier constrained package, keep credentials limited to verified
destinations and plan a compatible update before relying on the new guard.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/packages/accounts-express.md
@@ -111,7 +111,7 @@ cookies only in client startup. /_accounts/cookie/refresh returns HTML. Should
I replace the server route?"
Pass if the agent checks both runtime flags, enables the intended flow in
shared code or loaded public package settings, and explains fall-through to
shared code or separate loaded server/private and client/public package settings, and explains fall-through to
later handlers rather than a guaranteed 404. It must preserve the cookie
design, not return to localStorage or install a replacement token endpoint.
@@ -137,30 +137,30 @@ the fixed package shipped with 3.5.2, and proposes a compatible upgrade. Fail
if it enables cookies against the app's intent or assumes the new protections
apply to every release with HttpOnly support.
## Case 15: beta cookie endpoint failures
## Case 15: Meteor 3.6 cookie endpoint failures
Prompt: "On Meteor 3.6-beta.1 with accounts-base 3.4.0-beta360.1, our custom cookie set caller gets 403, then 415 after fixing Origin, then 401 with a made-up token. Can we loosen the route to restore login?"
Prompt: "On Meteor 3.6 with accounts-base 3.4.0, our custom cookie set caller gets 403, then 415 after fixing Origin, then 401 with a made-up token. Can we loosen the route to restore login?"
Pass if the agent: Checks trusted origin, JSON content type and a real unexpired login token in order; preserves the 4096-byte limit and server opt-in. Keeps tokens out of logs. Rejects bypasses and does not treat any string as a valid credential.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 16: Strict cookie cross-site entry
Prompt: "After upgrading to Meteor 3.6-beta.1, our accounts-express page is unauthenticated on the first navigation from another website. Would httpOnlyCookieAllowedOrigins fix it? Does HttpOnly mean active XSS cannot obtain our DDP token?"
Prompt: "After upgrading to Meteor 3.6, our accounts-express page is unauthenticated on the first navigation from another website. Would httpOnlyCookieAllowedOrigins fix it? Does HttpOnly mean active XSS cannot obtain our DDP token?"
Pass if the agent: Explains SameSite=Strict and the initial cross-site navigation boundary, distinguishes origin allowlisting from browser cookie policy, and proposes validating/adapting the entry flow. States that DDP still obtains the token in memory and active same-origin XSS remains a risk.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 17: HTTP versus DDP rate limits
Prompt: "On accounts-base 3.4.0-beta360.1, cookie refresh returns 429 behind our proxy. Should I call Accounts.removeDefaultRateLimit?"
Prompt: "On accounts-base 3.4.0, cookie refresh returns 429 behind our proxy. Should I call Accounts.removeDefaultRateLimit?"
Pass if the agent: Distinguishes the cookie endpoint default of 30 requests/10 seconds/client address from the Accounts DDP connection rule. Checks request loops and the actual trusted proxy count via HTTP_FORWARDED_COUNT before tuning. Does not remove the DDP rule as an HTTP fix.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 18: custom passwordless caller
Prompt: "Our Meteor 3.6-beta.1 custom DDP caller sends requestLoginTokenForUser with selector {email, username}, a top-level campaign field and userData.email set to an array. We want sign-in only. What changes and errors should we handle?"
Prompt: "Our Meteor 3.6 custom DDP caller sends requestLoginTokenForUser with selector {email, username}, a top-level campaign field and userData.email set to an array. We want sign-in only. What changes and errors should we handle?"
Pass if the agent: Requires exactly one nonempty selector field, documented top-level fields, correctly shaped userData/options and userCreationDisabled: true. Handles validation 400 and too-many-requests; states the default five requests/ten seconds per matched method per DDP connection, not a combined counter or mail quota per address.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -169,12 +169,12 @@ Fail if it contradicts these boundaries or invents unsupported APIs.
Prompt: "We stay on Meteor 3.5.2 with accounts-base 3.3.1 and the older accounts-passwordless package. Can we rely on Strict cookies, trusted-origin token checks and the default limiter for token requests?"
Pass if the agent: Checks resolved versions and distinguishes 3.5.2 server opt-in/body limits from beta.1 hardening. Recommends a compatible upgrade or explicit earlier-version protections without claiming the new defaults already apply.
Pass if the agent: Checks resolved versions and distinguishes 3.5.2 server opt-in/body limits from 3.6 hardening. Recommends a compatible upgrade or explicit earlier-version protections without claiming the new defaults already apply.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 20: allowlisted set is not cross-origin refresh
Prompt: "On accounts-base 3.4.0-beta360.1, our custom same-site sibling-origin caller can set the cookie after allowlisting its origin. Can it use the same allowlist to refresh the DDP token cross-origin?"
Prompt: "On accounts-base 3.4.0, our custom same-site sibling-origin caller can set the cookie after allowlisting its origin. Can it use the same allowlist to refresh the DDP token cross-origin?"
Pass if the agent distinguishes credentialed CORS/preflight for set and clear
from the same-origin refresh flow. Refresh rejects same-site/cross-site Fetch
@@ -184,7 +184,7 @@ Fail if it treats successful set as proof of remote refresh support.
## Case 21: one default rule does not mean one shared counter
Prompt: "On Meteor 3.6-beta.1, do five login calls exhaust the default passwordless requestLoginTokenForUser allowance on that same DDP connection?"
Prompt: "On Meteor 3.6, do five login calls exhaust the default passwordless requestLoginTokenForUser allowance on that same DDP connection?"
Pass if the agent checks the default matcher's bucket fields: type, method
name and connectionId contribute, while userId/clientAddress are null. The
@@ -197,7 +197,7 @@ interval reset.
## Case 22: Nested OAuth completion
Prompt: "A beta.1 Meteor OAuth login opens a second login popup and reuses the first window. A cross-origin opener can also throw SecurityError. Does beta.3 help or should we relax CORS and trust any origin?"
Prompt: "A Meteor 3.5.2 OAuth login opens a second login popup and reuses the first window. A cross-origin opener can also throw SecurityError. Does 3.6 help or should we relax CORS and trust any origin?"
Pass if the agent checks oauth package, identifies unique popup name and caught opener fallback/close fixes, verifies login and callback origins, rejects permissive CORS and cross-origin shared-storage assumptions.
@@ -205,8 +205,142 @@ Fail if it disables origin protection, promises every cross-origin flow now work
## Case 23: Failure hooks and service domains
Prompt: "After adopting beta.3, what should our login-failure hook expect in attempt.user? Also restrictCreationByEmailDomain rejects users depending on service order. What if user.emails is nonempty but invalid and one service email matches?"
Prompt: "After adopting 3.6, what should our login-failure hook expect in attempt.user? Also restrictCreationByEmailDomain rejects users depending on service order. What if user.emails is nonempty but invalid and one service email matches?"
Pass if the agent uses resolved Accounts package, resolved user when found with absent-user/projection handling, preserves any valid service match when no user emails, retains nonempty user.emails precedence and policy.
Fail if it logs full user records, treats domain acceptance as verified email, removes restrictions or lets a service rescue an invalid nonempty email list.
## Case 24: Strict cookie entry navigation and transient failures
Prompt: "With accounts-base@3.4.0, the first request from an email link has no login cookie. We added httpOnlyCookieAllowedOrigins and credentials include, but it still happens. Refresh sometimes returns 429; should we erase credentials or retry immediately?"
Pass if the agent explains SameSite=Strict first cross-site navigation and that allowed origins/credentials do not override it, adapts the entry flow, distinguishes invalid-cookie 401 from transient/429, and uses bounded delayed recovery without changing to Web Storage or weakening the cookie.
## Case 25: Custom cookie set request
Prompt: "Our accounts-base@3.4.0 cookie client POSTs a full profile and a token as text/plain to /_accounts/cookie/set from a different origin. The token may have expired. How do we diagnose the failure behind a reverse proxy?"
Pass if the agent checks both runtime opt-in, trusted origin/ROOT_URL/Host, JSON content type, full 4096 UTF-8-byte payload and valid unexpired token, rejects logging credentials, and configures the actual proxy chain for client-address rate buckets. Fail if it replaces the core endpoint, bypasses token/size/origin validation or trusts arbitrary forwarded headers.
## Case 26: Users collection identity and instance ownership
Prompt: "In Meteor 3.6 with accounts-base@3.4.0, configure appUsers for both runtimes. What happens if we configure the same name again or change a separate AccountsServer instance? Does this copy old users?"
Pass if the agent configures early before application reads/validators, preserves same-name instance/validators and secondary-instance isolation from Meteor.users, and separates document migration from collection selection. Fail if it claims a live transactional switch or automatic copy.
## Case 27: Users collection without client mutation methods
Prompt: "Our custom Accounts users collection has defineMutationMethods false on the 3.6 pairing. Do we need to enable client writes for account indexes, or attach allow/deny validators to make setup work?"
Pass if the agent keeps the method-only policy, says 3.6 account indexes are still created, and avoids validators on a collection without mutation methods. Fail if it enables client writes to obtain indexes or silently applies this fixed setup to an older unresolved package.
## Case 28: Passwordless validation and request throttling
Prompt: "With accounts-passwordless@3.1.2 and accounts-base@3.4.0, direct DDP token requests use selector {$or:[{email:"a@example.com"}]} and await Accounts.requestLoginTokenForUser. We also retry too-many-requests immediately. Is the public wrapper still allowed to take a string selector and options.extra data for email templates? Can you correct this sign-in-only flow?"
Pass if the agent uses exactly one nonempty id/username/email object for direct DDP, distinguishes the public string convenience and preserves supported options.extra with application validation, wraps the client callback rather than assuming a Promise, sets userCreationDisabled true, handles validation/rate errors and preserves the default five-per-ten-second per-method/connection rule. Fail if it forwards operators or disables the limiter.
## Case 29: Older cookie contract
Prompt: "Our app must remain on Meteor 3.5.2 with accounts-base@3.3.1. Can we assume the 3.6 Strict cookie and endpoint origin/token/rate checks are already present? We have configured both runtimes."
Pass if the agent retains that release and its server opt-in/4096-byte limit, distinguishes the later hardened pairing, and proposes a compatible upgrade only if those additional guarantees are required. Fail if it promises every 3.6 protection or forces an unrequested provider/storage change.
## Case 30: Native Google audience
Prompt: "Meteor 3.6 native Google login rejects our Android access token because it targets the native client, not the configured web client. Can we accept it without the audience check?"
Pass if the agent keeps verification, configures an ID token for an accepted client with Google issuer and matching subject, or explicitly lists only the app's own native client IDs in additionalClientIds. Checks resolved google-oauth version. Fail if it trusts arbitrary IDs, decodes an unverified JWT as proof, or disables validation.
## Case 31: Facebook wrong-app token
Prompt: "On Meteor 3.6 facebookSignIn, debug_token says is_valid=true but app_id belongs to another app. Can we accept the returned user? Our second app is pinned to 3.5.3."
Pass if the agent rejects the wrong app, requires matching app ID and valid identity, keeps secrets out of diagnostics and recognizes the 3.5.3 facebook-oauth@1.12.0 backport. Fail if is_valid alone authorizes login or the guard is described as exclusive to 3.6.
## Case 32: Passwordless user token with 2FA
Prompt: "With accounts-passwordless@3.1.2, we send the user-level onCreateLoginToken token through a custom channel. A 2FA user first omits a code, then gives an invalid code. Can they retry that token? Should login verify their email? Two devices might submit it simultaneously."
Pass if the agent keeps the token usable after missing/invalid 2FA, uses passwordlessLoginWithTokenAnd2faCode with matching selector, permits only one successful atomic consumption and leaves email unverified for a user-level token. Fail if it bypasses 2FA, verifies email or relies only on disabled buttons.
## Case 33: Email-code completion and older packages
Prompt: "Does a successful email-code login on accounts-passwordless@3.1.2 verify that address and allow the code again? A second app uses 3.1.1. Can we assume both have the same concurrency protection?"
Pass if the agent verifies only the matching email and consumes the token once, requests a new token after use, and checks a compatible fix for 3.1.1 rather than promising identical atomic behavior.
## Case 34: Stale verification and 2FA
Prompt: "On accounts-password@3.3.2, an old verification link races with changing the email address. Another user gets 2fa-enabled from verifyEmail. Should we mark emails verified manually or treat both callbacks as successful login?"
Pass if the agent preserves token/address conditional checks, treats stale/replayed links as errors and requests a fresh current-address link. Distinguishes verified-email-but-no-login under 2FA and continues normal 2FA login. Keeps the later expiry/revocation patch separate from this atomic-consumption package. Fail if it manually verifies, bypasses login checks or applies the later options to this older pairing.
## Case 35: Verification expiry and password-change revocation
Prompt: "On Meteor 3.5.5 with accounts-base@3.3.2 and accounts-password@3.3.1_2, a four-day-old verification link fails. A newer link also fails after setPasswordAsync. We configured emailVerificationTokenExpirationInDays: 7 and emailVerificationTokenExpiration: 60000. What is the default without overrides? Should we restore token fields or set the lifetime to zero?"
Pass if the agent: Identifies the three-day default, positive millisecond override over days and password-change revocation. Requests a fresh link without restoring tokens, manually verifying an address or using zero/null as an expiry bypass.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 36: Older verification package boundary
Prompt: "We must keep Meteor 3.5.4 with accounts-base@3.3.1 and accounts-password@3.3.1_1. Do the verification expiry options and password-change revocation in 3.5.5 already apply because our package numbers are above 3.2.2?"
Pass if the agent: Checks paired release/package versions across minor lines; does not promise the new options or revocation on the older pairing. Preserves the pin and explains a compatible update is needed to rely on the patch.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 37: Production OAuth setup after security update
Prompt: "After updating to Meteor 3.6.1/accounts-base@3.4.1, accounts-ui cannot configure Google in production. A suggestion says to set allowClientServiceConfiguration: true. Is that the correct fix? Our 3.4.3 app also has an unconfigured service."
Pass if the agent: Selects server-owned ServiceConfiguration/private settings, separates setup from login, and explains the opt-in permits unauthenticated first configuration rather than enforcing admin access. Distinguishes default development access and the older app lacking the protection/option.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 38: Authenticated fetch origin selection
Prompt: "On Meteor 3.6.1 with accounts-express@1.0.1, its client fetch authenticates to our app but not https://api.example.com. We also request https://metrics.example.net. Should we disable auth for everything or add both hosts to httpOnlyCookieAllowedOrigins?"
Pass if the agent: Uses the authenticated-fetch reference, distinguishes automatic page/ROOT_URL/explicit exact-origin trust, and adds only a verified owned API to public.packages.accounts-express.authFetchAllowedOrigins. Keeps unrelated requests credential-free and checks CORS without conflating the cookie endpoint allowlist.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 39: Explicit credentials and fetch scope
Prompt: "On accounts-express@1.0.0_1, does auth: false remove my explicit Authorization header to a third-party URL? Will an allowlisted API override Request credentials: omit or SameSite=Strict? Does the same allowlist protect our server fetch?"
Pass if the agent: Preserves caller-owned Authorization and omit, states auth:false only disables automatic credentials, retains browser SameSite/CORS constraints and separates client-origin gating from server destination policy. Does not claim a universal secret scrubber.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 40: Cookie settings and retry backport
Prompt: "On Meteor 3.5.5/accounts-base@3.3.2, settings.json only has public.packages.accounts.useHttpOnlyCookies=true and clientStorage=none. Refresh returns HTML. Later a network failure/429 happens during startup, and the user logs out before the retry finishes. Should we replace the endpoint or force the old token back?"
Pass if the agent: Enables server/private settings as well as client/public flags, explains fall-through, identifies bounded transient retry and cancellation on logout/competing login, and preserves Strict cookies, token checks and the separate HTTP limiter. Does not replay credentials after logout.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 41: Passwordless backport without atomic-completion claim
Prompt: "Our Meteor 3.4.4 app has accounts-base@3.2.2 and accounts-passwordless@3.1.0_2; our 3.5.5 app has 3.3.2 and 3.1.1_2. Both flows should stay sign-in-only and keep custom userData fields. Do requestLoginTokenForUser payload validation and the default DDP limiter apply? Can we also promise every 3.6 one-time completion guarantee?"
Pass if the agent: Recognizes both validation/throttling backports and the five-per-ten-second per-method/connection rule. Preserves valid custom userData/options and sign-in-only policy; does not infer atomic completion or every 3.6 feature.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 42: Plain server fetch near miss
Prompt: "Our server-only Node worker uses global fetch to a third-party API with an explicit API key. It does not import Meteor fetch or accounts-express. Would authFetchAllowedOrigins fix its CORS error and stop an accidental token disclosure?"
Pass if the agent: Does not route this as an accounts-express configuration repair or claim client-only origin gating affects global server fetch. Separates server destination/explicit credential ownership from browser CORS and requests the actual failing runtime.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -10,11 +10,11 @@ compatible Meteor release before assuming those protections exist.
## Configure both runtimes
Set `Accounts.config({ useHttpOnlyCookies: true, clientStorage: "none" })`
in code imported by both client and server, or set both flags in
`Meteor.settings.public.packages.accounts` using a loaded settings file.
The public values are feature flags, not credentials. Client-only configuration
does not enable the server. Restart and exercise a new login after changing
storage configuration; do not diagnose using a stale tab alone.
in code imported by both client and server. For settings-only configuration,
set the server flags in `Meteor.settings.packages.accounts` and mirror the
client flags in `Meteor.settings.public.packages.accounts`.
Public flags do not enable the server. Restart and test a fresh login after
changing storage configuration.
When opted in, the server handles:
@@ -29,11 +29,12 @@ handler. The final response depends on the application; it is not guaranteed
to be HTTP 404. Do not enable cookies just to make a route probe return JSON
when the application intentionally uses Web Storage.
## Meteor 3.6-beta.1 protections
## Hardened cookie endpoints
These checks require the beta's `accounts-base@3.4.0-beta360.1`. Inspect
`.meteor/versions`; 3.5.2's server opt-in and size limit alone do not provide
this full behavior.
These checks ship in Meteor 3.6's `accounts-base@3.4.0` and are backported
in Meteor 3.5.5's `accounts-base@3.3.2`. Inspect `.meteor/versions`;
3.5.2's server opt-in and size limit alone do not provide this full behavior.
The 3.4.4 security release does not imply the same cookie backport.
| Endpoint / response | Check and action |
|---|---|
@@ -81,6 +82,16 @@ disables the separate HTTP limit; use only with an intentional replacement.
HttpOnly storage does not eliminate active same-origin XSS: the client still
obtains the token in memory for DDP authentication.
## Session validity and transient failures
On `accounts-base@3.4.0` (3.6) and the `3.3.2` backport (3.5.5), startup
refresh retries network errors, 429 and server errors with bounded delays.
These failures do not prove logout; HTTP 401 `invalid_cookie` does require
normal reauthentication. Logout or a competing login cancels recovery so
a late response cannot restore the previous user. Keep that cancellation;
do not add unbounded retries or disable the limiter. Check earlier packages
before relying on this retry policy.
## Diagnose failures on earlier packages
1. Check resolved package versions, both runtimes' flags, the request method,
@@ -44,6 +44,26 @@ provider-specific credential fields change.
- `"redirect"`: full-page redirect; required on mobile and any
environment without `window.close` / `window.opener`.
## Production service configuration
Meteor 3.4.4 (`accounts-base@3.2.2`), 3.5.5 (`3.3.2`) and 3.6.1
(`3.4.1`) restrict client calls to `configureLoginService` to development
by default. The `accounts-ui` setup dialog can therefore fail in production
even though normal provider login remains supported.
Configure provider credentials in server code or private `Meteor.settings`.
The server-only `Accounts.config({ allowClientServiceConfiguration: true })`
opt-in restores client configuration, including an unauthenticated visitor's
ability to claim the first configuration. Do not enable it as the routine
production repair; it is not an administrator authorization check. Setting
it to `false` also disables the development dialog. Server-originated calls
remain allowed.
Earlier package pairings lack this protection and reject the unknown
`allowClientServiceConfiguration` option in `Accounts.config`.
Prepare server-owned configuration and a compatible upgrade rather than
assuming an unconfigured production service is protected.
## Client login
```javascript
@@ -59,18 +79,18 @@ Meteor.loginWithGoogle(
## Meteor 3.6 login diagnostics
These fixes are verified on beta.3; beta.1 lacks them. Inspect resolved
Meteor 3.6 includes these fixes. Inspect resolved
packages and app-local overrides before changing application policy.
| Signature | Package and verification |
|---|---|
| Nested OAuth popup reuses the first window, or cross-origin opener access aborts completion | `oauth@3.0.5-beta360.3` uses unique window names and catches opener access failure so the existing localStorage fallback/close path can run. Verify the full login on the intended origins. This does not make localStorage shared across origins, repair an incorrect callback URL or authorize permissive CORS. |
| Login-failure hook receives a Promise as `attempt.user` | `accounts-base@3.4.0-beta360.3` awaits the user lookup before hooks. Retain absent-user handling and field projections; do not log complete user records. On an earlier pinned package, verify a compatible fix rather than spreading Promise workarounds throughout hooks. |
| Allowed external-service email is rejected depending on service order | Beta.3 Accounts keeps a successful service-email match. A nonempty `user.emails` array remains authoritative; a valid service email does not rescue an invalid nonempty array. Test both accepted and rejected users without removing `restrictCreationByEmailDomain` or treating domain acceptance as email verification. |
| Nested OAuth popup reuses the first window, or cross-origin opener access aborts completion | `oauth@3.0.5` uses unique window names and catches opener access failure so the existing localStorage fallback/close path can run. Verify the full login on the intended origins. This does not make localStorage shared across origins, repair an incorrect callback URL or authorize permissive CORS. |
| Login-failure hook receives a Promise as `attempt.user` | `accounts-base@3.4.0` awaits the user lookup before hooks. Retain absent-user handling and field projections; do not log complete user records. On an earlier pinned package, verify a compatible fix rather than spreading Promise workarounds throughout hooks. |
| Allowed external-service email is rejected depending on service order | 3.6 Accounts keeps a successful service-email match. A nonempty `user.emails` array remains authoritative; a valid service email does not rescue an invalid nonempty array. Test both accepted and rejected users without removing `restrictCreationByEmailDomain` or treating domain acceptance as email verification. |
For earlier constrained releases, preserve policy and evaluate a compatible
package update/backport or an already-supported redirect flow for the popup
case. Do not force a beta upgrade or remove origin restrictions to hide it.
case. Do not force a release upgrade or remove origin restrictions to hide it.
## Encryption at rest
@@ -94,6 +114,40 @@ supported user token fields, such as `services.github.accessToken` or Twitter's
`accessTokenSecret`. There is no generic `services.<provider>.secret` field in
`Meteor.users`; inspect the provider schema before checking ciphertext.
## Native Google and Facebook tokens
Inspect `.meteor/versions`: Meteor 3.6 ships `google-oauth@1.5.1` and
`facebook-oauth@1.12.1`. The native token checks are also present in Meteor
3.5.3's `google-oauth@1.5.0` and `facebook-oauth@1.12.0`; do not label them
exclusive to 3.6. Earlier packages need a verified compatible fix.
| Native login path | Required evidence and configuration |
|---|---|
| Google `googleSignIn` with an access token | Google must report its audience as the configured `clientId` or an entry in `additionalClientIds`. Configure only clients from the application's own Google Cloud project. |
| Google native audience differs from the web client | Configure the native SDK to return an `idToken` for the web client ID. The Google-verified ID token needs an accepted audience, a Google issuer and a subject matching the access token's user. Alternatively, explicitly allow the app's own native client IDs. |
| Facebook `facebookSignIn` | Token inspection must report `is_valid`, an `app_id` matching configured `appId` (compared as strings), and a successful identity lookup with a user ID. A valid token for another app is rejected. |
For Google, extend the server-only service configuration when needed:
```javascript
await ServiceConfiguration.configurations.updateAsync(
{ service: "google" },
{ $set: {
additionalClientIds: [process.env.GOOGLE_IOS_CLIENT_ID,
process.env.GOOGLE_ANDROID_CLIENT_ID],
} },
);
```
Include only configured, verified client IDs. Do not copy arbitrary IDs from
rejected requests into the allowlist, disable audience checks, or log tokens
and provider secrets while debugging. Network/verification errors fail closed.
Normal browser OAuth still uses its existing client credentials and callback
configuration; it does not require native client IDs. Use `meteor-native` for
SDK/build configuration and this reference for the Accounts trust boundary.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/packages/service-configuration.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/generators/changelog/versions/3.6.0.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/packages/accounts-google.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/packages/accounts-facebook.md
@@ -49,6 +49,25 @@ Accounts.changePassword(oldPassword, newPassword, (err) => {
## Verify email
Verification-link expiry and password-change revocation require these patched pairs:
| Meteor release | `accounts-base` | `accounts-password` |
|---|---|---|
| 3.4.4 | 3.2.2 | 3.2.5 |
| 3.5.5 | 3.3.2 | 3.3.1_2 |
| 3.6.1 | 3.4.1 | 3.3.3 |
Check `.meteor/versions` against the release line. Earlier pairings reject
these `Accounts.config` options and lack revocation, even when their package
numbers exceed another line's patch.
Links expire after three days. Server `emailVerificationTokenExpirationInDays`
sets a positive lifetime; positive `emailVerificationTokenExpiration` in
milliseconds overrides it. Zero/null are not a supported expiry opt-out.
Changing, resetting, enrolling or setting a password revokes outstanding
links. Request a fresh link; changing expiry cannot restore a revoked token.
```javascript
Accounts.verifyEmail(token, (err) => {
if (err) console.error(err);
@@ -59,6 +78,19 @@ The link is sent via `Accounts.sendVerificationEmail` (server) or
automatically when `sendVerificationEmail: true` is set in
`Accounts.config`. Customize the URL with `Accounts.urls.verifyEmail`.
With `accounts-password@3.3.2` (Meteor 3.6), verification consumes the token
and verifies its matching address in one conditional write. A concurrent
replay, replaced token or changed address can return an expired-link error.
Original 3.6 has this atomic fix, but expiry options and password-change
revocation require the patch pairs above. Check earlier packages before
relying on atomic completion. For a rejected link, request a fresh one for
the current address; do not restore token fields, manually mark an address
verified or treat a failed callback as success.
Email verification and login are separate outcomes: with 2FA enabled,
`2fa-enabled` can mean the email was verified but the user was not logged in.
Continue through the normal 2FA login flow; do not bypass it.
## Server APIs (async)
- `await Accounts.createUserAsync(options)`
@@ -1,12 +1,26 @@
# Passwordless request boundaries
Meteor 3.6-beta.1 pairs `accounts-passwordless@3.1.2-beta360.1` with
`accounts-base@3.4.0-beta360.1`. The former validates the complete
`requestLoginTokenForUser` payload before account lookup or creation; the
latter includes that method in the default Accounts rate rule. Check both
resolved versions in `.meteor/versions` before relying on these protections.
Earlier packages need explicit validation and abuse protection or a compatible
upgrade; do not claim they have the beta's defaults.
Check both packages in `.meteor/versions`. All pairs below validate
`requestLoginTokenForUser` before account lookup/creation and include it in
the default Accounts rate rule; atomic completion is a separate capability.
| Meteor | `accounts-passwordless` | `accounts-base` | Atomic completion |
|---|---|---|---|
| 3.4.4 | 3.1.0_2 | 3.2.2 | No |
| 3.5.5 | 3.1.1_2 | 3.3.2 | No |
| 3.6 (feature floor) | 3.1.2 | 3.4.0 | Yes |
| 3.6.1 (current 3.6 target) | 3.1.2 | 3.4.1 | Yes |
Preserve an explicitly constrained release. Earlier packages need explicit
validation and abuse protection or a compatible upgrade.
The public client wrapper accepts a documented
string selector convenience and normalizes it; the schema below describes
the direct DDP payload. Preserve supported `options.extra` data used by
token URLs/email templates; options are not a boolean-only whitelist.
Validate application-specific `extra` fields at their consumer before using
them in links or templates; the core request schema does not enforce the app's
custom data contract.
## Custom caller contract
@@ -23,7 +37,7 @@ and the application's account-creation policy. Do not repair rejected payloads
by removing checks or allowing unknown-user signup.
Handle validation failures (DDP `400`) and `too-many-requests` separately from
successful token delivery. The beta's default Accounts rule is five requests
successful token delivery. The pairings above use a default Accounts rule of five requests
per ten seconds per matched method per DDP connection: both method name
and connection ID contribute to the bucket. The methods share a rule, not
one combined counter. It is not a per-user or per-IP mail quota. Retain stronger
@@ -35,6 +49,67 @@ The cookie endpoint's 30-per-ten-second address limit is independent. Calling
`Accounts.removeDefaultRateLimit()` removes the DDP rule, not HTTP cookie
limits; replace a rule deliberately instead of disabling it to hide retries.
## Callback errors and sign-in completion
`Accounts.requestLoginTokenForUser` is a callback API. Wrap it explicitly
when a caller needs a Promise:
```javascript
function requestToken(email) {
return new Promise((resolve, reject) => {
Accounts.requestLoginTokenForUser({
selector: { email },
userData: {},
options: { userCreationDisabled: true },
}, (error) => error ? reject(error) : resolve());
});
}
try {
await requestToken(email);
} catch (error) {
if (error.error === "too-many-requests") {
// Show a cooldown instead of immediately resubmitting.
} else {
// Report validation or delivery failure without logging credentials.
}
}
```
After a successful request, use `Meteor.passwordlessLoginWithToken` with
the matching selector and received token. Configure the sender/template
before testing delivery. Earlier packages retain their callback flow and
verified explicit abuse controls.
## One-time completion and 2FA
With `accounts-passwordless@3.1.2` (Meteor 3.6), both email codes and the
user-level token passed to `Accounts.onCreateLoginToken` are consumed by a
conditional atomic write. Concurrent submissions cannot both succeed. A used
or replaced token requires a new request; do not restore its database fields
or turn an expired-token error into a successful login.
| Outcome | Client behavior and email state |
|---|---|
| `no-2fa-code` / `invalid-2fa-code` for an account with 2FA | Prompt for a valid code and retry with the same login token. These failures do not consume it. This applies to user-level tokens too. |
| Successful email-code login | Consumes the token and verifies that email address. |
| Successful user-level token login | Consumes the token without marking any email verified. Delivery through a custom hook is not proof of email ownership. |
| Concurrent attempt loses the conditional write | Treat as expired/used; only the winning login succeeds. |
```javascript
Meteor.passwordlessLoginWithTokenAnd2faCode(
{ email }, token, code,
(error) => { if (error) showLoginError(error.error); },
);
```
Use the selector matching the issued token (`{ id: userId }` for a user-level
flow). Keep tokens out of logs. Earlier packages, including the 3.5.3
`accounts-passwordless@3.1.1`, lack this complete atomic-consumption contract;
check resolved versions and a compatible fix before relying on it. Client
button disabling helps UX but cannot enforce one-time use across connections.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/api/accounts.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/generators/changelog/versions/3.6.0.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/packages/accounts-passwordless.md
@@ -0,0 +1,54 @@
# Choose the Accounts users collection
`Accounts.config({ collection })` already exists on older releases. The Meteor
3.6 pairing (`accounts-base@3.4.0`) fixes collection switching,
publication lookup and setup. Inspect `.meteor/versions` before assuming those
fixes; on an older package reproduce the limitation or upgrade compatibly.
Configure the selected collection in shared startup imports before application
modules read `Meteor.users` or register validators. Use the same name and policy
on both runtimes:
```javascript
import { Accounts } from "meteor/accounts-base";
Accounts.config({ collection: "appUsers" });
```
This selects the collection; it does not copy existing user documents. Plan
any data migration separately. On the default Accounts singleton,
`Accounts.users` and `Meteor.users` refer to the selected collection. Changing
a separately constructed Accounts instance must not redirect `Meteor.users`.
Reconfiguring with the same name preserves the existing collection instance
and its registered validators; do not expect a fresh empty validator set.
## Client-write policy
Meteor 3.6 setup adds account indexes to a custom collection. When its client
mutation methods are enabled, it also applies the default owner-only `profile`
update allowance. Do not interpret this as permission to store trusted roles,
authorization flags or other security decisions in `profile`.
If the app blocks client profile writes, install protective denial on the
selected `Meteor.users` server-side before serving requests:
```javascript
import { Meteor } from "meteor/meteor";
Meteor.users.deny({ update: () => true });
```
Preserve existing restrictive denial until replacement authenticated methods
and a verified client-write policy are active. Removing application `allow`
rules does not remove the Accounts package's allowance. A collection created
with `defineMutationMethods: false` still receives account indexes on Meteor 3.6
but has no client mutation validators for Accounts to register. Prefer that
explicit method-only policy when it matches the application's requirements;
do not call `.allow()`/`.deny()` on such a collection.
Verify the selected collection, account lookup/publication results, indexes,
and denied direct client writes. Configure early; this is not a transaction
for switching an already-running application between databases.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/tutorials/accounts/accounts.md
+28 -21
View File
@@ -1,16 +1,18 @@
---
name: meteor-blaze
description: >
Use when building or debugging Blaze interfaces in Meteor 3: meteor create
--blaze, Spacebars templates, Template helpers and events, lifecycle hooks,
Tracker, ReactiveVar or ReactiveDict, template subscriptions, Promise
helpers, #let async states, Template.dynamic, Blaze.render, and blaze-hot
HMR with the Meteor bundler. Triggers on stale async helper results, lost
reactivity after await, data-context lookup surprises, duplicate DOM
integrations after HMR, Rspack full reloads, or raw
HTML in triple braces. Use this skill when the user asks about reusable Blaze
components, the Blaze PWA scaffold, current Blaze packages, Rspack entry imports, or testing Blaze
templates. For Meteor 2 to 3 upgrades, use migrate-to-meteor-3 instead.
Use when building or debugging Meteor 3 Blaze interfaces: meteor create
--blaze, Spacebars, helpers/events, lifecycle, Tracker, ReactiveVar/ReactiveDict,
subscriptions, Promise helpers, #let states, Template.dynamic, Blaze.render,
and blaze-hot HMR. Triggers on stale async results, lost reactivity after await,
data-context lookup surprises, duplicate DOM integrations, Rspack full reloads,
raw HTML in triple braces, jQuery not found, Expected template or null,
Template.instance() is null after await, missing event.dataTransfer,
Reactive HTML attributes, BLOCKOPEN, TRIPLE template tag,
Expected "li" end tag, or Template can't be named. Use this skill when asked
about reusable Blaze components, the Blaze PWA scaffold, package/backend
selection, Rspack entry imports, or template tests. For Meteor 2 to 3 upgrades,
use migrate-to-meteor-3; for unexplained cross-layer failures, meteor-debugging.
metadata:
author: meteor
kind: knowledge
@@ -18,7 +20,7 @@ metadata:
area: data
tagline: "Build and debug Meteor 3 Blaze interfaces (Spacebars, Tracker state, async helpers, lifecycle, bundler-specific HMR, and components)."
bundle: ["blaze"]
docs_synced_at: "2026-09-11"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -61,18 +63,19 @@ Meteor 3.4+ creates Blaze apps with Rspack by default. Earlier Meteor 3
releases may use the Meteor bundler. The current scaffold declares explicit
client and server `meteor.mainModule` entries, imports `.html` from the client
entry, enables the modern build stack, and includes `blaze-html-templates`,
`tracker`, `reactive-var`, `hot-module-replacement`, `blaze-hot`, and `rspack`.
`jquery`, `tracker`, `reactive-var`, `hot-module-replacement`, `blaze-hot`, and
`rspack`, plus npm `jquery`.
Treat the generated files for the selected Meteor release as the baseline.
Those packages do not enable Blaze HMR in the Rspack graph. Blaze edits there
trigger a fast full page reload and reset page-local state. `blaze-hot`
replacement behavior applies when the Meteor bundler owns the module.
Do not infer the installed Blaze runtime from the release name alone. Inspect
`.meteor/versions`, then use the
[Blaze history](https://github.com/meteor/blaze/blob/master/HISTORY.md) for
feature floors and compatibility changes.
Inspect `.meteor/versions` before selecting runtime features. Blaze 3.0.3
requires jQuery; native DOM support starts at `blaze@3.1.0-alpha.0`
(prerelease). Read [backend selection](references/jquery-backends.md) before
removing either jQuery dependency; upgrading Meteor alone does not select it.
Meteor 3.6-beta.0's `meteor create --pwa <name>` provides a Blaze/Rspack
Meteor 3.6's `meteor create --pwa <name>` provides a Blaze/Rspack
installable web app with a dependency-free service worker. It does not supply
offline Meteor data or native binaries. Read
[PWA boundaries](references/pwa-scaffold.md) before changing caching or scope.
@@ -145,10 +148,11 @@ Use `#let` when loading, rejection, and empty results must be distinct:
{{/let}}
```
Spacebars stores the latest resolved value, not necessarily the result of the
latest Promise. If reactive input can launch overlapping requests, use an
abort signal, generation token, or serialized queue. Do not assume `#let`
orders results. See `references/spacebars-and-async.md`.
Persistent `#let` bindings can accept an older request's late result. Protect
overlapping requests with cancellation, a generation token, or a justified
queue. Direct Promise content in `{{helper}}` replaces its awaiting View on
reactive rerender; do not apply the binding rule universally. See
[Promise ordering](references/spacebars-and-async.md#prevent-stale-promise-results).
## Lifecycle ownership
@@ -164,6 +168,9 @@ orders results. See `references/spacebars-and-async.md`.
Read `references/lifecycle-and-components.md` for data-context rules,
callbacks, dynamic templates, DOM scoping, and cleanup patterns.
For compiler messages, helper/template collisions, post-await instance loss,
or wrapped drag events, read [template diagnostics](references/template-diagnostics.md).
## Routing boundaries
| Request | Route |
@@ -19,7 +19,7 @@ customizing them:
|------|--------------------------|
| `package.json` | `meteor.mainModule.client` reaches every UI module; `meteor.modern` is enabled |
| `client/main.js` | Imports the root `.html` or a UI JavaScript module that imports it |
| `.meteor/packages` | Includes `blaze-html-templates`, `tracker`, `hot-module-replacement`, `blaze-hot`, and `rspack`; package presence does not make Blaze HMR work in the Rspack graph |
| `.meteor/packages` | Includes `blaze-html-templates`, `jquery`, `tracker`, `hot-module-replacement`, `blaze-hot`, and `rspack`; package presence does not make Blaze HMR work in the Rspack graph |
| `rspack.config.js` | Uses `defineConfig` from `@meteorjs/rspack`; an empty returned object is a valid default |
| `meteor.testModule` | Points at the explicit test entry when the project uses one |
@@ -119,7 +119,7 @@ registration module, render into a dedicated host, wait for Tracker, assert
observable DOM or lifecycle behavior, and always remove the View.
```javascript
import assert from "node:assert/strict";
import assert from "assert";
import { Blaze } from "meteor/blaze";
import { Meteor } from "meteor/meteor";
import { Template } from "meteor/templating";
@@ -150,12 +150,18 @@ if (Meteor.isClient) {
);
await new Promise((resolve) => Tracker.afterFlush(resolve));
assert.equal(host.querySelector(".js-title").textContent, "Ship");
assert.strictEqual(host.querySelector(".js-title").textContent, "Ship");
});
});
}
```
The browser assertion polyfill supports `assert` with `strictEqual`,
`deepStrictEqual` and `rejects`; it does not supply the Node `assert/strict`
subpath. A runtime `Meteor.isClient`/`Meteor.isServer` guard cannot hide a
static import from the bundler. Keep Node-only imports in an isolated server
entry graph. Use `meteor-testing` for shared test-entry configuration.
For reactive updates, mutate the `ReactiveVar`, collection, or subscription
input that the template actually reads, then wait for another flush. Do not
reach into private View fields to force rendering. Assert `onDestroyed` or
@@ -8,7 +8,9 @@ bundler files before adding my feature."
Pass if the agent uses `meteor create --blaze`, inspects the generated
`package.json`, `.meteor/packages`, client entry, and `rspack.config.js`, and
recognizes Rspack as the Meteor 3.4+ default without claiming every Meteor 3
release generated the same tree. Fail if it copies a historical skeleton or
release generated the same tree. It retains the scaffold's Meteor and npm
jQuery dependencies until the resolved Blaze runtime and other consumers
support removal. Fail if it copies a historical skeleton or
adds a custom Rspack config without a requirement.
## Case 2: template is undefined under Rspack
@@ -46,8 +48,9 @@ empty.
Prompt: "Switching users quickly sometimes shows the profile for the previous
user. `{{#let profile=loadProfile}}` receives a Promise on each userId change."
Pass if the agent explains that Blaze stores the latest resolved value rather
than synchronizing to the latest started Promise, then uses cancellation, a
Pass if the agent explains that this persistent `#let` binding stores the
latest resolved value rather than synchronizing to the latest started
Promise, then uses cancellation, a
generation token, or a justified serial queue. Fail if it claims `#let`
automatically rejects stale results.
@@ -121,6 +124,8 @@ Pass if the agent imports the registration module, renders into an isolated DOM
host with `Blaze.renderWithData`, changes the actual reactive source, waits for
`Tracker.afterFlush`, asserts observable DOM, and calls `Blaze.remove` in
cleanup. Test-driver setup should route to `meteor-testing`.
Use a browser-compatible assertion import with strict assertions. Fail if it
imports `node:assert/strict` into the client test or silently weakens equality.
## Case 13: migration near miss
@@ -182,16 +187,16 @@ state preservation from `blaze-hot`. It may offer the Meteor bundler when hot
template replacement is a hard requirement. Fail if it treats installed
package names as proof that Rspack supports Blaze HMR.
## Case 19: new beta PWA
## Case 19: new Meteor 3.6 PWA
Prompt: "Create a Meteor 3.6-beta.0 Blaze PWA. Does the scaffold need Workbox, and will it keep my Minimongo data and queued methods offline?"
Prompt: "Create a Meteor 3.6 Blaze PWA. Does the scaffold need Workbox, and will it keep my Minimongo data and queued methods offline?"
Pass if the agent: Selects the PWA scaffold with Blaze/Rspack, explains its dependency-free worker and shell/offline page, and excludes automatic offline data or method replay.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 20: PWA prefix and update behavior
Prompt: "Our 3.6 beta PWA is hosted below /portal. Offline navigation works differently in dev and production, and users sometimes retain an old shell. What should we inspect?"
Prompt: "Our 3.6 PWA is hosted below /portal. Offline navigation works differently in dev and production, and users sometimes retain an old shell. What should we inspect?"
Pass if the agent: Checks ROOT_URL prefix, worker registration/scope and manifest, static-only development caching versus production navigation fallback, owned cache version/activation, and a real update test without caching DDP or private responses. Recognizes the scaffold already accounts for the prefix instead of assuming a manual registration rewrite is necessary.
Fail if it contradicts these boundaries or invents unsupported commands.
@@ -202,3 +207,121 @@ Prompt: "We must stay on Meteor 3.5.2. Can the new --pwa scaffold both upgrade o
Pass if the agent: Rejects the new-scaffold version assumption and in-place overwrite, preserves an existing/manual worker path, and routes native binary delivery to meteor-native.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 22: direct Promise content versus a persistent binding
Prompt: "On Blaze 3.0.3, my helper reads a ReactiveVar and returns a Promise.
An old request resolves after the new request. Compare `{{loadProfile}}` with
`{{#let profile=loadProfile}}{{profile}}{{/let}}`. Neither helper writes any
other state. Does each need a generation token? What if it writes a shared
ReactiveVar instead?"
Pass if the agent distinguishes replaced direct-content Views from persistent
`#let` bindings, protects the binding and shared-state races, and does not
require a token solely for direct Promise content. Fail if it promises request
cancellation or general race immunity from direct mustache rendering.
## Case 23: removing jQuery on Blaze 3.0.3
Prompt: "Our Meteor 3.5.1 app resolves blaze 3.0.3. We removed both jquery
dependencies because our own code never calls $. Now the console starts with
'jQuery not found', followed by Blaze and meteorInstall errors. Fix startup."
Pass if the agent restores jQuery for the resolved runtime, diagnoses the
later errors as possible cascades, and makes native-backend adoption a
separate deliberate package upgrade. Fail if it changes Rspack loaders first
or claims a Meteor release alone selects the native backend.
## Case 24: native backend on the alpha
Prompt: "We deliberately pinned blaze and blaze-html-templates to
3.1.0-alpha.0. Can we remove the Meteor and npm jquery dependencies? We use
nested #if templates with delegated click selectors."
Pass if the agent recognizes the alpha's native capability and prerelease
status, audits other jQuery consumers before removal, and flags the alpha
selector defect with the fix included in 3.1.0-beta.0. It verifies the
resolved package set, startup and actual delegated events. Fail if it says
PR 513 is still open or considers absent console messages sufficient proof.
## Case 25: native backend on the beta with a jQuery widget
Prompt: "Our app resolves blaze 3.1.0-beta.0 and blaze-html-templates
3.1.0-beta.0. The console reports jQuery as the DOM backend. Can I remove it
now? A widget calls instance.$('.chart').addClass('ready')."
Pass if the agent explains that the beta includes the delegated-selector fix
but a loaded jQuery still selects that backend. It keeps jQuery until the
widget is ported, explains the array return type in native mode, then checks
both dependency sources, rendering, events and teardown. Fail if it promises
the jQuery method chain works on a native array or mandates a Meteor upgrade
without a package compatibility requirement.
## Case 26: helper shadows an included template
Prompt: "On Blaze 3.0.3, `<template name=\"page\">{{> panel}}</template>`
throws 'Expected template or null, found: true'. The panel template exists
and is imported. Template.page.helpers contains `panel: () => true`."
Pass if the agent selects `meteor-blaze`, identifies helper precedence in the
inclusion, and renames the helper without changing template loading or
installing packages. Fail if it treats the boolean as missing template data.
## Case 27: template instance lost after await
Prompt: "In a Blaze 3.0.3 async click handler, Template.instance() is present
before await Meteor.callAsync('save'), but null afterward. How do I safely
update this template's state after saving? The user can leave while it waits."
Pass if the agent retains the supplied `instance` or captures it before await,
handles the method rejection and possible destruction, and separates template
scope from Tracker computation scope. Fail if its repair is only
`Tracker.withComputation` or a post-await call to `Template.instance()`.
## Case 28: wrapped versus native drag event
Prompt: "On Blaze 3.0.3 with jQuery 3.7.1, event.dataTransfer is undefined in
dragstart. The same handler works on a 3.1.0-beta.0 app without jQuery. Make
the handler work in both, including synthetic events in tests."
Pass if the agent reads `(event.originalEvent || event).dataTransfer`, checks
for a missing transfer object, and explains the backend difference. Fail if
it always dereferences `originalEvent`, changes dependencies unnecessarily,
or assumes every synthetic event supplies a native DataTransfer.
## Case 29: Spacebars compiler signatures
Prompt: "Fix these independent Blaze template build errors: a checkbox with
`{{#if flag}}checked{{/if}}` in its start tag reports 'Reactive HTML attributes'
and 'BLOCKOPEN'; `class=\"small {{{raw}}}\"` reports 'TRIPLE template tag is not
allowed in an HTML attribute'; `<ul><li>a<li>b</ul>` reports 'Expected \"li\"
end tag'; and `<template name=\"let\">` reports 'Template can't be named'."
Pass if the agent selects `meteor-blaze`, uses a boolean named attribute or
dictionary, double braces in the attribute, explicit list-item closing tags,
and a non-reserved template name. It verifies compilation before runtime
debugging. Fail if it recommends new loaders, raw HTML attributes, or cache
deletion instead of repairing the syntax.
## Case 30: valid template syntax near miss
Prompt: "Should I rewrite `class=\"small {{#if flag}}on{{/if}}\"` because Blaze
rejects BLOCKOPEN in attributes? Also, does literal `import x from 'x'` inside
`<pre><code>` trigger module loading? These templates currently compile."
Pass if the agent preserves the valid built-in block inside the quoted value
and treats the import as literal text. Fail if it applies the start-tag block
restriction to every attribute value or changes build configuration.
## Case 31: with condition versus rendered data
Prompt: "On Blaze 3.1.0-beta.0, `{{#with load}}{{this}}{{else}}empty{{/with}}`
gets overlapping Promises. New resolves to 'new', then old resolves to 'old',
and the UI stays 'new'. Does that prove #with rejects stale requests? What if
the old Promise resolves to false instead?"
Pass if the agent distinguishes the inner data context from the persistent
condition, explains that a stale falsy resolution can switch to `else`, and
protects request ordering when correctness depends on that branch. Fail if
it promises immunity from the two-truthy experiment or insists stale truthy
data must always overwrite the new rendered data.
@@ -0,0 +1,52 @@
# Blaze jQuery and native DOM backends
Inspect `.meteor/versions`, `.meteor/packages`, `package.json`, the lockfile,
and any app-local Blaze overrides. Blaze is independently released; a Meteor
release label does not establish the installed backend capability.
| Resolved Blaze version | Backend decision |
|---|---|
| 3.0.x through 3.0.3 | jQuery is required at runtime. Retain the Meteor `jquery` wrapper and npm dependency unless another verified source supplies jQuery. Missing jQuery throws `jQuery not found` during initialization. |
| 3.1.0-alpha.0 | Prerelease introducing the optional native DOM backend. Removing jQuery is possible after resolving compatible Blaze packages, but this alpha has the native delegated-selector defect reported in issue 512. |
| 3.1.0-beta.0 | Prerelease retaining both backends and including the delegated-selector fix from PR 513. With jQuery loaded, Blaze selects it; without jQuery, Blaze selects native DOM. |
The reviewed Meteor Blaze scaffold includes `jquery` in `.meteor/packages`
and `jquery` in npm dependencies. Preserve generated dependencies until the
resolved runtime and other consumers support their removal.
## Removing jQuery deliberately
1. On Blaze 3.0.x, retain or restore jQuery first. Later `Blaze`,
`meteorInstall`, or `require` errors can be consequences of failed startup;
diagnose the earliest error before changing bundler configuration.
2. If native mode is required, choose compatible Blaze packages explicitly.
Keep prerelease adoption deliberate, verify the resolved `blaze` and
`blaze-html-templates` versions, and check their Meteor compatibility.
A Meteor upgrade is needed only if the selected packages require it.
3. Audit application code, Atmosphere packages and DOM widgets for `$`,
`jQuery` and `instance.$()` calls. In native mode, `instance.$()` returns
an array of elements, not a jQuery object. Prefer `instance.find` /
`instance.findAll` for backend-independent lookup; port jQuery methods and
widget cleanup before removal. Keep jQuery when a required consumer needs it.
4. After the compatible runtime is resolved, remove the direct dependencies
with `meteor remove jquery` and the project's npm/pnpm/Yarn uninstall
command. Preserve the authoritative lockfile. Check transitive dependencies
and global imports that might still supply jQuery.
5. Restart and inspect the console. The 3.1 prereleases log
`[Blaze] jQuery ... detected as DOM backend` and its load source when using
jQuery. Their absence is only a clue: verify successful startup, template
rendering, subscriptions, delegated events and widget teardown. Do not
make application behavior depend on private `_DOMBackend` fields.
Do not describe PR 513 as open: it merged on September 14, 2026, and the
September 23 beta history includes it. An alpha deployment with missing
delegated events needs the compatible fixed package set and a reproduction
of its selectors, not an assumption that removing jQuery was successful.
## Evidence
- [Stable runtime initialization](https://github.com/meteor/blaze/blob/e700f2d749ed037afae14dc3f055b9e52d5de375/packages/blaze/dombackend.js)
- [Alpha and beta release history](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/HISTORY.md)
- [Beta backend selection](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/blaze/dombackend.js)
- [Scaffold package declarations](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/tools/static-assets/skel-blaze/.meteor/packages)
- [Scaffold npm dependencies](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/tools/static-assets/skel-blaze/package.json)
@@ -134,6 +134,12 @@ from a route, method result, or stored user data.
Scope component lookup to `this.find`, `this.findAll`, or `this.$`. Initialize
imperative libraries only after the relevant nodes exist, then destroy them:
`this.$` has jQuery semantics only with the jQuery backend. Native DOM support
starts in Blaze 3.1.0-alpha.0 (prerelease), where it returns an array. See
[backend selection](jquery-backends.md) before porting widgets or removing
jQuery, and [runtime traps](template-diagnostics.md#runtime-traps) for event
wrappers and retaining the handler's instance across `await`.
```javascript
import { Tracker } from "meteor/tracker";
@@ -1,10 +1,10 @@
# Blaze PWA scaffold
Meteor 3.6-beta.0 supplies a Blaze/Rspack PWA skeleton with a dependency-free
Meteor 3.6 supplies a Blaze/Rspack PWA skeleton with a dependency-free
service worker, manifest, icons, and offline page:
```bash
meteor create --release 3.6-beta.0 --pwa my-pwa
meteor create --release 3.6.1 --pwa my-pwa
cd my-pwa
meteor
```
@@ -22,7 +22,8 @@ The authoritative version record is
## Lookup and explicit context
For `{{name}}`, current Blaze guidance resolves the name in this order:
Blaze resolves a name in this order; the template-name step applies to
inclusions such as `{{> name}}`:
1. Helper on the current template.
2. Current `#let` or `#each item in items` binding.
@@ -49,6 +50,11 @@ Template.taskRow.events({
Prefer `task=task` over replacing the entire child context with `task`. Avoid
`../` lookups in reusable templates. Pass a value or callback explicitly.
A local helper named `panel` also wins over the template in `{{> panel}}`.
If it returns `true`, inclusion throws `Expected template or null, found: true`.
Rename the helper, for example `isPanel` or `panelData`. See
[template diagnostics](template-diagnostics.md#runtime-traps).
## Promise behavior
| Spacebars location | Pending or rejected behavior | Better choice when states differ |
@@ -57,7 +63,7 @@ Prefer `task=task` over replacing the entire child context with `task`. Avoid
| `{{#if promise}}` or `{{#unless promise}}` | Renders neither branch until resolution | Bind with `#let` and test state |
| `{{#each value in promise}}` | Renders `else` for pending, rejected, or empty | Bind with `#let` to distinguish them |
| `{{#with promise}}` | Waits for resolution | Bind with `#let` when errors need UI |
| Direct Promise content | May recreate Views and flicker | Unwrap once with `#let` |
| Direct Promise content, such as `{{helper}}` returning a Promise | Recreates its awaiting View on reactive rerender; a replaced View's late result does not replace the new content | Use `#let` for explicit states, with separate ordering protection for its persistent binding |
`#let` unwraps each Promise and exposes `@pending`, `@rejected`, and
`@resolved`. Each helper accepts binding names. With no names, it checks all
@@ -118,9 +124,30 @@ does not make an external Promise reactive.
## Prevent stale Promise results
Spacebars bindings retain the latest value that resolved, not the value from
the latest Promise that was started. A slow request for an old reactive input
can overwrite a newer result. Own the request state when ordering matters:
Persistent `#let` bindings and `#if` / `#unless` conditions can accept an old
Promise's late result. Each pending Promise can write to the same binding;
resolution order need not match request order. For conditions, use opposite
truth values to expose the race.
`#with` (Promise support from Blaze 3.0.1) combines a condition and a data
context. Its condition can also accept a late stale falsy result and show
`else` after a newer truthy result. Two truthy results need not reproduce that
branch race: the inner context can still display the newer data. Do not claim
every `#with` result behaves identically to `#let`; test branch selection and
rendered data separately. See
[`Spacebars.With`](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/spacebars/spacebars-runtime.js).
Direct Promise content in `{{helper}}` has a different lifetime: reactive
rerender replaces its `_Await` View and destroys the old View. Its late result
does not overwrite the new content. Do not add a generation token solely
because this direct helper returns a Promise. This does not cancel requests
or protect side effects, shared state, or a helper that writes a ReactiveVar.
This distinction is verified in Blaze 3.0.3 and 3.1.0-beta.0 through
[`_createBinding` / `_Await`](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/blaze/builtins.js),
[`_materializeDOM`](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/blaze/materializer.js),
and [View replacement](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/blaze/view.js).
Own persistent request state when ordering matters:
```javascript
import { Meteor } from "meteor/meteor";
@@ -0,0 +1,67 @@
# Blaze template diagnostics
Read the earliest error and resolved Blaze/templating versions before changing
packages or bundler configuration. Compilation failures stop startup; runtime
failures happen after the template has compiled.
## Compile failures
| Error signature | Failing pattern | Repair |
|---|---|---|
| `Reactive HTML attributes must either have a constant name` and `BLOCKOPEN is not allowed here` | `<input type="checkbox" {{#if flag}}checked{{/if}}>` | Use `<input type="checkbox" checked="{{flag}}">` with a boolean helper, or a single attribute-dictionary helper. |
| `TRIPLE template tag is not allowed in an HTML attribute` | `<div class="small {{{raw}}}">` | Use `class="small {{raw}}"`. Triple braces are for trusted HTML content, not attribute interpolation. |
| `Expected "li" end tag` | `<ul><li>a<li>b</ul>` | Close both items: `<ul><li>a</li><li>b</li></ul>`. Spacebars requires the closing tags even where browser HTML parsing permits omission. |
| `Template can't be named "let"` | `<template name="let">` | Rename the template and its references to a non-reserved name, such as `letPanel`. |
A built-in block inside a quoted attribute value is valid:
`class="small {{#if flag}}on{{/if}}"`. Literal `import` text inside
`<pre><code>` is also valid; do not treat it as an executed JavaScript import.
During `meteor run`, `=> Errors prevented startup:` and the browser's
`Meteor App - Error` page can report the same compile failure. The development
proxy displays tool logs in that page; its presence does not establish a
browser runtime failure. Read the terminal's file, line and first compiler
message, fix that template, then verify compilation and rendering. For an
unknown failure outside template compilation, use `meteor-debugging`.
## Runtime traps
| Symptom | Cause | Repair |
|---|---|---|
| `{{> panel}}` throws `Expected template or null, found: true` despite a defined `panel` template | The current template's `panel` helper wins before template-name lookup and returns a boolean. | Rename the helper to `isPanel` or `panelData`; keep the inclusion targeting the template. Do not reinstall templating or convert the boolean to a template. |
| `Template.instance()` is `null` after `await` in an event handler | Blaze restores its synchronous template-instance scope when the handler returns its Promise. Tracker computation scope is a separate concern. | Use the handler's `instance` argument, or capture the instance before awaiting. Before DOM work, account for a template destroyed while the request was pending. |
| `event.dataTransfer` is missing in a drag handler with jQuery loaded | jQuery 3.7.1 wraps native events without copying `dataTransfer`; native Blaze mode passes the native event. | Read `(event.originalEvent || event).dataTransfer`. Handle synthetic events that have no transfer object. Do not require or remove jQuery solely to access this property. |
Keep the handler's instance and own method errors:
```javascript
Template.editor.events({
async "click .save"(event, instance) {
event.preventDefault();
try {
await Meteor.callAsync("items.save");
if (!instance.view.isDestroyed) instance.saved.set(true);
} catch (error) {
if (!instance.view.isDestroyed) instance.saveError.set(error);
}
},
"dragstart .item"(event) {
const transfer = (event.originalEvent || event).dataTransfer;
if (transfer) transfer.setData("text/plain", this._id);
},
});
```
The example assumes `saved` and `saveError` ReactiveVars initialized in
`onCreated`. Initialize error/success state for each new save as appropriate
for the UI; the example isolates instance retention and rejection ownership.
## Evidence
- [Attribute template-tag validation](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/spacebars-compiler/templatetag.js)
- [HTML closing-tag validation](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/html-tools/parse.js)
- [Reserved template names](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/templating-tools/compile-tags-with-spacebars.js)
- [Helper and template lookup](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/blaze/lookup.js)
- [Template-instance scope and event arguments](https://github.com/meteor/blaze/blob/068f0c09f4851bdc150e2e30f1a09bae3700914b/packages/blaze/template.js)
- [jQuery 3.7.1 event wrapping](https://github.com/jquery/jquery/blob/3.7.1/src/event.js)
- [Meteor development error page](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/tools/runners/run-proxy.js)
@@ -16,7 +16,7 @@ metadata:
area: ops
tagline: "Install, locate, repair, reset, or cleanly reinstall the Meteor 3 CLI with correct version and PATH handling."
bundle: ["essentials", "migration", "fullstack", "ops", "blaze", "react"]
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -50,7 +50,7 @@ installer permanently to global npm packages.
`.meteor/release`.
Do not reinstall the user-wide CLI merely because a project uses another
release.
Before selecting Meteor 3.6-beta.3 for existing local data, read
Before selecting Meteor 3.6 for existing local data, read
[local database preparation](references/local-database-upgrade.md).
Its bundled MongoDB 8 requires host checks and MongoDB 7 FCV preparation
under the previous release before the new release starts.
@@ -202,15 +202,15 @@ are unused.
## Case 20: Retained local data before 3.6
Prompt: "We want to update our Meteor 3.5.2 app to 3.6-beta.3. Its local MongoDB 7 has data we need to keep and FCV is 6.0. Give the ordered commands and checks before the first beta.3 run. Can reverting Meteor undo this?"
Prompt: "We want to update our Meteor 3.5.2 app to 3.6. Its local MongoDB 7 has data we need to keep and FCV is 6.0. Give the ordered commands and checks before the first 3.6 run. Can reverting Meteor undo this?"
Pass if the agent requires backup/recovery first, checks server version and FCV under the previous release, sets FCV 7.0 on the MongoDB 7 primary with confirm, verifies, stops the app before updating, and distinguishes fresh/external databases. Rejects release rollback as database rollback.
Fail if it starts beta.3 before preparation, sets FCV using MongoDB 8, recommends deleting retained data, prints connection credentials, or promises FCV 7.0 makes a MongoDB 8 binary downgrade supported.
Fail if it starts 3.6 before preparation, sets FCV using MongoDB 8, recommends deleting retained data, prints connection credentials, or promises FCV 7.0 makes a MongoDB 8 binary downgrade supported.
## Case 21: Bundled database host boundary
Prompt: "Beta.3 local Mongo fails on Linux x86_64 with glibc 2.31. A teammate has Windows 10. Would reinstalling Meteor or using our external MONGO_URL solve the requirements? We cannot upgrade either OS today."
Prompt: "3.6 local Mongo fails on Linux x86_64 with glibc 2.31. A teammate has Windows 10. Would reinstalling Meteor or using our external MONGO_URL solve the requirements? We cannot upgrade either OS today."
Pass if the agent identifies glibc 2.34+ x86_64, required OpenSSL 3/libcurl libraries and Windows 11/Server 2022 local DB requirements. Explains external DB avoids the bundled server but not Meteor/Node/provider requirements; preserves constraints.
@@ -222,4 +222,39 @@ Prompt: "Our app must stay on Meteor 3.5.2 with a fresh local database. Do the n
Pass if the agent keeps the constrained release and its actual database requirements; distinguishes fresh data from retained data and future 3.6 preparation.
Fail if it forces beta.3, runs FCV commands without identifying a matching server, or imposes the beta.3 bundled database on 3.5.2.
Fail if it forces 3.6, runs FCV commands without identifying a matching server, or imposes the 3.6 bundled database on 3.5.2.
## Case 23: Preserve local MongoDB 7 data before Meteor 3.6
Prompt: "We are moving a local app from MongoDB 7 to Meteor 3.6. FCV is 6.0 and the local data matters. What should happen before meteor update? Is reset --db a fix?"
Pass if the agent backs up first, checks server version/FCV on the previous release, prepares FCV 7.0 on the MongoDB 7 primary only within an authorized upgrade, verifies it, stops the app, then updates. Fail if it opens old data with 8 first, deletes it, treats a release downgrade as DB rollback or silently runs an FCV mutation.
## Case 24: Bundled platform versus external database
Prompt: "Our Windows 10 workstation cannot start the bundled database after upgrading to Meteor 3.6. Would reinstalling help? A second app stays on an older release, and production uses external MONGO_URL."
Pass if the agent identifies the local MongoDB 8 Windows 11/Server 2022 x64 floor, recommends a compatible host/release rather than identical reinstall, and separately checks earlier-release and external runtime/provider requirements. Fail if it mandates upgrading every external database to 8 or deletes local data.
## Case 25: Installer host versus 3.6 runtime
Prompt: "Our workstation has host Node 24 and we are preparing a Meteor 3.6 app whose bundle reports Node 26.13.0. Must we install host Node 26 just to use the npm Meteor installer?"
Pass if the agent distinguishes the reviewed host Node 24+ installer prerequisite from the bundled Node 26 runtime, checks the selected installer version's engines and project pin, and does not mandate a host upgrade solely from the runtime table.
## Case 26: Archive containment failure
Prompt: "On Meteor 3.6.1 an archive extraction rejects a link that escapes the package root. Should I turn off the check, unpack with another tar command or delete ~/.meteor? Does the framework tag prove the npm installer was republished?"
Pass if the agent: Keeps containment checks, preserves archive/source/error evidence, checks the intended trusted artifact and tool version before narrow repair, avoids broad deletion and separates npm installer publication/version from the framework tag.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 27: Pinned security runtime and host prerequisite
Prompt: "In our Meteor 3.4.4, 3.5.5 and 3.6.1 projects, meteor node -v reports 22.23.5, 24.22.3 and 26.13.1 respectively. A colleague wants to replace them all with host Node 26. Which runtime evidence should we use, and must host Node be 26 for every npm installer?"
Pass if the agent: Uses each project's reported 22.23.5/24.22.3/26.13.1 runtime, checks project release and selected installer engines separately, and preserves older release pins instead of forcing every project to Node 26.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -11,14 +11,16 @@ Check the host before selecting a command:
| Existing project | Read `.meteor/release`. The project release is separate from the npm installer package version. |
| Existing executable | Run `meteor --version`. Do not reinstall only to replace a project-specific release. |
Current Meteor 3.5 installation documentation requires host Node 24 or newer.
The reviewed installation documentation requires host Node 24 or newer.
Meteor bundles its own runtime; inspect it with `meteor node -v` in the
target project. Its version does not set the installer's host prerequisite.
Earlier Meteor 3 installers can have earlier host requirements. Preserve the
selected release when the user supplied one instead of silently choosing the
latest installer.
## Install
For Meteor 3.6-beta.3's bundled MongoDB 8, also check
For Meteor 3.6's bundled MongoDB 8, also check
[local database platform requirements](local-database-upgrade.md#bundled-mongodb-host-requirements).
These are separate from host npm-installer and bundled Node requirements.
@@ -1,6 +1,6 @@
# Local database preparation for Meteor 3.6
Meteor 3.6-beta.3 bundles MongoDB 8.0.29. Inspect the selected release and
Meteor 3.6 bundles MongoDB 8.0.29. Inspect the selected release and
actual database version before startup; earlier releases retain their own
database requirements.
@@ -48,8 +48,8 @@ set's primary and repeat the FCV check:
db.adminCommand({ setFeatureCompatibilityVersion: "7.0", confirm: true })
```
Exit the shell and stop the app, then run `meteor update --release 3.6-beta.3`
to resolve the release and compatible packages. Do not start beta.3 before
Exit the shell and stop the app, then run `meteor update --release 3.6`
to resolve the release and compatible packages. Do not start the new release before
preparation. Reverting Meteor is not a database rollback. Keeping FCV `7.0`
does not guarantee a supported MongoDB 8-to-7 binary downgrade; the
[MongoDB upgrade guidance](https://www.mongodb.com/docs/v8.0/release-notes/8.0-upgrade-replica-set/#downgrade-consideration)
@@ -60,7 +60,7 @@ requires support assistance. Use the verified pre-upgrade recovery plan.
## Bundled MongoDB host requirements
| Host for beta.3's local database | Requirement |
| Host for the bundled MongoDB 8 database | Requirement |
|---|---|
| Linux x86_64 | glibc 2.34+ |
| Linux ARM64 | glibc 2.35+ |
@@ -31,7 +31,9 @@ confirmation before running it.
|---|---|---|
| `meteor: command not found`, but the executable exists | Open a new shell and repair the exact PATH entry. If a Node version manager was just switched, rerun the npm installer under that host Node version. | Do not suggest deleting the user Meteor directory unless it is also corrupt. |
| Installer download, update, or extraction fails | Check network, firewall, proxy, certificate, disk, and permissions. On Windows, check Developer Mode or elevated symlink support. | Retrying a clean install before fixing the cause usually repeats the failure. Suggest reinstall only if broken partial user-wide state remains after the cause is fixed. |
| Archive extraction rejects a traversal path, escaping link, or multiple top-level roots | Preserve the failing archive source, error and selected tool version. Meteor 3.4.4, 3.5.5 and 3.6.1 harden tool extraction; verify/re-fetch the intended artifact from its trusted source. Inspect the actual npm installer version separately. | Fix the archive or download cause. Do not disable path/link validation, extract manually outside the checked path, or delete the user-wide installation to evade rejection. A framework tag does not prove an npm installer publication. |
| One application has stale generated build, resolver, bundler, or type state, while the CLI and other projects work | Suggest `meteor reset`, explain that Meteor 3 preserves the local database, and wait for confirmation. | Do not suggest a user-wide reinstall. |
| Local Mongo fails after a Meteor 3.6/MongoDB 8 upgrade | Check OS/shared libraries, backup and the pre-upgrade MongoDB 7 FCV using [local database preparation](local-database-upgrade.md). | Do not delete data or reinstall the CLI as an FCV migration. |
| A disposable local Mongo database itself must be recreated | Inspect database, port, disk, and permission evidence first. Suggest `meteor reset --db` with an explicit data-loss warning. | This is not a global reinstall case and does not affect an external `MONGO_URL`. |
| `meteor node` reports a Node version incompatible with the pinned Meteor release | Run from the project, fetch or select its pinned release, recheck, then inspect the resolved launcher, PATH, and symlink. Outside a project or inside a built bundle, verify release selection before treating the Node version as corruption. | Suggest reinstall only if the mismatch remains user-wide after release selection and launcher checks. |
| An expected release is unknown or catalog JSON/SQLite metadata is stale, while tool files still work | Verify network access, proxy settings, and that offline catalog mode is disabled. Try a normal online project command or update first. | Consider targeted catalog-metadata rebuilding before a full reinstall. Require confirmation for either cleanup. |
@@ -13,7 +13,7 @@ meteor create --release <release> <path>
Update an existing project and its pin:
Before selecting Meteor 3.6-beta.3 for a project with local data, complete
Before selecting Meteor 3.6 for a project with local data, complete
[MongoDB 7 to 8 preparation](local-database-upgrade.md) under the previous
release. The same precaution applies to an explicit `--release` run override.
@@ -32,6 +32,14 @@ Do not edit `.meteor/release` alone as an upgrade workflow. `meteor update`
also resolves compatible core package versions and reports constraint
failures.
## Preserve local data before MongoDB 8
Before selecting Meteor 3.6 for retained local data,
follow [local database preparation](local-database-upgrade.md): verify backup
and recovery, inspect the previous server and FCV, complete required MongoDB
7 preparation before updating, and distinguish local from external databases.
Do not use `meteor reset --db` to migrate data you intend to keep.
## Reset project state
For a build, bundler, generated-type, or resolver-cache failure confined to one
@@ -63,7 +63,7 @@ builds, or deployment.
Atmosphere package and `meteor npm install` for an npm package. Do not invent
a version constraint. After installation, confirm the resolved version in
`.meteor/versions` or the npm lockfile.
For Meteor 3.6-beta.0 interactive or Git-based Atmosphere adoption, read
For Meteor 3.6 interactive or Git-based Atmosphere adoption, read
[package installation](references/package-installation.md). Use explicit
registry names in scripts; preserve the npm workspace's selected manager.
8. Apply every relevant required check from the selected package guide. Do not
@@ -146,14 +146,14 @@ client simulation performs a real database transaction.
## Case 14: interactive versus scripted adoption
Prompt: "On Meteor 3.6-beta.0 I want to search for an Atmosphere package locally and then install it in CI. Should both environments run meteor add --search?"
Prompt: "On Meteor 3.6 I want to search for an Atmosphere package locally and then install it in CI. Should both environments run meteor add --search?"
Pass if the agent: Uses the TTY picker only interactively and explicit inspected registry names for CI, retaining owner/version/security checks.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 15: Git package adoption provenance
Prompt: "Adopt a reviewed Atmosphere package from a Git monorepo on Meteor 3.6-beta.0. We have its commit SHA and package subdirectory, but the destination already exists and its Package.describe name differs from the repo name."
Prompt: "Adopt a reviewed Atmosphere package from a Git monorepo on Meteor 3.6. We have its commit SHA and package subdirectory, but the destination already exists and its Package.describe name differs from the repo name."
Pass if the agent: Uses the source/ref/subdirectory/destination options, inspects and preserves the existing target without automatic force, reads the registered name, explains local override and reproducible provenance, and requires integration checks.
Fail if it contradicts these boundaries or invents unsupported commands.
@@ -6,7 +6,7 @@ or a successful clone does not establish compatibility or trust.
## Registry discovery and scripts
Meteor 3.6-beta.0 adds an interactive picker to bare `meteor add`,
Meteor 3.6 adds an interactive picker to bare `meteor add`,
`meteor add --search <term>`, and bare `meteor remove`. These require a TTY.
Do not use interactive commands in CI or combine `--search` with positional
package names or Git-source flags.
@@ -25,7 +25,7 @@ Review `.meteor/packages` and `.meteor/versions` after the solver runs.
## Git-hosted Atmosphere packages
Meteor 3.6-beta.0 accepts a Git URL or `owner/repo` as the sole positional
Meteor 3.6 accepts a Git URL or `owner/repo` as the sole positional
argument to `meteor add`, or an explicit `--from` source. This installs a
local Atmosphere package, not an npm dependency or a published fork.
+4 -3
View File
@@ -3,7 +3,8 @@ name: meteor-debugging
description: >
Use when diagnosing an unexplained failure in a Meteor 3 application before
the failing layer or fix is known. Triggers on server crashes, client-only
errors, stuck subscriptions, DDP or WebSocket disconnects, Minimongo/server
errors, Errors prevented startup, Meteor App - Error, stuck subscriptions,
DDP or WebSocket disconnects, Minimongo/server
data mismatches, hanging or flaky tests, slow builds, --inspect, console.log,
.only, Playwright traces, Instrumentation lifecycle events, or requests to debug a Meteor app. Use this skill
when evidence must distinguish Meteor tool, server, client, data, test,
@@ -17,7 +18,7 @@ metadata:
area: ops
tagline: "Diagnose Meteor 3 failures across builds, server/client runtime, DDP, Mongo, tests, browsers, mobile, and production."
bundle: ["essentials", "fullstack", "ops"]
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -74,7 +75,7 @@ can reject the current hypothesis.
full user documents.
- Treat `console.log` as temporary instrumentation. Use `meteor/logging` or the
application's established structured logger for evidence that must persist.
- For server lifecycle evidence on Meteor 3.6-beta.1 through beta.3, use the optional
- For server lifecycle evidence on Meteor 3.6, use the optional
`instrumentation` package through [Logs and instrumentation](references/logs-and-instrumentation.md).
Earlier releases retain targeted logs; listeners observe, not authorize.
- Use `.only` only as temporary local test focus when the runner and execution
@@ -14,6 +14,27 @@ Meteor tool or bundler. Start with:
3. `meteor run --verbose` for build output hidden by normal formatting.
4. One profile or build-specific log source selected below.
## Blaze compile errors in the browser
During `meteor run`, the development proxy's `Meteor App - Error` page
includes the tool log. A matching `=> Errors prevented startup:` message
in the terminal identifies a build failure, even when first seen in the
browser. Read its first file/line diagnostic before using browser tracing or
changing Rspack configuration.
| Compiler signature | First repair through `meteor-blaze` |
|---|---|
| `Reactive HTML attributes must either have a constant name` / `BLOCKOPEN is not allowed here` | Replace a block in a start tag with a named boolean attribute or one attribute-dictionary helper. |
| `TRIPLE template tag is not allowed in an HTML attribute` | Use ordinary double-brace attribute interpolation. |
| `Expected "li" end tag` | Close the list items explicitly. |
| `Template can't be named "let"` | Rename the reserved template and its references. |
Built-in blocks inside quoted attribute values are valid; literal `import`
text inside `<pre><code>` is not an executed module import. Hand confirmed
Spacebars errors to `meteor-blaze`; keep loader/configuration failures with
the build owner. Verify startup and rendering after the template repair.
Evidence: [development proxy error page](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/tools/runners/run-proxy.js).
## Profile by Meteor release
Meteor 3.0 and 3.1 use the earlier build timing output:
@@ -74,9 +95,54 @@ version and watched paths. Meteor 3.5.2 stops watching immutable package
warehouse files. Check that fix before broad application ignore rules or
arbitrary OS-limit changes; locally developed packages still need watching.
## Meteor 3.6 beta dependency and development boundaries
## HMR rejection and missing Rspack assets
These changes start in Meteor 3.6-beta.0 and its paired packages. For earlier
On Meteor 3.4.4, 3.5.5 and 3.6.1, rejected HMR registration requires checking
that the tool and generated client share the current app ID and run secret,
including `web.cordova`. Rebuild/reconnect a stale client; do not log secrets,
trust Origin alone or bypass the handshake. Malformed messages can close this
development socket; distinguish it from DDP and native hot code push. Earlier
tools lack this hardening. Rspack's non-native bootstrap restriction still applies.
For Rspack static assets on older release lines, inspect these backports:
| Meteor | `rspack` | `webapp` |
|---|---|---|
| 3.4.4 | 1.1.1 | 2.1.4 |
| 3.5.5 | 1.3.2 | 2.3.1 |
These pairs retain Rspack 1 and fix stale asset resolution and response headers
after rebuilds. Missing assets return a non-cacheable 404 instead of app HTML.
Check that the asset exists, then compare URL/prefix, status, content type and
cache headers before/after rebuilding. For older packages, inspect a compatible
patch before changing bundlers, disabling HMR or editing generated manifests.
## SWC native binding cache
For a resolved SWC carrier distribution that unpacks its native addon,
distinguish `ERR_SWC_NATIVE_CACHE` from a stale Meteor or
Rspack build cache. In Linux containers, inspect the effective UID, cache/home
ownership and mount options. Use an absolute cache directory owned and writable
by that container user on an executable filesystem, for example:
```bash
SWC_NATIVE_BINDING_CACHE=/tmp/meteor-swc-native meteor
```
Verify the actual filesystem permits addon execution; a writable `noexec`
mount still fails. Do not disable ownership/integrity checks, run the app as
root merely to avoid permissions, or clear every build/database cache. A
relative override is invalid. The 3.6 dependency minimum `@swc/core@1.16.2`
does not establish this carrier behavior; inspect the actual loaded version
and distribution before selecting the cache branch. On an older SWC distribution without this carrier,
inspect its original native binary/platform error instead of assuming the same
cache contract. See the modern build skill's SWC cache guidance.
After correcting the cache, rerun the same binding load/build path as the
effective container user to confirm the failure is resolved.
## Meteor 3.6 dependency and development boundaries
These changes start in Meteor 3.6 and its paired packages. For earlier
releases, reproduce on that release and compare a disposable paired upgrade
only when authorized; do not assume the new cache or logging behavior exists.
@@ -84,36 +150,36 @@ only when authorized; do not assume the new cache or logging behavior exists.
|---|---|
| A local Atmosphere package reinstalls npm dependencies on its first 3.6 build | Inspect its committed `.npm/package/npm-shrinkwrap.json`. The tool upgrades this package-specific format to `lockfileVersion: 5`, recording `meteorNpmDependencies`. One reinstall/rewrite can be expected; repeat identical builds to distinguish migration from churn. Keep exact pins and review the shrinkwrap diff, not generated `node_modules`. |
| An exact Git npm dependency is repeatedly fetched | Compare the declared `git+https://...#v<semver>` spec, recorded source, and installed resolved tree. The cache requires agreement; it does not make branch refs immutable. Do not change the dependency to a floating ref as a cache fix. |
| Windows fails installing the isolated npm `argon2` package | Capture the install command and tool/dev-bundle version. The 3.6 beta dev bundle adds `cross-env` to that isolated environment. Check the paired tool fix before changing application accounts packages or installing global helpers. |
| Rspack assets return 502 or the development WebSocket closes | In the beta integration, proxy logs identify the assets versus WS scope, error code, method, request, and target. Identical failures are deduplicated within each proxy for five seconds; fewer log lines do not mean requests recovered. Every failed HTTP request still gets 502 and failed sockets close. Check the Rspack child/target first; this is not evidence of a DDP subscription defect. |
| HMR client is absent from a native app or `meteor build` output | The beta bootstrap is for development web app runs, not tests, native targets, or build output, even with `NODE_ENV=development`. Verify command and target before adding a refresh client manually. |
| SWC reports a temporary cache-write failure during cleanup | The beta catches asynchronous writes; missing temporary paths (`ENOENT`/`ENOTDIR`) are non-fatal and other errors warn in verbose mode. Reproduce compilation separately. Do not generalize this handling to application filesystem errors or real compiler failures. |
| Windows fails installing the isolated npm `argon2` package | Capture the install command and tool/dev-bundle version. The 3.6 dev bundle adds `cross-env` to that isolated environment. Check the paired tool fix before changing application accounts packages or installing global helpers. |
| Rspack assets return 502 or the development WebSocket closes | In the Meteor 3.6 integration, proxy logs identify the assets versus WS scope, error code, method, request, and target. Identical failures are deduplicated within each proxy for five seconds; fewer log lines do not mean requests recovered. Every failed HTTP request still gets 502 and failed sockets close. Check the Rspack child/target first; this is not evidence of a DDP subscription defect. |
| HMR client is absent from a native app or `meteor build` output | The Meteor 3.6 bootstrap is for development web app runs, not tests, native targets, or build output, even with `NODE_ENV=development`. Verify command and target before adding a refresh client manually. |
| SWC reports a temporary cache-write failure during cleanup | Meteor 3.6 catches asynchronous writes; missing temporary paths (`ENOENT`/`ENOTDIR`) are non-fatal and other errors warn in verbose mode. Reproduce compilation separately. Do not generalize this handling to application filesystem errors or real compiler failures. |
Meteor 3.6-beta.1 adds these separate fixes; beta.0 does not have them:
Meteor 3.6 includes these fixes:
| Symptom | Version evidence and verification |
|---|---|
| TypeScript/Rspack server repeatedly restarts without an app edit | Check `rspack@1.4.0-beta360.1` and `@meteorjs/rspack@3.0.0-beta.2` together. The paired fix avoids runtime-entry rewrites for unchanged hashes and generated-only churn. A successful changed source rebuild should restart once; a failed compile preserves the last known-good entry. Observe startup count, a real source edit, compile failure and recovery before changing watcher settings. Do not edit `_build` markers or ignore the handoff directory. |
| `--port localhost:3060` or `--port http://localhost:3060/` leads to `ERR_SOCKET_BAD_PORT` / `NaN` | Check `tools-core@1.4.0-beta360.1`, which extracts the numeric port before deriving the Rspack port. Preserve valid host/URL syntax on the fixed version. On older packages, an explicit numeric app port plus the required bind configuration can isolate the cause; do not reinstall the CLI or rewrite proxies first. |
| `--inspect` dumps full Rspack configuration repeatedly | The beta.1 npm integration prints full configuration only in verbose mode. Inspector flags target the server; use `meteor run --verbose` when build configuration is the evidence needed. |
| TypeScript/Rspack server repeatedly restarts without an app edit | Check `rspack@1.4.0` and `@meteorjs/rspack@3.0.0` together. The paired fix avoids runtime-entry rewrites for unchanged hashes and generated-only churn. A successful changed source rebuild should restart once; a failed compile preserves the last known-good entry. Observe startup count, a real source edit, compile failure and recovery before changing watcher settings. Do not edit `_build` markers or ignore the handoff directory. |
| `--port localhost:3060` or `--port http://localhost:3060/` leads to `ERR_SOCKET_BAD_PORT` / `NaN` | Check `tools-core@1.4.0`, which extracts the numeric port before deriving the Rspack port. Preserve valid host/URL syntax on the fixed version. On older packages, an explicit numeric app port plus the required bind configuration can isolate the cause; do not reinstall the CLI or rewrite proxies first. |
| `--inspect` dumps full Rspack configuration repeatedly | The 3.6 npm integration prints full configuration only in verbose mode. Inspector flags target the server; use `meteor run --verbose` when build configuration is the evidence needed. |
Native declaration generation is separate from transpilation. On beta.1,
Native declaration generation is separate from transpilation. On Meteor 3.6,
`meteor types` can skip for a direct `zodern:types` dependency or missing root
config; `meteor run` does not generate native declarations. Diagnose provider
selection and compiler paths before resetting caches. For an upgrade, use
`migrate-to-meteor-3`'s TypeScript reference.
The following additional 3.6 fixes are verified on beta.3; beta.1 lacks them:
Meteor 3.6 also includes the following fixes:
For an upgrade comparison, use fresh disposable local data or complete the
retained-data preflight under the previous release before beta.3 starts.
retained-data preflight under the previous release before 3.6 starts.
| Signature | Evidence and next decision |
|---|---|
| Tool crashes or hangs during rapid source edits while IPC refresh is pending | Capture restart order and `inter-process-messaging`/tool versions. Beta.3 tolerates shutdown races and does not block future restarts waiting for an old child. Verify rapid server edits, a later client refresh, normal startup and shutdown. Do not suppress every IPC exception or disable hot code push. |
| Rspack development script fails only under a `ROOT_URL` path prefix | Inspect the script URL, prefix and selected architecture. Beta.3's `boilerplate-generator@2.2.0-beta360.3` includes the prefix in the dev script URL. Preserve the intended prefix and test page load, rebuild and production chunks separately; the dev-script fix is not a generic production chunk fix. |
| Root `test/` directory or an ignore exception yields zero Rspack tests | Inspect user rules, actual entry graph and suite counts. Beta.3's paired tool/integration separates root-scoped generated excludes from user rules and refreshes source-scan rules. Use `meteor-testing` for selection; do not ignore `_build` or edit generated tests. |
| Tool crashes or hangs during rapid source edits while IPC refresh is pending | Capture restart order and `inter-process-messaging`/tool versions. 3.6 tolerates shutdown races and does not block future restarts waiting for an old child. Verify rapid server edits, a later client refresh, normal startup and shutdown. Do not suppress every IPC exception or disable hot code push. |
| Rspack development script fails only under a `ROOT_URL` path prefix | Inspect the script URL, prefix and selected architecture. 3.6's `boilerplate-generator@2.2.0` includes the prefix in the dev script URL. Preserve the intended prefix and test page load, rebuild and production chunks separately; the dev-script fix is not a generic production chunk fix. |
| Root `test/` directory or an ignore exception yields zero Rspack tests | Inspect user rules, actual entry graph and suite counts. 3.6's paired tool/integration separates root-scoped generated excludes from user rules and refreshes source-scan rules. Use `meteor-testing` for selection; do not ignore `_build` or edit generated tests. |
For local MongoDB startup immediately after a beta.3 upgrade, inspect server
For local MongoDB startup immediately after a 3.6 upgrade, inspect server
version/FCV and host libraries before cache cleanup. Use
`meteor-cli-installation`'s database preparation reference; `meteor reset`
preserves the incompatible database and `--db` deletes it.
@@ -125,6 +191,7 @@ Do not clear every cache, reset the database, or reinstall all dependencies
as the first diagnostic action.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/cli/index.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/generators/changelog/versions/3.5.2.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/generators/changelog/versions/3.6.0.md
@@ -58,24 +58,41 @@ the first divergence is a change-stream observer replay. `mongo@2.5.1` in
3.5.2 fixes replay of events already represented by a causal primary snapshot.
Use `meteor-mongo-minimongo` for that confirmed cause, not a transport rewrite.
Meteor 3.6-beta.1 adds two narrower fixes. Inspect resolved packages and
Meteor 3.6 adds two narrower fixes. Inspect resolved packages and
capture message order before deciding either applies:
| Evidence | Version-specific next step |
|---|---|
| uWebSockets repeatedly reconnects during DDP version negotiation, with a missing `failed` frame | `ddp-server@3.4.1-beta360.1` flushes queued frames before closing. Compare the selected transport, offered/supported versions and delivered frame. Earlier adapters can drop that frame; general disconnects still need proxy/session/application evidence. |
| `Server sent add for existing id` during resubscribe with a pending optimistic method write | `ddp-client@3.4.2-beta360.1` merges repeated server fields into the saved snapshot and retains the visible stub value until writes settle. Check pending writes and publication strategy; use `meteor-pubsub` for retention/cleanup decisions. This does not legalize arbitrary duplicate server publications or eliminate all reconciliation bugs. |
| uWebSockets repeatedly reconnects during DDP version negotiation, with a missing `failed` frame | `ddp-server@3.6.0` flushes queued frames before closing. Compare the selected transport, offered/supported versions and delivered frame. Earlier adapters can drop that frame; general disconnects still need proxy/session/application evidence. |
| `Server sent add for existing id` during resubscribe with a pending optimistic method write | `ddp-client@3.4.2` merges repeated server fields into the saved snapshot and retains the visible stub value until writes settle. Check pending writes and publication strategy; use `meteor-pubsub` for retention/cleanup decisions. This does not legalize arbitrary duplicate server publications or eliminate all reconciliation bugs. |
For oversized events, lost history or a reconnect between fragments, inspect
`mongo@2.6.0` (Meteor 3.6) and MongoDB 6.0.9+/7+ before changing transports.
Use `meteor-mongo-minimongo` for split-event recovery and observer resync;
verify resulting DDP/Minimongo state instead of clearing client collections.
Meteor 3.4.4 (`ddp-server@3.2.3`), 3.5.5 (`3.5.1`) and 3.6.1 (`3.6.1`)
harden malformed control frames and contain handler failures. Inspect the frame and resolved
package before adding retries; earlier packages lack these guards. Keep
argument validation and authorization in application methods/publications.
DDP protocol checks do not protect the separate development HMR socket.
## Mongo and Minimongo inspection
On Meteor 3.6-beta.3, `mongo@2.5.2-beta360.3` separately fixes fieldless
On Meteor 3.6, `mongo@2.6.0` separately fixes fieldless
replication. An ID-only repeated `added` can omit `fields`; earlier packages
may throw `Cannot convert undefined or null to object`. Capture that message
and distinguish it from beta.1's pending-stub duplicate-add fix. Use
and distinguish it from 3.6's pending-stub duplicate-add fix. Use
`meteor-pubsub` for retained document ownership; missing fields preserve data,
while wire `cleared` removes fields; a null field value is not removal.
Do not clear shared Minimongo data.
After a 3.4.4, 3.5.5 or 3.6.1 update, a query can legitimately switch to
polling because its live selector differs from MongoDB semantics. For `$type`,
negated `$elemMatch`, PCRE-only regexes or divergent collation, use
`meteor-mongo-minimongo` to check package pairing and fallback. Do not strip
the selector, clear Minimongo or force a live driver as a diagnostic shortcut.
Use the local shells for read-first evidence:
```bash
@@ -237,37 +237,37 @@ diagnosis or treats a permission change as an HCP-only update.
## Case 25: package shrinkwrap migration
Prompt: "After our first Meteor 3.6-beta.0 build a local Atmosphere package reinstalled npm dependencies and changed .npm/package/npm-shrinkwrap.json to lockfileVersion 5. Should we delete this and the app lockfile?"
Prompt: "After our first Meteor 3.6 build a local Atmosphere package reinstalled npm dependencies and changed .npm/package/npm-shrinkwrap.json to lockfileVersion 5. Should we delete this and the app lockfile?"
Pass if the agent: Distinguishes package shrinkwrap migration from app lockfiles, reviews meteorNpmDependencies and exact pins, repeats an unchanged build, and preserves committed locks rather than clearing everything.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 26: Rspack proxy log deduplication
Prompt: "Our Meteor 3.6 beta Rspack asset proxy logs ECONNREFUSED once while many requests return 502, and its development WebSocket closes. Is the missing logging proof that DDP recovered?"
Prompt: "Our Meteor 3.6 Rspack asset proxy logs ECONNREFUSED once while many requests return 502, and its development WebSocket closes. Is the missing logging proof that DDP recovered?"
Pass if the agent: Identifies separate asset/WS proxy scopes and five-second deduplication, checks target and Rspack child, preserves per-request failure semantics, and does not infer DDP recovery.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 27: HMR excluded from native and builds
Prompt: "Meteor 3.6-beta.0 has no Rspack HMR client in my native target or meteor build output even with NODE_ENV=development. Should I inject the client manually?"
Prompt: "Meteor 3.6 has no Rspack HMR client in my native target or meteor build output even with NODE_ENV=development. Should I inject the client manually?"
Pass if the agent: Checks command/mode/architecture, recognizes the development web app-run boundary, and rejects manual injection as a repair for expected absence.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 28: SWC temporary path versus application failure
Prompt: "On the 3.6 beta a SWC temporary cache write races with cleanup and gets ENOENT. Our application also reports ENOENT while reading a required configuration file. Can we ignore both?"
Prompt: "On Meteor 3.6 a SWC temporary cache write races with cleanup and gets ENOENT. Our application also reports ENOENT while reading a required configuration file. Can we ignore both?"
Pass if the agent: Scopes non-fatal missing-path handling to compiler cache writes, checks compile outcome and verbose warnings, and separately diagnoses the application error.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 29: Git cache and Windows isolated argon2
Prompt: "For the 3.6 beta, should we switch a pinned git+https npm dependency to a branch to improve caching, and install cross-env globally for isolated argon2 builds on Windows?"
Prompt: "For Meteor 3.6, should we switch a pinned git+https npm dependency to a branch to improve caching, and install cross-env globally for isolated argon2 builds on Windows?"
Pass if the agent: Rejects both blanket fixes, compares exact Git spec/recorded/installed source, checks the beta tool's isolated cross-env dependency, and does not change accounts libraries.
Pass if the agent: Rejects both blanket fixes, compares exact Git spec/recorded/installed source, checks the Meteor 3.6 tool's isolated cross-env dependency, and does not change accounts libraries.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 30: older package cache boundary
@@ -279,21 +279,21 @@ Fail if it contradicts these boundaries or invents unsupported commands.
## Case 31: lifecycle observation
Prompt: "Our Meteor 3.6-beta.1 methods are slow and subscriptions sometimes never become ready. Show temporary supported lifecycle instrumentation without monkey-patching Meteor or collecting payloads."
Prompt: "Our Meteor 3.6 methods are slow and subscriptions sometimes never become ready. Show temporary supported lifecycle instrumentation without monkey-patching Meteor or collecting payloads."
Pass if the agent: Selects meteor-debugging, checks instrumentation package version, imports only server-side, registers relevant method/publication events and correlates invocation traceId. Keeps capture off, uses durationMs/phase and stop handles, and does not claim an installed APM backend.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 32: instrumentation is not authorization
Prompt: "On Meteor 3.6-beta.1, can a method.start listener throw to reject unauthorized calls and await a database audit write before the method proceeds?"
Prompt: "On Meteor 3.6, can a method.start listener throw to reject unauthorized calls and await a database audit write before the method proceeds?"
Pass if the agent: States listeners are read-only, best-effort and never awaited; throws/rejections cannot enforce access or durable writes. Routes authorization to method/security guidance and keeps required work in the actual handler.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 33: preview privacy and scope
Prompt: "With instrumentation 0.0.1-beta360.1, enable previews for an app method and a publication with the same name. Will configureMethod redact both, and can it expose login tokens for debugging?"
Prompt: "With instrumentation 0.0.1, enable previews for an app method and a publication with the same name. Will configureMethod redact both, and can it expose login tokens for debugging?"
Pass if the agent: Distinguishes per-method projection from publication global capture policy, bounded previews from secret redaction, and the Accounts denylist from app-specific privacy. Does not expose tokens; explains safe field projection and cleanup/disable controls.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -302,33 +302,33 @@ Fail if it contradicts these boundaries or invents unsupported APIs.
Prompt: "We must keep Meteor 3.5.2. Add the new core Instrumentation method/publication listeners to investigate a slow method."
Pass if the agent: States the beta.1 availability boundary, retains targeted redacted logs or existing compatible instrumentation, and does not add unavailable core hooks or force a release upgrade.
Pass if the agent: States the 3.6 availability boundary, retains targeted redacted logs or existing compatible instrumentation, and does not add unavailable core hooks or force a release upgrade.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 35: TypeScript server restart loop
Prompt: "On Meteor 3.6-beta.0 our TypeScript Rspack server restarts continuously without app edits. Would deleting the generated build marker or ignoring _build fix it? Which beta.1 packages and checks matter?"
Prompt: "On Meteor 3.6 our TypeScript Rspack server restarts continuously without app edits. Would deleting the generated build marker or ignoring _build fix it? Which 3.6 packages and checks matter?"
Pass if the agent: Rejects generated-file edits and handoff ignores. Checks paired rspack 1.4.0-beta360.1/@meteorjs/rspack 3.0.0-beta.2, observes generated-only churn and hashes, then validates one restart for a successful source rebuild plus compile failure/recovery preserving the last good entry.
Pass if the agent: Rejects generated-file edits and handoff ignores. Checks paired rspack 1.4.0/@meteorjs/rspack 3.0.0, observes generated-only churn and hashes, then validates one restart for a successful source rebuild plus compile failure/recovery preserving the last good entry.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 36: host-prefixed port and inspector output
Prompt: "On Meteor 3.6-beta.1, can we keep --port localhost:3060 with Rspack? Beta.0 produced NaN and ERR_SOCKET_BAD_PORT. Also --inspect no longer prints the full Rspack config."
Prompt: "On Meteor 3.6, can we keep --port localhost:3060 with Rspack? Our previous release produced NaN and ERR_SOCKET_BAD_PORT. Also --inspect no longer prints the full Rspack config."
Pass if the agent: Checks tools-core 1.4.0-beta360.1 numeric port parsing and paired npm integration, preserves valid host syntax, and uses --verbose for full configuration while distinguishing server inspection from build diagnostics.
Pass if the agent: Checks tools-core 1.4.0 numeric port parsing and paired npm integration, preserves valid host syntax, and uses --verbose for full configuration while distinguishing server inspection from build diagnostics.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 37: uWS negotiation loop
Prompt: "Our uWebSockets DDP connection repeatedly reconnects during version negotiation and never receives the failed frame. Is there a Meteor 3.6-beta.1 fix, and does it fix all reconnects?"
Prompt: "Our uWebSockets DDP connection repeatedly reconnects during version negotiation and never receives the failed frame. Is there a Meteor 3.6 fix, and does it fix all reconnects?"
Pass if the agent: Checks selected transport and frame sequence, identifies ddp-server 3.4.1-beta360.1 graceful flush/close, and does not generalize to all proxy/session/application disconnects or immediately replace transport.
Pass if the agent: Checks selected transport and frame sequence, identifies ddp-server 3.4.1 graceful flush/close, and does not generalize to all proxy/session/application disconnects or immediately replace transport.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 38: Closed dynamic import cache
Prompt: "On beta.1 a lazy import fails with an IndexedDB transaction error after browser suspension. Another route has a Rspack chunk HTTP 404. Will beta.3 fix both, and should I erase all browser storage?"
Prompt: "On Meteor 3.5.2 a lazy import fails with an IndexedDB transaction error after browser suspension. Another route has a Rspack chunk HTTP 404. Will 3.6 fix both, and should I erase all browser storage?"
Pass if the agent separates Meteor dynamic-import closed cache recovery from Rspack HTTP failures; checks exact package/stack/network, tests closure and subsequent import, preserves application storage and offline limits.
@@ -336,7 +336,7 @@ Fail if it deletes all storage, promises cache fix repairs missing chunks or tre
## Case 39: Rapid restart and prefixed dev script
Prompt: "Our beta.1 Rspack app hangs during rapid server rebuilds waiting on IPC, and a dev script URL loses our /app ROOT_URL prefix. Describe beta.3 checks without changing deployment URLs."
Prompt: "Our Meteor 3.5.2 Rspack app hangs during rapid server rebuilds waiting on IPC, and a dev script URL loses our /app ROOT_URL prefix. Describe 3.6 checks without changing deployment URLs."
Pass if the agent separates IPC/tool race and boilerplate dev-script fixes, captures resolved versions/order/URL, verifies rapid edits plus later client refresh, page/rebuild and production chunks separately.
@@ -344,8 +344,68 @@ Fail if it suppresses every IPC error, disables HCP or removes the requested pre
## Case 40: Instrumentation reporter and preview boundary
Prompt: "On instrumentation 0.0.1-beta360.1, our async onListenerError rejects and custom error/array/date previews behave unexpectedly. What is safe to rely on in beta.3, and can previews export secrets automatically redacted?"
Prompt: "On instrumentation 0.0.1, our async onListenerError rejects and custom error/array/date previews behave unexpectedly. What is safe to rely on in 3.6, and can previews export secrets automatically redacted?"
Pass if the agent separates listener isolation from beta.3 reporter rejection containment and nested preview hardening, prefers metadata, retains older self-handled reporter failures, requires redaction and checks no unhandled rejection.
Pass if the agent separates listener isolation from 3.6 reporter rejection containment and nested preview hardening, prefers metadata, retains older self-handled reporter failures, requires redaction and checks no unhandled rejection.
Fail if it promises arbitrary accessor safety, automatic application-secret redaction or authorization/durable writes from listeners.
## Case 41: Server lifecycle correlation with older fallback
Prompt: "For a Meteor 3.6 server, correlate slow concurrent methods, publication readiness and DDP reconnects without patching internals or logging credentials. We also have a Meteor 3.4 service. Show the smallest temporary diagnostic and cleanup."
Pass if the agent offers optional server-only instrumentation with event/context correlation and stop handles, keeps payload/address capture off or narrowly approved/redacted, retains original errors and older manual/logger fallback, and does not promise an installed APM backend.
## Case 42: Native SWC cache versus build cache
Prompt: "In a Linux container, the actual loaded SWC distribution unpacks its native addon and fails with ERR_SWC_NATIVE_CACHE. Its declared dependency minimum is 1.16.2. The root process has a host-owned home, and /tmp is writable but mounted noexec. Should we clear Rspack cache, use a relative SWC_NATIVE_BINDING_CACHE or disable ownership checks? How should we verify the fix using the same container process?"
Pass if the agent verifies the loaded carrier rather than inferring it from minimum 1.16.2, inspects UID/ownership/mounts, selects an absolute owned writable cache on an executable filesystem, verifies the same load path and retains integrity checks. Fail if it treats build reset as a native-cache repair, uses the noexec path anyway, disables protections or generalizes to old distributions without evidence.
## Case 43: browser error page mirrors a Blaze compile failure
Prompt: "The browser says 'Meteor App - Error'. The terminal says 'Errors
prevented startup', then client/editor.html: 'TRIPLE template tag is not
allowed in an HTML attribute'. Should I debug browser events or replace the
Rspack configuration? What should own the repair?"
Pass if the agent identifies the build boundary from the terminal, explains
the development page's log display, and hands the attribute interpolation
repair to `meteor-blaze`. It verifies compilation and rendering. Fail if it
starts browser tracing, reinstalls packages or changes Rspack first.
## Case 44: browser error page with a server startup failure
Prompt: "The browser says 'Meteor App - Error', but compilation succeeded.
The terminal's first failure is a server startup exception from our payment
client: 'Missing API key'. There is no template compiler error. Should we
rewrite Blaze attributes because the page looks like a build error?"
Pass if the agent uses `meteor-debugging` to follow the server startup and
configuration boundary, avoids exposing the key, and does not infer a Blaze
compile failure from the browser page alone. Fail if it changes templates or
asks to print secret configuration values.
## Case 45: HMR authentication on patched Cordova tools
Prompt: "After Meteor 3.5.5, our custom development HMR client registers web.cordova using only an Origin header and gets wrong-secret. A separate malformed message closes the socket. Should we remove secret validation or disable production hot code push?"
Pass if the agent: Checks current app/run ID and tool-provided secret, keeps Cordova authentication and message validation, distinguishes development HMR from DDP/production HCP and avoids logging secrets or spoofing Origin.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 46: Backported Rspack asset lifecycle
Prompt: "Our Meteor 3.5.4 Rspack 1 app returns HTML for a missing JS asset and headers look stale after rebuild. Does the 3.5.5 patch require Rspack 2 or disabling reload? A 3.4.3 app has the same symptom."
Pass if the agent: Checks compatible 3.5.5 rspack@1.3.2/webapp@2.3.1 and 3.4.4 rspack@1.1.1/webapp@2.1.4 backports, retains Rspack 1 and validates missing-asset 404/no-store plus real rebuild metadata. Does not force a bundler migration or edit generated manifests.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 47: DDP protocol versus method checks
Prompt: "On Meteor 3.4.4 with ddp-server@3.2.3, malformed ping and unsub IDs are rejected. Can we remove method argument checks now? Is this the same fix as HMR socket registration?"
Pass if the agent: Separates protocol validation/handler-throw containment from application check/auth guards and HMR. Retains method/publication validation, and does not promise the patch behavior on older packages.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -43,12 +43,12 @@ wrappers, binary values, and large documents need different handling.
- Capture `Error` name, message, and stack when the stack is part of the
hypothesis. Do not swallow or downgrade the original failure.
## Server lifecycle events on Meteor 3.6-beta.1
## Server lifecycle events on Meteor 3.6
See the tagged [package guide](https://github.com/meteor/meteor/blob/82ea8df295134e64f495338a2e63362408098a24/v3-docs/docs/packages/instrumentation.md)
and [API reference](https://github.com/meteor/meteor/blob/82ea8df295134e64f495338a2e63362408098a24/v3-docs/docs/api/instrumentation.md).
See the tagged [package guide](https://github.com/meteor/meteor/blob/e730097ce42a0dbc4c134aeb1706147b02112844/v3-docs/docs/packages/instrumentation.md)
and [API reference](https://github.com/meteor/meteor/blob/e730097ce42a0dbc4c134aeb1706147b02112844/v3-docs/docs/api/instrumentation.md).
With the beta's `instrumentation@0.0.1-beta360.1`, prefer supported lifecycle
With Meteor 3.6's `instrumentation@0.0.1`, prefer supported lifecycle
events for method timing, publication readiness and DDP connection evidence
over patching framework internals. Inspect `.meteor/versions` first. Earlier
Meteor releases do not supply these hooks; retain targeted redacted logs or
@@ -93,10 +93,10 @@ Do not enforce authorization, reject a method, or promise durable audit writes
from a listener. Keep validation and access checks in the actual handler.
The package supplies events, not an installed OpenTelemetry/APM backend.
For Meteor 3.6-beta.3's `instrumentation@0.0.1-beta360.3`, a rejected Promise
For Meteor 3.6's `instrumentation@0.0.1`, a rejected Promise
from `onListenerError` is also contained without recursively invoking the
reporter. Beta.1 isolates listeners but lacks that reporter hardening; use a
reporter that handles its own async failures on the older package. Verify the
reporter. Keep application reporters responsible for their own error handling
and verify the resolved package when using a custom or older integration. Verify the
observed operation completes and no unhandled rejection occurs.
Arguments, results and IP addresses are off by default. Prefer selected
@@ -116,11 +116,9 @@ does not protect a same-named publication. `captureMethodResult: "preview"`
and `captureClientAddress: true` are separate opt-ins. Keep application
credentials out of previews, error summaries and exported logs.
Beta.3 also bounds nested error properties, uses native Date operations and
avoids application array methods while previewing. Beta.1 lacks these
hardening fixes; do not generalize its bounds to arbitrary custom objects.
Even on beta.3, bounds are not secret redaction or a guarantee that arbitrary
application accessors have no effects. Prefer metadata and test the actual
Meteor 3.6 also bounds nested error properties, uses native Date operations
and avoids application array methods while previewing. These bounds are not
secret redaction or a guarantee that arbitrary application accessors have no effects. Prefer metadata and test the actual
payload shape before enabling capture.
Stop temporary handles during cleanup. `METEOR_INSTRUMENTATION_DISABLED=1`
@@ -167,3 +165,4 @@ appropriate level, and a reason to exist after the fix.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/packages/logging.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/packages/instrumentation.md
@@ -53,9 +53,9 @@ Use the React or Blaze skill after evidence identifies the framework boundary.
## Dynamic import after browser suspension
Meteor 3.6-beta.3's `dynamic-import@0.7.5-beta360.3` recovers from closed or
Meteor 3.6's `dynamic-import@0.8.1` recovers from closed or
version-changed IndexedDB connections, treats failed transactions as cache
misses and contains failed cache writes. Beta.1 lacks this fix.
misses and contains failed cache writes. Earlier packages can lack this fix.
Match the stack and module/network request before selecting a compatible fix.
Verify an import after suspension/cache closure and a subsequent import.
+19 -9
View File
@@ -18,7 +18,7 @@ metadata:
area: ops
tagline: "Ship Meteor 3 apps to production (meteor build, Galaxy, Docker/Kubernetes, settings.json, env vars, Node version matching)."
bundle: ["ops"]
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -39,14 +39,16 @@ Match the Node version to the bundled Meteor Node:
|---|---|
| 3.0 | 20 |
| 3.1 through 3.4 | 22 |
| 3.5.x and 3.6-beta.0 | 24 |
| 3.6-beta.1 through beta.3 | 26 (26.8.2) |
| 3.5.x | 24 |
| 3.6.x | 26 |
Run `meteor node -v` and `meteor npm -v` in the target app to confirm.
Meteor 3.6-beta.1 through beta.3 bundle npm 11.19.0. Match the deployed runtime and native
Match the deployed runtime and native
dependency build environment to that release; rebuild and test native npm
modules for Node 26 and the production architecture before deploying these betas.
The host installer prerequisite does not select the bundled runtime.
modules for Node 26 and the production architecture before deployment.
An extracted bundle records its exact Node version in
`bundle/.node_version.txt`; use that metadata when the source tool is
unavailable. The host installer prerequisite does not select the bundled runtime.
## Decision flow
@@ -63,10 +65,10 @@ The host installer prerequisite does not select the bundled runtime.
For linked workspace packages, move the bundle outside the checkout and test
used `.bin` commands and adjacent resource reads without source access.
3.6-beta.3 fixes copied command links; verify older tools before relying on
3.6 fixes copied command links; verify older tools before relying on
them. Preserve the manager/lockfile and do not patch generated links.
See [database boundaries](references/settings-and-env.md#mongo-connection-strings)
for beta.3's local MongoDB 8 versus an external database.
for 3.6's local MongoDB 8 versus an external database.
## Required environment
@@ -121,7 +123,8 @@ Choose one deployment path:
| Push to Deploy | Galaxy should build a connected GitHub or Bitbucket branch. | Every push to the selected branch. |
| Meteor CLI | A person or CI pipeline controls each upload. | `meteor deploy <app-name>`. |
For a Free app, pass a simple app name, not a custom domain:
Check `meteor whoami`; add `--owner my-account` to select a specific user or
organization. For a new Free app, pass a simple app name, not a custom domain:
```bash
meteor login
@@ -135,6 +138,13 @@ Galaxy assigns a `myapp.sandbox.galaxycloud.app` hostname on Free or a
regional `myapp.<region>.galaxycloud.app` hostname on paid plans. Add custom
domains in the dashboard. The active primary domain controls `ROOT_URL`.
For an existing app, confirm its current hostname, region, and owner before
redeploying. `--plan` and `--container-size` select creation settings; change
an existing app's plan or size in the dashboard. Optionally add `--mongo` to
provision free shared MongoDB for testing, following the
[Mongo TLS guidance](references/galaxy.md#mongodb-on-galaxy). It has no backups
and is not recommended for production.
Galaxy injects `PORT`, `ROOT_URL`, and `METEOR_SETTINGS`. Put `MONGO_URL`,
`MAIL_URL`, and other server environment values under
`galaxy.meteor.com.env` in the Galaxy settings JSON. Do not assume a CLI
@@ -37,10 +37,11 @@ CMD ["node", "main.js"]
This sample targets Meteor 3.1 through 3.4. Match both `node:` tags to the
bundled Node major: Meteor 3.0 uses Node 20, 3.1 through 3.4 use Node 22,
and 3.5.x / 3.6-beta.0 use Node 24. Meteor 3.6-beta.1 through beta.3 use Node 26.8.2
and npm 11.19.0. Match both images and native dependency builds to the exact
target release; do not carry Node 24 native binaries into a Node 26 runtime.
Run `meteor node -v` and `meteor npm -v` in the target app to verify. On
3.5.x uses Node 24, and 3.6.x uses Node 26. Match both images and native
dependency builds to the exact target runtime. Check the built image's
`node -v`; a floating major tag does not prove it contains a required patch.
Run `meteor node -v` and `meteor npm -v` in the target app, or read the
extracted bundle's `.node_version.txt`, to verify. On
M-series Macs targeting x86_64 Linux, add
`--architecture os.linux.x86_64` to `meteor build`.
@@ -48,8 +48,8 @@ Prompt: "On my server `node main.js` crashes with
`undefined symbol: node_module_register`."
Pass if the agent identifies the Node version mismatch (Meteor 3.0 uses Node
20, 3.1 through 3.4 use Node 22, 3.5.x/beta.0 use Node 24, and
3.6-beta.1 uses Node 26), and instructs running
20, 3.1 through 3.4 use Node 22, 3.5.x uses Node 24, and
3.6 uses Node 26), and instructs running
`meteor node -v` to find the exact version, then deploying with the matching
`node:<N>-bookworm-slim` image.
@@ -178,23 +178,23 @@ inferring a binary change from a Meteor release label or an edited filename;
native configuration can require a binary even when its compatibility hash is
unchanged. Fail if it claims every such edit necessarily changes that hash.
## Case 17: beta.1 runtime and native modules
## Case 17: 3.6 runtime and native modules
Prompt: "Deploy our Meteor 3.6-beta.1 bundle in Docker. Can we reuse Node 24 images and native modules from our Meteor 3.5.2 build?"
Prompt: "Deploy our Meteor 3.6 bundles in Docker. The target project reports Node 26.13.0 and npm 11.20.0. Can we reuse Node 24 images and native modules from our Meteor 3.5.2 build?"
Pass if the agent: Selects the target Node 26.8.2 runtime, notes bundled npm 11.19.0, verifies meteor node/npm versions, matches build/runtime architecture and rebuilds native modules. Does not reuse Node 24 native binaries or mistake host installer Node for app runtime.
Pass if the agent: Selects the target Node 26.13.0 runtime, notes bundled npm 11.20.0, verifies meteor node/npm versions, matches build/runtime architecture and rebuilds native modules. Does not reuse Node 24 native binaries or mistake host installer Node for app runtime.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 18: keep earlier runtime
Prompt: "Our app remains on Meteor 3.5.2, and another app is pinned to 3.6-beta.0. Does the 3.6-beta.1 Node 26 announcement require new Node 26 runtime images for either?"
Prompt: "Our app remains on Meteor 3.5.2, and another app is pinned to 3.5.1. Does the 3.6 Node 26 announcement require new Node 26 runtime images for either?"
Pass if the agent: Retains Node 24 for both targets, checks exact bundled versions, and does not upgrade their runtimes solely because a later beta exists.
Pass if the agent: Retains Node 24 for both targets, checks exact bundled versions, and does not upgrade their runtimes solely because a later release exists.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 19: Relocated workspace command
Prompt: "Our linked workspace package command works in development and in a bundle beside the checkout, but fails after moving the artifact to a clean deployment directory. What should we verify on beta.3?"
Prompt: "Our linked workspace package command works in development and in a bundle beside the checkout, but fails after moving the artifact to a clean deployment directory. What should we verify on 3.6?"
Pass if the agent checks tool version and copied workspace/bin links, executes used commands and adjacent resource reads from the moved artifact without source access; keeps manager/lockfile and earlier-version boundary.
@@ -202,8 +202,58 @@ Fail if it hand-patches generated links, uses absolute source paths, or claims s
## Case 20: External database and runtime
Prompt: "Deploy a beta.3 app with a separately administered MongoDB 7 through MONGO_URL. Does local bundled MongoDB 8 mean Meteor upgrades it or that the app container needs local Mongo binaries? Which Node runtime applies?"
Prompt: "Deploy a 3.6 app with a separately administered MongoDB 7 through MONGO_URL. Does local bundled MongoDB 8 mean Meteor upgrades it or that the app container needs local Mongo binaries? The target project's meteor node/npm commands report 26.13.0/11.20.0. Which runtime applies?"
Pass if the agent keeps database upgrade with provider/admin, separates local Mongo platform requirements, verifies Node 26.8.2/npm 11.19.0 and target native dependencies plus external compatibility.
Pass if the agent keeps database upgrade with provider/admin, separates local Mongo platform requirements, verifies Node 26.13.0/npm 11.20.0 and target native dependencies plus external compatibility.
Fail if it runs a remote FCV upgrade by inference, bundles local Mongo unnecessarily or uses Node 24 for beta.3.
Fail if it runs a remote FCV upgrade by inference, bundles local Mongo unnecessarily or uses Node 24 for 3.6.
## Case 21: 3.6 runtime versus older Docker target
Prompt: "Our Meteor 3.6 bundle reports Node 26.13.0 but our Docker stages use Node 24. We also maintain a separate Meteor 3.4 app. Which runtime should each deployment use?"
Pass if the agent matches Meteor 3.6 to Node 26 and verifies the exact pinned tool/bundle version, while retaining Node 22 for 3.4. Fail if it changes every older image to 26, uses the installer host Node as bundle evidence, or keeps the 3.5+ Node 24 rule.
## Case 22: Organization prototype with shared MongoDB
Prompt: "Prepare the CLI command for a new Meteor 3.5.1 prototype named myapp
on Galaxy's Free plan under my-team. I want Galaxy to provision free MongoDB
and load settings.json. Do not deploy yet."
Pass if the agent checks the login account and prepares a simple-name command
with `--owner my-team`, `--free`, `--mongo`, and `--settings settings.json`.
It includes the free shared database's TLS settings and explains that it has
no backups and is not recommended for production. Fail if it omits account
selection or provisioning, treats `--mongo` as sufficient TLS configuration,
or recommends the shared database for production data.
## Case 23: Existing app with a dedicated database
Prompt: "Our Meteor 3.5.1 app already runs in EU West under my-team with a
dedicated MongoDB. Prepare a CLI redeploy and tell me how to move from
Essentials Tiny to Professional Standard. Do not deploy yet."
Pass if the agent verifies the existing app's current hostname, uses the EU
`DEPLOY_HOSTNAME` and `--owner my-team`, preserves the dedicated database,
and directs plan and size changes to the dashboard. Fail if it adds `--mongo`
or the free database's certificate bypass, uses a new simple name, or claims
`--plan` and `--container-size` resize an existing app on redeploy.
## Case 24: Rejected discovery
Prompt: "Meteor 3.6 deploy refuses discovery pointing to https://galaxy-deploy.meteor.com.attacker.example. Should I put that URL in DEPLOY_HOSTNAME to get the deployment working?"
Pass if the agent rejects the lookalike destination, explains credential forwarding risk and verifies app DNS/region before retrying. Fail if it copies the response into the explicit override or claims discovery failure and invalid destination behave identically.
## Case 25: Deliberate deployment override
Prompt: "We verified ownership and TLS for our intentional custom deployment service. Can Meteor 3.6 use DEPLOY_HOSTNAME for it? Does the same discovery protection cover our old 3.4 tool?"
Pass if the agent allows the explicit trusted custom destination, explains it bypasses automatic discovery validation and checks older tool/backport behavior. Fail if it bans all overrides or assumes every Meteor 3 tool has the new guard.
## Case 26: Three security releases in containers
Prompt: "We are deploying Meteor 3.4.4, 3.5.5 and 3.6.1 from the same CI. Their extracted bundle/.node_version.txt files report 22.23.5, 24.22.3 and 26.13.1 respectively. Can all images use Node 26.13.0 because that was the 3.6 release runtime? Is node:24 enough to prove our existing 3.5.5 deployment has the patch?"
Pass if the agent: Uses the supplied bundle evidence to select 22.23.5/24.22.3/26.13.1 respectively, checks actual image runtime, aligns native dependency builds and does not treat a floating major tag or different minor line as proof.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -11,26 +11,37 @@ before changing traffic.
| Push to Deploy | Connect GitHub or Bitbucket, select the repository and branch, then set the Meteor root directory and build options. | Every push to the selected branch creates and deploys a version. Disconnect Push to Deploy to stop automatic builds without removing saved build settings. |
| Meteor CLI | Run `meteor login`, select an app name, and pass plan, region, settings, or Mongo flags. | The local Meteor CLI builds and uploads the application for each command. |
Free CLI deployments use a simple app name:
Check the signed-in user with `meteor whoami`. Add `--owner my-account` when
selecting a specific user or organization, including in CI commands.
New Free CLI deployments use a simple app name:
```bash
meteor deploy myapp --free --settings settings.json
meteor deploy myapp --owner my-account --free --settings settings.json
```
A domain-form target requests a custom domain and requires a paid plan. Let
Galaxy create `myapp.sandbox.galaxycloud.app` for Free. Paid apps receive a
regional `myapp.<region>.galaxycloud.app` hostname.
For a new app, a domain-form target requests a custom domain and requires a
paid plan. Let Galaxy create `myapp.sandbox.galaxycloud.app` for Free. Paid
apps receive a regional `myapp.<region>.galaxycloud.app` hostname.
Choose a paid region with `DEPLOY_HOSTNAME`:
Choose a paid region with `DEPLOY_HOSTNAME` when creating an app:
```bash
DEPLOY_HOSTNAME=eu-west-1.galaxy-deploy.meteor.com \
meteor deploy myapp --plan essentials --container-size standard \
--settings settings.json
--owner my-account --settings settings.json
```
Supported region hosts are `us-east-1`, `eu-west-1`, and
`ap-southeast-2` under `galaxy-deploy.meteor.com`. Free apps deploy in US East.
`--plan` and `--container-size` set a new app's plan and container size. Change
an existing app's plan or size in the Galaxy dashboard.
For an existing or migrated app, verify its current Galaxy hostname, region,
and account, then use that hostname with the matching `DEPLOY_HOSTNAME` and
`--owner`. Do not repeat creation flags as a requirement for every redeploy or
use a new simple name that could create another app.
For CLI automation, create a `METEOR_SESSION_FILE` token file, store its
contents in the CI secret store, reconstruct it for the job, and never commit
it.
@@ -40,6 +51,23 @@ METEOR_SESSION_FILE=token.json meteor login
METEOR_SESSION_FILE=token.json meteor deploy myapp --settings settings.json
```
## Discovery and credential destination
Meteor 3.6's CLI validates automatic discovery before sending account tokens.
It accepts HTTPS on `galaxy-deploy.meteor.com` or its subdomains, without
embedded credentials or a non-default port. A lookalike suffix or an external
host is rejected. Verify the app hostname, DNS and documented Galaxy region
when discovery returns an unexpected destination.
`DEPLOY_HOSTNAME` explicitly overrides discovery and trusts the supplied host.
Use it for a verified Galaxy region or an intentionally configured deployment
service whose ownership and TLS endpoint you have checked. Do not copy an
untrusted discovery response into that variable just to suppress the error.
Discovery request failure can fall back to the default Galaxy region; an
invalid discovered destination is refused. Check the actual target before
retrying. Older CLI releases may lack this guard, so verify tool updates or
backports instead of assuming rejection protects every Meteor 3 deployment.
## Select the settings source
Galaxy consumes one complete settings object:
@@ -143,6 +171,16 @@ The deployment-time Free MongoDB toggle is for testing and development. It
adds `MONGO_URL`, has no backups, and is tied to the app lifecycle. Use a paid
database or another production MongoDB service for production data.
From the CLI, optionally add `--mongo` to provision the free shared database:
```bash
meteor whoami
meteor deploy myapp --owner my-account --free --mongo --settings settings.json
```
Omit `--mongo` when using an existing database. Configure the free database's
TLS settings below before deploying; the flag does not replace them.
Galaxy's free shared MongoDB requires its documented certificate bypass:
```json
@@ -220,3 +258,5 @@ object. In Push to Deploy with Galaxy Mode, keep that block in the stored JSON.
- [Secrets Management](https://docs.galaxycloud.app/docs/apps/meteor/secrets-management)
- [MongoDB Oplog Setup](https://docs.galaxycloud.app/docs/databases/mongodb-oplog-setup)
- [MongoDB SSL for Meteor](https://docs.galaxycloud.app/docs/databases/mongodb-ssl-meteor)
- [Meteor 3.6 deployment discovery](https://github.com/meteor/meteor/blob/e730097ce42a0dbc4c134aeb1706147b02112844/tools/meteor-services/deploy.js)
@@ -63,11 +63,11 @@ change streams without this variable, with oplog and polling as fallbacks.
Choose from the actual Mongo topology and Meteor version; Atlas does not make
core change streams available before Meteor 3.5.
Meteor 3.6-beta.3 bundles MongoDB 8 for local development. It neither upgrades
Meteor 3.6 bundles MongoDB 8 for local development. It neither upgrades
an external `MONGO_URL` database nor imposes that local database's glibc/Windows
requirements on a backend that never launches it. Verify the deployed Node
runtime and the database provider's supported server/topology separately.
Local retained data needs MongoDB 7 FCV preparation before beta.3 startup;
Local retained data needs MongoDB 7 FCV preparation before 3.6 startup;
use `meteor-cli-installation` for that workflow.
## Secrets
+12
View File
@@ -131,6 +131,16 @@ per-connection vs per-user keys.
## Error handling
Own every client `callAsync` result: await it inside `try`/`catch`, return it
to a caller that handles rejection, or attach an explicit `.catch` for
intentional background work. Show a confirmed-save message only after the
awaited call succeeds; keep pending/optimistic UI distinct from confirmation.
The client async-stub queue attaches no-op rejection handlers, so an ignored
call can reject without console output or an `unhandledrejection` event.
Awaiting the returned Promise still exposes the error. This queue behavior
does not imply that server logs or every derived Promise are silent. See the
[client queue implementation](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/packages/ddp-client/client/queue_stub_helpers.js).
Throw `Meteor.Error(code, reason, details?)` for an intentional client-visible
failure. A plain `Error` is logged on the server and sanitized for the client
as `Meteor.Error(500, "Internal server error")`; its original message and
@@ -141,6 +151,8 @@ errors. Narrow the caught value before reading Meteor-specific fields.
## Anti-patterns
- Fire `Meteor.callAsync` without handling or deliberately forwarding its
Promise, then display success or rely on global rejection logging.
- Methods without `check()` on every argument. Reject the method in code
review.
- Reusing a method for both authenticated and unauthenticated calls. Split
@@ -71,3 +71,16 @@ crashes again while reading `err.error`. Are all `callAsync` rejections
Pass if the agent says intentional server-visible failures should use
`Meteor.Error` but local, misuse, and transport failures may not, then narrows
the caught value before reading `error`, `reason`, or `details`.
## Case 9: save succeeds only in the UI
Prompt: "In a Meteor 3.5.1 client click handler I call
`Meteor.callAsync('items.save', input); showSaved();`. The method throws a
Meteor.Error, nothing changes in Mongo, and neither the console nor our
unhandledrejection listener reports it. Why does the UI say Saved?"
Pass if the agent identifies the unowned Promise and client's internal
rejection handlers, makes the handler async with `try`/`await`/`catch`, and
shows confirmation only after resolution. It distinguishes optimistic state
from confirmed success. Fail if it relies on global rejection logging or
claims the method necessarily succeeded because no error was logged.
@@ -23,7 +23,7 @@ metadata:
area: build
tagline: "Configure the Meteor 3 modern build stack (SWC transpiler/minifier, `@parcel/watcher`, web-arch skipping, Rspack integration)."
bundle: ["essentials"]
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -100,8 +100,9 @@ moves to Rspack; Meteor still handles Atmosphere packages and produces the
final bundle. Requires entry points in `package.json` and no nested imports
in app code. To migrate an existing app, use the `migrate-to-rspack` skill.
Inspect `.meteor/versions`, `package.json`, and the lockfile. For 3.6-beta.3
and earlier targets, use the [release pairing](references/rspack-config.md)
Check `.meteor/versions`, `package.json` and lockfile. Meteor 3.6 pairs
`rspack@1.4.0` with `@meteorjs/rspack@3.0.0`, core/CLI 2.2.7.
Use the [release pairings](references/rspack-config.md)
and [dependency minimums](references/meteor-3.6-workspaces.md).
Rspack transpiles TypeScript. Use `meteor-typescript` for declarations and
checking; see
@@ -111,7 +112,7 @@ checking; see
For new-app skeleton selection or framework-neutral PWA/Workbox setup, read
[scaffolds and service workers](references/scaffolds-and-pwa.md).
Meteor 3.6 (verified on `3.6-beta.0`) adds separate `--pnpm` workspace and
Meteor 3.6 adds separate `--pnpm` workspace and
`--pwa` Blaze starters; neither converts an existing app. A worker caches
shell/assets, not offline Meteor data. Existing bundler migrations remain
owned by `migrate-to-rspack`.
@@ -137,6 +138,10 @@ Meteor's pipeline detects it and emits imports instead of inlining. If only a
production or legacy bundle fails on a helper import, inspect Rspack-generated
and final Meteor output before changing `.swcrc` or adding manual imports.
On a resolved SWC carrier, `ERR_SWC_NATIVE_CACHE` needs an absolute owned
`SWC_NATIVE_BINDING_CACHE` on an executable filesystem. Build-cache cleanup
will not repair it; see `meteor-debugging`.
## Rspack config files
```text
@@ -166,7 +171,7 @@ scripts/
For per-command rules, set the `METEOR_IGNORE` env var.
On the 3.6-beta.3 pairing, read the [test-ignore boundaries](references/meteor-3.6-workspaces.md#test-selection-and-generated-ignores)
On the 3.6 pairing, read the [test-ignore boundaries](references/meteor-3.6-workspaces.md#test-selection-and-generated-ignores)
for ordered user exceptions, included parents and separate generated rules.
Do not copy `.gitignore` into `.meteorignore` blindly. Exclude unrelated large
@@ -190,7 +195,7 @@ production output, source maps, build time, and runtime behavior.
## Production legacy builds
For separate `mainModule.legacy` or exact architecture entries on 3.6-beta.3,
For separate `mainModule.legacy` or exact architecture entries on Meteor 3.6,
read [architecture entry points](references/architecture-entrypoints.md).
Earlier integrations retain their shared-client behavior.
@@ -1,14 +1,11 @@
# Architecture-specific Rspack entries
# Rspack entries by architecture
Meteor 3.6, verified on beta.3 with `rspack@1.4.0-beta360.3` and
`@meteorjs/rspack@3.0.0-beta.3`, compiles explicit client architectures through
Rspack separately. Beta.0/beta.1 and earlier integrations do not provide this
complete flow; preserve their shared client behavior or use a compatible
paired upgrade. Keep deliberate version constraints.
Meteor 3.6 pairs `rspack@1.4.0` with `@meteorjs/rspack@3.0.0` to compile
modern, legacy and Cordova entry graphs separately. Earlier Meteor
integrations retain their shared-client behavior. Inspect the release,
resolved packages and app entry map before selecting this workflow.
## Select entries
Merge into the app's existing configuration:
Keep selection in `package.json`, not Rspack's reserved `entry` or `output`:
```json
{
@@ -18,68 +15,72 @@ Merge into the app's existing configuration:
"legacy": "client/legacy.js",
"server": "server/main.js"
},
"testModule": {
"client": "tests/client.js",
"legacy": "tests/legacy.js",
"server": "tests/server.js"
},
"modern": { "webArchOnly": false }
}
}
```
| Entry decision | Effect |
| Selection | Behavior |
|---|---|
| Only `client` | Existing shared client graph; no separate legacy compilation. |
| Explicit `legacy` | Separate `web.browser.legacy` graph even if its filename equals `client`. Can render an unsupported-browser message or a compatible app. |
| Explicit `web.browser`, `web.browser.legacy`, `web.cordova` | Select the exact architecture; the most specific matching key overrides a broader `client` entry. |
| `modern` / `legacy` shorthands | Follow Meteor architecture mapping. With `meteor.modern.cordova: false`, `legacy` also covers Cordova. Use exact keys when that distinction matters. |
| Entry value `false` | Disables that architecture's app entry, not Meteor package code or the browser program itself. |
| Only `mainModule.client` | Retains the existing shared-client compilation. |
| Explicit `legacy` or `web.browser.legacy` | Creates a separate legacy compilation, even for the same source path. |
| `web.browser`, `web.browser.legacy` or `web.cordova` | Selects the exact architecture; the most specific matching key overrides a broader `client` entry. |
| `modern` shorthand | Follows Meteor's architecture mapping. |
| `legacy` with `modern.cordova: false` | Also covers Cordova. |
| Entry `false` | Disables that architecture's app entry, not its Meteor package code or browser program. |
Import shared modules intentionally; a small legacy notice should not import
the modern bootstrap. Keep native binary/HCP decisions with `meteor-native`.
the modern bootstrap.
## Configure the compilation
All entries use the existing `rspack.config.*`, aliases, loaders and plugins.
`defineConfig` supplies `Meteor.arch` for an explicit architecture and
`Meteor.isLegacy` for its legacy mapping. Default client/server compilations
have no explicit `arch`. These are callback flags, not runtime Meteor APIs.
## Conditional configuration
```javascript
const { defineConfig } = require('@meteorjs/rspack');
const { defineConfig } = require("@meteorjs/rspack");
module.exports = defineConfig(Meteor => Meteor.isLegacy
? Meteor.compileWithRspack([/node_modules/])
: {});
```
Use this broad dependency rule only when the legacy graph needs it; narrower
dependency rules can reduce work. The helper inherits that compilation's SWC
options. The default legacy app transform and generated Rspack runtime target
ES5, including dynamic chunks. npm dependencies are excluded from the default
app rule. `meteor.nodeModules.recompile` governs Meteor-compiled dependencies,
not dependencies bundled by Rspack. Custom SWC/compiler settings can override
the legacy defaults.
`Meteor.isLegacy` and `Meteor.arch` describe the current configuration callback
compilation. They are not new properties on the app's runtime `Meteor` object.
Default client/server compilations have no explicit `arch`. All entries use
the existing config, aliases, loaders and plugins. Meteor does not
auto-discover `rspack.legacy.config.js`.
ES5 output does not supply missing browser APIs or make BigInt-dependent
libraries compatible. Add required polyfills and verify actual target browsers.
Use `Meteor.arch` for architecture-specific rules and `Meteor.isLegacy` for
legacy rules. Do not create an auto-discovered `rspack.legacy.config.js` or
override Meteor's reserved Rspack `entry`/`output` to choose these graphs.
Separate legacy compilation defaults to ES5 for app SWC output and the Rspack
runtime/dynamic chunks. npm dependencies are excluded from the default app
transform; use `compileWithRspack` for those that need it. The helper inherits
the legacy target; narrower dependency rules can reduce work. Custom SWC or
compiler settings can override the defaults. `meteor.nodeModules.recompile` only affects Meteor-owned
compilation, not Rspack dependencies. ES5 syntax does not polyfill browser APIs
or remove requirements such as BigInt; test the actual supported browser.
## Verify development, tests and production
## Verify the selected program
- Set `modern.webArchOnly: false` to include legacy programs in development
and app tests; an explicit `--exclude-archs` takes precedence.
- The separate legacy graph uses a watched build and Meteor reloads. The
default client keeps the Rspack dev server/HMR.
- Exercise `meteor run`, `meteor run --production` and the extracted
`meteor build` artifact. Production includes legacy unless platform
selection excludes it, for example `--platforms modern`.
- A legacy user-agent override verifies program selection and asset/chunk
delivery. Check `Meteor.isModern` in app code. It does not emulate an old
JavaScript engine.
- For app tests, add matching `meteor.testModule` entries, such as
`client`, `legacy`, and `server`, with real import graphs. Normal test mode
selects tests; `--full-app` includes the applicable app entry as well.
A modern headless `--once` run does not test the legacy program. Select it
explicitly and verify expected tests actually ran on each program.
Enable `modern.webArchOnly: false` for legacy development/tests, and check
explicit `--exclude-archs` settings, which take precedence. The modern client
keeps the Rspack dev server/HMR; separate legacy development uses a watched
build and Meteor reloads. Production includes legacy unless platform selection
excludes it, such as `--platforms modern`.
Check `meteor run`, production-mode run and the extracted production bundle.
Select a legacy user agent to verify program delivery and asset loading, then
test the actual browser engine for compatibility. Changing a modern browser's
user agent does not emulate an old JavaScript engine. App runtime
`Meteor.isModern` identifies the selected browser program.
Run the app's test driver against both selected browser programs and confirm
the expected suites and real test import graphs. Normal test mode selects
`testModule`; `--full-app` includes the applicable app entry as well.
A modern headless `meteor test --once` does not exercise
the legacy `testModule` entry by itself. Use `migrate-to-rspack`'s validation
matrix for migration acceptance and `meteor-native` for device/HCP work.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/about/modern-build-stack/rspack-bundler-integration.md
@@ -215,23 +215,23 @@ from computed or out-of-app paths, verifies the next build before cleanup,
and does not extend the TypeScript tracking fix to older integrations. Fail if
it disables persistent cache by default or guarantees arbitrary path tracking.
## Case 22: 3.6 beta dependency pairing
## Case 22: 3.6 dependency pairing
Prompt: "We are on Meteor 3.6-beta.0 with autoInstallDeps false. Which Rspack integration versions should we reconcile before our frozen CI build?"
Prompt: "We are on Meteor 3.6 with autoInstallDeps false. Which Rspack integration versions should we reconcile before our frozen CI build?"
Pass if the agent: Checks the resolved Atmosphere/npm pairing, uses rspack 1.4.0-beta360.0 and @meteorjs/rspack 3.0.0-beta.1 rather than a presumed stable 3.0.0, reconciles the coordinated dependencies locally, and preserves the lockfile in CI.
Pass if the agent: Checks the resolved Atmosphere/npm pairing, uses rspack 1.4.0 and @meteorjs/rspack 3.0.0 with core/CLI 2.2.7+ and dev server 2.2.1+, reconciles the coordinated dependencies locally, and preserves the lockfile in CI.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 23: pnpm workspace install ownership
Prompt: "Meteor 3.6-beta.0 runs from apps/app in a pnpm workspace. The root pins pnpm but the app has an old npm packageManager hint. Where should required Rspack installs run, and which lockfile should change?"
Prompt: "Meteor 3.6 runs from apps/app in a pnpm workspace. The root pins pnpm but the app has an old npm packageManager hint. Where should required Rspack installs run, and which lockfile should change?"
Pass if the agent: Uses the root manager and shared pnpm lockfile with the app as install cwd; does not create an app package-lock or require global pnpm when Corepack is available. Preserves existing locks and local work while investigating conflicting hints, rather than prescribing deletion from the hint alone. Does not forbid pnpm-managed links under apps/app/node_modules.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 24: local protocol dependencies
Prompt: "Our 3.6 beta workspace declares a required Rspack helper with workspace: and another with file:. Should the tool treat these strings as bad semver and replace them with registry versions?"
Prompt: "Our 3.6 workspace declares a required Rspack helper with workspace: and another with file:. Should the tool treat these strings as bad semver and replace them with registry versions?"
Pass if the agent: Inspects installed package versions and protocol targets, preserves intentional local sources, and does not apply registry semver parsing to protocol strings.
Fail if it contradicts these boundaries or invents unsupported commands.
@@ -240,12 +240,12 @@ Fail if it contradicts these boundaries or invents unsupported commands.
Prompt: "We are pinned to Meteor 3.5.2 and want the new pnpm monorepo scaffold and automatic workspace dependency handling without upgrading. Can we use the 3.6 commands unchanged?"
Pass if the agent: States the 3.6 beta boundary; offers existing/manual workspace setup or an explicitly scoped upgrade, not unsupported flags or guaranteed new automatic behavior.
Pass if the agent: States the 3.6 boundary; offers existing/manual workspace setup or an explicitly scoped upgrade, not unsupported flags or guaranteed new automatic behavior.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 26: React PWA with Workbox and offline data
Prompt: "Our Meteor 3.6-beta.0 React app already uses Rspack. We want a PWA
Prompt: "Our Meteor 3.6 React app already uses Rspack. We want a PWA
like Notes Offline. Should we convert to the new Blaze skeleton, copy its
worker, or use Workbox? Will the worker save Minimongo and replay edits?"
@@ -259,11 +259,11 @@ private traffic indiscriminately, or promises worker-provided method replay.
## Case 27: new pnpm workspace starter
Prompt: "Start a Meteor 3.6-beta.0 pnpm workspace with shared client and server
Prompt: "Start a Meteor 3.6 pnpm workspace with shared client and server
packages. Where is the Meteor app, how do I start it, and does the pnpm
skeleton include PWA offline behavior?"
Pass if the agent uses `meteor create --release 3.6-beta.0 --pnpm <name>`,
Pass if the agent uses `meteor create --release 3.6 --pnpm <name>`,
identifies `apps/app` and `packages/`, and explains the root start script
without switching dependency ownership to npm. It preserves the root pnpm
pin and lockfile, runs direct Meteor commands from the app, and treats PWA
@@ -272,7 +272,7 @@ when Corepack supplies the pinned version.
## Case 28: skeleton selection is not flag composition
Prompt: "For a new Meteor 3.6-beta.0 app, should I use --react, --typescript,
Prompt: "For a new Meteor 3.6 app, should I use --react, --typescript,
--svelte, --pnpm or --pwa? Can I combine --react --pnpm --pwa to get all three?
Does meteor create --list show every skeleton?"
@@ -309,16 +309,16 @@ actual registration and avoids unnecessarily broad control or an automatic
prefix rewrite. Fail if it requires moving a root worker to narrow its scope
or treats broader-than-directory scope as always impossible or unrestricted.
## Case 31: beta.1 pairing and declaration boundary
## Case 31: 3.6 pairing and declaration boundary
Prompt: "Configure Rspack on a new Meteor 3.6-beta.1 TypeScript app with autoInstallDeps false. Does SWC generate native Meteor declarations and type-check it automatically?"
Prompt: "Configure Rspack on a new Meteor 3.6 TypeScript app with autoInstallDeps false. Does SWC generate native Meteor declarations and type-check it automatically?"
Pass if the agent: Uses rspack 1.4.0-beta360.1/@meteorjs/rspack 3.0.0-beta.2 and Rspack core/CLI/dev-server 2.2.0 minimums, prepares dependencies with the existing manager and keeps frozen CI. Separates transpilation, explicit native opt-in and local tsc; new templates retain legacy providers. Does not remove zodern:types as a bundler requirement.
Pass if the agent: Uses rspack 1.4.0/@meteorjs/rspack 3.0.0 and Rspack core/CLI 2.2.7 and dev server 2.2.1 minimums, prepares dependencies with the existing manager and keeps frozen CI. Separates SWC transpilation, native generation during creation of the TypeScript scaffold, explicit regeneration after cloning/package changes, and local type-checking. Existing app upgrades preserve their provider. Does not remove zodern:types as a bundler requirement.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 32: standalone declaration resolution near miss
Prompt: "Our Meteor 3.6-beta.1 bundler works, but meteor/random is any in the editor and local tsc skips our app files. We want type resolution repaired, with no SWC or Rspack migration."
Prompt: "Our Meteor 3.6 bundler works, but meteor/random is any in the editor and local tsc skips our app files. We want type resolution repaired, with no SWC or Rspack migration."
Pass if the agent hands provider/configuration and source-set checking to
meteor-typescript, preserves working build/checker integration and does not
@@ -326,15 +326,15 @@ change loaders merely because the app uses TypeScript.
## Case 33: Current 3.6 dependency set
Prompt: "Configure the dependencies for our existing Meteor 3.6-beta.3 React app in a pnpm workspace. autoInstallDeps is false. Give the correct minimums and where to prepare the lockfile; native types already work."
Prompt: "Configure the dependencies for our existing Meteor 3.6 React app in a pnpm workspace. autoInstallDeps is false. Give the correct minimums and where to prepare the lockfile; native types already work."
Pass if the agent uses rspack 1.4.0-beta360.3 / @meteorjs/rspack 3.0.0-beta.3, core/CLI 2.2.7, dev server 2.2.1, SWC 1.16.2, Rsdoctor 1.6.4, helpers 0.5.23 and refresh 2.0.2/0.19.0; respects opt-out and workspace ownership.
Pass if the agent uses rspack 1.4.0 / @meteorjs/rspack 3.0.0, core/CLI 2.2.7, dev server 2.2.1, SWC 1.16.2, Rsdoctor 1.6.4, helpers 0.5.23 and refresh 2.0.2/0.19.0; respects opt-out and workspace ownership.
Fail if it copies beta.1 minimums, creates an app npm lockfile, rewrites CI dependencies, or changes declaration providers.
Fail if it copies earlier integration minimums, creates an app npm lockfile, rewrites CI dependencies, or changes declaration providers.
## Case 34: Separate legacy program
Prompt: "On Meteor 3.6-beta.3 with its paired Rspack packages, create a small legacy-browser notice beside our modern app. The notice imports one npm dependency. Explain config, compilation, reload behavior and how to test it."
Prompt: "On Meteor 3.6 with its paired Rspack packages, create a small legacy-browser notice beside our modern app. The notice imports one npm dependency. Explain config, compilation, reload behavior and how to test it."
Pass if the agent selects explicit legacy mainModule with shared config, distinguishes callback arch/isLegacy from runtime flags, enables legacy development, addresses npm transpilation separately from ES5/polyfills and tests delivery plus actual engines.
@@ -342,16 +342,43 @@ Fail if it overwrites reserved entry/output, invents rspack.legacy.config.js dis
## Case 35: Earlier architecture near miss
Prompt: "We are pinned to Meteor 3.6-beta.1 and cannot update. Can copying beta.3 mainModule.legacy and Meteor.isLegacy guidance provide a separate Rspack legacy application?"
Prompt: "We are pinned to Meteor 3.5.2 and cannot update. Can copying 3.6 mainModule.legacy and Meteor.isLegacy guidance provide a separate Rspack legacy application?"
Pass if the agent explains the complete flow is not present on beta.1 and preserves the constrained shared-client behavior; may describe a future compatible upgrade separately.
Pass if the agent explains the complete flow is not present on 3.5.2 and preserves the constrained shared-client behavior; may describe a future compatible upgrade separately.
Fail if it promises the beta.3 architecture flow or silently updates the app.
Fail if it promises the 3.6 architecture flow or silently updates the app.
## Case 36: Root generated ignores and live rules
Prompt: "Beta.3 Rspack tests report zero cases. We have root test/ data, generated _build/test/ and edited .meteorignore exceptions while meteor test was running. How should we inspect the rules?"
Prompt: "3.6 Rspack tests report zero cases. We have root test/ data, generated _build/test/ and edited .meteorignore exceptions while meteor test was running. How should we inspect the rules?"
Pass if the agent separates generated root exclusions from user last-match rules and included parents, checks actual versions and rule invalidation, preserves handoff visibility and expected test names/counts.
Fail if it copies internal METEOR_IGNORE variables, ignores _build, or accepts zero tests as green.
## Case 37: Explicit native types with existing configuration
Prompt: "Our Meteor 3.6 TypeScript app explicitly wants native Meteor types. It directly lists zodern:types and @types/meteor, extends another tsconfig and excludes .meteor/**. Rspack builds pass but scoped meteor imports are any. How do we switch safely?"
Pass if the agent separates declarations from transpilation, removes direct zodern only for the authorized choice, generates explicitly, retains prior output on failure, merges per-package paths plus barrel files, removes competing ambient meteor types only after success, and keeps existing config/TypeScript version. Fail if it maps imports to the barrel or overwrites unrelated settings.
## Case 38: Existing provider stays selected
Prompt: "Our Meteor 3.6 app uses zodern:types directly and we do not want a type migration. Does meteor run regenerate native types? What about meteor types with a direct versus transitive zodern dependency, or our JS-only app with no config?"
Pass if the agent preserves provider choice, says ordinary commands do not mutate native declarations, explains successful skip for direct zodern versus generation with transitive-only presence and no-op without tsconfig/jsconfig. Fail if it removes providers or makes native generation part of every build.
## Case 39: Legacy architecture configuration and tests
Prompt: "On the Meteor 3.6 paired Rspack integration, we need a small legacy bootstrap and separate legacy tests while retaining the modern client. Where should entries and config conditions live, and how do rebuilds behave for each program? Does a passing modern headless run prove legacy support?"
Pass if the agent uses mainModule/testModule maps, gates isLegacy/arch as config callback flags, preserves shared entry behavior, enables webArchOnly false and checks exclude-archs, distinguishes legacy watched reload from modern HMR and selects the legacy test program explicitly. Fail if it invents runtime flags/automatic legacy config files or treats user-agent override as old-engine emulation.
## Case 40: 3.6 dependencies and immutable pnpm ownership
Prompt: "Our Meteor 3.6 pnpm workspace app has rspack@1.4.0, tools-core@1.4.0, outdated npm dependencies and autoInstallDeps false. Which minimums and categories must we reconcile before immutable CI?"
Pass if the agent uses integration 3.0.0, core/CLI 2.2.7, dev-server 2.2.1, SWC 1.16.2, Rsdoctor 1.6.4, runtime helpers 0.5.23 and detected React refresh 2.0.2/0.19.0, preserves nested app manifest/root pnpm lockfile and frozen installs, and avoids unnecessary swc-loader/global peer flags or CI mutation.
@@ -1,15 +1,18 @@
# Meteor 3.6 dependencies and workspaces
Workspace integration starts with Meteor 3.6-beta.0; the current target is
beta.3. Select the [release pairing](rspack-config.md); `tools-core` has the
same version as `rspack` for these betas. Inspect the manifest and workspace
lockfile. Earlier 3.x releases retain their manually managed workspace setup.
Workspace integration starts with Meteor 3.6. Select the
[release pairing](rspack-config.md); `tools-core` and `rspack` both resolve
to 1.4.0. Inspect the manifest and workspace lockfile. Earlier 3.x releases
retain their manually managed workspace setup.
## Rspack 2 dependency set
| Package | Minimum selected by 3.6-beta.3 | Dependency category |
Meteor 3.6 pairs Atmosphere `rspack@1.4.0` and `tools-core@1.4.0` with npm
`@meteorjs/rspack@3.0.0`. Inspect the actual release and resolved packages.
| Package | Meteor 3.6 minimum | Category |
|---|---|---|
| `@meteorjs/rspack` | `3.0.0-beta.3` | Development |
| `@meteorjs/rspack` | `3.0.0` | Development |
| `@rspack/core`, `@rspack/cli` | `2.2.7` each | Development |
| `@rspack/dev-server` | `2.2.1` | Development |
| `@swc/core` | `1.16.2` | Development |
@@ -17,15 +20,10 @@ lockfile. Earlier 3.x releases retain their manually managed workspace setup.
| `@swc/helpers` | `0.5.23` | Runtime |
| React only: `@rspack/plugin-react-refresh`, `react-refresh` | `2.0.2`, `0.19.0` | Development |
Beta.0/beta.1 retain core/CLI/dev-server 2.2.0, SWC 1.15.32,
Rsdoctor 1.5.9, refresh plugin/runtime 2.0.0/0.17.0 and
`@meteorjs/rspack` 3.0.0-beta.1/beta.2 respectively. Do not copy beta.3's
minimums into a deliberately pinned earlier target.
`swc-loader@0.2.7` is a separately selected dependency when the beta.3 integration
needs it. Do not confuse the Meteor npm integration's major 3 with Rspack's
major 2. Do not substitute unreleased stable `@meteorjs/rspack@3.0.0` for this
beta's pin. Later releases require checking their own resolved pairing.
The Meteor npm integration's major 3 is distinct from Rspack's major 2.
`swc-loader` is not selected by the dependency manager; add a compatible
version only for an actual app-owned loader path. Later Meteor releases
require their resolved pairing.
Automatic installation updates the coordinated set. With
`meteor.autoInstallDeps: false`, inspect the warnings and prepare the changes
@@ -33,8 +31,8 @@ locally with the detected manager. `meteor update --npm` explicitly overrides
the opt-out for that invocation; keep it out of immutable CI builds.
On npm apps with installed Rspack 1.x core, CLI, or React Refresh peers,
the beta's automatic transition uses `--legacy-peer-deps`. Its manual warning
can omit that flag. If the reported dependency update fails with the confirmed
the coordinated automatic transition uses `--legacy-peer-deps`. The manual npm
command includes it. If the reported dependency update fails with the confirmed
old-peer conflict, apply the flag only to that coordinated npm install and
verify the resulting tree. Do not set a global npm option, add the flag to
pnpm/Yarn, or suppress unrelated peer errors. Prove the locked CI install
@@ -44,7 +42,7 @@ CI command and revisit it after the transition.
## Create a pnpm workspace
```bash
meteor create --release 3.6-beta.3 --pnpm my-workspace
meteor create --release 3.6.1 --pnpm my-workspace
cd my-workspace
meteor npm start
```
@@ -102,20 +100,21 @@ app-local source symlink that needs its apparent location calls for
linked package, client/server suite counts, and an extracted production bundle.
Move the bundle outside the checkout and test used workspace `.bin` commands
and adjacent resource reads without source access. Beta.3 repairs copied
and adjacent resource reads without source access. 3.6 repairs copied
command links, including cross-workspace links; verify older tools explicitly.
Keep generated links untouched and preserve the root manager/lockfile.
## Test selection and generated ignores
Verified on the beta.3 pairing; beta.0/beta.1 lack the complete fixes.
These rules apply to the Meteor 3.6 pairing. Earlier integrations need
their supported test-discovery workflow.
| Rule source | Decision |
|---|---|
| `.meteorignore`, then user `METEOR_IGNORE` | Last matching rule wins for Rspack test discovery. `!` can re-include a file only when its parents remain included. |
| Integration-generated exclusions | Scoped to the app root and entrypoint; distinct from user patterns. A root `test/` folder must not hide `_build/test/`. Do not set private `METEOR_IGNORE_*` variables to repair this. |
| Explicit user rule hiding the active build context | Still a configuration error. Keep Meteor-facing generated entries readable. |
| Edited ignore file during watch | Beta.3 invalidates the cached source scan. Check an actual inclusion/exclusion change and expected test names/counts. A zero-test exit is not success. |
| Edited ignore file during watch | 3.6 invalidates the cached source scan. Check an actual inclusion/exclusion change and expected test names/counts. A zero-test exit is not success. |
Use `meteor-testing` for setup. On a constrained earlier integration, use a
verified explicit test entry/import graph when eager discovery fails; keep
@@ -13,14 +13,18 @@ Match both integration packages to the Meteor release:
| 3.4.1 and 3.5 | `1.1.0` | `2.0.1` | Adds `replaceSwcConfig`, `persistDevFiles`, `enablePortableBuild`, and inherited `TOOL_NODE_FLAGS`. |
| 3.5.1 | `1.2.0` | `2.1.0` | Revised client polyfills and app-extension discovery; retains the v2 helper API. |
| 3.5.2 | `1.3.0` | `2.2.0` | Required-dependency diagnostics, mode isolation, TypeScript config dependency tracking, and full-app/TLA fixes. |
| 3.6-beta.0 | `1.4.0-beta360.0` | `3.0.0-beta.1` | Rspack 2.2.0, SWC React Compiler, workspace-aware installs and stable cache configuration. |
| 3.6-beta.1 | `1.4.0-beta360.1` | `3.0.0-beta.2` | Same Rspack 2 minimums, fixed TypeScript server rebuild lifecycle; Node 26.8.2. |
| 3.6-beta.3 | `1.4.0-beta360.3` | `3.0.0-beta.3` | Core/CLI 2.2.7, dev server 2.2.1, separate architecture graphs and corrected test ignores; Node 26.8.2. |
| 3.6 | `1.4.0` | `3.0.0` | Rspack core/CLI 2.2.7+, dev server 2.2.1+, React Compiler, workspace installs and separate architecture graphs. |
Inspect `.meteor/versions`, `package.json`, and the lockfile. Do not install an
arbitrary `@meteorjs/rspack` major to obtain one helper; upgrade the Meteor
release and its paired dependencies.
Meteor 3.4.4 backports static-asset fixes in `rspack@1.1.1` with
`webapp@2.1.4`; 3.5.5 uses `rspack@1.3.2` with `webapp@2.3.1`.
Both retain Rspack 1. Do not install the 3.6 npm integration to obtain these
fixes. For missing assets, stale response headers or rebuild resolver ordering,
use `meteor-debugging`'s build reference and verify the actual locked pairing.
## Required npm dependencies
With `rspack@1.3.0` and `tools-core@1.3.0` (Meteor 3.5.2), the shared manager
@@ -62,31 +66,31 @@ with dev dependencies available. When build-time dependency mutation is
prohibited, keep the opt-out in project configuration and resolve every warning
before CI. Do not rely on a container silently repairing an incomplete lockfile.
For Meteor 3.6-beta.0's coordinated npm peer transition, new dependency minimums,
For Meteor 3.6's dependency set and coordinated npm peer transition,
pnpm scaffold and workspace-owned installs, read
[3.6 dependencies and workspaces](meteor-3.6-workspaces.md).
Keep the existing manager and its frozen-install command in a workspace.
## Rspack 2 configuration boundary
Meteor 3.6-beta.0 supplies CSS through `css/auto`, persistent options under
Meteor 3.6 supplies CSS through `css/auto`, persistent options under
top-level `cache`, `externalsType: "commonjs2"`, and compatible output settings.
Preserve those defaults. Use `migrate-to-rspack` to review app overrides that
still contain `experiments.css`, `experiments.cache`, `output.libraryTarget`,
or a hand-written `webpack-merge` integration.
Rspack 2's built-in SWC loader no longer discovers `.swcrc` itself, but
`@meteorjs/rspack@3.0.0-beta.1` still reads Meteor's `.swcrc`, `swc.config.js`
`@meteorjs/rspack@3.0.0` still reads Meteor's `.swcrc`, `swc.config.js`
or `swc.config.ts` and passes options to the loader. Do not remove a working
Meteor SWC file based only on the generic Rspack migration guide. Inspect the
effective configuration; extend it with `Meteor.extendSwcConfig`.
## Configuration example
On Meteor 3.6-beta.3's pairing, use `Meteor.arch` and `Meteor.isLegacy` in
On Meteor 3.6's pairing, use `Meteor.arch` and `Meteor.isLegacy` in
the config callback for explicit architecture compilations. These are not
runtime APIs. Read [architecture entry points](architecture-entrypoints.md)
before selecting a legacy graph or npm transpilation rules; beta.0/beta.1
before selecting a legacy graph or npm transpilation rules; earlier Meteor integrations
do not supply the complete flow.
```javascript
@@ -113,10 +117,18 @@ module.exports = defineConfig(Meteor => ({
| `isDebug` | boolean | debug mode |
| `isRun` | boolean | `meteor run` |
| `isBuild` | boolean | `meteor build` |
| `arch` | string or undefined | Explicit client compilation architecture; unset for default/shared or server compilations. Meteor 3.6 pairing. |
| `isLegacy` | boolean | Explicit legacy compilation; Meteor 3.6 pairing. |
| `swcConfigOptions` | object | project-level SWC config (reuse in custom loaders) |
| `assetsContext` | string | name of the build-assets folder |
| `chunksContext` | string | name of the build-chunks folder |
For `arch`/`isLegacy`, inspect the coordinated 3.6 integration first. These
flags describe the config callback, not the application's runtime `Meteor`.
Use [architecture entries](architecture-entrypoints.md) for legacy/Cordova
selection, ES5 limits and separate test programs; earlier integrations retain
shared-client entries.
## Helpers
### `extendConfig`
@@ -355,9 +367,9 @@ Auto-added to `.gitignore`. To rename, set in `package.json`:
## TypeScript declarations
Rspack/SWC transpiles TypeScript but does not type-check it or select Meteor
package declarations. Meteor 3.6-beta.1 native declarations require explicit
`meteor types` and provider configuration. Ordinary builds and new templates
retain legacy providers. Keep working `zodern:types`/`@types/meteor` setups
package declarations. New Meteor 3.6 typed scaffolds select native declarations and generate them
during creation. Existing app upgrades preserve their provider. After cloning
or changing packages, native apps run `meteor types` before type-checking. Keep working `zodern:types`/`@types/meteor` setups
unless the task includes a provider migration; run the app's local `tsc`
separately. A configured checker such as the TypeScript scaffold's
`TsCheckerRspackPlugin` is a separate layer that can check during builds;
@@ -6,7 +6,7 @@ Enabling `meteor.modern` or adding Rspack does not itself make an app a PWA.
## Choose a starting point
These starters are documented for `3.6-beta.0`. Check the selected release's
These starters are documented for `3.6`. Check the selected release's
[CLI documentation](https://docs.meteor.com/cli/#meteorcreate) and generated
files rather than treating this as an exhaustive compatibility matrix.
+24 -7
View File
@@ -13,7 +13,7 @@ metadata:
area: data
tagline: "Write and debug Mongo queries in Meteor 3 (server async vs Minimongo, oplog vs change streams, indexes, selectors, modifiers)."
bundle: ["essentials", "fullstack"]
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -119,12 +119,23 @@ Meteor 3.5+ defaults to:
changeStreams -> oplog -> polling
```
Change streams require MongoDB 6+ on a replica set or sharded cluster, an
unordered observer, no `skip` or `limit`, and a selector Minimongo can compile.
An ineligible query falls through to the next configured driver. Oplog is
available only when `MONGO_OPLOG_URL` is configured.
With `mongo@2.6.0` (Meteor 3.6), change streams require MongoDB 6.0.9+
or 7+ on a replica set or sharded cluster. Earlier Meteor 3.5 packages accept
MongoDB 6+ but lack split-event handling and the new resync fence holds.
History-loss resynchronization already exists in the 3.5.3 source. Check
`.meteor/versions` for package updates/backports before selecting guidance.
All versions require an unordered observer, no `skip` or `limit`, and a
selector Minimongo can compile. An ineligible query or server falls through
to the next configured driver. Oplog requires `MONGO_OPLOG_URL` and its own
query eligibility check. A paginated query rejected by Change Streams is not
automatically polling-only; retain eligible oplog fallback.
Meteor 3.6, verified on beta.3's `mongo@2.5.2-beta360.3`, also declines
On MongoDB 6.0.8, do not force 3.6's Change Streams driver. Verify the selected
oplog/polling fallback or plan a database upgrade with backups and compatibility
checks. See [large events and recovery](references/change-streams.md) for
fragmented events, lost history and write-fence reconciliation.
Meteor 3.6 with `mongo@2.6.0` also declines
`$where`/`$near` selectors for Change Streams: live events cannot be re-matched
with MongoDB semantics by Minimongo. Oplog declines these selectors too, so
verify a usable polling fallback. Minimongo-incompatible projections also
@@ -132,10 +143,16 @@ require fallback. A MongoDB-supported projection rejected by Minimongo is a
driver boundary, not a reason to rewrite the publication or schema. Verify
fallback before proposing that broader change. Test initial and live results;
do not strip the selector, projection or pagination merely to force Change Streams.
Earlier 3.5/beta.1 packages can choose the wrong driver for `$where`/`$near`;
The 3.5.5 backport (`mongo@2.5.2`) also declines `$where`/`$near`.
Earlier 3.5 packages can choose the wrong driver;
on a constrained release, use a supported polling configuration or an
authorized compatible upgrade and rerun the exact query.
Meteor 3.4.4, 3.5.5 and 3.6.1 add further live-selector guards for `$type`,
negated `$elemMatch`, PCRE-only regexes and divergent collations. Preserve
the query and verify polling, using the exact package pairs and earlier-version
branches in [selector compatibility](references/selectors-modifiers.md#live-selector-compatibility).
On Meteor 3.5+, override the app-wide order with
`METEOR_REACTIVITY_ORDER=oplog,polling` or:
@@ -0,0 +1,26 @@
# Change Streams recovery
Inspect `.meteor/versions`, MongoDB server version/topology, selected reactivity
order and the first failing observer event. `mongo@2.6.0` shipped with Meteor
3.6 requires MongoDB 6.0.9+ or 7+ for `$changeStreamSplitLargeEvent`. Earlier
3.5 packages accepted MongoDB 6+. The 3.5.3 source already reopens and resyncs
after lost history; it lacks split-event assembly, residual `BSONObjectTooLarge`
classification and the new fence holds during resync. Do not describe all
recovery as new in 3.6. An independent package update/backport must be checked
by its resolved version and source.
| Evidence | Action |
|---|---|
| MongoDB 6.0.8 or standalone server | Verify fallback to a configured eligible driver. Oplog needs `MONGO_OPLOG_URL`; otherwise retain polling. Do not remove the server eligibility check. |
| Change event exceeds the 16 MB BSON event limit | The driver splits and reassembles the event before delivering it to observers or releasing its write fences. This does not increase MongoDB's document size limit. |
| Connection drops between fragments | The driver retains the partial event and resumes after the last received fragment. Do not implement application-level fragment delivery or discard events by timestamp. |
| Lost change-stream history, another non-resumable error, or residual `BSONObjectTooLarge` | The driver reopens the stream and resynchronizes observers against the collection before releasing pending writes. Retrying the unusable resume token indefinitely is not recovery. |
| Client remains stale after recovery | Compare initial and live query results, observer changes and DDP messages for the same records. Use `meteor-debugging` to locate the first divergence. Preserve selectors/projections and test a supported fallback if needed. |
Reproduce large-event and reconnect behavior in a disposable database before
changing production reactivity. A transient resumable network error is not
proof that stream history was lost. Do not delete application data, clear
Minimongo or bypass write-fence coordination to hide the symptom.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/performance/change-streams-observer-driver.md
@@ -103,7 +103,7 @@ disables change streams without evaluating the release fix.
## Case 10: Reactive selector fallback
Prompt: "Our Meteor 3.6-beta.3 app uses mongo 2.5.2-beta360.3, Change Streams first, and reactive $near and $where queries. Should I remove these selectors and projections so all queries use Change Streams?"
Prompt: "Our Meteor 3.6 app uses mongo 2.6.0, Change Streams first, and reactive $near and $where queries. Should I remove these selectors and projections so all queries use Change Streams?"
Pass if the agent preserves semantics, explains local matcher limitations and Change Streams/oplog rejection, verifies polling fallback plus initial and live results; retains skip/limit/projection eligibility checks.
@@ -111,8 +111,61 @@ Fail if it forces Change Streams by stripping constraints or says configuring MO
## Case 11: Older selector near miss
Prompt: "The same $near reactive query is wrong on Meteor 3.5.2 and we must stay pinned. Does the beta.3 fix already apply, and what can we try without changing results?"
Prompt: "The same $near reactive query is wrong on Meteor 3.5.2 and we must stay pinned. Does the 3.6 fix already apply, and what can we try without changing results?"
Pass if the agent checks resolved package/backports, does not claim the fix ships in 3.5.2 and offers supported polling configuration with query/live-update verification.
Fail if it promises fixed automatic eligibility or rewrites away the selector.
## Case 12: Change-stream selector fallback
Prompt: "Our Meteor 3.6 app resolves mongo@2.6.0 and uses $where and $near queries. MongoDB accepts them but Change Streams are not selected. Would adding MONGO_OPLOG_URL force realtime support? A second app is pinned to 3.4."
Pass if the agent distinguishes query validity from observer eligibility, explains both driver limitations and polling fallback, preserves older oplog/polling behavior and avoids forcing core Change Streams or rewriting the DB without need.
## Case 13: MongoDB version floor
Prompt: "Our Meteor 3.6 app resolves mongo@2.6.0 on a MongoDB 6.0.8 replica set. Change Streams stopped being selected. Should I force the driver or remove publication limits?"
Pass if the agent checks the 6.0.9+/7+ floor, preserves query semantics, verifies eligible oplog/polling fallback and plans any database upgrade with backup/compatibility checks. Fail if it forces Change Streams or treats all MongoDB 6 releases as eligible.
## Case 14: Large events and interrupted recovery
Prompt: "On Meteor 3.6 with mongo@2.6.0 and MongoDB 8, an event is split and the connection drops between fragments. Another run logs ChangeStreamHistoryLost or BSONObjectTooLarge. Should we release pending writes immediately or drop old events?"
Pass if the agent distinguishes retained partial-event resume from non-resumable reopen/resync, waits for reassembly/reconciliation before pending write completion and verifies observer/DDP state. Fail if it delivers fragments, drops events by timestamp, expands the document limit or bypasses fences.
## Case 15: Earlier Change Streams package
Prompt: "Our Meteor 3.5.3 app resolves mongo@2.5.1 on MongoDB 6.0.8. Does the 3.6 split-event recovery contract apply already?"
Pass if the agent checks the resolved package/backport, preserves the earlier MongoDB 6+ eligibility branch and does not promise the full 2.6.0 split-event/fence contract on 2.5.1. It recognizes existing 3.5.3 history-loss resync. Fail if it claims no earlier recovery exists, imposes the new server floor on unchanged older packages or claims their recovery is identical.
## Case 16: pagination does not rule out every fallback
Prompt: "On Meteor 3.6, skip/limit disqualifies our query from Change Streams. We have MONGO_OPLOG_URL. Does that automatically mean polling is the only option?"
Pass if the agent checks oplog's separate cursor eligibility and retains it when eligible, then polling as fallback. Fail if it treats every Change Streams limitation as an oplog limitation or removes pagination to force a driver.
## Case 17: Patched live-selector fallback
Prompt: "After Meteor 3.6.1 (mongo@2.6.1/minimongo@2.2.2), a publication using $type and $not with $elemMatch polls even though Change Streams worked before. We also use PCRE regexes and collation strength 4. Should we remove those filters or force the old driver?"
Pass if the agent: Recognizes semantic-divergence guards, preserves authorization/query semantics and a usable polling fallback, checks initial/live/removal results and performance without bypassing guards. Distinguishes ordinary supported selectors/collation.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 18: 3.4 backport does not add Change Streams
Prompt: "On Meteor 3.4.4 with mongo@2.3.2 and minimongo@2.1.2, $type causes an oplog query to poll. Should I set METEOR_REACTIVITY_ORDER=changeStreams,polling? Another app is still on 3.4.3."
Pass if the agent: Recognizes the patched selector guard but no core Change Streams/order settings on 3.4.4. Uses its existing oplog/polling path, checks the older app package pairing and does not assume the guard was already present.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 19: Ordinary selector near miss
Prompt: "We use Meteor 3.5.5 with mongo@2.5.2/minimongo@2.2.0_2, a plain equality selector and collation {locale:"en",strength:2}, without caseLevel. Must every query now poll?"
Pass if the agent: Does not generalize divergence guards to every selector or collation. Checks actual driver eligibility and configuration, preserving a supported live driver when eligible.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -10,6 +10,37 @@
{ "address.city": "Berlin" } // dotted path
```
## Live-selector compatibility
MongoDB can evaluate queries that Minimongo cannot faithfully re-match against
live events. These release/package pairs add explicit guards:
| Meteor | `mongo` | `minimongo` |
|---|---|---|
| 3.4.4 | 2.3.2 | 2.1.2 |
| 3.5.5 | 2.5.2 | 2.2.0_2 |
| 3.6.1 | 2.6.1 | 2.2.2 |
Inspect `.meteor/versions`; do not infer a backport from the minor release.
The guards decline live matching for `$type`, `$elemMatch` nested under
`$not`/`$nor`, PCRE syntax outside JavaScript regex semantics, and collation
options whose semantics diverge. Examples include collation strength above 3,
strength 2 with `caseLevel`, and enabled `backwards` or `normalization`.
This is not a blanket ban on collation or ordinary regex selectors.
On these packages, oplog and, where available, change streams decline the
affected query. Verify a usable polling fallback and compare initial results
with subsequent updates, removals and resubscriptions. Preserve authorization,
field projection and selector meaning; do not remove an operator to force a
faster driver or patch out the guard. Plan for polling's performance cost with
appropriate indexes and bounded queries.
Meteor 3.5+ can select polling with the documented reactivity order. Meteor
3.4.4 has no core change streams or `METEOR_REACTIVITY_ORDER`; use its existing
oplog/polling path. Older packages lack these guards and may select a live
driver whose results diverge. Check a compatible security update or use a
supported polling configuration for that release, then rerun the exact query.
## Modifiers
```javascript
+1 -1
View File
@@ -14,7 +14,7 @@ metadata:
meteor: ">=3.0"
area: build
tagline: "Build and maintain Meteor 3 Cordova apps for Android and iOS, including plugins, native artifacts, and hot code push."
docs_synced_at: "2026-09-08"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -305,3 +305,26 @@ enabled defaults. Fail if it treats the reporter statement as proof for 3.5, set
every modern feature true, removes the legacy opt-out, or requires a new binary
solely for changing JavaScript output with unchanged native compatibility and
supported WebViews.
## Case 28: Modern Cordova fetch registration
Prompt: "Importing meteor/fetch fails only in our modern Cordova 3.6 build. Does 3.6 require adding a polyfill? We also maintain an older app with meteor.modern.cordova false."
Pass if the agent checks fetch 0.2.1 and architecture/overrides, explains modern registration fix and actual WebView verification, preserves legacy mapping and earlier constraints.
Fail if it adds a global polyfill first, forces modern output on the legacy app or promises unsupported WebView APIs.
## Case 29: Native plugin requires a new binary
Prompt: "We deployed Meteor 3.6.1 but installed iOS and Android apps still embed cordova-plugin-meteor-webapp@2.0.4. Can HCP alone deliver the 2.0.5 manifest security fix? Our older backend uses 3.4.4."
Pass if the agent: Requires a rebuilt/distributed native binary for 2.0.5, verifies its plugin metadata rather than treating `.meteor/versions` as the Cordova plugin inventory, recognizes selection on all three patched releases and preserves backend compatibility for old installations. Does not use a compatibility hash override to claim native code changed.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 30: Rejected native manifest and legitimate assets
Prompt: "A 2.0.5 native webapp plugin rejects a manifest asset ../outside.js and version ../release. Another legitimate iOS asset contains a literal percent escape. Should we disable validation or decode all filenames?"
Pass if the agent: Repairs the producer to safe relative paths/single version directory names, preserves validation and legitimate filename/platform semantics, and verifies download/activation on both platforms. Does not blanket-decode paths or bypass compatibility.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -19,10 +19,27 @@ binary and preserve backend compatibility for old installations. Do not make
silence a platform/plugin mismatch. Such an override needs a verified native
and JavaScript API contract across the installed client population.
## Rejected asset manifests
Meteor 3.4.4, 3.5.5 and 3.6.1 select `cordova-plugin-meteor-webapp@2.0.5`,
which validates native asset paths and version directories. Inspect the
installed binary's plugin version: older binaries need a new tested build
to receive these checks; a server update or HCP cannot deliver them.
For a rejected manifest, inspect the specific asset or version value. Asset
paths must be nonempty, relative and free of traversal/null components;
version values must be safe single directory names, excluding reserved
download directories. Correct the producer or malformed manifest instead of
disabling validation or forcing a compatibility hash. Preserve valid filenames:
platform handling of percent escapes differs, so do not blanket-decode or
rename every percent-containing path. Verify download, activation and rollback
on each target platform.
## Find the failed stage
Inspect `.meteor/versions` for HCP packages (`hot-code-push`, `autoupdate`,
`reload`) and the native webapp plugin; minimal/custom apps may omit them.
`reload`); inspect the native build's Cordova plugin metadata for the webapp
plugin version. Minimal/custom apps may omit HCP packages.
The default `meteor-base` setup supplies HCP. Compare an installed native
build with its intended server, not just with a successful browser reload.
@@ -156,6 +156,7 @@ and the Meteor release label alone do not identify the loaded Atmosphere code.
| Confirmed signature | Fixed package / release | Decision |
|---|---|---|
| Importing `fetch` fails only in a modern Cordova program | Meteor 3.6 with `fetch@0.2.1` | The package registers its native implementation for the `modern` architecture, including modern Cordova. Check the selected architecture, resolved package and local overrides; retest on the actual WebView before adding a polyfill. Earlier packages lack this registration. `meteor.modern.cordova: false` retains the legacy path; this fix does not add browser API support to an old WebView. [Fix](https://github.com/meteor/meteor/pull/14781) |
| Early Meteor 3 Cordova startup fails after adding accounts, with a misleading missing `core-js` error from `url/legacy.js` | `url@1.3.5`, shipped in Meteor 3.1 | Check for a shadowing `packages/url` override before choosing a compatible fixed package/release, then verify account startup. Adding app-level `core-js` or removing accounts does not repair the URLSearchParams integration. [Fix](https://github.com/meteor/meteor/pull/13459) |
| `Map.keys`, `Map.entries` or iteration fails in a modern library only in the Cordova bundle | `ecmascript-runtime-client@0.13.0`, shipped in Meteor 3.4.1 | Check whether legacy polyfills replaced native Map/Set/Symbol. Modern transpilation alone is not this fix. Use the compatible fixed package/release; for a constrained branch, verify an app-local backport against its supported WebViews. Do not edit the global Meteor cache or downgrade unrelated UI libraries. [Meteor 3 confirmation](https://forums.meteor.com/t/solved-vue3-does-not-work-with-cordova/61109/10) |
| Meteor 3.4 Rspack `android-device`/iOS development produces SockJS `Invalid frame header` or proxy errors, including in a desktop browser attached to that native run | Atmosphere `rspack@1.1.0`, shipped in Meteor 3.4.1 | Use the fixed native integration that skips the desktop HMR bootstrap/proxy. Confirm the native run-mode reproduction and resolved package; healthy fixed versions or production proxy failures need separate diagnosis. [Fix](https://github.com/meteor/meteor/pull/14226) |
@@ -170,3 +171,4 @@ Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/about/cordova.m
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/cli/index.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/cli/environment-variables.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/generators/changelog/versions/3.3.2.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/generators/changelog/versions/3.6.0.md
@@ -70,9 +70,9 @@ Pass if the agent says `this.error` stops the subscription and sends the error
to the client. It should recommend a separate data or logging channel for a
non-fatal warning.
## Case 8: optimistic resubscribe in beta.1
## Case 8: optimistic resubscribe in 3.6
Prompt: "With NO_MERGE_NO_HISTORY and a pending optimistic method write, resubscribing sends another added for the same document. What changes with ddp-client 3.4.2-beta360.1, and who clears documents on unsubscribe?"
Prompt: "With NO_MERGE_NO_HISTORY and a pending optimistic method write, resubscribing sends another added for the same document. What changes with ddp-client 3.4.2, and who clears documents on unsubscribe?"
Pass if the agent: Describes merging server fields into the saved snapshot while stub values remain visible until writes settle. Retains application-owned cleanup; does not claim new history or removals on unsubscribe. Scopes cleanup to owned data instead of clearing a shared collection or prescribing private collection handles as a generic fix.
Fail if it contradicts these boundaries or invents unsupported APIs.
@@ -81,13 +81,13 @@ Fail if it contradicts these boundaries or invents unsupported APIs.
Prompt: "Our Meteor 3.5.2 client throws Server sent add for existing id only while a stub write is outstanding during resubscribe. Is duplicate-added tolerance already guaranteed on this version?"
Pass if the agent: Checks client package and pending-write path, identifies the beta.1 fix boundary, and recommends a compatible tested upgrade or app-level sequencing. Does not claim all older clients support that path or switch publication strategies without considering ownership/cleanup.
Pass if the agent: Checks client package and pending-write path, identifies the 3.6 fix boundary, and recommends a compatible tested upgrade or app-level sequencing. Does not claim all older clients support that path or switch publication strategies without considering ownership/cleanup.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 10: Fieldless retained document
Prompt: "On beta.1, after stopping a NO_MERGE_NO_HISTORY subscription, an ID-only resubscription throws Cannot convert undefined or null to object. DDP added has no fields. Is this the pending-stub fix, and what should beta.3 preserve?"
Prompt: "On Meteor 3.5.2, after stopping a NO_MERGE_NO_HISTORY subscription, an ID-only resubscription throws Cannot convert undefined or null to object. DDP added has no fields. Is this the pending-stub fix, and what should 3.6 preserve?"
Pass if the agent distinguishes mongo fieldless replication from ddp-client pending-stub fix; beta.3 missing fields are a no-op preserving retained values, wire cleared removes fields while null sets a value, and later updates are tested.
Pass if the agent distinguishes mongo fieldless replication from ddp-client pending-stub fix; 3.6 missing fields are a no-op preserving retained values, wire cleared removes fields while null sets a value, and later updates are tested.
Fail if it deletes retained fields for absent fields, publishes dummy fields, clears a shared collection, confuses null with removal, or promises unsubscribe removals.
@@ -15,7 +15,7 @@ messages become changes, a `changed` for an unknown ID becomes an add, and a
`removed` for an unknown ID is ignored.
There is an earlier-client exception while an optimistic method write is
pending. Meteor 3.6-beta.1's `ddp-client@3.4.2-beta360.1` handles a repeated
pending. Meteor 3.6's `ddp-client@3.4.2` handles a repeated
`added` by merging server fields into the saved server snapshot; the stub's
visible value remains until outstanding writes settle. Earlier clients can
throw `Server sent add for existing id` in this path. Inspect the client's
@@ -23,12 +23,12 @@ resolved version and reproduce with a pending write before relying on the fix.
It also covers resubscribing under `NO_MERGE_NO_HISTORY`; it does not add
server history, ownership merging or unsubscribe removals to that strategy.
A separate fix is verified in Meteor 3.6-beta.3's
`mongo@2.5.2-beta360.3`: DDP can omit empty `fields`, including an ID-only
A separate fix is verified in Meteor 3.6's
`mongo@2.6.0`: DDP can omit empty `fields`, including an ID-only
publication's repeated `added` after a retained full document. The resulting
fieldless change is a no-op, preserving existing fields, and later updates
continue. Earlier packages can throw `Cannot convert undefined or null to
object` in this path even with beta.1's pending-stub fix. Capture the wire
object` in this path independently of pending optimistic writes. Capture the wire
message and loaded client packages before selecting the fix. Explicit field
clears still remove fields: the wire uses `cleared: ["fieldName"]`, which
parses into `fields: { fieldName: undefined }`. `fields: { fieldName: null }`
+2 -2
View File
@@ -19,7 +19,7 @@ metadata:
area: data
tagline: "Build and debug Meteor 3 React interfaces (Rspack scaffold, reactive data hooks, Suspense, Fast Refresh, and testing)."
bundle: ["react"]
docs_synced_at: "2026-09-23"
docs_synced_at: "2026-10-07"
license: MIT
---
@@ -156,7 +156,7 @@ an optional SVGR rule; the TypeScript skeleton demonstrates type checking.
Compose custom rules through `defineConfig` from `@meteorjs/rspack` without
replacing Meteor's SWC defaults.
For React Compiler, the Meteor 3.6-beta.0 pairing supports SWC; older pairings
For React Compiler, the Meteor 3.6 pairing supports SWC; older pairings
can retain Babel. Read [compiler setup](references/build-refresh-and-testing.md#react-compiler)
for React-version targets and runtime requirements.
@@ -62,10 +62,11 @@ Meteor's parser, helper, and refresh defaults unless reconstructed exactly.
## React Compiler
Meteor 3.6-beta.0 pairs Atmosphere `rspack@1.4.0-beta360.0` with npm
`@meteorjs/rspack@3.0.0-beta.1` and Rspack 2.2.0. Beta.1 pairs
`rspack@1.4.0-beta360.1` with `@meteorjs/rspack@3.0.0-beta.2` and retains
that Rspack minimum. Both pairings support the
Meteor 3.6 pairs Atmosphere `rspack@1.4.0` with npm
`@meteorjs/rspack@3.0.0`, core/CLI 2.2.7 and React Refresh plugin/runtime
2.0.2/0.19.0. Use the
[coordinated table](../../meteor-modern-build-stack/references/meteor-3.6-workspaces.md#rspack-2-dependency-set)
for the complete set. Do not inject a duplicate refresh stack. This supports the
SWC React Compiler path introduced upstream in Rspack 2.1. Inspect the resolved
packages and React major before selecting it. Earlier Meteor/Rspack pairings
can keep `babel-plugin-react-compiler` with `babel-loader`; do not copy the
@@ -185,14 +185,14 @@ assumes the Meteor 3 release supplies every package feature.
## Case 22: React 19 SWC compiler
Prompt: "Our Meteor 3.6-beta.0 app has the paired Rspack packages and React 19. Enable React Compiler without a second Babel pass."
Prompt: "Our Meteor 3.6 app has the paired Rspack packages and React 19. Enable React Compiler without a second Babel pass."
Pass if the agent: Uses extendSwcConfig and jsc.transform.reactCompiler true with automatic React runtime, preserves Meteor defaults, and verifies production/component/refresh behavior without adding duplicate transforms.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 23: React 18 compiler target
Prompt: "We upgraded the build to Meteor 3.6-beta.0 but must retain React 18. Can SWC React Compiler work without a React major upgrade?"
Prompt: "We upgraded the build to Meteor 3.6 but must retain React 18. Can SWC React Compiler work without a React major upgrade?"
Pass if the agent: Keeps React 18, selects target 18 and react-compiler-runtime, verifies installed dependencies, and does not force React 19 or copy the React 19 boolean setting alone.
Fail if it contradicts these boundaries or invents unsupported commands.
@@ -203,3 +203,9 @@ Prompt: "This app stays on Meteor 3.5.2 with its paired Rspack packages and Babe
Pass if the agent: Rejects assuming the new SWC path exists in the old pairing, retains the required Babel transform or requests a scoped paired upgrade, and inventories other Babel plugins.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 25: 3.6 React Compiler with an older React major
Prompt: "On Meteor 3.6 with the paired Rspack integration, our app stays on React 18 and has a custom React Refresh plugin. What dependency pairing and React Compiler path should we review? The other app stays on Meteor 3.5.2."
Pass if the agent selects the current coordinated 3.6 integration/refresh versions, preserves automatic refresh injection without duplication, uses extendSwcConfig and target 18/runtime when choosing the supported SWC compiler, and retains the older app's compatible Babel/compiler path without forcing React 19 or Rspack 2.
+33 -10
View File
@@ -16,7 +16,7 @@ metadata:
area: security
tagline: "Audit and harden Meteor 3 apps (`check()` coverage, `this.userId` guards, browser-policy CSP, rate limits, oauth-encryption)."
bundle: ["essentials", "fullstack"]
docs_synced_at: "2026-09-23"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -38,8 +38,29 @@ reaches users are methods (write paths) and publications (read paths).
reset, resource creation).
6. If the app uses OAuth, set `oauthSecretKey` to encrypt provider
secrets at rest.
7. Remove `allow` / `deny` rules. They are legacy and easy to misuse;
use methods instead.
7. Replace permissive client-write rules with authenticated methods. Keep
protective `deny` rules until an equivalent method-only policy is active;
Accounts can add an owner-only `profile` allowance even without an app
`allow` rule. See the profile policy below.
## Framework security patches
Check package versions. For 3.4.4/3.5.5/3.6.1 Accounts expiry,
OAuth setup and backports, use `meteor-accounts`; for live-selector fallback,
use `meteor-mongo-minimongo`. Security backports do not include every 3.6 feature.
## Accounts collection and profile policy
In Meteor 3.6's fixed Accounts pairing, custom users collections receive account
indexes and, when client mutation methods exist, the default owner-only profile
allowance. Configure the collection on both runtimes before installing guards.
Keep `Meteor.users.deny({ update: () => true })` on the selected collection when
all client profile writes must be blocked, until an equivalent policy is proven.
`defineMutationMethods: false` supplies a method-only collection without those
validators; Meteor 3.6 still creates account indexes. Earlier packages require
checking their setup behavior. See `meteor-accounts` for collection identity,
secondary Accounts instances and package boundaries. Never trust roles stored
in a client-editable profile.
## Method guard checklist
@@ -133,13 +154,15 @@ Meteor 3.0 through 3.4, matchers must stay synchronous. Use a fixed rule,
precomputed synchronous state, or upgrade rather than awaiting Mongo there.
The default rule (5 in 10s per matched method per DDP connection for login / signup / password
reset) ships with `accounts-base`. Meteor 3.6-beta.1's
`accounts-base@3.4.0-beta360.1` also matches `requestLoginTokenForUser`;
`accounts-passwordless@3.1.2-beta360.1` validates its complete payload before
lookup/creation. Inspect both package versions on older apps. Remove the
reset) ships with `accounts-base`. Passwordless request validation and default
`requestLoginTokenForUser` throttling ship together in 3.6 and backports
3.5.5/3.4.4. Inspect both resolved packages; use `meteor-accounts` for pairings
and atomic-completion differences. Earlier packages need explicit validation
and abuse protection. Handle callback rate errors without immediate retries. Remove the
default rule with `Accounts.removeDefaultRateLimit()` only if you replace it.
For cookie hardening on `accounts-base@3.4.0-beta360.1`, use `meteor-accounts`:
For cookie hardening on `accounts-base@3.4.0` (3.6) or the `3.3.2`
backport (3.5.5), use `meteor-accounts`:
trusted origins, valid tokens, no-store responses and `SameSite=Strict` have
specific migration checks. Its 30-per-10-second HTTP endpoint limit is per
client address, separate from DDP. HttpOnly storage does not stop active
@@ -190,8 +213,8 @@ Meteor.methods({
## Anti-patterns
- `Collection.allow` / `Collection.deny` rules. Legacy; easy to combine
into a soft-fail. Replace with methods.
- Permissive client-write rules for sensitive data. Replace with guarded
methods; do not remove protective denial before proving equivalent policy.
- `Meteor.settings.public.<secret>`. The client sees `public`. Move
secrets to the top level of `settings.json`.
- Publish the entire `Meteor.users` collection. Always project (e.g.
@@ -54,7 +54,7 @@ generic `services.<provider>.secret` path.
Prompt: "I have `Items.allow({ insert: () => true });` in my code. Is
that ok?"
Pass if the agent rejects allow/deny as a legacy pattern and rewrites the
Pass if the agent rejects the permissive client-write rule and rewrites the
mutation as a `Meteor.method` with `check()` + `this.userId` guard.
## Case 6: async publication authorization
@@ -111,7 +111,20 @@ framework upgrade. Fail if it applies the current 3.5 API unconditionally.
## Case 12: Accounts hardening ownership
Prompt: "Review Meteor 3.6-beta.1 passwordless and HttpOnly-cookie abuse protections. Are their rate limits interchangeable, and does HttpOnly remove our XSS risk?"
Prompt: "Review Meteor 3.6 passwordless and HttpOnly-cookie abuse protections. Are their rate limits interchangeable, and does HttpOnly remove our XSS risk?"
Pass if the agent: Keeps security audit ownership and uses meteor-accounts for configuration details. Separates per-connection DDP and per-address HTTP limits, checks resolved beta packages, preserves method authorization and notes the in-memory DDP token remains exposed to active same-origin XSS.
Pass if the agent: Keeps security audit ownership and uses meteor-accounts for configuration details. Separates per-connection DDP and per-address HTTP limits, checks resolved packages, preserves method authorization and notes the in-memory DDP token remains exposed to active same-origin XSS.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 13: Protective profile denial during a method migration
Prompt: "On Meteor 3.6 we moved Accounts to a custom users collection. It has deny({update:()=>true}) and no application allow rules. Should we remove the deny rule now that our new updateProfile method exists? Should we remove the default passwordless rate rule to fix request errors?"
Pass if the agent preserves protective denial until equivalent method-only client-write protection is proven, accounts for core owner-only profile allowance, audits the guarded method, and keeps the default request limiter while handling errors. Fail if absence of app allow rules is treated as proof that direct profile writes are blocked.
## Case 14: Security backport scope
Prompt: "Audit our Meteor 3.5.5 app: accounts-base@3.3.2, accounts-password@3.3.1_2, accounts-passwordless@3.1.1_2. Can we rely on expiry, production OAuth setup restrictions and cookie/passwordless hardening? Does that mean all Meteor 3.6 security-related behavior is present and our application guards are redundant?"
Pass if the agent: Recognizes the concrete backports and hands detailed configuration to accounts, preserves app authorization/validation and distinct HTTP/DDP rate limits, and does not infer every 3.6 completion feature or eliminate XSS risk.
Fail if it contradicts these boundaries or invents unsupported APIs.
+11 -7
View File
@@ -18,7 +18,7 @@ metadata:
area: testing
tagline: "Set up and write Meteor 3 tests (`meteortesting:mocha`, async signatures, methods/publications, Playwright/Cypress E2E)."
bundle: ["fullstack"]
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -76,7 +76,7 @@ Test-mode conventions:
`*.app-spec[s].*` files, and sets `Meteor.isAppTest`.
For Meteor 3.5.2 Rspack full-app/TLA behavior, parent-`private` discovery
failures, and the jQuery harness regression fixed in Meteor 3.6-beta.0, read
failures, and the jQuery harness regression fixed in Meteor 3.6, read
[release-specific test setup](references/meteor-3.5.2-testing.md). Check
resolved packages and suite counts before applying a version-specific fix.
@@ -89,15 +89,19 @@ narrowing entrypoint imports. Restore every temporary focus or import change,
then run the normal affected suite. Read the configured entrypoint paths from
the project; do not invent paths, scripts, ports, or helpers.
For 3.6-beta.3 Rspack ignore exceptions or separate legacy test entries, use
For Meteor 3.6 Rspack ignore exceptions or separate legacy test entries, use
the [architecture and ignore boundaries](references/focused-runs.md#rspack-architecture-and-ignore-boundaries).
Check expected suites on each selected program; a modern zero-exit run is
not proof of legacy coverage.
## Method test (server-side)
Shared test entries also reach the browser. Use `assert` with strict methods;
server guards cannot hide static imports. See
[assertion imports](references/mocha-setup.md#assertion-imports).
```javascript
import assert from "node:assert/strict";
import assert from "assert";
import { Meteor } from "meteor/meteor";
import { Items } from "/imports/api/items";
@@ -113,7 +117,7 @@ if (Meteor.isServer) {
[{ title: "x", qty: 1 }],
);
const doc = await Items.findOneAsync(_id);
assert.equal(doc.title, "x");
assert.strictEqual(doc.title, "x");
});
it("rejects when unauthed", async function () {
@@ -143,7 +147,7 @@ if (Meteor.isServer) {
const cursor = await Meteor.server.publish_handlers["items.mine"]
.apply({ userId: "u1" }, []);
const docs = await cursor.fetchAsync();
assert.deepEqual(docs.map((d) => d._id), ["a"]);
assert.deepStrictEqual(docs.map((d) => d._id), ["a"]);
});
});
}
@@ -193,7 +197,7 @@ if (Meteor.isClient) {
it("filters by ownerId", function () {
Items.insert({ _id: "a", ownerId: "u1" });
Items.insert({ _id: "b", ownerId: "u2" });
assert.equal(Items.find({ ownerId: "u1" }).count(), 1);
assert.strictEqual(Items.find({ ownerId: "u1" }).count(), 1);
});
});
}
@@ -17,6 +17,8 @@ Pass if the agent uses
with a stubbed userId and awaits the Mongo result. Avoids
`Meteor.call`/`callAsync` from the test (which goes over the wire and
adds DDP overhead).
Any example that can reach the browser uses a compatible assertion import
and strict equality; a server guard alone does not isolate static imports.
## Case 3: publication test
@@ -28,6 +30,7 @@ Pass if the agent uses
{ userId: "u1" }, [])`, calls `.fetchAsync()` on the resolved cursor, and
asserts on the returned `_id`s. The `await` is required so the test also
works when the publication handler is async.
Keep array comparison strict when using the portable `assert` import.
## Case 4: --full-app DDP test
@@ -180,25 +183,25 @@ whether the browser UI starts. It offers an invocation-only extra package or
compatible fixed driver for a failing 1.6.0 harness, not for working 1.5.x.
Fail if it claims jQuery-free tests prove driver 1.6.0 cannot need jQuery.
## Case 19: restored driver in the 3.6 beta
## Case 19: restored driver in Meteor 3.6
Prompt: "Meteor 3.6-beta.0 resolves test-in-browser 1.6.1-beta360.0. My package
Prompt: "Meteor 3.6 resolves test-in-browser 1.6.2. My package
tests do not use jQuery. Should I add it to the app before running test-packages?"
Pass if the agent recognizes the restored driver dependency, adds no app or
unneeded package-test dependency, and verifies the UI and expected client/server
assertions. Fail if it repeats the 1.6.0 workaround unconditionally.
## Case 20: CI runtime for beta.1
## Case 20: CI runtime for 3.6
Prompt: "Adapt our Node 24 Meteor 3.5.2 test job for a deliberate upgrade to 3.6-beta.1. What runtime and native dependency checks belong in CI?"
Prompt: "Adapt our Node 24 Meteor 3.5.2 test job for a deliberate upgrade to 3.6. The target project's meteor node/npm commands report 26.13.0/11.20.0. What runtime and native dependency checks belong in CI?"
Pass if the agent: Verifies the selected Meteor runtime, uses Node 26.8.2/npm 11.19.0 for beta.1, rebuilds native deps and preserves frozen dependency installation and actual client/server test execution. Does not claim existing 3.5.2 jobs need Node 26.
Pass if the agent: Verifies the selected Meteor runtime, uses Node 26.13.0/npm 11.20.0 for 3.6, rebuilds native deps and preserves frozen dependency installation and actual client/server test execution. Does not claim existing 3.5.2 jobs need Node 26.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 21: Rspack test file exceptions
Prompt: "In beta.3 our Rspack CI should load *.tests.js only directly under imports/accounts, while excluding other *.tests.js. We also have a root test/ data directory. How should the file selection and pass check work?"
Prompt: "In 3.6 our Rspack CI should load *.tests.js only directly under imports/accounts, while excluding other *.tests.js. We also have a root test/ data directory. How should the file selection and pass check work?"
Pass if the agent uses ordered file/environment negation with included parents, distinguishes file selection from Mocha title filtering, preserves generated entries and checks expected names/counts.
@@ -206,8 +209,34 @@ Fail if it uses only MOCHA_GREP to prevent module evaluation, ignores _build/tes
## Case 22: Legacy program coverage
Prompt: "On beta.3 our testModule maps client and legacy separately. meteor test --once with current Chromium passes. Have we covered legacy browsers? What about the same setup pinned to beta.1?"
Prompt: "On Meteor 3.6 our testModule maps client and legacy separately. meteor test --once with current Chromium passes. Have we covered legacy browsers? What about the same setup pinned to 3.5.2?"
Pass if the agent requires explicit legacy program selection with webArchOnly false/no exclusion, expected cases per program and actual engine tests; explains beta.1 lacks the complete separate-graph flow.
Pass if the agent requires explicit legacy program selection with webArchOnly false/no exclusion, expected cases per program and actual engine tests; explains 3.5.2 lacks the complete separate-graph flow.
Fail if it claims a modern headless pass or user-agent spoof alone proves old-engine compatibility, or promises beta.3 behavior on beta.1.
Fail if it claims a modern headless pass or user-agent spoof alone proves old-engine compatibility, or promises 3.6 behavior on 3.5.2.
## Case 23: shared Rspack test entry cannot resolve assert/strict
Prompt: "On Meteor 3.5.1 with Rspack, meteor.testModule is 'tests/main.js'.
The method test imports assert from 'node:assert/strict' and guards its suite
with Meteor.isServer. The client build fails with Cannot find module
'assert/strict' for matched aliased key 'assert'. Fix it without weakening
assert.equal/assert.deepEqual checks or disabling browser tests."
Pass if the agent uses `import assert from "assert"`, changes equality calls
to `strictEqual`/`deepStrictEqual`, retains rejection assertions, and explains
why shared static imports reach the client despite runtime guards. It checks
both client and server execution and full-app mode if configured. Fail if it
installs `assert/strict`, suppresses the client build, or retains coercive
equality after changing the import.
## Case 24: isolated server assertion import near miss
Prompt: "Our Meteor 3.5.1 Rspack app maps meteor.testModule.client to
'client/tests.js' and meteor.testModule.server to 'server/tests.js'. Only the
server graph imports node:assert/strict, and both suites pass. Must we replace
it because a shared Blaze test example uses assert?"
Pass if the agent preserves the valid server import after checking no client
entry reaches it. Fail if it bans Node assertions everywhere or suggests a
runtime guard alone establishes server isolation.
@@ -49,8 +49,8 @@ the normal affected suite. Never commit a narrowed test graph or a focus marker.
## Rspack architecture and ignore boundaries
Meteor 3.6-beta.3 with `rspack@1.4.0-beta360.3` and
`@meteorjs/rspack@3.0.0-beta.3` supports separate `testModule.legacy` and
Meteor 3.6 with `rspack@1.4.0` and
`@meteorjs/rspack@3.0.0` supports separate `testModule.legacy` and
exact `web.browser`, `web.browser.legacy`, `web.cordova` entries. Preserve the
matching app entries in `--full-app` mode. Enable legacy development/test
programs with `modern.webArchOnly: false` and do not exclude that architecture.
@@ -58,11 +58,11 @@ Use `meteor-modern-build-stack` for graph/config details.
A modern headless `meteor test --once` does not exercise the legacy test
program. Select it explicitly and check test names/counts for both programs.
A user-agent override tests delivery, not an old JS engine. Beta.0/beta.1
A user-agent override tests delivery, not an old JS engine. Earlier integrations
and older Rspack integrations lack this complete flow; retain their supported
test graph or select a compatible paired upgrade.
For beta.3 eager Rspack test selection, `.meteorignore` precedes user
For Meteor 3.6 eager Rspack test selection, `.meteorignore` precedes user
`METEOR_IGNORE`; the last matching pattern wins. Keep parent directories
included before a file exception:
@@ -1,4 +1,4 @@
# Meteor 3.5.2 and 3.6 beta testing boundaries
# Meteor 3.5.2 and 3.6 testing boundaries
## Rspack full-app tests
@@ -38,8 +38,8 @@ known regression. Do not remove the actual app `private/` asset exclusion.
`test-in-browser@1.6.0`, shipped with Meteor 3.5.2, removes transitive jQuery.
Its released Blaze test UI can still require jQuery even when the package's
own tests do not. Meteor 3.6-beta.0 restores the driver's client dependency in
`test-in-browser@1.6.1-beta360.0`. Inspect the resolved driver independently of
own tests do not. Meteor 3.6 restores the driver's client dependency in
`test-in-browser@1.6.2`. Inspect the resolved driver independently of
the Meteor release and distinguish a harness startup failure from a failed
application assertion.
@@ -65,7 +65,7 @@ the tests themselves are jQuery-free. Prefer the fixed driver through a
compatible release when an upgrade is in scope. Do not add a production app
dependency to repair the test harness.
Earlier 1.5.x and the fixed 1.6.1 beta driver supply jQuery themselves; a
Earlier 1.5.x and the fixed 1.6.2 driver supply jQuery themselves; a
jQuery-free package does not need a new test dependency there. Tests that do
use jQuery should declare it explicitly regardless of transitive availability.
Confirm the browser UI loads and expected client and server assertions run;
@@ -7,6 +7,35 @@ meteor add meteortesting:mocha
meteor npm install --save-dev @types/mocha
```
## Assertion imports
A string `meteor.testModule`, such as `"tests/main.js"`, supplies a shared
entry that can be bundled for both client and server. Static imports resolve
before `if (Meteor.isServer)` runs. With the scaffold's `meteor-node-stubs`,
`node:assert/strict` can fail the Rspack browser build with
`Cannot find module 'assert/strict' for matched aliased key 'assert'`.
Use the scaffold's portable import and explicit strict methods:
```javascript
import assert from "assert";
assert.strictEqual(actual, expected);
assert.deepStrictEqual(actualItems, expectedItems);
await assert.rejects(() => rejectedOperation());
```
Changing the import alone would make `equal` and `deepEqual` coercive. Keep
strict assertions when porting an example from `node:assert/strict`.
Do not add a fictitious `assert/strict` package or disable client tests.
`node:assert/strict` remains valid in a genuinely server-only entry graph;
verify the actual `meteor.testModule` mapping and imports before changing it.
Build and execute the selected client and server suites, including the
configured full-app mode when used. A passing server suite alone does not
verify browser bundling. Evidence: [generated scaffold test](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/tools/static-assets/skel-blaze/tests/main.js)
and [browser stub map](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/npm-packages/meteor-node-stubs/map.json).
## Env vars
| Var | Purpose |
@@ -75,8 +104,8 @@ jobs:
```
Node version must match the bundled Node.js for the Meteor version: 3.0 uses
Node 20, 3.1 through 3.4 use Node 22, and 3.5.x / 3.6-beta.0 use Node 24.
Meteor 3.6-beta.1 through beta.3 use Node 26.8.2 and npm 11.19.0. The example above targets
Node 20, 3.1 through 3.4 use Node 22, 3.5.x uses Node 24, and 3.6.x uses
Node 26. The example above targets
3.1-3.4; adjust its Node selection for the actual job. Run `meteor node -v`
and `meteor npm -v` in the target app, rebuild native dependencies for that
runtime, and keep dependency installation frozen in CI.
+12 -10
View File
@@ -17,7 +17,7 @@ metadata:
meteor: ">=3.0"
area: build
tagline: "Configure Meteor app declarations, TypeScript and JavaScript IntelliSense, and reliable local/CI type-checking."
docs_synced_at: "2026-09-29"
docs_synced_at: "2026-10-07"
license: MIT
---
@@ -48,15 +48,15 @@ runs just because SWC itself only transpiles.
## Choose the provider
Native declarations start with 3.6-beta.1; this workflow is verified through
3.6-beta.3. Beta.3 adds the declaration corrections linked below.
Native package declarations are available on Meteor 3.6+.
| Target and intent | Decision |
|---|---|
| Meteor 3 before 3.6-beta.1 | Native `meteor types` is unavailable. Keep a working legacy provider; use the earlier-release branch in the provider reference if declarations are missing. |
| New or existing 3.6 native-types-capable app with working legacy types | Keep them unless native adoption is requested. Beta.1 TypeScript templates still install direct `zodern:types` and `@types/meteor`; a native fallback path is not opt-in. |
| Explicit native adoption on 3.6-beta.1 through beta.3 | Root config required. Checkpoint providers/config, remove direct `zodern:types`, generate successfully, then merge native resolution and remove overlapping ambient types. |
| JavaScript-only 3.6 beta app wanting IntelliSense | Use `jsconfig.json` and the same provider decision; no TypeScript source conversion or bundler switch is required. |
| Meteor 3 before 3.6 | Native `meteor types` is unavailable. Keep a working legacy provider; use the earlier-release branch in the provider reference if declarations are missing. |
| New Meteor 3.6 TypeScript, TypeScript + Tailwind, Svelte or Angular app | The scaffold selects native declarations and `meteor create` generates them. Keep that provider; regenerate after cloning or changing packages. |
| Existing app upgraded to 3.6 with working legacy types | Keep its provider and paths unless native adoption is requested. A runtime upgrade does not migrate them. |
| Explicit native adoption on 3.6+ | Root config required. Checkpoint providers/config, remove direct `zodern:types`, generate successfully, then merge native resolution and remove overlapping ambient types. |
| JavaScript-only 3.6+ app wanting IntelliSense | Use `jsconfig.json` and the same provider decision; no TypeScript source conversion or bundler switch is required. |
Read [declaration providers](references/declaration-providers.md) for the
ordered switch, native adapter/barrel distinction, inherited configuration,
@@ -66,8 +66,9 @@ TypeScript 7 compatibility, earlier providers and rollback. Read
## Verify the effective project
1. Confirm generation actually ran. A direct `zodern:types` makes
`meteor types` skip with exit zero; ordinary Meteor commands do not select
or regenerate native declarations on beta.1 through beta.3.
`meteor types` skip with exit zero; `meteor create` generates for the four native
scaffolds above. After cloning or changing packages, run `meteor types`
explicitly; normal `run`, `build`, `test` and `lint` do not regenerate them.
2. Use the app's local compiler. With native declarations, run
`meteor types && tsc --noEmit` in a package script; select the intended
config explicitly when the app has several projects.
@@ -91,8 +92,9 @@ TypeScript 7 compatibility, earlier providers and rollback. Read
| Ordinary imports work, scoped/sub-path imports fail | Explicit native barrel in `files` plus wildcard mapping to per-package adapters, not to the barrel. |
| Duplicate identifiers | Overlapping providers, inherited `types: ["meteor"]` and app ambient declarations before symlinks. |
| Peer types fail inside Meteor's package store | Consider `preserveSymlinks` for that specific boundary; it cannot remove duplicate providers. |
| User service fields are `unknown`, or OAuth signatures disagree | Use [application augmentation and provider signatures](references/declaration-providers.md#application-user-services-and-oauth-signatures); keep generated types unedited. |
| One community package remains untyped | Check whether the installed package publishes declarations. No generator can promise coverage for every Atmosphere package. |
| Generated relative imports or subscription handle types disagree with runtime | Check the beta.3 declaration corrections in [provider guidance](references/declaration-providers.md#compiler-configuration-compatibility), then regenerate and type-check. Keep generated files unedited and structural handles usable. |
| Generated relative imports or subscription handle types disagree with runtime | Check the native declaration contract in [provider guidance](references/declaration-providers.md#compiler-configuration-compatibility), then regenerate and type-check. Keep generated files unedited and structural handles usable. |
If the actual task is publishing package declarations, use the official
[Atmosphere package types documentation](https://docs.meteor.com/packages/7.writing-atmosphere-packages#typescript-types).
@@ -8,19 +8,22 @@ changing providers. Keep compiler upgrades separate from declaration changes.
| Project state | Action |
|---|---|
| Meteor 3 before 3.6-beta.1 | Native `meteor types` is unavailable. Retain working legacy declarations; use the `zodern:types` branch below when package declarations are missing. |
| Upgrading to 3.6-beta.1 with working `@types/meteor` or `zodern:types` | Keep the provider and paths unless the task includes adopting native declarations. Runtime upgrade alone does not switch providers. |
| Explicit native adoption on 3.6-beta.1 | Follow the ordered migration below. The command needs a root `tsconfig.json` or `jsconfig.json`. |
| Meteor 3 before 3.6 | Native `meteor types` is unavailable. Retain working legacy declarations; use the `zodern:types` branch below when package declarations are missing. |
| Upgrading to 3.6 with working `@types/meteor` or `zodern:types` | Keep the provider and paths unless the task includes adopting native declarations. Runtime upgrade alone does not switch providers. |
| Explicit native adoption on 3.6 | Follow the ordered migration below. The command needs a root `tsconfig.json` or `jsconfig.json`. |
| Direct `zodern:types` still installed | `meteor types` skips successfully and leaves both output trees unchanged. A zero exit is not proof of native generation. A transitive dependency alone does not select the legacy provider. |
The classic `typescript` package and Rspack/SWC transpile app source without
type-checking it themselves. A separately configured checker, such as the
TypeScript scaffold's `TsCheckerRspackPlugin`, is a distinct layer; preserve
a working checker when changing declarations. On beta.1, ordinary `run`, `build`, `test` and `lint` neither
generate nor select native declarations. New TypeScript templates also keep
the legacy provider first; a native fallback path is not an opt-in.
a working checker when changing declarations. On Meteor 3.6, ordinary `run`,
`build`, `test` and `lint` neither generate nor select native declarations.
New TypeScript, TypeScript + Tailwind, Svelte and Angular scaffolds select
native declarations and generate them during `meteor create`. Existing app
upgrades preserve their configured provider. After cloning or changing
packages, regenerate with `meteor types` before checking.
## Native adoption on Meteor 3.6-beta.1
## Native adoption on Meteor 3.6
1. Checkpoint provider dependencies, lockfile and TypeScript configuration.
Keep the app's local TypeScript compiler version fixed during this change.
@@ -93,25 +96,23 @@ Ordinary Meteor commands and a skipped native run do not delete either tree.
## Compiler configuration compatibility
For Meteor 3.6, the workflow above is verified through beta.3. Keep native
generation explicit; working legacy providers remain valid. After a paired
tool/package upgrade, rerun `meteor types` before checking the app.
If reaching beta.3 requires changing the framework release, use
`meteor-cli-installation` for retained local-data preparation before that
release starts; declaration regeneration is not a database migration.
On Meteor 3.6+, keep generation explicit after cloning or changing packages;
working legacy providers remain valid. If adopting native declarations also
requires a framework upgrade, use `meteor-cli-installation` for retained
local-data preparation before starting the new release.
| Native declaration symptom | Beta.3 verification and earlier-version boundary |
| Native declaration symptom | Meteor 3.6 behavior and checks |
|---|---|
| Relative imports/re-exports or ambient declarations break inside a generated package | Beta.3's generator preserves the declaration tree and ambient entry location and parses module structure. Beta.1 lacks these layout fixes. Inspect the package's shipped files, regenerate with the fixed tool and run the local checker; do not flatten or patch `.meteor/types`. A package that did not ship its referenced file still needs a package fix. |
| DDP imports or subscription handles disagree with runtime | Beta.3's `ddp-client`, `ddp-common`, `meteor` and `minimongo` declarations are corrected. Match resolved package versions and regenerate. Real `Meteor.subscribe` / DDP handles return `SubscriptionHandleWithId`; structural `Meteor.SubscriptionHandle` still requires only `ready()` and `stop()`. Do not require synthetic adapters to invent `subscriptionId`, or loosen all handles to `any`. |
| Relative imports/re-exports or ambient declarations break inside a generated package | Meteor 3.6's generator preserves the declaration tree and ambient entry location and parses module structure. Inspect the package's shipped files, regenerate with the fixed tool and run the local checker; do not flatten or patch `.meteor/types`. A package that did not ship its referenced file still needs a package fix. |
| DDP imports or subscription handles disagree with runtime | Meteor 3.6's `ddp-client`, `ddp-common`, `meteor` and `minimongo` declarations are corrected. Match resolved package versions and regenerate. Real `Meteor.subscribe` / DDP handles return `SubscriptionHandleWithId`; structural `Meteor.SubscriptionHandle` still requires only `ready()` and `stop()`. Do not require synthetic adapters to invent `subscriptionId`, or loosen all handles to `any`. |
These are declaration corrections, not new runtime methods. Internal Minimongo
helper declarations do not make those helpers a recommended public app API.
Keep earlier native/legacy setups when the app cannot change its release;
do not promise beta.3 declaration fixes on beta.1 or force a provider switch.
Keep a working legacy setup when the app cannot change its release;
Meteor before 3.6 does not provide native generation. Do not force a provider switch.
Inspect the installed npm `typescript`, not only the Atmosphere `typescript`
package: they are separate tools. Beta.1's TypeScript scaffold selects npm
package: they are separate tools. Meteor 3.6's TypeScript scaffold selects npm
TypeScript 7 and `moduleResolution: "bundler"`. Do not copy older docs'
`baseUrl` or `moduleResolution: "node"` into it.
[TypeScript 7 removes both options](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/).
@@ -120,6 +121,46 @@ targets, preserving aliases and their previous base directory. Keep an older
app's compatible compiler/configuration rather than upgrading its compiler
just to change Meteor declarations.
For `TS2882` on stylesheet side-effect imports with TypeScript 6+, inspect
`noUncheckedSideEffectImports`. Meteor 3.6 TypeScript templates set it to
`false`. Existing apps can select that setting or include explicit stylesheet
module declarations such as `declare module '*.css';` in a `.d.ts` file.
Do not disable unrelated checks or edit generated Meteor declarations.
## Application user services and OAuth signatures
Meteor 3.6 native declarations expose the mergeable `Meteor.UserServices`
interface. Extend it in an application-owned `.d.ts` file included by the
active config, preserving `Meteor.User.services` and its optionality:
```typescript
// imports/types/user-services.d.ts
import "meteor/meteor";
declare module "meteor/meteor" {
namespace Meteor {
interface UserServices {
internalSso?: { subject: string; tenantId: string };
}
}
}
```
Then `user.services?.internalSso?.tenantId` is typed without replacing all
services with `any`. Declare the actual stored shape and retain optional
fields; types do not publish private service data or authorize client access.
Do not add access tokens to client publications to satisfy a type error.
Regenerate after package changes, include the application declaration in
`tsc --listFiles --noEmit`, and check with the project's compiler. Do not edit
the generated adapter or native barrel.
For OAuth method/return-type errors, inspect the installed provider's native
declarations and API locus, regenerate and check the real call signature.
Corrected declarations do not convert a callback API into a Promise. On an
earlier Meteor release or a working legacy provider, inspect that provider's
augmentation contract instead of assuming native `UserServices` exists or
forcing a provider switch.
## Earlier-release legacy declarations
If required package declarations are missing on an older Meteor 3 app:
@@ -3,37 +3,37 @@
Use the current skill and relevant references in a fresh conversation or a
disposable app. Keep pass/fail criteria hidden until the run finishes.
## Case 1: native provider adoption in a beta.1 app
## Case 1: native provider adoption in a 3.6 app
Prompt: "Our Meteor 3.6-beta.1 TypeScript app was just created, and we explicitly want native declarations. We have direct zodern:types and @types/meteor, tsconfig extends, a files array and meteor in compilerOptions.types. Give the migration and CI sequence."
Prompt: "Our existing TypeScript app was upgraded to Meteor 3.6, and we explicitly want native declarations. We have direct zodern:types and @types/meteor, tsconfig extends, a files array and meteor in compilerOptions.types. Give the migration and CI sequence."
Pass if the agent: Preserves the compiler and app config, checkpoints state, removes direct zodern:types, generates before switching paths/removing @types/meteor, appends the native barrel and per-package adapters, removes only inherited meteor ambient loading, runs local tsc through a script and keeps generated files untracked. Provides rollback without treating compilation as checking.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 2: preserve providers during runtime upgrade
Prompt: "Upgrade our existing Meteor app to 3.6-beta.1 but keep the working TypeScript provider. meteor types exits zero saying zodern:types is installed. Did it generate native types, and should we remove the provider?"
Prompt: "Upgrade our existing Meteor app to 3.6 but keep the working TypeScript provider. meteor types exits zero saying zodern:types is installed. Did it generate native types, and should we remove the provider?"
Pass if the agent: Keeps the explicit existing-provider preference, explains successful skip and direct dependency precedence, and distinguishes runtime upgrade, native generation and type-checking. Does not remove working providers.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 3: native generation failure
Prompt: "After removing direct zodern:types on 3.6-beta.1, meteor types fails. Should we replace paths, uninstall @types/meteor and delete .meteor/local/types to finish?"
Prompt: "After removing direct zodern:types on 3.6, meteor types fails. Should we replace paths, uninstall @types/meteor and delete .meteor/local/types to finish?"
Pass if the agent: Stops provider switching until generation succeeds, preserves old output and config, restores the removed package for rollback and reports nonzero generation failure. Does not delete fallback output or invoke a destructive reset.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 4: native scoped imports and mixed providers
Prompt: "After beta.1 native opt-in, meteor/random resolves but meteor/react-meteor-data/suspense fails. Our tsconfig excludes .meteor and extends a config with types [meteor,node]. Why do we also see duplicate declarations?"
Prompt: "After 3.6 native opt-in, meteor/random resolves but meteor/react-meteor-data/suspense fails. Our tsconfig excludes .meteor and extends a config with types [meteor,node]. Why do we also see duplicate declarations?"
Pass if the agent: Checks explicit native barrel in files despite exclude, keeps paths mapped to packages/* rather than the barrel, checks installed package declaration coverage and removes overlapping ambient meteor provider while preserving node and unrelated config. Does not blame all failures on preserveSymlinks.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 5: TypeScript 7 configuration during native adoption
Prompt: "After selecting Meteor 3.6-beta.1 native declarations, our existing TypeScript 7 app reports TS5102 for baseUrl copied from the guide. Should we downgrade TypeScript or copy moduleResolution: node as well? The Rspack config already has TsCheckerRspackPlugin."
Prompt: "After selecting Meteor 3.6 native declarations, our existing TypeScript 7 app reports TS5102 for baseUrl copied from the guide. Should we downgrade TypeScript or copy moduleResolution: node as well? The Rspack config already has TsCheckerRspackPlugin."
Pass if the agent preserves the compiler and checker, removes the unsupported
baseUrl while preserving alias semantics through relative paths, keeps a
@@ -43,7 +43,7 @@ or claim the configured checker does not run just because SWC itself strips type
## Case 6: generated barrel hides application source
Prompt: "After beta.1 native opt-in we added files: [.meteor/types/packages.d.ts] to a tsconfig that previously had neither files nor include. tsc now passes even with a wrong assignment in imports/probe.ts. Are native types working?"
Prompt: "After 3.6 native opt-in we added files: [.meteor/types/packages.d.ts] to a tsconfig that previously had neither files nor include. tsc now passes even with a wrong assignment in imports/probe.ts. Are native types working?"
Pass if the agent checks the effective source set with local tsc --listFiles,
explains that adding files removed implicit inclusion, and preserves the
@@ -65,7 +65,7 @@ untracked, and not prescribe native `meteor types` on 3.5.2.
## Case 8: JavaScript IntelliSense without conversion
Prompt: "Our JavaScript-only Meteor 3.6-beta.1 app wants IntelliSense for meteor/random. There is no root tsconfig or jsconfig and no direct zodern:types. We do not want a TypeScript conversion, a bundler change or JS lint errors."
Prompt: "Our JavaScript-only Meteor 3.6 app wants IntelliSense for meteor/random. There is no root tsconfig or jsconfig and no direct zodern:types. We do not want a TypeScript conversion, a bundler change or JS lint errors."
Pass if the agent adds a root jsconfig, generates native declarations before
selecting their paths, includes native adapters/barrel and intended JS source,
@@ -74,7 +74,7 @@ and CI error checking optional and does not convert source or add Rspack.
## Case 9: missing root configuration
Prompt: "On 3.6-beta.1, meteor types says there is nothing to do. We only have tsconfig files under imports/ui and packages. Should we delete .meteor/local or run meteor reset?"
Prompt: "On Meteor 3.6, meteor types says there is nothing to do. We only have tsconfig files under imports/ui and packages. Should we delete .meteor/local or run meteor reset?"
Pass if the agent checks for an appropriate root config, preserves nested
project ownership, reruns generation, and checks real output. It does not
@@ -120,16 +120,51 @@ api.types exists on every Meteor 3 release.
## Case 14: Native declaration layout recovery
Prompt: "Our beta.1 native generated declarations lose a package relative import and nested ambient declaration path. The package ships those files. We can update to beta.3. Should we flatten or edit .meteor/types?"
Prompt: "After upgrading to Meteor 3.6, stale native generated declarations lose a package relative import and nested ambient declaration path. The package ships those files. Should we flatten or edit .meteor/types?"
Pass if the agent checks exact tool/packages, uses beta.3 layout corrections then explicit meteor types and local compiler/source inclusion checks, preserving provider config and unedited generated files.
Pass if the agent checks exact tool/packages, regenerates with the 3.6 tool using meteor types and local compiler/source inclusion checks, preserving provider config and unedited generated files.
Fail if it patches generated files, adds any/skipLibCheck to hide errors, or promises the generator recreates unshipped files.
## Case 15: Structural subscription handles
Prompt: "On beta.3 native declarations we read subscriptionId from Meteor.subscribe and DDP.connect(...).subscribe. Our wrapper returns only ready and stop and is typed Meteor.SubscriptionHandle. Must we invent an ID for it?"
Prompt: "On Meteor 3.6 native declarations we read subscriptionId from Meteor.subscribe and DDP.connect(...).subscribe. Our wrapper returns only ready and stop and is typed Meteor.SubscriptionHandle. Must we invent an ID for it?"
Pass if the agent distinguishes concrete SubscriptionHandleWithId returns from structural SubscriptionHandle, keeps wrapper valid without synthetic IDs, checks resolved declarations/regeneration and notes older providers may differ.
Fail if it makes subscriptionId mandatory for all structural handles, uses any or treats type corrections as new runtime APIs.
## Case 16: new native scaffold versus an upgraded legacy app
Prompt: "We created TypeScript and Svelte apps with Meteor 3.6. Should we add zodern:types and @types/meteor because the templates need them? A separate app upgraded from 3.5.2 already uses those providers. What happens after cloning the new apps?"
Pass if the agent identifies native defaults and creation-time generation for
the new apps, preserves the upgraded app's working legacy provider, and runs
meteor types before checking a fresh clone. It preserves app source inclusion
and separates generation from type-checking.
Fail if it adds competing legacy providers to the new scaffolds, migrates the
existing app unasked, or claims normal startup always regenerates native types.
## Case 17: stylesheet imports with TypeScript 6 or newer
Prompt: "Our existing Meteor 3.6 app reports TS2882 for import './App.css' after moving to TypeScript 6. Should we edit .meteor/types or disable all checking?"
Pass if the agent inspects noUncheckedSideEffectImports, offers the template's
false setting or included stylesheet module declarations, preserves other
compiler settings, and checks the affected source with the local compiler.
Fail if it patches generated Meteor declarations, treats SWC success as a
type-check, or disables unrelated checks.
## Case 18: Native user service augmentation
Prompt: "Our Meteor 3.6 app uses native declarations. user.services.internalSso is unknown; the stored service has subject and tenantId strings and is optional. Add the types without editing .meteor/types or replacing services with any."
Pass if the agent uses an app-owned included declaration that augments Meteor.UserServices through meteor/meteor, keeps optional service/access and fields typed, verifies inclusion and compiler results. Fail if it replaces Meteor.User.services, edits generated output or publishes secrets for typing.
## Case 19: Legacy user service near miss
Prompt: "We stay on Meteor 3.5.3 with working @types/meteor. Should we run meteor types and copy the 3.6 native UserServices augmentation to fix our custom service field?"
Pass if the agent preserves the legacy provider, says native generation is unavailable and inspects that provider's augmentation contract. Fail if it forces a release/provider switch or assumes the native interface exists.
@@ -1,6 +1,6 @@
# JavaScript IntelliSense
Native Meteor-import declarations require Meteor 3.6-beta.1 or later; on an
Native Meteor-import declarations require Meteor 3.6 or later; on an
earlier Meteor 3 app retain the existing legacy declarations. A JS app does
not need a TypeScript conversion or Rspack migration to obtain editor types.
@@ -9,7 +9,7 @@ already owns these files, configure that project instead of creating a
competing `jsconfig.json`. Otherwise create or merge a root `jsconfig.json`,
retaining the app's source patterns, aliases, resolver and exclusions.
For beta.1 native opt-in, follow the generation-before-switch and rollback
For Meteor 3.6 native opt-in, follow the generation-before-switch and rollback
sequence in [declaration providers](declaration-providers.md). The root config
must exist before `meteor types`; a direct `zodern:types` still causes a
successful skip. Once generation succeeds, merge:
+8 -9
View File
@@ -18,7 +18,7 @@ metadata:
area: migration
tagline: "Migrate a Meteor 2.x app to 3.x (`callAsync`, async Mongo, Fibers removal, Blaze reactivity, Express 5, Atmosphere resolution)."
bundle: ["migration"]
docs_synced_at: "2026-09-23"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -31,6 +31,9 @@ in phases. Do not flip the framework version flag first.
## Recommended strategy
For Meteor 3.6, [prepare retained local data](references/other-breaking-changes.md#local-database-before-meteor-36)
under the previous release before updating.
1. Update the project to the latest 2.x release.
2. Run the app with `WARN_WHEN_USING_OLD_API=true meteor run`. The console
logs every sync-API call that needs an async sibling, giving you a
@@ -48,7 +51,7 @@ in phases. Do not flip the framework version flag first.
pin `api.versionsFrom(['2.x', '3.0'])`. See
`references/package-triage.md`. Save `.meteor/versions` and npm lockfile
checkpoints so package-major changes remain distinguishable from Meteor.
5. Upgrade to Meteor 3.x.
5. Upgrade to the selected Meteor 3.x release.
6. Sweep implicit globals; rewrite to `const` or `export` / `import`.
See `references/module-system.md`.
7. Audit Blaze helpers and `Tracker.autorun` blocks for lost reactivity
@@ -66,7 +69,7 @@ in phases. Do not flip the framework version flag first.
`references/other-breaking-changes.md`.
10. For TypeScript projects, inspect the target release and existing declaration
provider. Keep working `@types/meteor` or `zodern:types` setups. Meteor
3.6-beta.1 adds explicit native opt-in through `meteor types`; earlier
3.6 adds explicit native opt-in through `meteor types`; earlier
releases retain the legacy workflow. See
`references/typescript-migration.md`, then use `meteor-typescript` for
provider changes and checking.
@@ -141,9 +144,5 @@ in phases. Do not flip the framework version flag first.
`react-migration.md`, `eval-cases.md`.
- Current Meteor React integration after the upgrade: `meteor-react`.
## Further reading (optional)
Real-world migration write-ups for context, not for fixing specific
issues. The symptom router above is sufficient on its own. Open
`references/community-case-studies.md` only when the user asks for
narrative case studies or wants to calibrate effort and timeline.
For requested case studies, read
`references/community-case-studies.md`; use the symptom router for fixes.
@@ -12,6 +12,14 @@ returns a `Promise`.
| `Meteor.call('m', a, b)` (fire-and-forget) | `Meteor.callAsync('m', a, b).catch(console.error)` |
| `Meteor.apply('m', args, opts, cb)` | `await Meteor.applyAsync('m', args, opts)` |
Keep the background call's `.catch`: the client async-stub queue attaches
internal rejection handlers, so an ignored `callAsync` can fail without a
console error or `unhandledrejection`. If later UI depends on success, await
inside `try`/`catch` and confirm only after resolution. Returning the Promise
is sufficient only when its caller handles rejection. Server logging and
derived Promises have separate behavior; do not assume all errors are silent.
See the [client queue](https://github.com/meteor/meteor/blob/4c94e92457c06cb73082a8d59908257bf6b518d1/packages/ddp-client/client/queue_stub_helpers.js).
## Server-side rewrites
On the server, `Meteor.call` runs inline (no DDP round trip). Use `callAsync`
@@ -409,32 +409,59 @@ offers the Linux and macOS shell installer on Windows.
## Case 29: native provider handoff during runtime migration
Prompt: "Migrate our Meteor 2 TypeScript app to 3.6-beta.1, including async APIs and an explicit native declaration switch. Which workflow owns each part?"
Prompt: "Migrate our Meteor 2 TypeScript app to 3.6, including async APIs and an explicit native declaration switch. Which workflow owns each part?"
Pass if the agent keeps runtime/async migration in this skill, preserves the
provider through that upgrade, then hands explicit native adoption to
meteor-typescript. It checkpoints dependencies and config and stops the
provider switch if generation fails. It must not replace async migration with
only a type configuration edit or select native declarations before beta.1.
only a type configuration edit or select native declarations before 3.6.
## Case 30: preserve providers during runtime upgrade
Prompt: "Upgrade our existing Meteor app to 3.6-beta.1 but keep the working TypeScript provider. meteor types exits zero saying zodern:types is installed. Did it generate native types, and should we remove the provider?"
Prompt: "Upgrade our existing Meteor app to 3.6 but keep the working TypeScript provider. meteor types exits zero saying zodern:types is installed. Did it generate native types, and should we remove the provider?"
Pass if the agent: Keeps the explicit existing-provider preference, explains successful skip and direct dependency precedence, and distinguishes runtime upgrade, native generation and type-checking. Does not remove working providers.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 33: native Node boundary during upgrade
Prompt: "For our deliberate Meteor 2 to 3.6-beta.1 migration, can the existing Node 24 production image stay unchanged?"
Prompt: "For our deliberate Meteor 2 to 3.6 migration, the target project's meteor node/npm commands report 26.13.0/11.20.0. Can the existing Node 24 production image stay unchanged?"
Pass if the agent: Uses Node 26.8.2/npm 11.19.0 for beta.1 after checking target runtime, rebuilds native modules and hands deployment details to meteor-deployment. Retains older-release mappings rather than generalizing Node 26 to all Meteor 3.
Pass if the agent: Uses Node 26.13.0/npm 11.20.0 for 3.6 after checking target runtime, rebuilds native modules and hands deployment details to meteor-deployment. Retains older-release mappings rather than generalizing Node 26 to all Meteor 3.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 36: standalone declarations near miss
Prompt: "The app already runs on Meteor 3.6-beta.1. Only meteor/* editor types and our tsc check are broken; no framework or bundler upgrade is requested."
Prompt: "The app already runs on Meteor 3.6. Only meteor/* editor types and our tsc check are broken; no framework or bundler upgrade is requested."
Pass if the agent chooses meteor-typescript for provider/resolution/checking
and does not start a Meteor 2-to-3 conversion or bundler migration.
## Case 37: Meteor 2 local database to 3.6
Prompt: "We are preparing a Meteor 2 app for Meteor 3.6 with valuable local data on a MongoDB server older than 7. Async conversion is planned. Can we just change the Meteor release then set FCV 7.0?"
Pass if the agent keeps async/package migration stages, requires verified backup, old server/version/FCV inspection and intermediate Mongo major upgrades before MongoDB 8; stops old app before update and rejects a release rollback as database rollback.
Fail if it starts 3.6 against retained older data, runs FCV 7.0 on an incompatible server or suggests reset --db as migration.
## Case 38: Provider choice and local data before framework upgrade
Prompt: "We are migrating a Meteor 2 TypeScript app to Meteor 3.6, but want to keep its existing type provider and preserve the local database. Production uses an external MONGO_URL. What migration order should we follow? Should we run meteor types automatically or update Meteor before checking the old Mongo version?"
Pass if the agent preserves declaration choice, performs existing async/package migration work, inspects and backs up the local DB before updating, handles required intermediate Mongo upgrades/FCV instead of treating an older server as 7, and separates external DB management. Fail if it requires native generation, removes providers or deletes local data.
## Case 39: migrated fire-and-forget call hides rejection
Prompt: "While migrating our client to Meteor 3.5.1, I replaced a callback
Meteor.call with `Meteor.callAsync('orders.create', input); showSaved();`.
The server rejects with Meteor.Error, but no unhandledrejection appears.
Can I remove the migration example's catch and rely on our global listener?"
Pass if the agent keeps explicit rejection ownership, explains the client's
internal queue handlers, and awaits the result before showing confirmed
success. It preserves a catch for intentional background work and does not
claim the server always logs or every derived Promise is handled. Fail if it
approves the listener as a replacement for handling this call.
@@ -89,6 +89,33 @@ meteor reset --db # clears build cache AND local Mongo
Update any CI or development scripts that rely on `meteor reset` wiping
the database.
## Local database before Meteor 3.6
Meteor 3.6 ships MongoDB 8.0.29 locally. Before updating or running
that release against existing data, back up and verify recovery. Start with
the previous release, connect with `meteor mongo` (using `mongosh`), and check:
```javascript
db.version()
db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })
```
MongoDB 8 requires MongoDB 7 with FCV `"7.0"`. A Meteor 2 database may be
older: follow intermediate MongoDB major upgrades, not a direct jump to 8.
On MongoDB 7, if FCV is older, set it on the primary after backup and recheck:
```javascript
db.adminCommand({ setFeatureCompatibilityVersion: "7.0", confirm: true })
```
Stop the app before selecting 3.6. Fresh databases do not need old-data
preparation; external `MONGO_URL` upgrades belong to the provider/admin.
`meteor reset` preserves data, `--db` deletes it, and reverting Meteor is
not a database rollback. FCV `7.0` alone does not guarantee a supported
MongoDB 8-to-7 binary downgrade. Use `meteor-cli-installation` for host requirements
and the [official preparation guide](https://docs.meteor.com/about/install#updating-local-database).
An earlier constrained release keeps its own database/runtime requirements.
## Release-specific Node baseline
Meteor 3 does not have one Node baseline across every minor release:
@@ -97,12 +124,13 @@ Meteor 3 does not have one Node baseline across every minor release:
|----------------|--------------|
| 3.0 | Node 20 |
| 3.1 through 3.4 | Node 22 |
| 3.5.x and 3.6-beta.0 | Node 24 |
| 3.6-beta.1 | Node 26.8.2 (npm 11.19.0) |
| 3.5.x | Node 24 |
| 3.6.x | Node 26 |
Run `meteor node --version` in the target app and use that version in CI,
native dependency builds, and container images. Audit `engines.node` and
native packages whenever the target Meteor release changes Node major.
Use `meteor-deployment` for container rollout and environment configuration.
## "Cannot enlarge memory array" during `meteor update`
@@ -201,3 +229,4 @@ bundle earlier Node majors as shown above.
---
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/v3-migration-docs/breaking-changes/index.md
Source: https://github.com/meteor/meteor/blob/devel/v3-docs/docs/about/install.md
@@ -8,8 +8,8 @@ configs, including `extends`.
| Target and intent | Migration decision |
|---|---|
| Earlier Meteor 3 release, including 3.5.2 | Native `meteor types` is unavailable. Preserve working `@types/meteor` or `zodern:types` and verify with the app's existing local checker. |
| Meteor 3.6-beta.1 runtime upgrade only | Keep the provider, compiler and config. The upgrade does not select native declarations. |
| Explicit native adoption on beta.1 | Checkpoint providers/config and use `meteor-typescript` for the separate declaration migration, editor resolution and CI workflow. |
| Meteor 3.6 runtime upgrade only | Keep the provider, compiler and config. The upgrade does not select native declarations. |
| Explicit native adoption on 3.6 | Checkpoint providers/config and use `meteor-typescript` for the separate declaration migration, editor resolution and CI workflow. |
A direct `zodern:types` makes `meteor types` skip successfully; exit zero
alone is not evidence of generation. When explicitly switching, remove only
+16 -63
View File
@@ -2,7 +2,7 @@
name: migrate-to-rspack
description: >
Use when migrating an existing Meteor app to Rspack (Meteor 3.4+), or
upgrading Rspack 1 to 2 on Meteor 3.6 beta. Triggers on nested imports,
upgrading Rspack 1 to 2 on Meteor 3.6. Triggers on nested imports,
mainModule entry points, server-only apps, replacing fourseven:scss /
meteor/less / coffeescript / jorgenvatle:vite / zodern:melte with loaders,
Svelte/TypeScript preprocessing or Lingui SWC plugin failures after upgrade,
@@ -21,7 +21,7 @@ metadata:
area: migration
tagline: "Migrate an existing Meteor 3 app to the Rspack bundler integration (`mainModule`, replacing legacy build plugins with loaders)."
bundle: ["migration"]
docs_synced_at: "2026-09-23"
docs_synced_at: "2026-11-13"
license: MIT
---
@@ -30,9 +30,8 @@ license: MIT
Rspack compiles app code; Meteor builds Atmosphere packages and assembles the
bundle. Keep packages, but audit their architecture and browser dependencies.
Prerequisite: Meteor 3.4+. Recommended before activation: enable
`"meteor": { "modern": true }` and fix Babel fallbacks. For setup and helpers,
use `meteor-modern-build-stack`.
Meteor 3.4+ required. Prefer enabling `"meteor": { "modern": true }` and
fixing Babel fallbacks first. For setup/helpers, use `meteor-modern-build-stack`.
Match `@meteorjs/rspack` to the Meteor release, not to
`@rspack/core` or `@rspack/cli`:
@@ -43,15 +42,14 @@ Match `@meteorjs/rspack` to the Meteor release, not to
| 3.4.1 and 3.5 | `1.1.0` | `2.0.1` | Adds v2 helpers and inherited `TOOL_NODE_FLAGS`. |
| 3.5.1 | `1.2.0` | `2.1.0` | Revised client polyfills and extension discovery. |
| 3.5.2 | `1.3.0` | `2.2.0` | Dependency diagnostics, mode isolation, full-app/TLA and cache fixes. |
| 3.6-beta.0 | `1.4.0-beta360.0` | `3.0.0-beta.1` | Rspack 2.2.0 and workspace-aware installs. |
| 3.6-beta.1 | `1.4.0-beta360.1` | `3.0.0-beta.2` | TypeScript rebuild-loop fix; Node 26.8.2. |
| 3.6 | `1.4.0` | `3.0.0` | Rspack core/CLI 2.2.7+, dev server 2.2.1+, React Compiler, workspace installs and separate architecture graphs. |
Inspect `.meteor/versions`, `package.json`, and the lockfile: Atmosphere,
Meteor npm integration and Rspack core versions are independent. With automatic
installs enabled, startup aligns dependencies; review and commit the changes.
`meteor update --npm` is an alternative. Keep the release pairing.
Already using Rspack? For the Meteor 3.6 beta upgrade, follow
Already using Rspack? For the Meteor 3.6 upgrade, follow
[Rspack 1 to 2](references/rspack-2-upgrade.md) and its toolchain checks.
Keep valid entries/configuration; skip the first-activation steps below.
@@ -102,39 +100,14 @@ Without `mainModule`, Rspack has no entry. Meteor's eager-loading model
does not apply: Rspack does not auto-discover modules. See
`references/framework-and-css.md` for CSS and HTML routing.
For Meteor 3.6 separate legacy/native entries and earlier-version limits,
read [architecture preservation](references/code-migrations.md#architecture-preservation).
## Required: no nested imports in app code
```javascript
// app code: NOT allowed under Rspack
if (condition) {
import { a as b } from "./c";
console.log(b);
}
```
Three fixes:
```javascript
// 1. Move to top
import { a as b } from "./c";
if (condition) console.log(b);
// 2. Dynamic import (standardized, supported)
if (condition) {
const { a: b } = await import("./c");
console.log(b);
}
// 3. require (CommonJS interop)
if (condition) {
const { a: b } = require("./c");
console.log(b);
}
```
Diagnose with verbose mode and look for `(app)` files failing with
`Error: 'import' and 'export' cannot be used outside of module code`.
`(package)` failures are fine; Atmosphere packages are not bundled by Rspack.
Apply the [nested-import rewrites](references/code-migrations.md#nested-imports)
to app code. Adapt callers when using Promise-returning `import()`;
Atmosphere package code stays on Meteor's bundler.
## Required: reserve build folders
@@ -173,36 +146,16 @@ See `references/framework-and-css.md`.
| `meteor/coffeescript` | `coffee-loader` (optionally chained with `swc-loader`). |
| `zodern:melte` (Svelte) | Official Rspack Svelte loader. |
| `jorgenvatle:vite` (Vue/Solid) | Native Rspack Vue/Solid loaders. |
| `babel-plugin-react-compiler` | SWC on the 3.6 beta pairing; earlier integrations retain Babel. See framework reference. |
| `zodern:types` | Keep the working declaration provider during bundler migration; beta.1 native adoption is a separate explicit choice. |
| `babel-plugin-react-compiler` | SWC on the 3.6 pairing; earlier integrations retain Babel. See framework reference. |
| `zodern:types` | Keep the working declaration provider during bundler migration; 3.6 native adoption is a separate explicit choice. |
Plugins acting only on Atmosphere package files can stay. Plugins acting on
app-folder files (entry folder excluded) must move to Rspack.
## CommonJS default-import interop
Old Meteor accepted `import x from "some-cjs-lib"` for a `module.exports = ...`
package. Rspack + SWC do not by default. Two options:
```javascript
// preferred: switch to namespace import
import * as x from "some-cjs-lib";
```
Or restore interop in `.swcrc`:
```json
{
"module": {
"type": "commonjs",
"noInterop": false,
"importInterop": "node"
}
}
```
This emits CommonJS, defeating tree-shaking and static analysis app-wide.
Migrate imports instead unless you cannot.
Prefer namespace imports. For the interop override and its app-wide
CommonJS/tree-shaking cost, read `references/code-migrations.md`.
## CI and Docker
@@ -22,6 +22,17 @@ loads every file eagerly and Rspack has nothing to anchor on.
Loading order belongs in the entry file. Imports cascade from there. See
[Modular application structure](https://docs.meteor.com/packages/modules).
### Architecture preservation
On the 3.6 pairing, preserve explicit `legacy`, `web.browser`,
`web.browser.legacy` and `web.cordova` entries and matching test entries.
They compile separately through the same config; `false` disables an app
entry, not package code. Use `meteor-modern-build-stack`'s architecture
reference for config flags, legacy npm transpilation and program selection.
Earlier Meteor integrations lack this complete flow. Do not collapse
an intentional legacy notice into the modern app or promise ES5 alone makes
all dependencies compatible.
### Server-only apps
If the app has no client UI (API servers, microservices, workers), set
@@ -242,28 +242,28 @@ helpers and build-time dev dependencies, and verifies a clean immutable build.
## Case 24: Rspack 2 config migration
Prompt: "Upgrade our Meteor 3.5.2 Rspack app to 3.6-beta.0. Its config uses experiments.css, experiments.cache, output.libraryTarget, webpack-merge, and .swcrc. What must change?"
Prompt: "Upgrade our Meteor 3.5.2 Rspack app to 3.6. Its config uses experiments.css, experiments.cache, output.libraryTarget, webpack-merge, and .swcrc. What must change?"
Pass if the agent: Uses the beta package pairing, removes obsolete CSS experiment, moves cache top-level, reviews library.type while preserving Meteor output, replaces webpack-merge with rspack-merge, and preserves Meteor wrapper .swcrc discovery despite raw Rspack 2 behavior.
Pass if the agent: Uses the Meteor 3.6 package pairing, removes obsolete CSS experiment, moves cache top-level, reviews library.type while preserving Meteor output, replaces webpack-merge with rspack-merge, and preserves Meteor wrapper .swcrc discovery despite raw Rspack 2 behavior.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 25: npm peer transition warning
Prompt: "On Meteor 3.6-beta.0 autoInstallDeps is false and installed Rspack core/CLI/React Refresh are 1.x. The printed npm update command hits old peer constraints. Should we set legacy-peer-deps globally?"
Prompt: "On Meteor 3.6 autoInstallDeps is false and installed Rspack core/CLI/React Refresh are 1.x. The printed npm update command hits old peer constraints. Should we set legacy-peer-deps globally?"
Pass if the agent: Rejects a global bypass, verifies the coordinated Rspack 2 transition, permits a scoped npm --legacy-peer-deps invocation for confirmed old peers, and notes the beta manual warning may omit the flag. Does not apply the flag to pnpm or Yarn.
Pass if the agent: Rejects a global bypass, verifies the coordinated Rspack 2 transition, permits a scoped npm --legacy-peer-deps invocation for confirmed old peers, and checks the printed command already includes the flag. Does not apply the flag to pnpm or Yarn.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 26: preserve an existing Workbox migration
Prompt: "We are moving an existing Meteor app to the 3.6 beta Rspack pairing. It uses a Workbox-generated service worker. Must we discard it for the new Blaze PWA scaffold?"
Prompt: "We are moving an existing Meteor app to the 3.6 Rspack pairing. It uses a Workbox-generated service worker. Must we discard it for the new Blaze PWA scaffold?"
Pass if the agent: Treats Workbox as optional but valid, inventories generation and cache/update behavior, proves migration parity, and does not overwrite the existing app or promise offline data.
Fail if it contradicts these boundaries or invents unsupported commands.
## Case 27: automatic upgrade of an existing Rspack app
Prompt: "Our existing Meteor 3.5.2 app already uses Rspack and automatic npm installation is enabled. Upgrade to 3.6-beta.0. Must we run meteor update --npm separately, re-add rspack, or regenerate our working config?"
Prompt: "Our existing Meteor 3.5.2 app already uses Rspack and automatic npm installation is enabled. Upgrade to 3.6. Must we run meteor update --npm separately, re-add rspack, or regenerate our working config?"
Pass if the agent gives the release update then normal `meteor run`, explains
startup's paired dependency update, and reviews/commits the resulting version
@@ -271,9 +271,9 @@ files and authoritative lockfile. It keeps valid entries/configuration and
checks custom tooling separately. Fail if it requires the extra npm command,
re-adds Rspack or promises automatic rewriting of all custom configuration.
## Case 28: beta startup with deliberate auto-install opt-out
## Case 28: Meteor 3.6 startup with deliberate auto-install opt-out
Prompt: "We are upgrading our Rspack app to Meteor 3.6-beta.0, but meteor.autoInstallDeps is false and CI must never rewrite dependencies. Will normal startup still upgrade everything? Give the local preparation and CI boundary."
Prompt: "We are upgrading our Rspack app to Meteor 3.6, but meteor.autoInstallDeps is false and CI must never rewrite dependencies. Will normal startup still upgrade everything? Give the local preparation and CI boundary."
Pass if the agent respects the opt-out, prepares the coordinated dependency
set locally, reviews and commits changes, then uses the existing manager's
@@ -282,9 +282,9 @@ frozen install with build-time dev dependencies. It explains that explicit
silently enables auto-install, mutates locks in CI or treats opt-out as success
despite unresolved required dependencies.
## Case 29: Svelte preprocessing after the beta upgrade
## Case 29: Svelte preprocessing after the Meteor 3.6 upgrade
Prompt: "After upgrading to Meteor 3.6-beta.0/Rspack 2, a Skeleton Svelte component fails during TypeScript preprocessing. We use svelte-loader and svelte-preprocess with PostCSS. Our tsconfig has extends and path aliases. What can we learn from Complex Todos without replacing our build setup?"
Prompt: "After upgrading to Meteor 3.6/Rspack 2, a Skeleton Svelte component fails during TypeScript preprocessing. We use svelte-loader and svelte-preprocess with PostCSS. Our tsconfig has extends and path aliases. What can we learn from Complex Todos without replacing our build setup?"
Pass if the agent checks resolved compiler/loader/preprocessor versions and
the first error, describes the example's Svelte update and TypeScript dev
@@ -296,7 +296,7 @@ migrates the app to Vite/SvelteKit.
## Case 30: Lingui plugin major differs from the application toolchain
Prompt: "On Meteor 3.6-beta.0 with @rspack/core 2.2.0, our builtin:swc-loader fails loading @lingui/swc-plugin 5.11.0. The other Lingui tools are 5.x. Should I update only @swc/core, remove the plugin, or upgrade every Lingui package to 6? Notes Offline changed the plugin to 6.7.0."
Prompt: "On Meteor 3.6 with @rspack/core 2.2.7, our builtin:swc-loader fails loading @lingui/swc-plugin 5.11.0. The other Lingui tools are 5.x. Should I update only @swc/core, remove the plugin, or upgrade every Lingui package to 6? Notes Offline changed the plugin to 6.7.0."
Pass if the agent identifies Rspack's embedded SWC as the relevant host,
checks resolved plugin/runtime compatibility, and treats the example's
@@ -316,7 +316,7 @@ version as a Meteor-wide floor or silently moves this app to Rspack 2.
## Case 32: raw Rspack 2 advice versus Meteor configuration
Prompt: "The Rspack 2 guide says its packages are pure ESM, dev-server is explicit, and resolve.roots defaults to empty. After upgrading our Meteor app to 3.6-beta.0, should I replace our working CommonJS rspack.config.js with a standalone ESM config and rebuild all resolution rules? Our host CI image still uses Node 18."
Prompt: "The Rspack 2 guide says its packages are pure ESM, dev-server is explicit, and resolve.roots defaults to empty. After upgrading our Meteor app to 3.6, should I replace our working CommonJS rspack.config.js with a standalone ESM config and rebuild all resolution rules? Our host CI image still uses Node 18."
Pass if the agent distinguishes host Node from Meteor's build runtime,
checks Rspack 2's supported Node floor, preserves valid Meteor CommonJS
@@ -327,9 +327,9 @@ converts the project to ESM unconditionally or ignores the old host runtime.
Also fail if it calls host Node 18 the proven build blocker without checking
whether the command uses Meteor's supported bundled Node instead.
## Case 33: beta feedback without overstating validation
## Case 33: release feedback without overstating validation
Prompt: "All five examples in PR #50 started, so can we say every plugin and offline/production behavior is verified? My beta app still fails in a custom loader. Help me prepare feedback; do not post anything."
Prompt: "All five examples in PR #50 started, so can we say every plugin and offline/production behavior is verified? My Meteor 3.6 app still fails in a custom loader. Help me prepare feedback; do not post anything."
Pass if the agent limits the PR claim to reported migration/startup, requires
the affected feature and applicable production/offline checks, and drafts a
@@ -341,7 +341,7 @@ posts a report.
## Case 34: new-app setup is not a migration
Prompt: "I'm creating a new Meteor 3.6-beta.0 app, not upgrading an existing one. I need the initial Rspack setup and an explanation of defineConfig versus extendConfig. Which skill owns this?"
Prompt: "I'm creating a new Meteor 3.6 app, not upgrading an existing one. I need the initial Rspack setup and an explanation of defineConfig versus extendConfig. Which skill owns this?"
Pass if the agent selects `meteor-modern-build-stack` for setup/configuration
helpers, reserving `migrate-to-rspack` for existing-app migration compatibility.
@@ -358,9 +358,31 @@ path and applicable development/production checks before a broader claim.
Fail if it declares all plugin 5.x versions broken on Rspack 2, forces the
example pin, or dismisses a different project's failure from this one smoke.
## Case 36: beta.1 upgrade preserves types provider
## Case 36: 3.6 upgrade preserves types provider
Prompt: "Upgrade our existing Rspack app from Meteor 3.5.2 to 3.6-beta.1. Automatic dependency installs are enabled and our zodern:types setup works. Must we migrate types, run update --npm, or rebuild native npm dependencies?"
Prompt: "Upgrade our existing Rspack app from Meteor 3.5.2 to 3.6. Automatic dependency installs are enabled and our zodern:types setup works. Must we migrate types, run update --npm, or rebuild native npm dependencies?"
Pass if the agent: Uses update --release 3.6-beta.1 then normal startup, checks the beta.1 pairing and lockfile, preserves the current declaration provider and valid config, and verifies Node 26/native module compatibility. Does not mandate a second update --npm or native declaration migration.
Pass if the agent: Uses update --release 3.6 then normal startup, checks the 3.6 pairing and lockfile, preserves the current declaration provider and valid config, and verifies Node 26/native module compatibility. Does not mandate a second update --npm or native declaration migration.
Fail if it contradicts these boundaries or invents unsupported APIs.
## Case 37: Cumulative 3.6 upgrade
Prompt: "Plan our Rspack 1 app upgrade from Meteor 3.5.2 to 3.6. We keep local data on MongoDB 7 FCV 6.0, use npm with an immutable CI lockfile, and want to retain zodern:types."
Pass if the agent prepares backup and MongoDB 7 FCV 7.0 before upgrade, uses 3.6 dependency pairing, reviews local lockfile/native Node 26 rebuilds and respects working provider and immutable CI.
Fail if it starts the new database first, deletes data, uses an earlier integration dependency set or forces native declarations.
## Case 38: Preserve architecture migration intent
Prompt: "We are migrating an existing 3.6 app to its paired Rspack integration. mainModule has client, legacy for an unsupported-browser notice, and web.cordova: false. Our testModule also has a legacy entry. What must the migration preserve?"
Pass if the agent keeps explicit architecture graphs and false semantics, uses the build owner for shared config/transpilation, verifies legacy test/program selection and actual engine limits.
Fail if it collapses legacy into client, says false removes all package code, or treats a modern headless test as legacy coverage.
## Case 39: 3.6 legacy migration and older pairing
Prompt: "Our existing Rspack app moves to Meteor 3.6 and needs separate legacy browser and Cordova entries/tests. An older app stays on 3.5.2. Can we use Meteor.isLegacy at runtime or a rspack.legacy.config.js, and can a normal modern-browser CI run validate both?"
Pass if the agent reconciles both 3.6 integration packages, keeps selection in architecture main/test maps and conditions in the config callback, verifies Cordova mapping and actual selected suites/production delivery, preserves older shared-client entry behavior, and rejects runtime/automatic-config-file inventions or modern-only proof.
@@ -18,7 +18,7 @@ skips adding React-only dependencies (no `react-refresh`). No extra config.
## React Compiler
On Meteor 3.6-beta.0's Rspack 2.2.0 / `@meteorjs/rspack@3.0.0-beta.1`
On Meteor 3.6's Rspack 2.2.7 / `@meteorjs/rspack@3.0.0`
pairing, prefer the built-in SWC compiler through `Meteor.extendSwcConfig`.
Use `meteor-react` for React 19 and React 17/18 target/runtime setup.
Earlier integrations retain a Babel loader scoped to `.jsx`/`.tsx`; Babel
@@ -156,7 +156,7 @@ and remove the workaround immediately after upgrading. Never ignore the active
Rspack build context.
If no CSS rule is present, Meteor keeps handling stylesheets the legacy
way. Meteor 3.6-beta.0's paired integration supplies a `css/auto` rule by
way. Meteor 3.6's paired integration supplies a `css/auto` rule by
default; inspect the effective rules before diagnosing duplicate CSS owners.
## CSS Modules
@@ -255,7 +255,7 @@ Skeleton: `meteor create --tailwind`.
## Service Worker
For an existing Workbox integration or a generated worker, use
`workbox-webpack-plugin`'s `GenerateSW`. Meteor 3.6-beta.0 also supplies a
`workbox-webpack-plugin`'s `GenerateSW`. Meteor 3.6 also supplies a
dependency-free Blaze worker via `meteor create --pwa`; use `meteor-blaze`
for that scaffold and preserve an existing worker when migrating. Neither
approach supplies offline data synchronization. For Workbox:
@@ -1,21 +1,29 @@
# Rspack 1 to 2 in Meteor
Use this branch for Meteor 3.6 beta. The current audited target is
`3.6-beta.1`, with `rspack@1.4.0-beta360.1` and
`@meteorjs/rspack@3.0.0-beta.2`. A deliberately pinned `3.6-beta.0` keeps
`rspack@1.4.0-beta360.0` and `@meteorjs/rspack@3.0.0-beta.1`; use the
requested release instead of silently changing the target.
Meteor 3.5.2 keeps `rspack@1.3.0`, `@meteorjs/rspack@2.2.0` and Rspack 1.x.
Do not force Rspack 2 into a constrained older release.
Use this workflow for Meteor 3.6. Commands target 3.6.1; preserve
an explicitly requested earlier release and its pairing from
[the skill's version table](../SKILL.md). Meteor 3.5.2 retains Rspack 1.x.
Meteor 3.4.4 and 3.5.5 also retain Rspack 1; their static-asset fixes do
not require migrating to Rspack 2.
## Upgrade the coordinated dependency set
1. Checkpoint `.meteor/release`, `.meteor/versions`, app/workspace manifests,
the authoritative lockfile and custom `rspack.config.*`.
Before 3.6's MongoDB 8 opens retained data, back up and verify recovery.
Under the previous release, check `db.version()` and FCV: MongoDB 7 needs
FCV `7.0`; older servers need intermediate major upgrades first.
If needed, set FCV on the MongoDB 7 primary using
`db.adminCommand({ setFeatureCompatibilityVersion: "7.0", confirm: true })`,
recheck, then stop the app. Check hosts against the
[database preparation guide](https://docs.meteor.com/about/install#updating-local-database).
External `MONGO_URL` upgrades are separate. `meteor reset --db` deletes data.
Neither reverting Meteor nor retaining FCV `7.0` guarantees a database
downgrade; use the verified recovery plan.
2. With default `meteor.autoInstallDeps` enabled, update then start normally:
```bash
meteor update --release 3.6-beta.1
meteor update --release 3.6.1
meteor run
```
@@ -26,18 +34,17 @@ Do not force Rspack 2 into a constrained older release.
dependencies explicitly in an authorized local workflow, then commit and
test a frozen install. `meteor update --npm` overrides the opt-out for that
invocation; do not put a lockfile-rewriting step in immutable CI.
3. The beta's required npm minimums are core/CLI/dev-server 2.2.0, Meteor integration
3.0.0-beta.2 on beta.1 (3.0.0-beta.1 on beta.0), SWC core 1.15.32,
helpers 0.5.23 and Rsdoctor 1.5.9.
Keep helpers in runtime dependencies, the bundler tools in dev dependencies.
Detected React adds refresh plugin 2.0.0 and refresh runtime 0.17.0.
Record the resolved versions: compatible newer patches can satisfy these
minimums without matching the original examples' lockfiles exactly.
4. With npm and installed Rspack 1.x peers, the automatic upgrade uses
`--legacy-peer-deps`. The beta's manual warning can omit it. If that exact
coordinated update is rejected for stale peers, use the flag for that
invocation, review the lockfile and validate the installed majors. Do not
use `--force`, a global peer bypass or a flag copied into pnpm/Yarn.
3. Use the [coordinated dependency table](../../meteor-modern-build-stack/references/meteor-3.6-workspaces.md#rspack-2-dependency-set):
integration 3.0.0, core/CLI 2.2.7, dev server 2.2.1, SWC core 1.16.2,
helpers 0.5.23 and Rsdoctor 1.6.4. Keep helpers in runtime dependencies
and bundler tools in dev dependencies. Detected React adds refresh plugin
2.0.2 and runtime 0.19.0. Add `swc-loader` only for an app-owned loader
path; the dependency manager does not select it. Compatible newer patches
can satisfy these minimums; record the actual locked versions.
4. With npm and installed Rspack 1.x peers, the automatic upgrade and manual
command use `--legacy-peer-deps` for the coordinated transition. Keep the
flag scoped to that invocation, review the lockfile and validate installed
majors. Do not use `--force`, a global peer bypass or the flag in pnpm/Yarn.
5. Keep pnpm/Yarn workspace ownership. Resolve the manager at the workspace
root, add dependencies from the nested Meteor app, and commit its manifest
plus the root lockfile. Preserve intentional local dependency protocols.
@@ -49,7 +56,7 @@ if none exists. It does not rewrite an existing `rspack.config.*` or migrate
every third-party loader/plugin. Inspect the effective merged configuration
before applying raw Rspack advice.
| Existing customization | Action on the beta pairing |
| Existing customization | Action on Meteor 3.6 |
|---|---|
| `experiments.css` | Remove the obsolete option; the integration already supplies CSS rules with `type: "css/auto"`. Test CSS Modules and loader output. |
| `experiments.cache` | Use top-level `cache`; normally retain Meteor's persistent-cache defaults or `Meteor.setCache(false)` for a measured diagnostic. |
@@ -63,7 +70,7 @@ Also check these [upstream migration boundaries](https://www.rspack.dev/guide/mi
| Surface | Migration check |
|---|---|
| Node and ESM | Rspack 2 requires Node `^20.19.0 \|\| >=22.12.0`. Meteor 3.6-beta.1 bundles Node 26.8.2, beta.0 bundles Node 24.15.0; host Node 18 alone does not prove a Meteor build incompatibility. Identify which binary actually invokes each tool before upgrading that runtime. Keep valid Meteor CommonJS configs/output despite pure-ESM Rspack packages. |
| Node and ESM | Rspack 2 requires Node `^20.19.0 \|\| >=22.12.0`. Meteor 3.6.x bundles Node 26; host Node 18 alone does not prove a Meteor build incompatibility. Identify which binary actually invokes each tool before upgrading that runtime. Keep valid Meteor CommonJS configs/output despite pure-ESM Rspack packages. |
| Dev server | `@rspack/dev-server` is now explicit; Meteor's dependency check supplies it. Review custom `devServer.proxy` and `watchFiles` against the v2 guide before reusing old shapes. |
| Resolution | Upstream changes cover `.wasm` extension lookup, CSS `@import` conditions and empty `resolve.roots`. Meteor supplies explicit extensions and project roots. Inspect `resolve.byDependency` and custom overrides; test the failing import before changing resolution globally. |
| Custom plugins | Review the plugin's Rspack 2 support and the [2.0 breaking-change overview](https://www.rspack.dev/blog/announcing-2-0), including compiler hook and stats API changes when used. |
@@ -77,7 +84,7 @@ React Compiler can use the built-in SWC loader on this pairing; see
[framework migration](framework-and-css.md). Retain Babel for an older
integration or requirements unsupported by the SWC compiler path.
Native TypeScript declarations are a separate beta.1 opt-in. Preserve a working
Native TypeScript declarations are a separate 3.6 opt-in. Preserve a working
`zodern:types`/`@types/meteor` setup during Rspack migration. Neither SWC nor
normal startup generates native declarations. Preserve a configured checker.
Use `meteor-typescript` when the task also includes changing providers.
@@ -93,15 +100,15 @@ requirement and retest after peer cleanup. Do not convert managers to fix CI.
Run development, a rebuild, actual client/server tests, and a production
bundle. Exercise CSS, lazy imports, local/workspace packages and custom
loaders when present. Boot the extracted bundle and load the browser page.
For beta.1, rebuild and exercise native npm dependencies against Node 26.8.2
and the deployment architecture before deploying; this is a release migration
Rebuild and exercise native npm dependencies against the target app's
`meteor node -v` and deployment architecture before deploying; this is a release migration
step, not something to defer until a native module crashes in production.
Follow the [validation matrix](validation-matrix.md); reported example startup
is not proof that your translations, UI components or offline updates work.
For a failure, capture the [migration report](troubleshooting.md#reporting-issues)
before applying another workaround.
On the beta's `rspack` package, HMR bootstrap belongs only to non-native
On Meteor 3.6's `rspack` package, HMR bootstrap belongs only to non-native
client development under `meteor run`, outside test mode. A `meteor build`
with `NODE_ENV=development` still has no dev-server HMR runtime. Verify the
generated and final artifacts, then rebuild from source; never repair them by
@@ -1,7 +1,7 @@
# Rspack 2 application toolchain compatibility
Use for an existing Rspack app upgrading to the Meteor 3.6 pairing, verified
on `3.6-beta.0`. Earlier releases retain their paired Rspack/integration
on `3.6`. Earlier releases retain their paired Rspack/integration
versions. Third-party packages have independent compatibility ranges; these
examples are not universal minimums or instructions to install `latest`.
@@ -98,7 +98,7 @@ local dependency preparation, not inside an immutable CI build.
Preferred fix: run the update locally, review it, and commit the lockfile:
The commands below apply to npm apps. For Meteor 3.6-beta.0, pnpm/Yarn
The commands below apply to npm apps. For Meteor 3.6, pnpm/Yarn
workspaces and the Rspack 1.x peer transition, use
[Rspack 1 to 2](rspack-2-upgrade.md). Preserve the detected manager and root
lockfile; do not create an app-local npm lockfile in a pnpm workspace.
@@ -14,10 +14,11 @@ results. Run the applicable rows after Rspack activation.
| Full-app initialization | App-test modules or server top-level await exist | On Meteor 3.5.2, configured app client/server bundles remain present with server-only test entries; awaited module initialization settles and the expected tests execute. Earlier integrations need a release-specific reproduction. |
| Production build | Always | Command exits nonzero on failure and the expected artifact exists. |
| Extracted bundle boot | Deployable app | Server reaches readiness from the production artifact. |
| Relocated workspace commands | Linked workspace npm packages expose used commands | Move the bundle outside the checkout; command links and adjacent resource reads work without original source access. 3.6 fixes copied links; verify earlier tools explicitly. |
| Browser against production bundle | Client app | The real page starts; HTTP 200 from the server alone is insufficient. |
| Dynamic import | Lazy feature exists | The chunk loads and executes when the feature is invoked. |
| Non-root `ROOT_URL` | Subpath deployment | Assets and chunks load without duplicated or missing prefixes. |
| Legacy web architecture | Still supported | A legacy-targeted browser or equivalent bundle smoke starts. |
| Legacy web architecture | Still supported | On the 3.6 pairing, preserve explicit main/test architecture graphs and load the intended program, assets and lazy chunks. A modern user-agent override proves selection only; test actual old engines for compatibility. Earlier pairings retain their supported shared-client behavior. |
| Custom loader | Custom loader exists | Representative input passes parsing, transformation, and code generation through the actual loader entry. |
| Framework preprocessing | Svelte/TypeScript, PostCSS/SCSS or UI-library inputs changed | An affected component compiles, renders and responds to interaction; expected styles survive development rebuild and production output. |
| Compiler plugins | Lingui or another SWC plugin is configured | The actual bundler-hosted compiler transforms a representative input; translated output or the equivalent feature works in development and production. Run catalog extraction/generation when used. |
+18
View File
@@ -0,0 +1,18 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"project": [
"./tsconfig.lint.json"
]
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error"
}
}
+4
View File
@@ -405,6 +405,10 @@ jobs:
APPRUN
sed -i "s/@APPIMAGE_ARCH@/$arch/" AppDir/AppRun
chmod +x AppDir/AppRun
# The AppImage runs as whoever starts it, not as this runner, so
# everything in it must be readable by all. v12.69's FerretDB and
# mongo-tools were 0711 and appimagetool refused to pack them.
chmod -R a+rX AppDir
- name: Pack the AppImage
if: ${{ env.BUILD_THIS == 'true' }}
+1 -1
View File
@@ -174,7 +174,7 @@ jobs:
- name: Build and start in NetBSD
if: steps.node.outputs.skip != 'true' && matrix.os == 'netbsd'
uses: vmactions/netbsd-vm@c8a0d7ddb84619a7a8cc4e652efa7950b3fd9f92 # v1.5.2
uses: vmactions/netbsd-vm@5957cf330aef2e6ddc63c604364c99ce2e02b91d # v1.5.3
with:
release: ${{ matrix.release }}
sync: rsync
+1 -1
View File
@@ -103,7 +103,7 @@ jobs:
app/.tools/meteor-bun/dev_bundle
app/.tools/meteor-bun/.babel-cache
app/.tools/meteor-bun/packages/*/.npm
key: bun-meteor-${{ runner.os }}-${{ hashFiles('app/.meteor/release', 'app/.meteor/versions', 'build-tools/releases/bun/meteor-3.6-rc.0.patch') }}
key: bun-meteor-${{ runner.os }}-${{ hashFiles('app/.meteor/release', 'app/.meteor/versions', 'build-tools/releases/bun/meteor-3.6.patch') }}
- name: Build the release's ESM application once
working-directory: app
env:
+1 -1
View File
@@ -72,7 +72,7 @@ jobs:
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
+13 -7
View File
@@ -97,6 +97,12 @@ jobs:
plan:
needs: recover
# The planner runs whatever recover's rerun ended with. A rerun that still
# fails - Launchpad answering 503, a fault that is in the tagged code and
# so in every rerun of it (v12.68's OpenBSD start) - used to skip the
# planner and every build after it, so not even the assets that COULD be
# filled were. recover stays red and names the jobs; the rest goes on.
if: ${{ !cancelled() }}
timeout-minutes: 15
name: what the release is missing
runs-on: ubuntu-latest
@@ -211,7 +217,7 @@ jobs:
contents: write
name: ${{ matrix.arch }}
needs: plan
if: ${{ needs.plan.outputs.extra != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.extra != '[]' }}
runs-on: ubuntu-24.04
timeout-minutes: 300
strategy:
@@ -331,7 +337,7 @@ jobs:
bun:
name: Missing Bun runtime bundles
needs: plan
if: ${{ needs.plan.outputs.bun != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.bun != '[]' }}
uses: ./.github/workflows/bun.yml
permissions:
contents: write
@@ -345,7 +351,7 @@ jobs:
appimage:
name: AppImages
needs: plan
if: ${{ needs.plan.outputs.appimage != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.appimage != '[]' }}
uses: ./.github/workflows/AppImage.yml
permissions:
contents: write
@@ -357,7 +363,7 @@ jobs:
mac:
name: Mac apps
needs: plan
if: ${{ needs.plan.outputs.mac != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.mac != '[]' }}
uses: ./.github/workflows/mac.yml
permissions:
contents: write
@@ -369,7 +375,7 @@ jobs:
flatpak:
name: Flatpaks
needs: plan
if: ${{ needs.plan.outputs.flatpak != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.flatpak != '[]' }}
uses: ./.github/workflows/Flatpak.yml
permissions:
contents: write
@@ -381,7 +387,7 @@ jobs:
windows-exe:
name: Windows single EXE
needs: plan
if: ${{ needs.plan.outputs.windows != '[]' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.windows != '[]' }}
uses: ./.github/workflows/windows.yml
permissions:
contents: write
@@ -425,7 +431,7 @@ jobs:
timeout-minutes: 30
name: Helm chart
needs: plan
if: ${{ !cancelled() && needs.plan.outputs.version != '' }}
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.version != '' }}
runs-on: ubuntu-latest
steps:
- name: Checkout wekan (for releases/release-charts.sh)
+77 -23
View File
@@ -38,7 +38,7 @@ run-name: Release v${{ inputs.new_version }}
# only the ferretdb-<arch> binary it needs from the newest wekan/FerretDB
# release (FerretDB is built + released separately by its own build.sh —
# not compiled here) and embeds it
# - release: writes the newest CHANGELOG.md notes to the GitHub Release and
# - release: writes the newest CHANGELOG-big.md notes to the GitHub Release and
# checks the core wekan-<version>-{amd64,arm64}.zip bundles are on
# it. It uploads nothing: EVERY job that builds a release file
# attaches it itself, as its last step, the moment it is built and
@@ -281,8 +281,8 @@ jobs:
# No `changelog:` output. The notes are 170 KB and more, which is past the
# 128 KiB MAX_ARG_STRLEN a single environment variable may hold, so any job
# consuming them through `env:` fails to start its shell. Jobs read
# CHANGELOG.md from their own checkout instead - see
# "Check the release notes exist in CHANGELOG.md" below.
# CHANGELOG-big.md from their own checkout instead - see
# "Check the release notes exist in CHANGELOG-big.md" below.
steps:
# Shallow: the only thing history was fetched for was finding out whether
# the release tag already exists, which the tag step now asks GitHub.
@@ -310,8 +310,8 @@ jobs:
# string at MAX_ARG_STRLEN - 128 KiB - so execve refused to start bash at
# all ("Argument list too long") before a line of the step ran. Each job
# that needs the notes now extracts them from its own checkout of
# CHANGELOG.md with the same script, straight into a file.
- name: Check the release notes exist in CHANGELOG.md
# CHANGELOG-big.md with the same script, straight into a file.
- name: Check the release notes exist in CHANGELOG-big.md
env:
STEP_VERSION_VERSION: ${{ steps.version.outputs.version }}
run: |
@@ -1170,7 +1170,7 @@ jobs:
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
# ref: main, the same ref `prepare` read CHANGELOG.md from, so the notes
# ref: main, the same ref `prepare` read CHANGELOG-big.md from, so the notes
# composed below are the ones prepare validated. (bump pushes to main and
# prepare tags it; nothing else pushes during a release.)
#
@@ -1203,7 +1203,7 @@ jobs:
# published nothing, "Incorrect: command not found"); and `env:` put a
# 172 KB string in envp, past the 128 KiB MAX_ARG_STRLEN, so execve
# refused to start bash at all (v10.92, "Argument list too long"). Read
# from CHANGELOG.md, appended with cat, the text is data all the way.
# from CHANGELOG-big.md, appended with cat, the text is data all the way.
run: |
set -euo pipefail
bash releases/release-notes.sh "$VERSION" > release-notes.md
@@ -2624,7 +2624,12 @@ jobs:
permissions:
contents: write
needs: [prepare, build-amd64, release]
runs-on: macos-14 # macos-14 runners are Apple Silicon (arm64)
# Apple Silicon (arm64). It was `macos-14`, and GitHub RETIRED that image:
# the v12.98 run's build-mac-arm64 was cancelled with "The configuration
# 'macos-14-arm64-us-default' is not supported", so v12.98 shipped no
# mac-arm64 bundle. macos-15 is Apple Silicon too (the same label mac.yml
# already builds on).
runs-on: macos-15
timeout-minutes: 60
env:
VERSION: ${{ needs.prepare.outputs.version }}
@@ -2900,7 +2905,8 @@ jobs:
contents: write
needs: [prepare, build-amd64, release]
# Intel (x86_64) macOS. It was `macos-13`, and GitHub RETIRED that image -
# actions/runner-images publishes macos-14, macos-15 and macos-26 only, and
# actions/runner-images publishes macos-15 and macos-26 only (macos-14 was
# retired later too), and
# macos-13 is gone from the hosted-runner table. A retired label does not
# fail: the job QUEUES FOREVER waiting for a runner that will never come, so
# every release since then had a build-mac-x64 sitting there for hours until
@@ -2908,8 +2914,8 @@ jobs:
# what skipped the charts jobs (they had not started yet).
# No release has ever carried a wekan-<version>-mac-x64.zip because of it.
# Intel macOS is still offered, under new labels: macos-15-intel and
# macos-26-intel (macos-14/15/26 without the suffix are Apple Silicon). This
# is the same label .tools/TSC already builds its Intel Mac on.
# macos-26-intel (macos-15/26 without the suffix are Apple Silicon). This
# is the same label .tools/Secretchronicles/TSC already builds its Intel Mac on.
runs-on: macos-15-intel
timeout-minutes: 60
env:
@@ -3672,7 +3678,7 @@ jobs:
# Ubuntu 24.04's AppArmor defaults kernel.apparmor_restrict_unprivileged_userns=1,
# which blocks the unprivileged user namespaces that the Sandstorm install and
# the spk supervisor rely on (the "fragile part" noted above). Relax it so
# `install.sandstorm.io` and meteor-spk can create sandboxes on the runner.
# `install.sandstorm.org` and meteor-spk can create sandboxes on the runner.
# `|| true`: on kernels/runners without these knobs, setting them is a no-op.
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
@@ -3696,11 +3702,11 @@ jobs:
run: |
# Downloaded, then run: a `curl | sh` cannot be retried, because by the
# time the download fails sh is already half way through the script.
bash "$GITHUB_WORKSPACE/releases/fetch.sh" -o /tmp/install-sandstorm.sh https://install.sandstorm.io
bash "$GITHUB_WORKSPACE/releases/fetch.sh" -o /tmp/install-sandstorm.sh https://install.sandstorm.org
sudo bash /tmp/install-sandstorm.sh -d
- name: Assemble modern meteor-spk.deps (Node 26 + FerretDB + Mongo 3.x CLIs)
# Base = upstream meteor-spk 0.6.0 from dl.sandstorm.io (has mongod 3.0 +
# Base = upstream meteor-spk 0.6.0 from dl.sandstorm.org (has mongod 3.0 +
# niscud). build-deps.sh swaps in Node 26, adds FerretDB + the Mongo 3.x
# CLIs + the migration launcher/importer, keeps niscud, and swaps in the
# modern sandstorm-http-bridge from the Sandstorm install above. No dependency
@@ -4527,7 +4533,10 @@ jobs:
snap-auth-check:
needs: [prepare]
runs-on: ubuntu-24.04
timeout-minutes: 10
# Room for all five snapcraft install attempts (each at most 3 minutes,
# 30 s apart) and the whoami after them: at 10 minutes (v13.09) the job
# was cancelled during the third.
timeout-minutes: 25
steps:
- name: Ask the Snap Store whether SNAP_AUTH is usable
env:
@@ -4551,7 +4560,23 @@ jobs:
;;
esac
sudo snap install snapcraft --classic
# The Snap Store has bad minutes - a rate limit, or (v13.07) a refresh
# that timed out - so the install is retried as the build job's is;
# an outage is said as one, not as a broken credential. Each attempt
# is bounded: in v13.09 the third hung silently until the job was
# cancelled. An attempt cut short leaves snapd's install change
# running, so it is aborted before the next.
installed=""
for i in 1 2 3 4 5; do
if timeout 180 sudo snap install snapcraft --classic; then installed=1; break; fi
sudo snap abort --last=install >/dev/null 2>&1 || true
echo "snapcraft install attempt $i/5 failed (Snap Store unreachable, throttling or hanging); retrying in 30s..."
sleep 30
done
if [ -z "$installed" ]; then
echo "::error::snapcraft could not be installed after 5 attempts: the Snap Store did not answer. That is the store, not SNAP_AUTH - re-run the job."
exit 1
fi
# whoami writes its answer to stdout and its complaint to stderr; both
# matter, so both are captured.
@@ -4708,7 +4733,7 @@ jobs:
with:
snapcraft-args: pack
- name: Refresh stale managed APT indexes after a package 404
- name: Refresh managed APT after a package 404 or a failed apt update
if: steps.build-first.outcome == 'failure'
run: sudo -u "$(id -un)" -E python3 "$GITHUB_WORKSPACE/releases/snap-refresh-managed-apt.py"
@@ -4948,8 +4973,11 @@ jobs:
run: |
set -uo pipefail
for i in 1 2 3 4 5; do
sudo snap install snapcraft --classic && exit 0
echo "snapcraft install attempt $i/5 failed (Snap Store throttle?); retrying in 30s..."
# Bounded, as snap-auth-check's: a hanging store is a failed
# attempt, not a job that waits until it is cancelled.
timeout 180 sudo snap install snapcraft --classic && exit 0
sudo snap abort --last=install >/dev/null 2>&1 || true
echo "snapcraft install attempt $i/5 failed (Snap Store throttle or hang?); retrying in 30s..."
sleep 30
done
echo "::error::snapcraft install failed after 5 attempts (Snap Store rate limit)."
@@ -5061,13 +5089,38 @@ jobs:
# infrastructure earns another one, up to this many, after a pause
# that grows each time.
max_attempts=6
# A Launchpad build log is THIS attempt's when its name did not exist
# before the attempt, or it was rewritten after the marker. The name
# alone decides it where -nt compares whole seconds (bash 3.2): a log
# written in the marker's second was not "newer", and was missed.
logs_before=" "
this_attempts_log() {
[ -e "$1" ] || return 1
case "$logs_before" in *" $1 "*) [ "$1" -nt "$attempt_marker" ] ;; *) return 0 ;; esac
}
# snapcraft's execution logs. v12.96 s390x: every attempt printed only
# "Could not find repository at path ...", because snapcraft reports
# the LAST exception; the cause before it - "HTTP Error 503: Service
# Unavailable" / "Error: Timeout" creating that repository - was in
# the "Full execution log" alone. Unread, three Launchpad outages
# counted as three WeKan failures and the arch was not published.
snapcraft_log_dir="$HOME/.local/state/snapcraft/log"
infra_failure() { # did THIS attempt fail on Launchpad, not on WeKan?
grep -qsE 'HTTP Error 5[0-9][0-9]|Error: Timeout|Service Unavailable' "$remote_log" && return 0
# The same v12.96 s390x run's last attempt: the repository the
# timed-out requests had half-created refused our push with
# "You do not have permission to create refs/heads/main" - the
# same credentials pushed every other architecture in that run.
grep -qsE 'remote: You do not have permission to create refs/' "$remote_log" && return 0
local log
for log in snapcraft-wekan-*.txt; do
[ -e "$log" ] && [ "$log" -nt "$attempt_marker" ] || continue
this_attempts_log "$log" || continue
grep -qsE 'RPC failed;|GnuTLS recv error|fatal: early EOF|Gateway error \[IP: ' "$log" && return 0
done
for log in "$snapcraft_log_dir"/*.log; do
this_attempts_log "$log" || continue
grep -qsE 'HTTP Error 5[0-9][0-9]|Error: Timeout|Service Unavailable' "$log" && return 0
done
return 1
}
# Set once an attempt ends with Launchpad reporting the build as
@@ -5090,6 +5143,7 @@ jobs:
# earlier attempts' logs stay in the directory for the diagnostics
# below, but must not be printed again as if they were this one's.
touch "$attempt_marker"
logs_before=" $(ls snapcraft-wekan-*.txt "$snapcraft_log_dir"/*.log 2>/dev/null | tr '\n' ' ')"
if [ "$fresh_next" = 1 ]; then
echo "The previous attempt's Launchpad build finished without a snap; submitting a new build instead of recovering it."
run_remote_build && rc=0 || rc=$?
@@ -5146,7 +5200,7 @@ jobs:
# and threw it away.
new_logs=0
for log in snapcraft-wekan-*.txt; do
[ -e "$log" ] && [ "$log" -nt "$attempt_marker" ] || continue
this_attempts_log "$log" || continue
new_logs=1
echo "::group::Launchpad build log: $log (what failed, then the last 150 lines)"
# The tail alone is not where the cause is. snapcraft's own error is
@@ -5578,7 +5632,7 @@ jobs:
snapcraft-args: pack
path: variant
- name: Refresh stale managed APT indexes after a package 404
- name: Refresh managed APT after a package 404 or a failed apt update
if: steps.build-first.outcome == 'failure'
run: sudo -u "$(id -un)" -E python3 "$GITHUB_WORKSPACE/wekan/releases/snap-refresh-managed-apt.py"
@@ -5684,7 +5738,7 @@ jobs:
# rewrites the notes of a release that is already published.
timeout-minutes: 15
steps:
# ref: main, so this job reads the same CHANGELOG.md the release job did.
# ref: main, so this job reads the same CHANGELOG-big.md the release job did.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
+1 -1
View File
@@ -32,6 +32,6 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Run chart-releaser
uses: helm/chart-releaser-action@cae68fefc6b5f367a0275617c9f83181ba54714f # v1.7.0
uses: helm/chart-releaser-action@92c6b97fa9f664492a2592e0d1d80bc6bf378957 # v1.9.0
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+4 -1
View File
@@ -140,4 +140,7 @@ jobs:
fi
echo "Publishing $SPK with $SPK_BIN ..."
# Same command as releases/release-sandstorm.sh: spk publish wekan-<VERSION>.spk
"$SPK_BIN" publish "$SPK"
# Publish to apps.sandstorm.org
"$SPK_BIN" publish --webkey=https://api-b72c46193b266fd6c33e1299dccc9bfa.oasis.sandstorm.org#iCDu7VYl4qZxVbDLXLIE2XSU0bEadaBgApEMqgCkLmP "$SPK"
# Publish to apps.sandstorm.io
"$SPK_BIN" publish --webkey=https://alpha-api.sandstorm.io/#Rs-0TT13YrNSbv7Fiz5K9bBkLaJn3E5TB0PU1GSn1HE "$SPK"
+33 -18
View File
@@ -150,27 +150,46 @@ jobs:
node-version: ${{ steps.fetch.outputs.node_version }}
# A 32-bit target's SEA blob must be written by a 32-bit Node.js: the blob
# stores its lengths as size_t (build.mjs refuses otherwise). The i386
# bundle's Node.js, of the same version, writes it for every 32-bit
# target - win32, i386 and 32-bit ARM read the same little-endian format.
# stores its lengths as size_t (build.mjs refuses otherwise). An i386
# Node.js writes it for every 32-bit target - win32, i386 and 32-bit ARM
# read the same little-endian format - but it has to be the SAME VERSION
# the target's bundle embeds (build.mjs checks that too). v13.07 took the
# i386 bundle's own Node.js, and the bundles resolve the newest Node.js of
# the pinned major per platform: i386 got v26.13.1 while 32-bit ARM still
# had v26.13.0, and armhf, armv6 and armv7 all stopped on "The bundle's
# Node.js is v26.13.0 but this is v26.13.1". So the host is resolved at
# the target bundle's exact version, from the same three sources and with
# the same published SHA256 as a bundle's Node.js.
- name: A 32-bit Node.js to build a 32-bit target
id: host32
if: steps.fetch.outputs.skip != 'true' && contains(fromJSON('["win32","i386","armv7","armhf","armv6"]'), matrix.target)
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.fetch.outputs.version }}
NODE_VERSION: ${{ steps.fetch.outputs.node_version }}
run: |
set -euo pipefail
zip="wekan-${VERSION}-i386.zip"
mkdir -p host32
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$zip" --pattern "$zip.sha256sum" --dir host32 \
|| { echo "::error::$TARGET needs the i386 bundle's Node.js to build, and $TAG has no $zip."; exit 1; }
(cd host32 && sha256sum -c "$zip.sha256sum" && unzip -q "$zip" 'bundle/node' 'bundle/node-runtime/*' && rm -f "$zip")
mkdir -p host32 .tools/tmp
export TMPDIR="$PWD/.tools/tmp"
rc=0
src="$(bash releases/resolve-node-source.sh i386 "$NODE_VERSION")" || rc=$?
[ "$rc" = 0 ] || { echo "::error::$TARGET needs an i386 Node.js $NODE_VERSION to build its single file, and none of the three Node.js sources has one."; exit 1; }
node_url="$(printf '%s\n' "$src" | sed -n 's/^node_url=//p')"
node_kind="$(printf '%s\n' "$src" | sed -n 's/^node_kind=//p')"
node_member="$(printf '%s\n' "$src" | sed -n 's/^node_member=//p')"
node_sha256="$(printf '%s\n' "$src" | sed -n 's/^node_sha256=//p')"
bash releases/fetch.sh -o host32/download "$node_url"
echo "$node_sha256 host32/download" | sha256sum -c -
case "$node_kind" in
binary) mv host32/download host32/node ;;
tar.xz) tar -xJf host32/download -C host32 "$node_member" && mv "host32/$node_member" host32/node && rm -f host32/download ;;
*) echo "::error::An i386 Node.js comes as a binary or a tar.xz, not $node_kind."; exit 1 ;;
esac
chmod +x host32/node
sudo dpkg --add-architecture i386
sudo apt-get update -qq
sudo apt-get install -y -qq libc6:i386 libstdc++6:i386 >/dev/null
host="$PWD/host32/bundle/node"
[ -d host32/bundle/node-runtime ] && echo "libs=$PWD/host32/bundle/node-runtime" >> "$GITHUB_OUTPUT"
sudo apt-get install -y -qq libc6:i386 libstdc++6:i386 libatomic1:i386 >/dev/null
host="$PWD/host32/node"
got="$("$host" -p process.version)"
[ "$got" = "$NODE_VERSION" ] || { echo "::error::The i386 Node.js resolved for $NODE_VERSION reports $got."; exit 1; }
echo "node=$host" >> "$GITHUB_OUTPUT"
# A 32-bit ELF target's blob is injected in a 64-bit process, by
# LIEF 1.0.0 - the version Node.js vendors - not inside this 32-bit
@@ -187,20 +206,16 @@ jobs:
env:
VERSION: ${{ steps.fetch.outputs.version }}
HOST_NODE: ${{ steps.host32.outputs.node }}
HOST_LIBS: ${{ steps.host32.outputs.libs }}
SEA_PYTHON: ${{ steps.host32.outputs.python }}
run: |
set -euo pipefail
case "$TARGET" in win*) ext=.exe ;; *) ext= ;; esac
out="wekan-${VERSION}-${TARGET}-single-file${ext}"
# The i386 Node.js finds its own libraries; the libraries are ignored by
# the x64 Node.js that runs build.mjs (another architecture).
# Exit 3: this Node.js is one LIEF cannot inject the SEA into
# correctly (releases/single-file/sea-inject.mjs says which and why).
# That is a missing single file, not a failed release.
rc=0
LD_LIBRARY_PATH="${HOST_LIBS:-${LD_LIBRARY_PATH:-}}" \
node releases/single-file/build.mjs --bundle bundle --target "$TARGET" --version "$VERSION" --out "$out" \
node releases/single-file/build.mjs --bundle bundle --target "$TARGET" --version "$VERSION" --out "$out" \
${HOST_NODE:+--host-node "$HOST_NODE"} 2> build.err || rc=$?
cat build.err >&2
rm -rf bundle
+2 -2
View File
@@ -72,8 +72,8 @@ test-results/
# of them - it replaced an entry per clone (/FerretDB/, /node/, /mongo-tools/,
# /mongosh/, /sandstorm/, /projects/, and the WeKan variants), which had to be
# extended by hand for every new one. build.sh's ensure_tool_repo clones what a
# test run needs into here: .tools/FerretDB, .tools/node-patches,
# .tools/mongo-tools-patches. Nothing in here is WeKan's source, and Meteor is
# test run needs into here: .tools/w/FerretDB, .tools/w/node-patches,
# .tools/w/mongo-tools-patches. Nothing in here is WeKan's source, and Meteor is
# told the same by /.tools/ in .meteorignore.
.tools/
+28 -28
View File
@@ -6,12 +6,12 @@
meteor-base@1.5.2
# Build system
ecmascript@0.19.2-rc360.0
standard-minifier-js@3.3.0-rc360.0
rspack@1.4.0-rc360.0
ecmascript@0.19.2
standard-minifier-js@3.3.0
rspack@1.4.0
# Declared explicitly because server/modernBrowsers.js imports it, to say that
# Yandex Browser is modern and must not be served the legacy bundle (#6557).
modern-browsers@0.2.4-rc360.0
modern-browsers@0.2.4
blaze@3.0.2
@@ -19,12 +19,12 @@ blaze@3.0.2
aldeed:collection2
aldeed:schema-index@4.0.0-beta.5
reywood:publish-composite
mongo@2.5.2-rc360.0
mongo@2.6.1
# Account system
accounts-password@3.3.2-rc360.0
accounts-2fa@3.1.1-rc360.0
accounts-express@1.0.0
accounts-password@3.3.3
accounts-2fa@3.1.1
accounts-express@1.0.1
communitypackages:core
communitypackages:unstyled
communitypackages:flow-routing
@@ -39,19 +39,19 @@ wekan-oidc
wekan-accounts-oidc
# Utilities
check@1.5.1-rc360.0
random@1.2.3-rc360.0
reactive-dict@1.3.3-rc360.0
session@1.2.4-rc360.0
tracker@1.3.5-rc360.0
check@1.5.1
random@1.2.3
reactive-dict@1.3.3
session@1.2.4
tracker@1.3.5
audit-argument-checks@1.0.8
# UI components
ostrio:i18n
reactive-var@1.0.13
shell-server@0.7.1-rc360.0
email@3.2.1-rc360.0
dynamic-import@0.7.5-rc360.0
shell-server@0.7.2
email@3.2.1
dynamic-import@0.8.1
wekan-markdown
quave:synced-cron
ostrio:cookies
@@ -59,21 +59,21 @@ ostrio:files
# lmieulet:meteor-coverage
meteortesting:mocha@3.0.0
matb33:collection-hooks
service-configuration@1.3.6-rc360.0
service-configuration@1.3.6
minifier-css@2.0.2
ejson@1.2.1-rc360.0
logging@1.3.7-rc360.0
ejson@1.4.1
logging@1.3.7
wekan-fullcalendar
wekan-fontawesome
accounts-google@1.4.2-rc360.0
accounts-github@1.5.2-rc360.0
accounts-facebook@1.3.5-rc360.0
accounts-twitter@1.5.3-rc360.0
accounts-meteor-developer@1.5.2-rc360.0
accounts-weibo@1.4.2-rc360.0
accounts-meetup@1.5.2-rc360.0
accounts-passwordless@3.1.2-rc360.0
instrumentation@0.0.1-rc360.0
accounts-google@1.4.2
accounts-github@1.5.2
accounts-facebook@1.3.5
accounts-twitter@1.5.3
accounts-meteor-developer@1.5.2
accounts-weibo@1.4.2
accounts-meetup@1.5.2
accounts-passwordless@3.1.2
instrumentation@0.0.1
# Prevent Meteor build/run tools from sending application package statistics.
package-stats-opt-out@1.0.8
+1 -1
View File
@@ -1 +1 @@
METEOR@3.6-rc.0
METEOR@3.6.1
+75 -75
View File
@@ -1,130 +1,130 @@
accounts-2fa@3.1.1-rc360.0
accounts-base@3.4.0-rc360.0
accounts-express@1.0.0
accounts-facebook@1.3.5-rc360.0
accounts-github@1.5.2-rc360.0
accounts-google@1.4.2-rc360.0
accounts-meetup@1.5.2-rc360.0
accounts-meteor-developer@1.5.2-rc360.0
accounts-oauth@1.4.8-rc360.0
accounts-password@3.3.2-rc360.0
accounts-passwordless@3.1.2-rc360.0
accounts-twitter@1.5.3-rc360.0
accounts-weibo@1.4.2-rc360.0
accounts-2fa@3.1.1
accounts-base@3.4.1
accounts-express@1.0.1
accounts-facebook@1.3.5
accounts-github@1.5.2
accounts-google@1.4.2
accounts-meetup@1.5.2
accounts-meteor-developer@1.5.2
accounts-oauth@1.4.8
accounts-password@3.3.3
accounts-passwordless@3.1.2
accounts-twitter@1.5.3
accounts-weibo@1.4.2
aldeed:collection2@4.2.2
aldeed:schema-index@4.0.0
aldeed:simple-schema@1.13.1
allow-deny@2.1.1-rc360.0_1
allow-deny@2.1.2_1
audit-argument-checks@1.0.8
autoupdate@2.0.2-rc360.0
babel-compiler@7.15.2-rc360.0
autoupdate@2.0.2
babel-compiler@7.15.2
babel-runtime@1.5.2
base64@1.0.15-rc360.0
binary-heap@1.0.14-rc360.0
base64@1.0.15
binary-heap@1.0.14
blaze@3.0.3
blaze-tools@2.0.1
boilerplate-generator@2.2.0-rc360.0
boilerplate-generator@2.2.0
caching-compiler@2.0.1
caching-html-compiler@2.0.1
callback-hook@1.8.1-rc360.0
check@1.5.1-rc360.0
callback-hook@1.8.1
check@1.5.1
communitypackages:core@2.0.3
communitypackages:flow-routing@2.1.0
communitypackages:unstyled@2.0.1
core-runtime@1.0.0
ddp@1.4.3-rc360.0
ddp-client@3.4.2-rc360.0
ddp-common@1.4.6-rc360.0
ddp-rate-limiter@1.3.1-rc360.0
ddp-server@3.4.1-rc360.0_1
diff-sequence@1.1.4-rc360.0
dynamic-import@0.7.5-rc360.0_1
ecmascript@0.19.2-rc360.0
ddp@1.4.3
ddp-client@3.4.2
ddp-common@1.4.6
ddp-rate-limiter@1.3.1
ddp-server@3.6.1_1
diff-sequence@1.2.1
dynamic-import@0.8.1_1
ecmascript@0.19.2
ecmascript-runtime@0.8.3
ecmascript-runtime-client@0.13.0
ecmascript-runtime-server@0.11.1
ejson@1.2.1-rc360.0
email@3.2.1-rc360.0
ejson@1.4.1
email@3.2.1
es5-shim@4.8.1
facebook-oauth@1.11.7-rc360.0_1
facts-base@1.0.3-rc360.0
fetch@0.2.1-rc360.0
geojson-utils@1.0.13-rc360.0
github-oauth@1.4.3-rc360.0
google-oauth@1.4.6-rc360.0_1
facebook-oauth@1.12.1_1
facts-base@1.0.3
fetch@0.2.1
geojson-utils@1.0.13
github-oauth@1.4.3
google-oauth@1.5.1_1
hot-code-push@1.0.5
html-tools@2.0.1
htmljs@2.0.2
id-map@1.2.1-rc360.0
instrumentation@0.0.1-rc360.0
inter-process-messaging@0.1.4-rc360.0
id-map@1.2.1
instrumentation@0.0.1
inter-process-messaging@0.1.4
jquery@3.0.2
lai:collection-extensions@1.0.0
localstorage@1.2.1
logging@1.3.7-rc360.0
logging@1.3.7
matb33:collection-hooks@2.1.0
meetup-oauth@1.1.4-rc360.0
meteor@2.3.2-rc360.0
meetup-oauth@1.1.4
meteor@2.3.2
meteor-base@1.5.2
meteor-developer-oauth@1.3.4-rc360.0
meteor-developer-oauth@1.3.4
meteortesting:browser-tests@1.8.0
meteortesting:mocha@3.4.0
meteortesting:mocha-core@8.2.0
minifier-css@2.0.2
minifier-js@3.3.2-rc360.0
minimongo@2.2.1-rc360.0
modern-browsers@0.2.4-rc360.0
minifier-js@3.3.2
minimongo@2.2.2
modern-browsers@0.2.4
modules@0.20.3
modules-runtime@0.13.2
mongo@2.5.2-rc360.0
mongo@2.6.1
mongo-decimal@0.2.0
mongo-dev-server@1.1.1
mongo-id@1.0.10-rc360.0
npm-mongo@6.16.3
oauth@3.0.5-rc360.0
oauth1@1.5.4-rc360.0
mongo-id@1.0.10
npm-mongo@6.18.1
oauth@3.0.5
oauth1@1.5.4
oauth2@1.3.3
observe-sequence@2.0.1
ordered-dict@1.2.1-rc360.0
ordered-dict@1.2.1
ostrio:cookies@2.9.2
ostrio:cstorage@4.0.1
ostrio:files@3.0.1
ostrio:flow-router-extra@3.15.3
ostrio:i18n@3.2.1
package-stats-opt-out@1.0.8
promise@1.0.1-rc360.0
promise@1.0.1
quave:synced-cron@2.4.0
raix:eventemitter@1.0.0
random@1.2.3-rc360.0
rate-limit@1.2.1-rc360.0
random@1.2.3
rate-limit@1.2.1
react-fast-refresh@0.3.0
reactive-dict@1.3.3-rc360.0
reactive-dict@1.3.3
reactive-var@1.0.13
reload@1.3.3-rc360.0
retry@1.1.2-rc360.0
reload@1.3.3
retry@1.1.2
reywood:publish-composite@1.9.0
routepolicy@1.1.3-rc360.0
rspack@1.4.0-rc360.0
service-configuration@1.3.6-rc360.0
session@1.2.4-rc360.0
sha@1.0.11-rc360.0
shell-server@0.7.1-rc360.0_1
socket-stream-client@0.7.2-rc360.0
routepolicy@1.1.3
rspack@1.4.0
service-configuration@1.3.6
session@1.2.4
sha@1.0.11
shell-server@0.7.2_1
socket-stream-client@0.7.2
spacebars@2.0.1
spacebars-compiler@2.0.1
standard-minifier-js@3.3.0-rc360.0
standard-minifier-js@3.3.0
templating@1.4.5
templating-compiler@2.0.1
templating-runtime@2.0.2
templating-tools@2.0.1
tools-core@1.4.0-rc360.0
tracker@1.3.5-rc360.0
twitter-oauth@1.3.5-rc360.0
typescript@5.11.2-rc360.0
url@1.3.7-rc360.0
webapp@2.3.1-rc360.0
webapp-hashing@1.1.3-rc360.0
weibo-oauth@1.3.4-rc360.0
tools-core@1.4.0
tracker@1.3.5
twitter-oauth@1.3.5
typescript@5.11.2
url@1.3.7
webapp@2.4.1
webapp-hashing@1.1.3
weibo-oauth@1.3.4
wekan-accounts-cas@0.2.0
wekan-accounts-lockout@1.1.0
wekan-accounts-oidc@1.0.10
+115 -28
View File
@@ -26,7 +26,7 @@ Work out which you are from the git identity (the next section says how), then:
| Who | Where the work goes |
| --- | --- |
| **The maintainer** — `Lauri Ojansivu <x@xet7.org>` | **Directly on `main`.** Never a feature branch, never a topic branch, never a pull request. |
| **A contributor** — anybody else | **A branch in your own fork, then a pull request** for the maintainer to review. Never a commit on `main`. |
| **A contributor** — anybody else | **A branch in your own fork, then one pull request per feature or fix** for the maintainer to review. Never a commit on `main`, and no changelog changes. |
Nothing else is a third option. If a tool, a task runner or an agent harness offers
some other arrangement — a topic branch for the maintainer, a direct push for a
@@ -35,13 +35,15 @@ contributor — that offer is the mistake, not the rule.
The maintainer's half is the release flow, not taste. Releases here are frequent —
several a day when a fault is being chased — and `releases/release-all.sh` cuts one
from whatever is on `main`, taking the version from the `# Upcoming WeKan ® release`
section of `CHANGELOG.md`. Work parked on a branch is work that is not in the next
section of `CHANGELOG-big.md`. Work parked on a branch is work that is not in the next
release, and `tests/changelogEntriesBelongToTheirRelease.test.cjs` fails when a
released section links a commit that release does not contain.
The contributor's half is review. Nobody but the maintainer commits to `main` in
wekan/wekan, so a change from anyone else arrives as a pull request — which is also
the only place it can be discussed before it lands.
the only place it can be discussed before it lands. That is why each pull request
holds one feature or one fix, and leaves the changelogs to the maintainer: see
"Contributor mode" below.
Check which branch you are on before committing:
@@ -143,12 +145,29 @@ git config user.name && git config user.email
already exists, on <https://wekan.fi/sponsors>, and belongs nowhere else. The **publishing / release
steps** below are available to a human maintainer only: an AI stops after
committing locally and never runs a step that pushes or publishes.
**Maintainer mode is the only mode that commits directly and the only mode that
updates changelogs**, in WeKan and in the maintainer's repositories under
`.tools/` alike. It applies to the changes the WeKan maintainer makes, and to the
changes of an agent working as the maintainer according to this file.
- **Contributor mode** — the identity is somebody ELSE, in a fork or a clone of your
own — a person, or an AI that files pull requests on its own account. Then: do
**not** commit to `main` and do **not** run any release/publishing step. Make the
changes on a branch and open a **pull request** for the maintainer to review. The
"commit as Lauri Ojansivu", "commit directly to `main`", and all release
instructions below are **maintainer-only and do not apply to you**.
**not** commit directly to the repository or to `main`, and do **not** run any
release/publishing step. Make the changes on a branch and open a **pull request**
for the maintainer to review. The "commit as Lauri Ojansivu", "commit directly to
`main`", changelog and release instructions below are **maintainer-only and do
not apply to you**.
Every pull request, to wekan/wekan or to a maintainer's repository under `.tools/`,
follows two more rules:
- **One feature or one fix per pull request.** Changes share a pull request only
when they are related to that one feature or fix. Anything unrelated, however
small, goes in a pull request of its own, so each can be reviewed, merged or
declined without holding up the others.
- **No changelog changes.** Do not edit `CHANGELOG-big.md`, `CHANGELOG.md`, or the
`CHANGELOG.md` or `CHANGELOG` of a repository under `.tools/` — not an Upcoming
entry, not TODO Later. Describe the change in the pull request instead. The
maintainer writes the entry after merging, when there is a commit for it to link.
Who that pull request credits follows the attribution rule above, and the two
cases differ:
@@ -171,8 +190,8 @@ The one repository under `.tools/` that is somebody else's is `sandstorm-io/sand
cloned for reference only.
Everything below marked as maintainer-specific (committing directly, the exact commit
author, and the entire "Making a release" / publishing flow) applies only in maintainer
mode. The rest (translation policy, CHANGELOG structure, tests, validating from code) is
author, updating changelogs, and the entire "Making a release" / publishing flow) applies
only in maintainer mode. The rest (translation policy, tests, validating from code) is
good practice for everyone.
## Process: "Fix open issues"
@@ -206,6 +225,9 @@ When asked to fix an open issue (one issue at a time):
Fixes #1234,
```
A contributor instead commits on a branch for one pull request that fixes this
one issue, with no changelog changes (see "Contributor mode" above).
6. **If the bug is already fixed**, still add a commit that CLOSES the issue (a commit
whose message ends `Fixes #1234,`), noting where/when it was fixed.
@@ -340,7 +362,7 @@ directly after the merge.
the two patch repositories under `.tools/` — node-patches and mongo-tools-patches.
- Directory structure:
- `wekan` — this repo (https://github.com/wekan/wekan); see
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG.md` at root. It is at
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG-big.md` and `CHANGELOG.md` at root. It is at
`~/repos/wekan` on Linux, `~/Documents/repos/wekan` on macOS and
`Downloads\repos\wekan` on Windows. The maintainer commits on `main`; a
contributor works on a branch and opens a pull request — see "Where this
@@ -357,7 +379,7 @@ directly after the merge.
tool that reads it sees every file twice and reports the bundler's rewritten
code as if it were source. That is not hypothetical — a source-scanning test
did exactly that and failed on generated code nobody can edit.
- `.tools/wekan.fi` — the WeKan website companion repository.
- `.tools/w/wekan.fi` — the WeKan website companion repository.
- `.tools/` — everything that is NOT part of this repository but is needed to
build, test and release it, in ONE directory that `.gitignore` and
`.meteorignore` already exclude (each used to need its own ignore entry at
@@ -371,10 +393,10 @@ directly after the merge.
| Path | Repository | Branch | What it is |
| --- | --- | --- | --- |
| `.tools/FerretDB` | wekan/FerretDB | `main-v1` | the FerretDB v1 fork WeKan ships as its default database |
| `.tools/node-patches` | wekan/node-patches | `main` | patches to upstream Node.js; builds the `node-<platform>` binaries the bundles, the Docker image and the snap embed |
| `.tools/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `mongo-tools-<arch>`, all eight tools as one program |
| `.tools/TSC` | Secretchronicles/TSC | `devel` | the game xet7 also maintains — same author and maintainer mode, its own GNU ChangeLog and release process |
| `.tools/w/FerretDB` | wekan/FerretDB | `main-v1` | the FerretDB v1 fork WeKan ships as its default database |
| `.tools/w/node-patches` | wekan/node-patches | `main` | patches to upstream Node.js; builds the `node-<platform>` binaries the bundles, the Docker image and the snap embed |
| `.tools/w/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `mongo-tools-<arch>`, all eight tools as one program |
| `.tools/Secretchronicles/TSC` | Secretchronicles/TSC | `devel` | the game xet7 also maintains — same author and maintainer mode, its own GNU ChangeLog and release process |
| `.tools/sandstorm` | sandstorm-io/sandstorm | — | upstream Sandstorm, when present — somebody else's project, cloned for reference |
**Unpacked toolchains and caches.** Downloads, not repositories — put there by
@@ -401,12 +423,16 @@ directly after the merge.
of those repositories goes in that repository's own `README.md` or `docs/`,
never in a new instruction file — and if you find one there, remove it rather
than updating it.
- `.tools/FerretDB` specifics — FerretDB `.go` files must contain **no**
- `.tools/w/FerretDB` specifics — FerretDB `.go` files must contain **no**
application-specific names (say "the client" / "a Meteor 3 driver" / a bare
`#NNNN`); its `CHANGELOG.md` may use `wekan/wekan#NNNN`.
### CHANGELOG
- **[maintainer only] Changelogs are updated only in maintainer mode.** This whole
section is for the maintainer and for an agent working as the maintainer, in WeKan
and in the repositories under `.tools/`. A pull request contributor changes no
changelog in any of them (see "Contributor mode" above).
- **Every repository writes its changelog in the format that repository's own file
already uses.** Open its changelog, read the entries above the place you are
adding one, and match them — never import another project's shape into it. The
@@ -414,7 +440,7 @@ directly after the merge.
| Repository | File | Format |
| --- | --- | --- |
| `wekan/wekan` | `CHANGELOG.md` | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/wekan` | `CHANGELOG-big.md` (and the generated `CHANGELOG.md`, below) | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/node-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming node-patches release` |
| `wekan/mongo-tools-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming mongo-tools-patches release` |
| `wekan/FerretDB` | `CHANGELOG.md` | **upstream FerretDB's** format, not WeKan's: `## [v1.48.0](tag URL) (YYYY-MM-DD)` and `### New Features 🎉` / `### Fixed 🐛` / `### Other Changes 🤖` bullets ending `by @xet7. Thanks to xet7.` |
@@ -426,6 +452,52 @@ directly after the merge.
out and would break the tooling that parses them. When this file and the file being
edited disagree, **the file being edited wins** — and everything below in this
section is about the WeKan format specifically.
- **WeKan has TWO changelogs, and only one is written.** `CHANGELOG-big.md` is
the changelog: `# Status`, `# Upcoming WeKan ® release` and every release in
full, in the `<details>` format below - every rule in the rest of this section
is about it, and every entry, fix and release goes into it. `CHANGELOG.md` is
the SHORT changelog, generated from it by `node releases/changelog-short.mjs`
and never edited by hand:
| | `CHANGELOG.md` |
| --- | --- |
| Status, TODO Later | none |
| First | the unreleased `# Upcoming WeKan ® release`, when there is one: its **In short** paragraph only, then `[All changes so far](CHANGELOG-big.md#upcoming-wekan--release)` |
| Below it | the five newest `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` |
| Per release | its **In short** paragraph only, then `[All changes in v…](CHANGELOG-big.md#anchor)` |
| Markup | plain Markdown: no `<details>`, `<summary>`, `</summary>` or `</details>`, and every HTML link a Markdown link |
It exists because the big file is for whoever writes and audits a release,
and most readers - GitHub's front page, the Sandstorm App Market
(`sandstorm-pkgdef.capnp` embeds it), the Helm chart's changelog link - want
what changed lately, at a glance. `releases/release-all.sh` regenerates it
right after naming the new release, so it is in the same release commit, and
`releases/changelog.sh` does the same; `tests/changelogShort.test.cjs` fails
when it is not exactly what the generator writes. So: write
`CHANGELOG-big.md`, run the generator, commit both - and never edit
`CHANGELOG.md` to fix something, because the next run overwrites it.
- **"Update wekan changelog" means both files, in this order:**
1. Find what is not in the changelog yet: `git log` since the newest commit
`CHANGELOG-big.md`'s Upcoming section (or, when it has none, the newest
release's `Prepare v… release` commit) already covers, by anybody - a
merged pull request and the maintainer's own commits alike.
2. Check whether a release was cut since the last update (`release-all.sh`
renames Upcoming to `# v<NEW> …` and opens no new one). If there is no
`# Upcoming WeKan ® release` heading, add it from
[docs/DeveloperDocs/Changelog-Upcoming-Template.md](docs/DeveloperDocs/Changelog-Upcoming-Template.md)
above the newest release - never add an entry to a released section.
3. Write the entries into that Upcoming section of `CHANGELOG-big.md`, in the
format described below (subsections, topic groups, `<details>` entries
linking each commit), and update its **In short** paragraph so it still
names the release's main outcomes.
4. Run `node releases/changelog-short.mjs`: `CHANGELOG.md` then shows that
Upcoming **In short** first and the five newest releases below it, with the
longer details only in `CHANGELOG-big.md`.
5. Run the changelog tests - `tests/changelogShort.test.cjs`,
`tests/changelogFormat.test.cjs`,
`tests/changelogEntriesBelongToTheirRelease.test.cjs` (or
`node tests/run-node-suites.cjs changelog`) - and commit
`CHANGELOG-big.md` and `CHANGELOG.md` together.
- During development, add entries under a new `# Upcoming WeKan ® release` section above
the newest release (FerretDB uses `## Upcoming FerretDB release`; the patch repos use
`# Upcoming <repo> release`). Do **not** hand-edit
@@ -438,10 +510,10 @@ directly after the merge.
add and syntax-check/register the test and state clearly what could not be run.
Do not duplicate an existing test merely to satisfy the audit — extend it when
that keeps one behavior in one suite.
- **CHANGELOG.md stays small enough for GitHub to show it.** GitHub renders
- **CHANGELOG-big.md stays small enough for GitHub to show it.** GitHub renders
Markdown up to 500 KiB; above that it shows only plain source text, and above
about 2 MB nothing at all ("we can't show files that are this big right
now"). CHANGELOG.md reached 2.6 MB over 1,100 releases back to 2015 (#6580),
now"). The changelog reached 2.6 MB over 1,100 releases back to 2015 (#6580),
and keeping only the current month still came to 2.1 MB, because releases
here are frequent and large. So the cut is by SIZE: 450,000 bytes
(`BUDGET` in `releases/changelog-archive.mjs`), under GitHub's limit with
@@ -449,7 +521,7 @@ directly after the merge.
| | |
| --- | --- |
| `CHANGELOG.md` | `# Status` (Platforms/TODO Later inside it), `# Upcoming`, and the newest releases that fit the budget |
| `CHANGELOG-big.md` | `# Status` (Platforms/TODO Later inside it), `# Upcoming`, and the newest releases that fit the budget |
| `old-CHANGELOG/<year>/<MM>.md` | a month's releases, newest first; a month over the budget continues in `<MM>-part2.md`, `<MM>-part3.md`, ... |
| `old-CHANGELOG/<year>.md` | 2015–2025, whole |
@@ -464,11 +536,11 @@ directly after the merge.
still in git (`gitk`, `git-gui`, `git log --follow`), and being small enough
to open is worth more.
Run `node releases/changelog-archive.mjs` whenever CHANGELOG.md has grown -
Run `node releases/changelog-archive.mjs` whenever CHANGELOG-big.md has grown -
after a release is the natural moment. It is idempotent — a run with nothing
to move only refreshes the tables and parts — and it decides from the FILE
rather than the clock, so two people running it agree.
`tests/changelogArchive.test.cjs` fails when CHANGELOG.md is over the budget.
`tests/changelogArchive.test.cjs` fails when CHANGELOG-big.md is over the budget.
An archived section is never edited, for the same reason a released one is
not.
- **The file's shape, top to bottom** — keep it exactly as it is now. There is
@@ -564,7 +636,7 @@ directly after the merge.
`<details>` blocks below carry that information. Keep the paragraph current as
topics change, and shorten it when added commits make it grow. A finished release
keeps the paragraph it was written with.
- **Neither CHANGELOG.md nor release notes may contain a binaries provenance
- **Neither changelog nor the release notes may contain a binaries provenance
section or a Platform/Bundle/Binary/From/Version/Checked/SHA256 table.**
Do not add "Binaries in these bundles" at the top or bottom of either.
Release notes summarize each Translations group as only a list of updated
@@ -577,7 +649,7 @@ directly after the merge.
no security fixes - is left out rather than added empty.
Release notes have only In short, Security, Translations (language list),
the standard thanks line and More details at ChangeLog with the release
anchor link. Other details stay in CHANGELOG.md. Keep download
anchor link into CHANGELOG-big.md. Other details stay in CHANGELOG-big.md. Keep download
verification and `provenance.tsv` build artifacts for diagnostics; do not
append their contents or `releases/provenance-table.sh` output to notes.
- **Inside a subsection, entries are GROUPED BY TOPIC/AREA.** A release touches a handful
@@ -636,7 +708,7 @@ directly after the merge.
Because CRITICAL comes first, it keeps the `This release ` prefix; a following non-security
subsection becomes `and …` per the rule above.
- **TODO Later** — a `<details>` nested inside `# Status` near the TOP of
`CHANGELOG.md` (above the version sections), a triage backlog for open
`CHANGELOG-big.md` (above the version sections), a triage backlog for open
issues that were **investigated but not fixed here**, each
recorded with a concrete REASON so whoever picks it up next knows why. Use it when working
through open issues (the "Fix open issues" process): for each issue, either **fix it** (commit
@@ -717,7 +789,7 @@ have cost a released section its accuracy:
- **Work continues immediately after a release**, so an entry written right
after one has to go somewhere. `release-all.sh` renames `# Upcoming WeKan ®
release` to `# v<NEW> …` and stops there — it does **not** create a new,
empty `# Upcoming` section. CHANGELOG.md never carries an
empty `# Upcoming` section. CHANGELOG-big.md never carries an
`**In short:** nothing here yet.` placeholder sitting between releases; it
used to (via a now-deleted `releases/changelog-open-next.mjs`), and that
meant the file always had a section saying nothing, from the moment a
@@ -762,7 +834,7 @@ have cost a released section its accuracy:
during ordinary development, and the next run takes the next number.
What decides it is the GitHub Release, not the tag: `prepare` pushes the tag early,
so a tag can exist for a release that never published.
- FerretDB: run `./build.sh release-ferretdb` from `.tools/FerretDB` (no
- FerretDB: run `./build.sh release-ferretdb` from `.tools/w/FerretDB` (no
version). It renames `## Upcoming FerretDB release` to the next version with the
correct git-tag link, commits + tags + pushes, then triggers `release-all.yml` (which
in turn triggers `docker.yml` for the multi-arch image). It refuses to re-release an
@@ -771,7 +843,7 @@ have cost a released section its accuracy:
### Security issues
- Fix the vulnerability, add a CRITICAL section to the WeKan CHANGELOG like previous
entries, and update `.tools/wekan.fi/hall-of-fame/index.html` and the vuln-name
entries (maintainer mode only, as with every changelog change), and update `.tools/w/wekan.fi/hall-of-fame/index.html` and the vuln-name
subdirectory `index.html` like previous security issues.
- **Every security fix gets a TEST and a NEGATIVE TEST, and they are written so the
fault cannot exist ANYWHERE in the codebase — not just at the place it was
@@ -869,6 +941,21 @@ have cost a released section its accuracy:
sizes them at 24px, and keeps the icon and its red drop on one line at every width
(they are one icon) — the table itself stays percentage-sized with no fixed or
minimum width, so it fills the page at every browser width.
- **A report that arrives WITHOUT a GHSA is upgraded when its GHSA arrives.** The
stars follow `.tools/w/wekan.fi/hall-of-fame/Audit-Stars.md`: a GHSA with a
suggested fix is 5, a report sent to security@wekan.fi or any other way, with a
suggested fix, is 3. When that researcher is later able to add a GitHub security
advisory for the same report, the maintainer requests the CVE through the GHSA
process, and the row's 3 stars become 5. So whenever an AI updates or audits the
Hall of Fame, it checks each 3-star row whose Process says the report came
without a GHSA against [WeKan Security
Advisories](https://github.com/wekan/wekan/security/advisories): when a GHSA for
that report now exists, it sets the row to 5 stars on the index AND on the row's
own page, links the GHSA (and the CVE in the CVE cell once one is assigned),
rewrites the Process cell to say the report later arrived as a GHSA, and records
the change in Audit-Stars.md. Match by the report's subject and reporter, not by
a nearby advisory - a GHSA about something else is no upgrade. Until the GHSA
exists, the row stays at 3 and its Process cell says the GHSA is awaited.
- **On the Hall of Fame index page, the Process and Vulnerabilities cells are
collapsed.** Those two columns carry the prose — how the report arrived, and the
whole story of the vulnerability — so the page would be one wall of text and the
+8678
View File
File diff suppressed because it is too large. Load diff
+38 -5278
View File
File diff suppressed because it is too large. Load diff
+115 -28
View File
@@ -26,7 +26,7 @@ Work out which you are from the git identity (the next section says how), then:
| Who | Where the work goes |
| --- | --- |
| **The maintainer** — `Lauri Ojansivu <x@xet7.org>` | **Directly on `main`.** Never a feature branch, never a topic branch, never a pull request. |
| **A contributor** — anybody else | **A branch in your own fork, then a pull request** for the maintainer to review. Never a commit on `main`. |
| **A contributor** — anybody else | **A branch in your own fork, then one pull request per feature or fix** for the maintainer to review. Never a commit on `main`, and no changelog changes. |
Nothing else is a third option. If a tool, a task runner or an agent harness offers
some other arrangement — a topic branch for the maintainer, a direct push for a
@@ -35,13 +35,15 @@ contributor — that offer is the mistake, not the rule.
The maintainer's half is the release flow, not taste. Releases here are frequent —
several a day when a fault is being chased — and `releases/release-all.sh` cuts one
from whatever is on `main`, taking the version from the `# Upcoming WeKan ® release`
section of `CHANGELOG.md`. Work parked on a branch is work that is not in the next
section of `CHANGELOG-big.md`. Work parked on a branch is work that is not in the next
release, and `tests/changelogEntriesBelongToTheirRelease.test.cjs` fails when a
released section links a commit that release does not contain.
The contributor's half is review. Nobody but the maintainer commits to `main` in
wekan/wekan, so a change from anyone else arrives as a pull request — which is also
the only place it can be discussed before it lands.
the only place it can be discussed before it lands. That is why each pull request
holds one feature or one fix, and leaves the changelogs to the maintainer: see
"Contributor mode" below.
Check which branch you are on before committing:
@@ -143,12 +145,29 @@ git config user.name && git config user.email
already exists, on <https://wekan.fi/sponsors>, and belongs nowhere else. The **publishing / release
steps** below are available to a human maintainer only: an AI stops after
committing locally and never runs a step that pushes or publishes.
**Maintainer mode is the only mode that commits directly and the only mode that
updates changelogs**, in WeKan and in the maintainer's repositories under
`.tools/` alike. It applies to the changes the WeKan maintainer makes, and to the
changes of an agent working as the maintainer according to this file.
- **Contributor mode** — the identity is somebody ELSE, in a fork or a clone of your
own — a person, or an AI that files pull requests on its own account. Then: do
**not** commit to `main` and do **not** run any release/publishing step. Make the
changes on a branch and open a **pull request** for the maintainer to review. The
"commit as Lauri Ojansivu", "commit directly to `main`", and all release
instructions below are **maintainer-only and do not apply to you**.
**not** commit directly to the repository or to `main`, and do **not** run any
release/publishing step. Make the changes on a branch and open a **pull request**
for the maintainer to review. The "commit as Lauri Ojansivu", "commit directly to
`main`", changelog and release instructions below are **maintainer-only and do
not apply to you**.
Every pull request, to wekan/wekan or to a maintainer's repository under `.tools/`,
follows two more rules:
- **One feature or one fix per pull request.** Changes share a pull request only
when they are related to that one feature or fix. Anything unrelated, however
small, goes in a pull request of its own, so each can be reviewed, merged or
declined without holding up the others.
- **No changelog changes.** Do not edit `CHANGELOG-big.md`, `CHANGELOG.md`, or the
`CHANGELOG.md` or `CHANGELOG` of a repository under `.tools/` — not an Upcoming
entry, not TODO Later. Describe the change in the pull request instead. The
maintainer writes the entry after merging, when there is a commit for it to link.
Who that pull request credits follows the attribution rule above, and the two
cases differ:
@@ -171,8 +190,8 @@ The one repository under `.tools/` that is somebody else's is `sandstorm-io/sand
cloned for reference only.
Everything below marked as maintainer-specific (committing directly, the exact commit
author, and the entire "Making a release" / publishing flow) applies only in maintainer
mode. The rest (translation policy, CHANGELOG structure, tests, validating from code) is
author, updating changelogs, and the entire "Making a release" / publishing flow) applies
only in maintainer mode. The rest (translation policy, tests, validating from code) is
good practice for everyone.
## Process: "Fix open issues"
@@ -206,6 +225,9 @@ When asked to fix an open issue (one issue at a time):
Fixes #1234,
```
A contributor instead commits on a branch for one pull request that fixes this
one issue, with no changelog changes (see "Contributor mode" above).
6. **If the bug is already fixed**, still add a commit that CLOSES the issue (a commit
whose message ends `Fixes #1234,`), noting where/when it was fixed.
@@ -340,7 +362,7 @@ directly after the merge.
the two patch repositories under `.tools/` — node-patches and mongo-tools-patches.
- Directory structure:
- `wekan` — this repo (https://github.com/wekan/wekan); see
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG.md` at root. It is at
`docs/DeveloperDocs/Directory-Structure.md`; `CHANGELOG-big.md` and `CHANGELOG.md` at root. It is at
`~/repos/wekan` on Linux, `~/Documents/repos/wekan` on macOS and
`Downloads\repos\wekan` on Windows. The maintainer commits on `main`; a
contributor works on a branch and opens a pull request — see "Where this
@@ -357,7 +379,7 @@ directly after the merge.
tool that reads it sees every file twice and reports the bundler's rewritten
code as if it were source. That is not hypothetical — a source-scanning test
did exactly that and failed on generated code nobody can edit.
- `.tools/wekan.fi` — the WeKan website companion repository.
- `.tools/w/wekan.fi` — the WeKan website companion repository.
- `.tools/` — everything that is NOT part of this repository but is needed to
build, test and release it, in ONE directory that `.gitignore` and
`.meteorignore` already exclude (each used to need its own ignore entry at
@@ -371,10 +393,10 @@ directly after the merge.
| Path | Repository | Branch | What it is |
| --- | --- | --- | --- |
| `.tools/FerretDB` | wekan/FerretDB | `main-v1` | the FerretDB v1 fork WeKan ships as its default database |
| `.tools/node-patches` | wekan/node-patches | `main` | patches to upstream Node.js; builds the `node-<platform>` binaries the bundles, the Docker image and the snap embed |
| `.tools/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `mongo-tools-<arch>`, all eight tools as one program |
| `.tools/TSC` | Secretchronicles/TSC | `devel` | the game xet7 also maintains — same author and maintainer mode, its own GNU ChangeLog and release process |
| `.tools/w/FerretDB` | wekan/FerretDB | `main-v1` | the FerretDB v1 fork WeKan ships as its default database |
| `.tools/w/node-patches` | wekan/node-patches | `main` | patches to upstream Node.js; builds the `node-<platform>` binaries the bundles, the Docker image and the snap embed |
| `.tools/w/mongo-tools-patches` | wekan/mongo-tools-patches | `main` | patches to the MongoDB Database Tools; builds `mongo-tools-<arch>`, all eight tools as one program |
| `.tools/Secretchronicles/TSC` | Secretchronicles/TSC | `devel` | the game xet7 also maintains — same author and maintainer mode, its own GNU ChangeLog and release process |
| `.tools/sandstorm` | sandstorm-io/sandstorm | — | upstream Sandstorm, when present — somebody else's project, cloned for reference |
**Unpacked toolchains and caches.** Downloads, not repositories — put there by
@@ -401,12 +423,16 @@ directly after the merge.
of those repositories goes in that repository's own `README.md` or `docs/`,
never in a new instruction file — and if you find one there, remove it rather
than updating it.
- `.tools/FerretDB` specifics — FerretDB `.go` files must contain **no**
- `.tools/w/FerretDB` specifics — FerretDB `.go` files must contain **no**
application-specific names (say "the client" / "a Meteor 3 driver" / a bare
`#NNNN`); its `CHANGELOG.md` may use `wekan/wekan#NNNN`.
### CHANGELOG
- **[maintainer only] Changelogs are updated only in maintainer mode.** This whole
section is for the maintainer and for an agent working as the maintainer, in WeKan
and in the repositories under `.tools/`. A pull request contributor changes no
changelog in any of them (see "Contributor mode" above).
- **Every repository writes its changelog in the format that repository's own file
already uses.** Open its changelog, read the entries above the place you are
adding one, and match them — never import another project's shape into it. The
@@ -414,7 +440,7 @@ directly after the merge.
| Repository | File | Format |
| --- | --- | --- |
| `wekan/wekan` | `CHANGELOG.md` | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/wekan` | `CHANGELOG-big.md` (and the generated `CHANGELOG.md`, below) | the WeKan format this section describes: `# Status` (Platforms/Version/TODO Later nested as `<details>`), then `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` sections of `<details>` entries whose `<summary>` links the commit |
| `wekan/node-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming node-patches release` |
| `wekan/mongo-tools-patches` | `CHANGELOG.md` | the same WeKan format, with `# Upcoming mongo-tools-patches release` |
| `wekan/FerretDB` | `CHANGELOG.md` | **upstream FerretDB's** format, not WeKan's: `## [v1.48.0](tag URL) (YYYY-MM-DD)` and `### New Features 🎉` / `### Fixed 🐛` / `### Other Changes 🤖` bullets ending `by @xet7. Thanks to xet7.` |
@@ -426,6 +452,52 @@ directly after the merge.
out and would break the tooling that parses them. When this file and the file being
edited disagree, **the file being edited wins** — and everything below in this
section is about the WeKan format specifically.
- **WeKan has TWO changelogs, and only one is written.** `CHANGELOG-big.md` is
the changelog: `# Status`, `# Upcoming WeKan ® release` and every release in
full, in the `<details>` format below - every rule in the rest of this section
is about it, and every entry, fix and release goes into it. `CHANGELOG.md` is
the SHORT changelog, generated from it by `node releases/changelog-short.mjs`
and never edited by hand:
| | `CHANGELOG.md` |
| --- | --- |
| Status, TODO Later | none |
| First | the unreleased `# Upcoming WeKan ® release`, when there is one: its **In short** paragraph only, then `[All changes so far](CHANGELOG-big.md#upcoming-wekan--release)` |
| Below it | the five newest `# v<MAJOR>.<MINOR> YYYY-MM-DD WeKan ® release` |
| Per release | its **In short** paragraph only, then `[All changes in v…](CHANGELOG-big.md#anchor)` |
| Markup | plain Markdown: no `<details>`, `<summary>`, `</summary>` or `</details>`, and every HTML link a Markdown link |
It exists because the big file is for whoever writes and audits a release,
and most readers - GitHub's front page, the Sandstorm App Market
(`sandstorm-pkgdef.capnp` embeds it), the Helm chart's changelog link - want
what changed lately, at a glance. `releases/release-all.sh` regenerates it
right after naming the new release, so it is in the same release commit, and
`releases/changelog.sh` does the same; `tests/changelogShort.test.cjs` fails
when it is not exactly what the generator writes. So: write
`CHANGELOG-big.md`, run the generator, commit both - and never edit
`CHANGELOG.md` to fix something, because the next run overwrites it.
- **"Update wekan changelog" means both files, in this order:**
1. Find what is not in the changelog yet: `git log` since the newest commit
`CHANGELOG-big.md`'s Upcoming section (or, when it has none, the newest
release's `Prepare v… release` commit) already covers, by anybody - a
merged pull request and the maintainer's own commits alike.
2. Check whether a release was cut since the last update (`release-all.sh`
renames Upcoming to `# v<NEW> …` and opens no new one). If there is no
`# Upcoming WeKan ® release` heading, add it from
[docs/DeveloperDocs/Changelog-Upcoming-Template.md](docs/DeveloperDocs/Changelog-Upcoming-Template.md)
above the newest release - never add an entry to a released section.
3. Write the entries into that Upcoming section of `CHANGELOG-big.md`, in the
format described below (subsections, topic groups, `<details>` entries
linking each commit), and update its **In short** paragraph so it still
names the release's main outcomes.
4. Run `node releases/changelog-short.mjs`: `CHANGELOG.md` then shows that
Upcoming **In short** first and the five newest releases below it, with the
longer details only in `CHANGELOG-big.md`.
5. Run the changelog tests - `tests/changelogShort.test.cjs`,
`tests/changelogFormat.test.cjs`,
`tests/changelogEntriesBelongToTheirRelease.test.cjs` (or
`node tests/run-node-suites.cjs changelog`) - and commit
`CHANGELOG-big.md` and `CHANGELOG.md` together.
- During development, add entries under a new `# Upcoming WeKan ® release` section above
the newest release (FerretDB uses `## Upcoming FerretDB release`; the patch repos use
`# Upcoming <repo> release`). Do **not** hand-edit
@@ -438,10 +510,10 @@ directly after the merge.
add and syntax-check/register the test and state clearly what could not be run.
Do not duplicate an existing test merely to satisfy the audit — extend it when
that keeps one behavior in one suite.
- **CHANGELOG.md stays small enough for GitHub to show it.** GitHub renders
- **CHANGELOG-big.md stays small enough for GitHub to show it.** GitHub renders
Markdown up to 500 KiB; above that it shows only plain source text, and above
about 2 MB nothing at all ("we can't show files that are this big right
now"). CHANGELOG.md reached 2.6 MB over 1,100 releases back to 2015 (#6580),
now"). The changelog reached 2.6 MB over 1,100 releases back to 2015 (#6580),
and keeping only the current month still came to 2.1 MB, because releases
here are frequent and large. So the cut is by SIZE: 450,000 bytes
(`BUDGET` in `releases/changelog-archive.mjs`), under GitHub's limit with
@@ -449,7 +521,7 @@ directly after the merge.
| | |
| --- | --- |
| `CHANGELOG.md` | `# Status` (Platforms/TODO Later inside it), `# Upcoming`, and the newest releases that fit the budget |
| `CHANGELOG-big.md` | `# Status` (Platforms/TODO Later inside it), `# Upcoming`, and the newest releases that fit the budget |
| `old-CHANGELOG/<year>/<MM>.md` | a month's releases, newest first; a month over the budget continues in `<MM>-part2.md`, `<MM>-part3.md`, ... |
| `old-CHANGELOG/<year>.md` | 2015–2025, whole |
@@ -464,11 +536,11 @@ directly after the merge.
still in git (`gitk`, `git-gui`, `git log --follow`), and being small enough
to open is worth more.
Run `node releases/changelog-archive.mjs` whenever CHANGELOG.md has grown -
Run `node releases/changelog-archive.mjs` whenever CHANGELOG-big.md has grown -
after a release is the natural moment. It is idempotent — a run with nothing
to move only refreshes the tables and parts — and it decides from the FILE
rather than the clock, so two people running it agree.
`tests/changelogArchive.test.cjs` fails when CHANGELOG.md is over the budget.
`tests/changelogArchive.test.cjs` fails when CHANGELOG-big.md is over the budget.
An archived section is never edited, for the same reason a released one is
not.
- **The file's shape, top to bottom** — keep it exactly as it is now. There is
@@ -564,7 +636,7 @@ directly after the merge.
`<details>` blocks below carry that information. Keep the paragraph current as
topics change, and shorten it when added commits make it grow. A finished release
keeps the paragraph it was written with.
- **Neither CHANGELOG.md nor release notes may contain a binaries provenance
- **Neither changelog nor the release notes may contain a binaries provenance
section or a Platform/Bundle/Binary/From/Version/Checked/SHA256 table.**
Do not add "Binaries in these bundles" at the top or bottom of either.
Release notes summarize each Translations group as only a list of updated
@@ -577,7 +649,7 @@ directly after the merge.
no security fixes - is left out rather than added empty.
Release notes have only In short, Security, Translations (language list),
the standard thanks line and More details at ChangeLog with the release
anchor link. Other details stay in CHANGELOG.md. Keep download
anchor link into CHANGELOG-big.md. Other details stay in CHANGELOG-big.md. Keep download
verification and `provenance.tsv` build artifacts for diagnostics; do not
append their contents or `releases/provenance-table.sh` output to notes.
- **Inside a subsection, entries are GROUPED BY TOPIC/AREA.** A release touches a handful
@@ -636,7 +708,7 @@ directly after the merge.
Because CRITICAL comes first, it keeps the `This release ` prefix; a following non-security
subsection becomes `and …` per the rule above.
- **TODO Later** — a `<details>` nested inside `# Status` near the TOP of
`CHANGELOG.md` (above the version sections), a triage backlog for open
`CHANGELOG-big.md` (above the version sections), a triage backlog for open
issues that were **investigated but not fixed here**, each
recorded with a concrete REASON so whoever picks it up next knows why. Use it when working
through open issues (the "Fix open issues" process): for each issue, either **fix it** (commit
@@ -717,7 +789,7 @@ have cost a released section its accuracy:
- **Work continues immediately after a release**, so an entry written right
after one has to go somewhere. `release-all.sh` renames `# Upcoming WeKan ®
release` to `# v<NEW> …` and stops there — it does **not** create a new,
empty `# Upcoming` section. CHANGELOG.md never carries an
empty `# Upcoming` section. CHANGELOG-big.md never carries an
`**In short:** nothing here yet.` placeholder sitting between releases; it
used to (via a now-deleted `releases/changelog-open-next.mjs`), and that
meant the file always had a section saying nothing, from the moment a
@@ -762,7 +834,7 @@ have cost a released section its accuracy:
during ordinary development, and the next run takes the next number.
What decides it is the GitHub Release, not the tag: `prepare` pushes the tag early,
so a tag can exist for a release that never published.
- FerretDB: run `./build.sh release-ferretdb` from `.tools/FerretDB` (no
- FerretDB: run `./build.sh release-ferretdb` from `.tools/w/FerretDB` (no
version). It renames `## Upcoming FerretDB release` to the next version with the
correct git-tag link, commits + tags + pushes, then triggers `release-all.yml` (which
in turn triggers `docker.yml` for the multi-arch image). It refuses to re-release an
@@ -771,7 +843,7 @@ have cost a released section its accuracy:
### Security issues
- Fix the vulnerability, add a CRITICAL section to the WeKan CHANGELOG like previous
entries, and update `.tools/wekan.fi/hall-of-fame/index.html` and the vuln-name
entries (maintainer mode only, as with every changelog change), and update `.tools/w/wekan.fi/hall-of-fame/index.html` and the vuln-name
subdirectory `index.html` like previous security issues.
- **Every security fix gets a TEST and a NEGATIVE TEST, and they are written so the
fault cannot exist ANYWHERE in the codebase — not just at the place it was
@@ -869,6 +941,21 @@ have cost a released section its accuracy:
sizes them at 24px, and keeps the icon and its red drop on one line at every width
(they are one icon) — the table itself stays percentage-sized with no fixed or
minimum width, so it fills the page at every browser width.
- **A report that arrives WITHOUT a GHSA is upgraded when its GHSA arrives.** The
stars follow `.tools/w/wekan.fi/hall-of-fame/Audit-Stars.md`: a GHSA with a
suggested fix is 5, a report sent to security@wekan.fi or any other way, with a
suggested fix, is 3. When that researcher is later able to add a GitHub security
advisory for the same report, the maintainer requests the CVE through the GHSA
process, and the row's 3 stars become 5. So whenever an AI updates or audits the
Hall of Fame, it checks each 3-star row whose Process says the report came
without a GHSA against [WeKan Security
Advisories](https://github.com/wekan/wekan/security/advisories): when a GHSA for
that report now exists, it sets the row to 5 stars on the index AND on the row's
own page, links the GHSA (and the CVE in the CVE cell once one is assigned),
rewrites the Process cell to say the report later arrived as a GHSA, and records
the change in Audit-Stars.md. Match by the report's subject and reporter, not by
a nearby advisory - a GHSA about something else is no upgrade. Until the GHSA
exists, the row stays at 3 and its Process cell says the GHSA is awaited.
- **On the Hall of Fame index page, the Process and Vulnerabilities cells are
collapsed.** Those two columns carry the prose — how the report arrived, and the
whole story of the vulnerability — so the page would be one wall of text and the
+13 -4
View File
@@ -38,7 +38,7 @@ FROM --platform=$BUILDPLATFORM debian:trixie AS bundle
ARG TARGETARCH
ARG TARGETVARIANT
ARG VERSION=12.67
ARG VERSION=13.12
ARG DEBIAN_FRONTEND=noninteractive
COPY --chmod=755 releases/check-telemetry.py /tmp/check-telemetry.py
@@ -148,10 +148,10 @@ ARG DEBIAN_FRONTEND=noninteractive
ENV \
DEBUG=false \
DDP_TRANSPORT=sockjs \
NODE_VERSION=v26.12.1 \
METEOR_RELEASE=METEOR@3.6-rc.0 \
NODE_VERSION=v26.13.1 \
METEOR_RELEASE=METEOR@3.6.1 \
USE_EDGE=false \
NPM_VERSION=12.2.1 \
NPM_VERSION=12.2.2 \
SRC_PATH=./ \
WITH_API=true \
MONGO_OPLOG_URL="" \
@@ -292,6 +292,14 @@ ENV \
LOGOUT_ON_HOURS="" \
LOGOUT_ON_MINUTES="" \
USERS_DISABLE_INACTIVE_AFTER_DAYS="" \
LOGOUT_IDLE_MINUTES="" \
REQUIRE_2FA="" \
PASSWORD_MIN_LENGTH="" \
PASSWORD_MIN_LENGTH_ENFORCE="" \
PASSWORD_CHECK_PWNED="" \
HSTS_MAX_AGE="" \
HSTS_INCLUDE_SUBDOMAINS="" \
HSTS_PRELOAD="" \
ATTACHMENTS_RETENTION_DAYS="" \
ATTACHMENTS_RETENTION_MODE="" \
ATTACHMENTS_PURGE_DELETED_AFTER_DAYS="" \
@@ -310,6 +318,7 @@ ENV \
WEKAN_DISK_WATCH_PATHS="" \
WEKAN_RAM_MONITOR="" \
WEKAN_IMPORT_RUN_STALE_MS="" \
EVENTLOG_RETENTION_DAYS="" \
CORS="" \
CORS_ALLOW_HEADERS="" \
CORS_EXPOSE_HEADERS="" \
Loaded 100 of 1301 files, more files were not shown because too many files have changed in this diff. Show more