Commit Graph
1209 Commits
Author SHA1 Message Date
Ahmed Tawfik f6d7d90bde Initial commit: PLANKA project
Build and Test / build-and-test (push) Failing after 6m57s
Build and Push Docker Nightly Image / build-and-push-docker-nightly-image (push) Canceled after 0s
2026-09-22 02:20:23 -04:00
Daniel Hiller 627701dda3 feat: Let a board filter be put down and picked up again
Two things, and neither of them happens on its own.

Clearing the filter row now keeps a snapshot, and the same spot turns into an
undo arrow that puts it back. Taking the filter apart chip by chip does not
leave one, because that is someone changing their mind rather than setting
something aside.

A filter can also be saved under a name and picked again later, per person and
per board.

Nothing is restored automatically when a board opens, which is the whole
reason this may live in localStorage at all. Two windows on the same board
would otherwise pull one stored filter back and forth. The storage is read in
three places and only one of them dispatches, inside a click handler.

The keys and the payload are Pro's, so saved filters carry over to an upgrade
instead of being left behind. Community's own filter dimensions ride along as
extra fields that Pro reads as absent.
2026-09-17 03:51:52 +02:00
Daniel Hiller b581a97c2c feat: Show how many cards a list holds
A board level switch, off by default. With it on, each list puts its card
count on the add-card button. Useful to a board that is being kept to a size
and noise to one that is not, which is why it is a choice rather than always
on.

The count is every card in the list, not the ones a filter leaves visible.
Those answer different questions: how full is this list, versus how many match
what I am looking at right now. The first is the one a counter is for, and it
is also what the same switch counts in Pro, so a board keeps its meaning
across an upgrade.

The column matches Pro's name, type and default, so the setting survives that
upgrade rather than being dropped on the way.
2026-09-17 03:51:40 +02:00
Daniel Hiller ca035d670e fix: Restore two files committed empty by the list filter merge
75fcddcd wrote `client/src/models/Board.js` and the board filter bar out as
zero bytes. `BoardActions` imports `Filters` from that module, so the client
could not render a board at all.

The conflict resolution there was scripted, and the script opened each file
for writing before reading it, which truncates it first and then reads back
nothing. ESLint accepts an empty module, so lint stayed green and the commit
looked fine.

Both files are rebuilt from the two merge parents. All six conflicts resolve
to keeping both sides, which is what the merge intended: excluded labels,
cards without a member and lists are three independent filters.

One behaviour fix rides along, because the union merge would otherwise have
carried it in. The no-member branch ended its block with `return cardModels`,
which was correct while it was the last filter and wrong once the list filter
followed it: combining the two would have dropped the list filter without a
word. The three now compose.
2026-09-17 03:50:09 +02:00
Daniel Hiller f1e8c59d6e Merge pull request #1571 from lustsazeus-lab/fix/issue-165-feature-bounty
feat: improve card communication with reply and mentions
2026-09-17 03:27:21 +02:00
Daniel Hiller 3edce1cc04 fix: Date the webhook scope migration after the one before it
The file came in named 20260502000000, which sorts ahead of the TOTP attempt
limit from August. Knex records migrations in the order it runs them, so the
last row in the table is not the same on every instance: a fresh install runs
both in name order and ends on the August one, while an instance that already
had it runs only this one and ends here.

Pro reads that last row to recognise which Community version it is upgrading
from, and it can only carry one name per version. Renaming to a timestamp
after the August migration makes the last row the same everywhere.

The rename is safe while this is only a few commits old on master and in no
tag. Anyone who pulled in that window and already ran it would see knex offer
it a second time.
2026-09-17 02:38:56 +02:00
Daniel Hiller 67d3ccf425 chore: Add the German strings for the two new filters 2026-09-17 02:05:06 +02:00
Daniel Hiller 1c683604ea Merge pull request #1649 from symonbaikov/feat/list-view-reorder
feat: allow re-ordering cards in list view

Conflicted only on the import block, where the icon button tooltips had added
a line. Both imports stay.
2026-09-17 02:03:25 +02:00
Daniel Hiller 75fcddcd0b Merge pull request #1648 from symonbaikov/feat/list-filter
feat: add filter by list (status) in list view

