Wekan logo

Wekan

Open source kanban board application built with Meteor

Alternative to: trello


About Versions (218)

v10.96

2026-08-16

Binaries in these bundles

Each bundle carries a Node.js, a FerretDB and the MongoDB Database Tools. Which source has a given CPU varies from release to release - nodejs.org builds some architectures, unofficial-builds others, and the wekan/node-patches build the ones neither of them does - and not every source publishes a checksum. This is what went into this release, and which downloads were checked against a published SHA256.

BundleBinaryFromVersionCheckedSHA256
amd64FerretDBwekan/FerretDBv1.53.0verifiedeae1f0a8f73bfc97…
amd64Node.jsnodejs.orgv24.19.0verified14b342e71204f811…
arm64FerretDBwekan/FerretDBv1.53.0verifiedbdc50caee3ac2849…
arm64Node.jsnodejs.orgv24.19.0verified01443c1e1a29e531…
armhfFerretDBwekan/FerretDBv1.53.0verifiedf7f050207e656d65…
armhfNode.jswekan/node-patchesv24.19.0verifiedb55350f3071b765a…
armv6FerretDBwekan/FerretDBv1.53.0verified34f5f553dcd2bff3…
armv6Node.jswekan/node-patchesv24.19.0verified128ded0cda638c1f…
armv7FerretDBwekan/FerretDBv1.53.0verifiedf7f050207e656d65…
armv7Node.jswekan/node-patchesv24.19.0verified8dbe0a9aa8550ad5…
i386FerretDBwekan/FerretDBv1.53.0verified515b1189b4c7d58f…
i386Node.jswekan/node-patchesv24.19.0verified3b0b3bbfe27daf58…
mac-arm64FerretDBwekan/FerretDBv1.53.0verifiedcb14ffe93e285903…
mac-arm64Node.jsnodejs.orgv24.19.0verified3f1cf157479c1480…
mac-x64FerretDBwekan/FerretDBv1.53.0verifiedd97dfa9afa60aa05…
mac-x64Node.jsnodejs.orgv24.19.0verifiedd35e95230f46f6f0…
ppc64leFerretDBwekan/FerretDBv1.53.0verified481d36a80ec73673…
ppc64leNode.jsnodejs.orgv24.19.0verifiedc510c6ce12f07010…
riscv64FerretDBwekan/FerretDBv1.53.0verified4b43f86eb0f3fa0b…
riscv64Node.jsunofficial-builds.nodejs.orgv24.19.0verifiedcd1f14af28121480…
s390xFerretDBwekan/FerretDBv1.53.0verifiedfe74e03894b84897…
s390xNode.jsnodejs.orgv24.19.0verifieda4792e65962ffa0a…
win-arm64FerretDBwekan/FerretDBv1.53.0verifiedf1186c310a4dd144…
win-arm64Node.jsnodejs.orgv24.19.0verified8502f4a50b458d4c…
win64FerretDBwekan/FerretDBv1.53.0verifiedc6dad7bc8ad56f47…
win64Node.jsnodejs.orgv24.19.0verified57f71ab3652e797d…

A row saying no checksum published is not a failed check - it is a source that publishes nothing to check against. Those are the ones worth fixing at the source.

v10.96 2026-08-16 WeKan ® release

In short: one CRITICAL fix. With registration turned OFF in the Admin Panel, POST /users/register created accounts anyway — for anybody who asked, on an instance whose administrator had decided nobody else may join. The guard that was supposed to stop it read a Meteor option WeKan never sets, so it was always false and the endpoint had never refused anyone. It was found while reviewing a pull request about the opposite symptom. Then the release workflow: pressing Cancel did not stop a run, so docker went on building an image for a release being abandoned, and the Sandstorm .spk gets under its 1 GiB limit at last, now that its size report says what filled the gigabyte. What that measurement found ended up changing every platform, not just Sandstorm: uws is not reliable enough yet, so every default is now sockjs, and no bundle carries uWebSockets.js (121M), the legacy client (81M) or source maps (152M) — around 354M a bundle, none of it on any loading path. Below that: two AWS SDK updates for the S3 attachment path.

