Skip to content

wekan

v9.76 Breaking

This release includes breaking changes for platform teams planning a safe upgrade.

Published 21d Productivity & Wikis
✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →

✓ No known CVEs patched in this version

Topics

docker javascript kanban meteor real-time sandstorm
+2 more
snapcraft wekan

Affected surfaces

auth

Summary

AI summary

Updates https://github.com/wekan/wekan/commit/8b21158736f9e1360332903707c100bce3d6b164, board, and https://github.com/wekan/wekan/commit/a2802a850b57ef7ac183ddb581be666d14d43705 across a mixed release.

Full changelog

v9.76 2026-07-06 WeKan ® release

This release adds the following fixes:

  • Fix notification emails linked to /b/undefined/board/<cardId> instead of the real board:
    On the server ReactiveCache.getBoard() is async and returns a Promise, but Cards.board() did not await it,
    so the synchronous Card.originRelativeUrl()/absoluteUrl() interpolated a Promise —
    board._id and board.slug were undefined, producing /b/undefined/board/<cardId> in
    card activity notification emails (the client UI was unaffected because this.board() is
    synchronous there). Fixed by making Card.originRelativeUrl(board)/absoluteUrl(board) accept an
    already-resolved board and fall back to this.boardId (always available synchronously) when the
    board is a Promise, and by passing the awaited board from server/models/activities.js so the
    correct board id and slug are used.
    Thanks to titver968 and xet7.

  • Release workflow: publish the Helm chart only after the Docker image is pushed:
    In .github/workflows/release-all.yml the charts job now depends on the docker job
    (needs: docker) instead of running in parallel right after bump. GitHub Actions runs a job
    only when all of its needs jobs succeed, so the wekan/charts Helm chart is published only after
    the multi-arch image is live on Docker Hub / Quay.io / GHCR (and is skipped if docker fails).
    This prevents ArtifactHub from scanning a freshly published chart whose image tag does not exist
    yet and emailing the maintainer about the missing Docker image.
    Thanks to xet7.

  • Fix thousands of unsolicited empty "Default" swimlanes created on some boards:
    Board.getDefaultSwimline()/getDefaultSwimlineAsync() self-heal a missing default swimlane by
    reading the board's swimlanes and inserting one if none exist. That check-then-insert is a race:
    concurrent or repeated server calls for a swimlane-less board each saw zero swimlanes and each
    inserted a new one, so some boards accumulated 30 000+ empty "Default" swimlanes and became
    unloadable (the key 'default (en)' returned an object instead of string log is a harmless i18n
    side effect — the title correctly falls back to the string Default). Follow-up to the
    client-side #6382 fix, which only stopped the
    browser from auto-creating them. Fixed by making the server self-heal idempotent: the default
    swimlane is now upserted with a deterministic _id (<boardId>-default), so the _id unique
    index guarantees at most one default swimlane per board no matter how many times, or how
    concurrently, the getters run. archived/type are set explicitly in the $setOnInsert because
    their schema autoValue/defaultValue only fire on insert, not upsert. Note: this prevents new
    duplicates; boards that already accumulated thousands still need a one-off cleanup.
    Thanks to brlin-tw and xet7.

  • Reduce card flicker on drag by only writing changed fields on a card move:
    Card.move() wrote boardId/swimlaneId/listId into the update unconditionally, even for a
    same-board drag to another list (the reported case). Keeping boardId in the $set on every drag
    re-ran the boardId-gated Cards.after.update hook that re-syncs the card's checklists and
    checklist items via multi updates, ran the cross-board consistency guard and the
    denyCrossBoardMove deny-rule DB lookup, and invalidated more reactive dependents than
    necessary — server work and reactivity churn that contributed to a ~1s card flicker on large
    boards. Card.move() now writes only the fields that actually change (via the pure
    computeCardMoveModifier helper) and skips the write entirely when a card is dropped back in the
    same place, so a same-board move no longer touches boardId or the cross-board hooks. The
    moveCard/moveCardBoard activity generators already re-check doc.X !== oldX, so trimming the
    modifier does not drop any activity. Note: this reduces the drag-time work behind the flicker; the
    residual reactive re-render cost on very large boards is a separate performance topic.
    Thanks to mimZD and xet7.

  • Fix DEFAULT_AUTHENTICATION_METHOD env var ignored, and Admin Panel Layout save hanging:
    Two related problems with the default login authentication method.

    • Env var ignored: the stored setting was only ever seeded as password and
      DEFAULT_AUTHENTICATION_METHOD was never applied, so operators setting it (e.g. Kubernetes/Helm
      DEFAULT_AUTHENTICATION_METHOD: ldap) saw no effect. Startup now applies the env var
      authoritatively: it seeds the value on a fresh install and, on existing installs, keeps the
      stored defaultAuthenticationMethod in sync with the env var on every boot (the operator's env
      is the source of truth), so the method can be configured entirely by env without the Admin Panel.
      The value is normalized (trimmed + lower-cased), so DEFAULT_AUTHENTICATION_METHOD=LDAP works.
    • Layout save hanging / not persisting: the authentication-method <select> is populated by an
      async Meteor.call, so clicking Admin Panel > Layout > Save before it loaded sent an empty
      value for the required defaultAuthenticationMethod field, which silently failed validation —
      the save looked stuck and nothing changed. The save now falls back to the currently stored method
      when the select is empty, so a real value is never overwritten by ''.
      Both paths share one pure helper (resolveDefaultAuthenticationMethod) that never resolves to an empty string.
      Thanks to joe-speedboat and xet7.
  • Fix #5808: linking a card to another linked card made both cards inaccessible:
    The "Link to this card" target picker only excluded template cards, so an existing linked card
    (or a card that already links back to the current board) could be chosen as a link target. That
    builds a chain/cycle of linkedId pointers, but the card helpers
    (getTitle/getBoardTitle/getRealId) resolve linkedId only one hop, so such a card
    renders as an empty/broken pointer and becomes effectively inaccessible (the reported freeze). As
    the reporter suggested, the fix prevents the configuration rather than allowing it: only a real
    card — not a linked card/linked board, not one of the linking board's own cards, and not a card
    that links back to one of them — may now be a link target. This is enforced both in the picker's
    query and re-checked at creation time (the options can be stale), via the pure
    isLinkableCardTarget guard, mirroring the existing #3328 parent/subtask cycle guard. Note: this
    stops new inaccessible links; any already-created ones still need manual cleanup.
    Thanks to the reporter and xet7.

  • Fix the "Board not found" flicker (stale-while-revalidate for the client board cache):
    While viewing a board, the board view could briefly flash the "Board not found" shell — and on
    WebKit throw a Blaze Can't select in removed DomRange error tearing down the card view. Root
    cause: the client board cache (imports/lib/dataCache.js) re-fetches its value inside a reactive
    computation, and when the board doc is momentarily absent from minimongo (a subscription stops and
    restarts, so Meteor transiently removes the doc) the re-fetch returns undefined and that empty
    value is surfaced immediately. It self-recovers when the subscription re-delivers the doc, so it
    presents as a flicker — reliably reproduced only on Firefox/WebKit, where the reactive-render
    timing hits the window (Chromium did not, which is why it surfaced as browser-specific Playwright
    failures in 14-voting-watchers and 24-feature-issues). Fixed with an opt-in
    stale-while-revalidate mode on DataCache, enabled only for getBoard: a transient miss over
    an already-cached board keeps the last value and re-checks after a short delay, surfacing an empty
    result only if the board is still gone then (a genuine deletion / access loss). First-ever loads
    and caches that did not opt in are unchanged. Core decision extracted to the pure
    shouldDeferCacheMiss helper with unit tests.

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

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track wekan

Get notified when new releases ship.

Sign up free

About wekan

The Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . New English strings for new features at imports/i18n/data/en.i18n.json . Non-English translations at https://app.transifex.com/wekan/wekan only.

All releases →

Related context

Beta — feedback welcome: [email protected]