Three filters landed in the same places while this was open: excluded labels,
cards without a member, and now lists. They are independent of each other, so
every conflict resolves to keeping both sides.

The selector was the one exception worth naming. Both branches had written the
same body under a different name, so the merge put one header on one body; it
is now two complete selectors.
2026-09-17 02:03:04 +02:00
Daniel Hiller bb2da7f36a Merge pull request #1236 from symonbaikov/feat/no-member-filter
Add filter for cards without members ("No member" filter)

Conflicted with the negative label filter, which had added its own field to
the board model next to this one. Both stay; they filter on different things.
2026-09-17 02:01:58 +02:00
Daniel Hiller fe00e81a81 fix: Chown the chart's volume once rather than on every start
Setting `fsGroup` without a change policy leaves Kubernetes on its default of
`Always`, which walks the whole volume and chowns every file each time the pod
starts. On an instance with a few thousand attachments that turns every
restart into a wait, and a slow enough one fails the readiness probe.

`OnRootMismatch` checks the top directory and stops there when it already
matches, so an existing volume pays the walk once.
2026-09-17 02:01:26 +02:00
Daniel Hiller 1f2f3f297e Merge pull request #1680 from symonbaikov/fix/new-card-disappears-with-filters
fix: inherit active board filters when creating a card ()
2026-09-17 02:00:36 +02:00
Daniel Hiller 35f14b479c Merge pull request #1650 from symonbaikov/feature/webhook-scope-filter
feat: filter webhooks per project, board, and account
2026-09-17 02:00:30 +02:00
Daniel Hiller 7024673632 Merge pull request #1645 from symonbaikov/feature/apprise-config-options
feat: add operator config options for Apprise notifications (closes #1576)
2026-09-17 02:00:24 +02:00
Daniel Hiller 840526ade1 Merge pull request #1574 from Br1an67/fix/issue-956-helm-fsgroup
Set fsGroup in podSecurityContext for proper volume permissions
2026-09-17 02:00:17 +02:00
Daniel Hiller d32215a235 Merge pull request #1780 from plankanban/dependabot/npm_and_yarn/client/nanoid-3.3.19
build(deps): Bump nanoid from 3.3.16 to 3.3.19 in /client
2026-09-17 00:53:22 +02:00
Daniel Hiller 1b3e5d6acb docs: Record the unreleased changes in the changelog
Covers what has landed since 2.2.1: the tooltips, negative label filtering,
the remembered board view, the locale check, the translation pass, the four
fixes from contributors, and the dependency updates that carry security
fixes. The version heading is left alone until the release is cut.
2026-09-17 00:49:24 +02:00
Daniel Hiller 8236cde954 fix: Keep the seed from refusing to start an instance that already ran
The validation added with the admin user checks reached further than intended.
The seed runs on every container start, not only on the first, and it threw on
three conditions that are normal on a second start.

The worst was the check for an existing user: after the first start the
default admin exists by definition, so every restart after that ended in a
container that would not come up. The insert is wrapped in a try and falls
back to an update precisely because it is meant to run again, and that path
was never reached.

The other two were narrower but the same shape. `validator.isEmail` rejects
`admin@localhost`, which is a common value in self-hosted setups, so an
instance that had been running for a year would stop coming up after the
upgrade.

None of it throws now. Bad input is reported on stderr and skipped, the way
an invalid username already was, and a username that belongs to someone else
is left alone rather than taken. `db:create-admin-user` keeps the strict
validation, which is where a person is there to read it and type again.
2026-09-17 00:48:08 +02:00
Daniel Hiller 84827ab354 Merge pull request #1683 from symonbaikov/feat/negative-label-filter
Add negative label filtering for cards

Two conflicts, both from work that landed while this branch was open.

In the endless list query, master had added an ORDER BY so the cursor and the
limit agree. The exclusion clause belongs in the WHERE part, so it is placed
before it rather than after; the other way round the statement does not parse.

The label item had been restructured here for the tri-state filter and had
gained a tooltip on master. The restructured version is kept and the tooltip
put back on top of it, which also brings back the translation hook this branch
had dropped.
2026-09-17 00:46:31 +02:00
Daniel Hiller 6cfdf839ca Merge pull request #1719 from crmaris/fix/task-reorder-hidden-completed
fix: Fix task reordering when completed tasks are hidden

Conflicted with the icon button tooltips, which had added a Tooltip import to
the same file. Both imports stay except `useToggle`, which this branch makes
redundant by lifting the per-list eye toggle up to TaskLists so the position
calculation can see it.
2026-09-17 00:45:20 +02:00
Daniel Hiller caa3f720e9 Merge pull request #1723 from AymanAlSuleihi/fix/create-admin-user-seed-validation
fix: Create admin user seed validation
2026-09-17 00:44:37 +02:00
Daniel Hiller aa7e2c49fc Merge pull request #1726 from AymanAlSuleihi/fix/login-and-user-forms-validation-feedback
fix: Show error messages on invalid login and user settings form inputs
2026-09-17 00:44:30 +02:00
Daniel Hiller c17d43b97d Merge pull request #1721 from babu-ch/fix/ime-composition-enter-submit
fix: Ignore Enter pressed during IME composition in input fields
2026-09-17 00:44:24 +02:00
Daniel Hiller 8340ebb1f5 Merge branch 'master' of github.com:plankanban/planka 2026-09-17 00:42:44 +02:00
Daniel Hiller 8314be0a04 fix(i18n): Correct the Swedish translations
Thanks to NickWick13, who found these while working on something else.

`removeFromBoard` and `removeFromProject` still had the English "from" in
them. `serverConnectionFailed` was never translated at all. The Pro banner
called boards "bräden", which is a plank of wood, where the rest of the file
says "tavlor". "E-mail" is written "e-postadress" in Swedish, "Timer" is a
"stoppur", and a viewer is a "läsare", not a "visare".

The date formats were still the en-US patterns, so Swedish dates rendered in
American order with the English word "at" in the middle. They now read
"d MMMM 'kl.' p".

The tooltip strings from the same pass are included here so Swedish lands in
one piece.
2026-09-17 00:42:24 +02:00
Daniel Hiller 71724a7532 chore: Translate the icon button tooltips into every locale
The tooltips arrived with their strings in en-US and ru-RU only, which would
have left the hint layer half English in thirty-two languages. The same
twenty-four keys are now in all of them.

Each locale follows its own file: the word it already uses for board, card,
list and project, its register, and its own key order. Where a locale's
existing wording for a neighbouring key is wrong, the new strings use the
correct form rather than repeating the mistake, and the mistake is left for a
separate pass.
2026-09-17 00:42:24 +02:00
Daniel Hiller 2030722242 feat: Remember which view a board was last put into
Choosing a view and finding the board's default again after every reload is
the board forgetting something the reader clearly meant.

The choice lives in `sessionStorage`, not on the account. A view is how one
person is looking at a board right now, not a property of the board: two
windows may hold the same board open as a grid and as a list, and neither is
wrong. A stored preference would make one of them change under the other's
hands.

The context is stored alongside and has to match on the way back out, so a
view picked in the archive does not follow the board into its own context. A
view this build no longer knows falls through to the board's default, as does
anything stored while the browser refuses to keep it.
2026-09-17 00:42:11 +02:00
Daniel Hiller 764bd106a7 fix: Keep tooltips off touch devices
A tooltip triggered by hover and focus also fires on a tap, because tapping a
button focuses it. On a phone the hint then covers the thing that was just
pressed, and it stays there until something else takes focus. There is no
moment before a click for it to fill.

`IS_TOUCH_PRIMARY` reads `(hover: none)` once at load, the way `IS_MAC` reads
the platform, and the tooltip renders its trigger bare when it is set.
2026-09-17 00:42:11 +02:00
Daniel Hiller 0e8a41adf8 Merge pull request #1685 from symonbaikov/codex/icon-button-tooltips
feat: add tooltips for icon buttons

Conflicts came from the branch predating the Pro banner work and the deleted
API key step. Resolved by keeping master's `onEdit` on the user actions popup
and wrapping it in the tooltip, and by leaving `ApiKeyStep.jsx` deleted.

The `switchToView` key is dropped in favour of one complete string per view.
Interpolating a view name into "Switch to {{view}} view" required
`t('common.' + view).toLowerCase()` at the call site, and lowercasing a
translated string is wrong wherever case carries meaning: German capitalises
nouns, and Turkish turns I into a dotless ı. The five replacements need no
interpolation and no lowercasing.
2026-09-17 00:41:33 +02:00
dependabot[bot] 63a4c36294 build(deps): Bump nanoid from 3.3.16 to 3.3.19 in /client
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.16 to 3.3.19.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.16...3.3.19)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-version: 3.3.19
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 22:41:21 +00:00
Daniel Hiller f7aefd9af3 Merge pull request #1777 from plankanban/dependabot/npm_and_yarn/server/js-yaml-4.3.2
build(deps-dev): Bump js-yaml from 4.3.1 to 4.3.2 in /server
2026-09-17 00:40:12 +02:00
Daniel Hiller 14ea440ade Merge pull request #1776 from plankanban/dependabot/npm_and_yarn/server/sharp-0.35.4
build(deps): Bump sharp from 0.35.3 to 0.35.4 in /server
2026-09-17 00:40:06 +02:00
Daniel Hiller 7ad0f9b324 Merge pull request #1775 from plankanban/dependabot/npm_and_yarn/server/nodemailer-9.1.1
build(deps): Bump nodemailer from 9.0.3 to 9.1.1 in /server
2026-09-17 00:40:00 +02:00
Daniel Hiller a2568d9e83 Merge pull request #1771 from plankanban/dependabot/npm_and_yarn/server/fast-uri-3.1.7
build(deps-dev): Bump fast-uri from 3.1.5 to 3.1.7 in /server
2026-09-17 00:39:54 +02:00
Daniel Hiller 83f803e15c Merge pull request #1778 from plankanban/dependabot/npm_and_yarn/client/js-yaml-3.15.2
build(deps-dev): Bump js-yaml from 3.15.1 to 3.15.2 in /client
2026-09-17 00:39:49 +02:00
Daniel Hiller 1f419cdff0 Merge pull request #1774 from plankanban/dependabot/npm_and_yarn/client/svgo-3.3.5
build(deps): Bump svgo from 3.3.4 to 3.3.5 in /client
2026-09-17 00:39:43 +02:00
Daniel Hiller b135c7031d Merge pull request #1769 from plankanban/dependabot/npm_and_yarn/client/sanitize-html-2.17.7
build(deps): Bump sanitize-html from 2.17.6 to 2.17.7 in /client
2026-09-17 00:39:37 +02:00
dependabot[bot] 73cabf9e30 build(deps-dev): Bump js-yaml from 3.15.1 to 3.15.2 in /client
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.15.1 to 3.15.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/3.15.2/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.15.1...3.15.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.15.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-13 05:00:35 +00:00
dependabot[bot] 8ac152f00e build(deps-dev): Bump js-yaml from 4.3.1 to 4.3.2 in /server
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.1 to 4.3.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.2/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.3.1...4.3.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-13 05:00:27 +00:00
dependabot[bot] 6425a2ac17 build(deps): Bump sharp from 0.35.3 to 0.35.4 in /server
Bumps [sharp](https://github.com/lovell/sharp) from 0.35.3 to 0.35.4.
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.3...v0.35.4)