PlatformBinaryFromVersionSHA256
amd64Node.jsnodejs.orgv24.19.014b342e71204f811bde6153be8e04b62aef63c236fef92b55f9c83154b409647
amd64FerretDBwekan/FerretDBv1.53.0eae1f0a8f73bfc979738bfff7284d40fd1bc55de2cc56514721fc155c3624f7d
arm64Node.jsnodejs.orgv24.19.001443c1e1a29e531ccad5a46fefa6df490d2189c49f7955904aecdbb0fe86fdc
arm64FerretDBwekan/FerretDBv1.53.0bdc50caee3ac28495b42d2130b94a042a9dd6d3a38f732cac02b648f36c891da
mac-arm64Node.jsnodejs.orgv24.19.03f1cf157479c1480352083105e13faf9d008ede98e7e157746b6df940d197b94
mac-arm64FerretDBwekan/FerretDBv1.53.0cb14ffe93e285903e5a8a9c1821687ddb5b8a979a11c584bf4af534b272c6d3e
mac-x64Node.jsnodejs.orgv24.19.0d35e95230f46f6f0751df497c56622c6735e05d5e1fb1630996a005b9d328fe4
mac-x64FerretDBwekan/FerretDBv1.53.0d97dfa9afa60aa05f25384327de82efe7b71d958ed24c1f66618284294a65cd3

This release fixes the following CRITICAL SECURITY ISSUE of SignupBleed:

Account creation - who may make an account, and who decides.

SignupBleed: registration disabled, and the REST API created accounts anyway. Thanks to AhmedLukman and xet7.

Turning Registration off in the Admin Panel closes the sign-up form. It did not close POST /users/register, which went on creating accounts and handing back login tokens to anyone who asked. A closed instance was open.

The endpoint did have a guard. It asked Accounts._options.forbidClientAccountCreation — and nothing in WeKan ever sets that:

  • the only Accounts.config() call, in server/accounts-common.js, sets loginExpirationInDays and nothing else;
  • forbidClientAccountCreation: disableRegistration in config/accounts.js is passed to AccountsTemplates.configure(), which is the useraccounts package’s own options object, not Meteor’s Accounts;
  • and that disableRegistration is only assigned inside an async Meteor.call('isDisableRegistration', …) callback that fires AFTER configure() has already run — something the file’s own comment records, for a different reason, a few lines above.

Three near-misses, and the condition was always falsy. The guard read as though it worked, which is why it lasted: the name says exactly what it ought to do.

It reads the setting from where the setting lives now - the same getCurrentSetting().disableRegistration === true that the isDisableRegistration Meteor method behind the sign-up form uses, so the form and the API can no longer disagree about whether the door is open. An instance with no Settings document yet still allows registration rather than locking itself out.

Every call that reaches the refusal is recorded and shows in Admin Panel / Problems under authz.register. That is safe to log without drowning the page precisely because there is no legitimate caller: the administrator has turned registration off.

How it was found. #6598 by AhmedLukman changed this guard to read WeKan’s setting, filed against #4774 — a 403 Forbidden from this endpoint that nobody had been able to reproduce, and which this line cannot produce, since it never returned 403 at all. The pull request was closed and the fix written here with a security log entry and the tests the route had never had; the finding is the reporter’s.

Nine tests, four of them negative, on an endpoint that had none: that the setting is read where the Meteor method reads it, that the dead option is not consulted again AND is still set nowhere — so a later change cannot quietly reintroduce two sources of truth that disagree — that an enabled instance still creates the user and answers with its token, and that a missing Settings document does not refuse everybody.

and updates the following dependencies:

  • @aws-sdk/client-s3 3.1108.0 → 3.1109.0 — the S3 client WeKan stores attachments through when S3 storage is configured.
  • @aws-sdk/lib-storage 3.1104.0 → 3.1109.0 — the multipart upload helper beside it, which is what actually streams a large attachment to S3.

Thanks to dependabot.

and fixes the following build failures:

The release workflow - what a release builds, and who can stop it.

Cancelling a release run stops it, and a stuck job no longer needs a human. Thanks to xet7.