---
updated-dependencies:
- dependency-name: sharp
  dependency-version: 0.35.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-11 04:11:58 +00:00
dependabot[bot] 75608c33fd build(deps): Bump nodemailer from 9.0.3 to 9.1.1 in /server
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 9.0.3 to 9.1.1.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v9.0.3...v9.1.1)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 9.1.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-10 02:48:58 +00:00
dependabot[bot] 3cf7e15945 build(deps): Bump svgo from 3.3.4 to 3.3.5 in /client
Bumps [svgo](https://github.com/svg/svgo) from 3.3.4 to 3.3.5.
- [Release notes](https://github.com/svg/svgo/releases)
- [Commits](https://github.com/svg/svgo/compare/v3.3.4...v3.3.5)

---
updated-dependencies:
- dependency-name: svgo
  dependency-version: 3.3.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-09 22:34:34 +00:00
Daniel Hiller 26b512193a chore: Translate the strings missing from every locale
Thirty-three locales were short the same sixty-six keys and had been since
2.2.1. Fifty-six of them are the two-factor setup and the auto-logout
preference; six are the two-factor challenge itself; the rest are the promo
strings the banner rotates through.

The six in `login.js` were the urgent ones. They render during sign-in, before
`core.js` is fetched, so a user with two-factor enabled met the whole challenge
in English — on the one screen they cannot get past without understanding it.
Three of those six exist in `core.js` as well and now read identically in both
files, which nothing checks and which two translators working from two files
would otherwise have got subtly different.

Plural forms follow each language rather than the English pair: Arabic carries
all six CLDR categories with the agreement its counting requires, Czech,
Polish, Russian, Slovak and Ukrainian four with the right case in each, and
Chinese, Japanese, Korean, Vietnamese and Indonesian only `other`, since a
`_one` variant would be a form i18next never reads.

Every file is additions only — no existing line was moved, reworded or
reformatted, so the diff shows what was missing and nothing else. Several
existing mistranslations turned up along the way and were left alone; they are
a separate concern from filling the gaps.
2026-09-08 19:54:41 +02:00
Daniel Hiller 9069b24cfb chore: Add a locale completeness check
An incomplete locale falls back to English at runtime, so a gap breaks nothing
and nobody notices it on their own. 2.2.1 shipped fifty-six user-facing strings
that existed only in English and German, and it took someone looking for
something else to find them. `npm run client:locales:check` compares every
locale against the reference and exits non-zero when one is behind.

It compares `login.js` and `core.js` separately and never merges them.
`login.js` is the embedded bundle and is all that exists until a session is
established, so a key present only in `core.js` is still missing from every
screen shown before login — the two-factor challenge among them. Merging the
two hides exactly that, which is how the six strings of the challenge stayed
English in thirty-three languages.

Keys are compared on their base name, with the CLDR plural category stripped,
because how many categories a language needs is a property of that language:
English has two, Japanese one, Arabic six. Comparing the suffixed names would
have demanded an English `_one` from Japanese, where i18next never reads it.
Each plural key is then checked against the categories the language itself
declares, through the same Intl.PluralRules that i18next resolves with — a
missing category falls back to `_other` silently, and Russian `few` and `many`
are reachable with counts a user really sees.

It is deliberately not part of `lint`: a pull request that adds a string is
expected to leave the other locales behind for a while, and a check that fails
every such request would only be switched off. This one belongs before a
release.
2026-09-08 19:54:27 +02:00
Daniel Hiller 76d3911d03 docs: Add a PLANKA Pro section to the README
Pro funds the Community edition, but the README mentioned it only in a single
line under How to Deploy. The new section sits between the demo and the key
features: what Pro adds, two buttons, four screenshots from 2.5.0, a
Community-versus-Pro table, and what an upgrade involves.

Every row of that table was checked against both codebases rather than taken
from the comparison matrix on the website, which carries cells that are
demonstrably wrong. The dangerous error is denying Community something it has,
because the reader has the opposite in front of them in their own instance —
so the stopwatch, webhooks, the REST API, custom fields, two-factor and the
notification providers are all marked as shared, and only what the code
actually gates is marked Pro. Where a claim could not be backed by the source
it was left out.

The upgrade paragraph names the releases the promote accepts as a snapshot
rather than as a standing fact, and points at the guide for the current list.
A hard-coded version window in the README of the repository whose version
keeps moving is wrong the moment the next minor ships, and nobody would
remember it was there.

The screenshots are referenced through raw.githubusercontent.com, so they
appear once this is pushed, the same way the logo and the demo already do.
2026-09-08 19:54:18 +02:00
Daniel Hiller 4e15748e4b feat: Widen the Pro banner rotation and split it by role
The banner still advertised the calendar, recurring cards and guest roles —
features from 2.3 — to everyone who ran an instance. It now carries nine
features up to 2.5.0, and the ten strings behind them sit in their
alphabetical place rather than in the middle of the two-factor block, where
the first three had ended up.

Who sees what now depends on the role. An admin can put a trial on their own
server today and a board user cannot, so admins get the trial line and land
on planka.app/trial while everyone else gets the overview on /pro. The
landing path is also what tells the two groups apart in the referral figures,
since nothing is measured in the app.

A full pass through nine features takes over two minutes, longer than most
visits, and the rotation restarted at the first entry on every load. The tail
of the list would have advertised nothing. The list now starts at an offset
picked per visit, which spreads the features over the visitors instead of over
the length of a session — the headline still opens every session, only the
feature that follows it varies.

The headline falls back per language. `proTrialSelfHosted` is new and exists
in two locales; `discoverPlankaPro` exists in all of them. Where the sharper
line is untranslated the softer one is used rather than a line of English, on
the one string that opens every session and fills every second slot. The link
still goes to the trial, and as translations land the check flips on its own.

The thirty-day dismissal is untouched, deliberately: any version-aware
dismissal would resurface the banner for every user at once on rollout,
because the entries already in local storage carry no version.
2026-09-08 19:54:07 +02:00
dependabot[bot] 296f869ce7 build(deps-dev): Bump fast-uri from 3.1.5 to 3.1.7 in /server
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.5 to 3.1.7.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.5...v3.1.7)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 23:42:14 +00:00
dependabot[bot] 5c64c55cef build(deps): Bump sanitize-html from 2.17.6 to 2.17.7 in /client
Bumps [sanitize-html](https://github.com/apostrophecms/apostrophe/tree/HEAD/packages/sanitize-html) from 2.17.6 to 2.17.7.
- [Changelog](https://github.com/apostrophecms/apostrophe/blob/main/packages/sanitize-html/CHANGELOG.md)
- [Commits](https://github.com/apostrophecms/apostrophe/commits/HEAD/packages/sanitize-html)

---
updated-dependencies:
- dependency-name: sanitize-html
  dependency-version: 2.17.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 03:12:22 +00:00
Daniel Hiller de4d768831 fix: close the gaps a security review found in the default install
Reported against 2.2.1 by someone reading the source. Every finding held.

**Sign-in had no ceiling.** Failures were logged with the caller's address
and nothing more. Two counters now — one per address, one per account —
because the two attacks look different: one source working through many
accounts is caught by the first, many sources working on one account by the
second, and behind a proxy only the second still means anything.

The count is kept in the process that serves the request. PLANKA needs no
Redis and the stock deployment is one container; run several and each keeps
its own count, which multiplies the ceiling by their number. That trade is
written where the limits are configured.

**The second factor could be guessed at leisure.** Six digits, and a
pending token that stayed valid for its full ten minutes however many codes
were wrong. Wrong codes are now counted on the session row — in the
database, so the count survives a restart and holds across every process —
and when the budget is spent the session is destroyed. After that even the
right code is refused and the login starts over from the password.

**Avatars, background images and favicons** checked the token's signature
and nothing else, so a revoked session, a deactivated account or a changed
password all kept working there for as long as the signature lasted, which
is a year by default. The five checks the API makes now live in one helper
that both use, rather than the shortened copy that had drifted from it.

**A link attachment's favicon** was fetched from wherever the URL pointed.
Storing a link is harmless — it is a string the user typed — but fetching
its icon is a request the server makes to an address the user chose, and
whether an icon came back reported on what is reachable from inside the
network. Server-side fetches now refuse private, loopback and link-local
addresses, `169.254.169.254` among them. The attachment is still created:
linking to an internal wiki is a legitimate thing to do, and it was the
server's own request that had to stop.

**The signing key.** Our own compose file ships `notsecretkey`, and it is
printed in the documentation — so on any instance that copied it, anyone can
sign a token for any account. PLANKA now says so on every start, and keeps
saying it, along with a key that is missing or shorter than 32 characters.
The placeholder carries the warning inline, where it is copied from.

**The backup script** wrote password hashes, live sessions, TOTP secrets and
SMTP credentials to an unencrypted archive. `BACKUP_PASSPHRASE` now encrypts
it, and without one the script says what it just put on disk. It also says
what it is — an example for the stock compose stack, not a backup concept —
and names the window between the database dump and the file copy, which no
ordering closes.
2026-08-28 20:55:54 +02:00
Daniel Hiller 6def0330c2 chore: Move the changelog to the repository root
The release notes rendered in the about modal only existed as an asset
inside the client, where nothing outside a build would look for them. The
file moves to CHANGELOG.md at the root and the about modal reads it from
there, so the client build and the Docker image copy in the same file the
repository keeps.

An unreleased section collects the orphaned reference fixes and the
pagination fixes.
2026-08-26 10:15:56 +02:00