Pressing Cancel on a release-all run did not stop the docker job. It kept going for another twenty minutes and would have pushed a multi-arch image for a release that was being abandoned.

The cause is always(). It is true while a run is CANCELLING, which is exactly why it was chosen in v10.80: a build-mac-x64 queued on a runner label GitHub had retired sat for two hours, was cancelled by hand, and that cancellation SKIPPED charts, ucs and nextcloud — so the Helm chart for an already published WeKan was never pushed, and no error anywhere said why. always() fixed that by ignoring cancellation entirely, and ignoring cancellation is also what disabled the Cancel button on a job that takes hours.

!cancelled() keeps the half that matters — do not skip me because a SIBLING failed — and drops the half that overrides a person. Nine job-level conditions change, across release-all.yml, release-all-missing.yml, Flatpak.yml and AppImage.yml. The 22 step-level if: always() reporting steps do not: those correctly print CANCELLED.

That alone would reopen v10.80, so the other half of the fix is that no job needs a hand cancellation any more. The eight jobs that had no timeout-minutes now have one, and every job in the release workflow is bounded. A stuck job FAILS on its own, and a failure satisfies !cancelled() the same way always() let it through — Cancel is left meaning only what a person meant by it.

The Sandstorm .spk gets under 1 GiB, by dropping what a grain cannot run. Thanks to xet7.

v10.93, v10.94 and v10.95 all failed to pack with App exceeds uncompressed size limit of 1 GiB. v10.95 was the first run whose size report worked, and it named the gigabyte: .meteor-spk/bundle 852M plus .meteor-spk/deps 336M, so 1188M against a 1024M ceiling. It also answered the question that report was written to ask — a second meteor-spk pack REUSES the bundle rather than rebuilding it, since the pruned tree stayed pruned across the retry (852M to 833M). So trimming between the two attempts works. 19M was simply never going to close a 165M gap.

Measuring the bundle rather than guessing at it turned up two passengers that are large, unreachable at runtime, and safe to drop:

  • uWebSockets.js ships twenty prebuilt binaries — Linux/macOS/Windows times x64/arm64 times four Node ABIs, 121M — and its loader is one line: require('./uws_' + process.platform + '_' + process.arch + '_' + process.versions.modules + '.node'). A machine is one platform running one Node, so the other sixteen files can never be opened by it. Keeping every ABI of the target platform and CPU, so a Node major bump still finds its binary, frees ~93M. The entry below drops the remaining 28M as well.
  • Source maps — 4766 files, 188M, over a fifth of the bundle. They exist for a debugger attached to the process. A packed app has none, and a missing .map degrades a stack trace at worst.

releases/bundle-trim.mjs does both, measured at 281 MiB on a real bundle, and the Sandstorm leg runs it beside the existing prune before the retry pack.

An architecture with NO uWebSockets.js prebuild at all — ppc64le, s390x, riscv64, where ddp-server falls back to sockjs — is left completely alone, since deleting the other platforms’ files there would free nothing that matters and could only break the fallback. tests/bundleTrim.test.cjs pins that, the kept ABIs, that a directory merely ending in .map is not a source map, and that the trim runs before the retry pack rather than after it.

The Sandstorm .spk ships no uWebSockets.js at all, because a grain talks sockjs. Thanks to xet7.

Trimming uWebSockets.js down to the prebuilds a grain’s own platform can open freed 93M of its 121M. All of it can go, because a grain loads NONE of it.

The uws transport is OPTIONAL in Meteor 3. ddp-server’s transports/index.js resolves the transport from Meteor.settings.packages['ddp-server'].transport, then DDP_TRANSPORT, then DISABLE_SOCKJS, and DEFAULTS to sockjs — and Npm.require('uWebSockets.js') sits inside the uws transport’s setup(), which runs only for the transport that was actually chosen. sandstorm-pkgdef.capnp sets none of those, and its environ is, by its own comment, the app’s ENTIRE environment. So a grain has been running sockjs all along while carrying 121M of a module it never required.

The pkgdef now PINS DDP_TRANSPORT=sockjs, because the removal should rest on a stated fact rather than on an upstream default staying put, and bundle-trim.mjs takes --transport sockjs, which removes the module whole instead of thinning its prebuilds. 120 MiB measured, 27 MiB more than the platform trim: 1188M becomes ~860M, with over 160M of headroom against the 1 GiB limit.

It is a flag rather than the default because WeKan asks for uws nearly everywhere else — docker-compose, start-wekan.sh, build.sh — and those bundles keep the module, trimmed to the prebuilds their own platform can open.

tests/bundleTrim.test.cjs ties the grain’s pinned transport to the flag the .spk is trimmed with. If those two ever disagreed, the grain would require a module that was left out and fail to boot, which is the one way this can go wrong.

and changes what every platform ships:

Bundles and images - what a build carries, and what it no longer does.

Every platform talks sockjs, and no bundle carries uws or the legacy client. Thanks to xet7.

uws is not reliable enough yet to be what a default points at, so nothing WeKan ships selects it any more, and nothing carries the module. Three halves, and each is useless or harmful without the others.

The defaults. Every docker-compose*.yml, start-wekan.sh, start-wekan.bat, build.sh, build.bat, the image’s own ENV block, the snap config and the Sandstorm grain now say sockjs. The commented-out #- DDP_TRANSPORT=uws alternatives beside them are replaced by a note saying why there is no alternative, rather than advertising a value that no longer works.

The bundles. Every place a built bundle is post-processed — the amd64 build, each per-arch rebuild, the repack container, releases/install-node-for-arch.sh and the Dockerfile — now runs bundle-trim.mjs beside the prune it already ran, dropping two things:

  • uWebSockets.js, 121 MiB, for the reason in the entry above: a sockjs server never requires the module at all.

  • programs/web.browser.legacy, a whole second copy of the client built for browsers without modern JS. 81 MiB measured. Meteor supports running with architectures excluded and says so in webapp’s categorizeRequest(): “If our preferred arch is not available, it’s better to use another client arch that is available than to guarantee the site won’t work”. An old browser is served web.browser; the 404 branch below that is reached only when NO arch matches, which cannot happen while web.browser is there, and autoupdate iterates the programs that actually loaded rather than a fixed list.

    The files are only half of it. The arch is NAMED in programs/server/config.json and star.json, and boot.js builds a dynamic-import root for every name in the first — so the name is removed with the files. Those manifests are mode 444 as Meteor writes them, so they are made writable, rewritten, and set back: failing there would leave the one state that actually breaks a server, files gone and manifests still naming the arch.

The upgrade. An existing docker-compose.yml that says DDP_TRANSPORT=uws would ask the new image for a module it does not have and crash-loop on the require. So the Docker entrypoint and the bundle’s own start-wekan.sh coerce uws back to sockjs before starting anything, and print why — a setting silently ignored is worse than one that fails.

tests/sockjsEverywhere.test.cjs pins the three halves together: a default without the coercion is an upgrade trap, a coercion without the trim is dead weight, and a trim without both is a crash.

Source maps go from every platform, not only the Sandstorm .spk. Thanks to xet7.

The .zip bundles and the Docker image were trimmed with --keep-maps, so only Sandstorm — which had a 1 GiB ceiling to get under — dropped them. Every platform drops them now: 152 MiB per bundle, measured. That is 188 MiB across 4766 files, less the 36 MiB inside web.browser.legacy that the entry above already takes with it.

Nothing on any loading path reads one. A source map translates a position in built code back to the source that produced it — this bundle’s server side is one 117 MiB programs/server/app/app.js, and its 58 MiB app.js.map is what turns app.js:1284531 into a file and a line. The two things that read it are browser devtools, which fetch the .map only while they are open, and Node stack traces through source-map-support. A released bundle has neither attached to it.

The //# sourceMappingURL comments stay behind, and are comments: a missing target means devtools show compiled positions and a server stack trace prints bundle offsets. Debugging a production crash goes back to reproducing it against a development build, which is where the maps still are. The guard pins that no call site keeps them, so one platform cannot quietly drift back to carrying them.

Thanks to above GitHub users for their contributions and translators for their translations.