24466 Commits

Author SHA1 Message Date
David Barragán Merino
b2ea46a71a 🔧 Use client-id instead of app-id in auto-label workflow
actions/create-github-app-token v3 deprecates the app-id input in
favour of client-id. Switch to the new TRIAGE_APP_CLIENT_ID secret.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-05 13:11:08 +02:00
dependabot[bot]
66c5e6fdfd ⬆️ Bump the github-actions group across 1 directory with 8 updates
Bumps the github-actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/create-github-app-token](https://github.com/actions/create-github-app-token) | `1.12.0` | `3.2.0` |
| [actions/github-script](https://github.com/actions/github-script) | `7.1.0` | `9.0.0` |
| [actions/checkout](https://github.com/actions/checkout) | `6.1.0` | `7.0.1` |
| [pnpm/setup](https://github.com/pnpm/setup) | `2.0.2` | `3.0.0` |
| [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) | `4.3.1` | `6.3.0` |
| [actions/cache/restore](https://github.com/actions/cache) | `5.1.0` | `6.1.0` |
| [actions/cache/save](https://github.com/actions/cache) | `5.1.0` | `6.1.0` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `7.0.0` | `8.0.1` |



Updates `actions/create-github-app-token` from 1.12.0 to 3.2.0
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Changelog](https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md)
- [Commits](d72941d797...bcd2ba4921)

Updates `actions/github-script` from 7.1.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](f28e40c7f3...3a2844b7e9)

Updates `actions/checkout` from 6.1.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](d23441a48e...3d3c42e5aa)

Updates `pnpm/setup` from 2.0.2 to 3.0.0
- [Release notes](https://github.com/pnpm/setup/releases)
- [Commits](84cb39b217...fbda4c85fc)

Updates `aws-actions/configure-aws-credentials` from 4.3.1 to 6.3.0
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](7474bc4690...e1253824e5)

Updates `actions/cache/restore` from 5.1.0 to 6.1.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](caa2961268...55cc834586)

Updates `actions/cache/save` from 5.1.0 to 6.1.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](caa2961268...55cc834586)

Updates `actions/download-artifact` from 7.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](37930b1c2a...3e5f45b2cf)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/github-script
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: pnpm/setup
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache/restore
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache/save
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 12:35:56 +02:00
Andrey Antukh
cf46b53bcb 📚 Update changelog 2.18.2 2026-10-05 12:18:37 +02:00
Andrey Antukh
4a4f50e7ea 🔥 Stop printing separator lines in reports
Drop the equals-sign banners from the CLJS throwable formatter,
so new frontend error reports are born without them. The JVM
branch never printed them. Adds a test asserting full reports
carry sections but no separator runs.

AI-assisted-by: Muse Spark 1.3 Free
2026-10-05 12:17:06 +02:00
Shreyash Agare
331e9168b5
🐛 Keep interactions of flex children when syncing components (#11788)
* 🐛 Keep interactions of flex children when syncing components

The flex-child sync between main and copy children propagated every
attribute in swap-keep-attrs, which includes :interactions. Since
:interactions has no sync group, the touched check never protected it,
so any change that reflowed a flex component (like toggling a child's
visibility) overwrote the copy children's interactions with the main's.

Closes #11746

AI-assisted-by: claude-opus-5
Signed-off-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com>

* 🐛 Add another test and simplify comments

---------

Signed-off-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com>
Co-authored-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-10-05 12:04:11 +02:00
Andrey Antukh
341e5d1027 Merge remote-tracking branch 'origin/staging' into develop 2026-10-05 11:59:05 +02:00
Andrey Antukh
238a4d8613
🐛 Sanitize SVG namespace-prefixed elements and attributes (#12073)
* 🐛 Sanitize SVG namespace-prefixed elements and attributes

The SVG sanitizer compared qualified tag and attribute names against
its blocklists. The XML parser keeps the namespace prefix in the
keyword name (❌script), so a script element written with a prefix
bound to the SVG namespace passed sanitization untouched and was
later served as image/svg+xml, executing in the Penpot origin.

Match tags and attributes on the local name (the part after the last
colon) instead, so dangerous elements and attributes are removed no
matter which prefix they use. Legitimately prefixed attributes such
as xlink:href keep working.

Closes #12071

AI-assisted-by: glm-5.3-flash

* ♻️ Cover prefixed SVG vectors and hoist sanitizer blocklist

Apply the code review findings on top of the sanitizer fix:

- Add a test for the javascript: href vector using a prefix bound to
  the xlink namespace, the other payload variant the old exact-key
  check missed.
- Add a test asserting that legitimately prefixed attributes such as
  xml:space are preserved by the local-name matching.
- Hoist the dangerous-tags blocklist next to the other sanitizer
  policy defs and bind the keyword name once in local-name.

Relates to #12071

AI-assisted-by: glm-5.3-flash
2026-10-05 11:56:59 +02:00
Andrey Antukh
b6a108c456 📚 Restrict new ! suffix to app.db and transients
Default is now no `!` for new functions. Existing `!`
stays as legacy until its gradual refactor.

Allowed cases live in clojure/idioms: mutating public
app.db API and functions working over transients. Any
further case must be registered there first.

AI-assisted-by: muse-spark-1.3-contributor
2026-10-05 09:54:46 +00:00
Elena Torró
aed4e12c63
⚡ Avoid decoding all pages when building file thumbnail data (#12054) 2026-10-05 11:34:10 +02:00
Eva Marco
b06a43404b
🎉 Add detach to typography and shadow tokens (#11969)
* 🎉 Add detach to typography tokens

* 🎉 Add detach to shadow tokens

* 🎉 Add tests
2026-10-05 11:32:29 +02:00
Dr. Dominik Jain
4c507e56e3
📚 Update MCP API type information (#12078) 2026-10-05 11:22:32 +02:00
Eva Marco
c04a3d346d
🔧 Register the missing test namespaces in the common JS runner (#12074)
Kaocha finds every test namespace on the JVM, but the JS runner only
runs the ones listed in runner.cljc. Sixteen namespaces were missing
from both lists, and files.shapes-builder-test was required but not
listed, so 125 tests never ran in JS. Add them to both lists, sorted.

The only one that failed in JS was make-tokens-status-from-tokens-lib:
(.sets base-lib) is a method call in ClojureScript. On the JVM it read
the field, but (:sets ...) on the result gave nil, so the legacy lib
was built without sets or themes. Use (.-sets base-lib) and
(.-themes base-lib), as the first part of the same test does.

AI-assisted-by: claude-opus-5-5
2026-10-05 08:25:15 +02:00
Belén Albeza
7c039231f7
🎉 Implement paste of basic HTML formatting (v3) (#12050)
* ✨ Add rich HTML paste to the v3 text editor

Pasting HTML into the v3 text editor keeps bold, italic, decoration and
text-transform, and drops fonts, colors and links. Gated behind
text-editor-wasm/v1-html-paste.

Refs #10465

AI-assisted-by: claude-opus-5-5

* ✨ Paste HTML on the canvas as a styled text shape

With v3 HTML paste on, pasting HTML on the canvas creates a text shape
with the same emphasis. Clipboard options are now read at paste time.

Refs #10465

AI-assisted-by: claude-opus-5-5
2026-10-02 15:07:12 +02:00
Dr. Dominik Jain
8efbd9b5d3
🎉 Allow MCP to work with multiple files in parallel (#11927)
* 🎉 Add multi-file session routing to the MCP server

Session routing:
- Track each user's Penpot connections by session ID.
- Dispatch explicit sessions directly; otherwise discover the sole session.
- Return a retryable error when discovery fails or is incomplete.

Plugin and tools:
- Initialize connections with a fresh session ID and file metadata.
- Share the optional sessionId schema and place it last in tool inputs.

Validation:
- 84 tests, server type checking, and MCP formatting checks pass.
- Disable test logging to avoid the logging worker shutdown hang.

AI-assisted-by: gpt-6

* 🎉 Support independent MCP connections

- Require explicit connection intent for each integrated workspace.
- Derive short session IDs from the Penpot app instance and file.
- Show and copy session IDs in the MCP menu and standalone plugin UI.
- Stop stale callbacks and reconnect attempts after disconnect.

Validate with frontend and plugin tests, type checking, live multi-tab
checks, and standalone UI checks with a simulated connection. Server
tests pass with --test-force-exit; the normal runner can hang on shutdown.

AI-assisted-by: gpt-6

* 📚 Document multi-file MCP sessions

- Explain independent connections, session selection, and copying IDs.
- Describe session ID lifetimes for integrated and standalone plugins.
- Correct browser focus, connection, and recovery guidance.

Validate with the documentation site build and diff checks.

AI-assisted-by: gpt-6

* ✨ Change behavior on session ID duplication

* ✨ Open MCP toolbar menu on hover and match menu style

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
Co-authored-by: elhombretecla <delacruzgarciajuan@gmail.com>
2026-10-02 14:22:52 +02:00
David Barragán Merino
61bc585386 👷 Accept remaining zizmor exceptions
wrangler stays an ad hoc install in the plugin deploys and the devenv
image keeps its floating tag: both are deliberate, so mark them as
accepted instead of pending.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:49:04 +02:00
Elena Torró
d531c0050b
⚡ Skip builder local apply on plain translations (#12036)
Translations that touch no group-like shape or ancestor do not need
the builder objects updated, since only parent resizing reads them.

AI-assisted-by: claude-opus-5-5
2026-10-02 12:39:18 +02:00
David Barragán Merino
e65d10017d 👷 Use a slug of the ref for Docker tags
A git ref accepts characters that a Docker tag does not, such as "/" in
feature/foo, which made the promote and retag steps fail. Tag images with
a slug of the ref instead (anything outside [A-Za-z0-9._-] becomes "-",
at most 100 chars), as payments already does. develop, staging and
release tags are unchanged.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:27:34 +02:00
David Barragán Merino
63ee26a502 👷 Require ci-maintainers review for .github
Add a CODEOWNERS entry for .github/, so changes to workflows, CI
configuration and repository automation need a review from the
ci-maintainers team.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:27:34 +02:00
David Barragán Merino
7e73a42123 👷 Label Dependabot PRs and skip them in auto-label
Dependabot PRs get the dependencies, team infra and no issue required
labels, and auto-label no longer adds them to the triage board.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:27:34 +02:00
Elena Torró
da55d6723c
⚡ Reduce per-frame CLJS work while dragging (#12027)
Memoize snap points and frame titles on the base objects and compute
the drag target frames once per gesture.
2026-10-02 12:24:04 +02:00
Elena Torró
822dbd6517
⚡ Speed up sidebar menus for large selections (#12028)
Reuse extracted colors for unchanged shapes and cache the text attrs
read from each text content.
2026-10-02 12:05:25 +02:00
Elena Torró
c148ba5267
⚡ Rebuild drag tiles per moved subtree (#12030)
WASM receives only the transforms that differ from the parent (bool
operands and masks are kept), rebuilds tiles once per moved subtree
and invalidates only the previous frame coverage.
2026-10-02 11:39:24 +02:00
Elena Torró
13a9da3bc3
⚡ Reduce commit cost when dropping large selections (#12032)
Collect children ids into one transient vector and track WASM shape
changes in a JS Map instead of a volatile persistent map.

AI-assisted-by: claude-opus-5-5
2026-10-02 11:22:19 +02:00
Dr. Dominik Jain
3a0ed93360
📚 Improve MCP README (#12022)
* Make the scope of the usage instructions clear (local usage)
* Provide overview of the server's different modes of operation
* Improve npm release instructions
* Remove deprecated multi-user mode documentation
2026-10-02 11:08:53 +02:00
Andrey Antukh
ea3d18c0a5
✨ Audit-log docs, error-reporting flag and frontend initiator (#11997)
* ✨ Gate error reporters behind :error-reporting flag

Error reporters used to start on every boot with no way to
switch them off. They now only start when the new
:error-reporting flag is on (off by default, enable with
PENPOT_FLAGS=enable-error-reporting). The database reporter
needs the flag alone; mattermost needs the flag plus its
webhook url. Shutdown with the flag off is safe, and the
database loop now honors its runtime switch.

AI-assisted-by: muse-spark-1.3-contributor

* 📚 Add audit-log reference memory and update dev tooling

Adds the top-level audit-log reference memory with all event
flows and turns backend/audit-log into a redirect stub.
Also includes the staged dev-tooling updates (playwright
deps, mdts args, gitignore).

AI-assisted-by: muse-spark-1.3-contributor

* ✨ Assign server-side initiator to frontend audit events

Frontend audit events now carry a server-assigned initiator
derived from the x-client header (penpot-frontend goes to
app, penpot-admin-console goes to admin-console, legacy
penpot-nitrate stays on admin-console, all else goes to
app). Client-sent values are always overwritten, and the
key survives on telemetry shadow rows.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Keep initiator on frontend telemetry context

The strips-pii-keys test still asserted the old contract
(initiator stripped). The initiator only names the sending
app, so it is not personal data and must survive the
telemetry filter like on the backend channel.

AI-assisted-by: muse-spark-1.3-contributor

* 📚 Document initiator on PostHog and browser origins

Complete the audit-log memory for the latest changes: the
initiator property forwarded to PostHog on both channels
and the eventOrigin rule for admin-console browser events.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix unscaped argument in script

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-10-02 11:08:13 +02:00
Elena Torró
1554847d40
⚡ Compute the drop ignore tree in a single pass (#12033)
Walk only from the top-most transformed shapes, carry the component
root down the walk and memoize each shape transform so the update
step reuses it.

AI-assisted-by: claude-opus-5-5
2026-10-02 10:51:21 +02:00
Elena Torró
09ffcb1fbc
⚡ Reduce render-wasm GPU memory and move cost on large pages (#11980) 2026-10-02 10:24:05 +02:00
María Valderrama
c8ef223f67
🐛 Fix organization/team switcher ui (#12019) 2026-10-02 09:58:38 +02:00
David Barragán Merino
571a8805e6 👷 Document remaining zizmor exceptions
Mark each remaining ignore in zizmor.yml as a pending clean-up or an
accepted risk, with its reason.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
8c1a813b14 👷 Drop dependency cache from plugin doc deploys
Deploy workflows must not restore a cache that other workflows can
write to. The pnpm store cache is removed from the api-doc and
styles-doc deploys; installs are slower but deploys are infrequent.

Also install with --frozen-lockfile in the three plugin deploys, as CI
already does by default, so a deploy never resolves dependencies that
differ from the lockfile.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
46d64da931 👷 Scope the triage GitHub App token
Request a token for this repository only, instead of every repository
in the installation, and only with the permissions the job uses: issues
(labels) and organization projects (the board). Also document why the
workflow needs pull_request_target and why it is safe.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
214cbbf678 👷 Drop pull_request_target from commit checker
The job only reads commit messages, which works with the pull_request
trigger for PRs from forks too. Running it on both triggers checked every
PR twice, once with a privileged token it does not need.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
7f95439bc2 👷 Declare least-privilege workflow permissions
Add an explicit permissions block to every workflow that relied on the
repository default. Entry points that only call reusable workflows get
permissions: {} and grant each call what the called workflow needs:
contents: read for the bundle and docker builds, contents: write for the
release, nothing for the admin-console dispatch (it uses its own token).
Workflows that check out code get contents: read; the commit checker
also gets pull-requests: read to list the PR commits.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
Andrey Antukh
84c794c5b8 Merge remote-tracking branch 'origin/staging' into develop 2026-10-01 19:25:59 +02:00
Andrey Antukh
3f610ad793
✨ Isolate backend-test database per devenv instance (#12048)
* ✨ Isolate backend-test database per devenv instance

Each wsN now gets its own backend-test database
(penpot_test_wsN) and Valkey DB (6+N) on the shared
infra, so parallel test runs no longer wipe each
other. The main database stays shared on purpose.

manage.sh creates the test database on bring-up and
passes PENPOT_TEST_* into the main container. Test
helpers already read those vars, so no runtime code
changes. scripts/psql and db-schema gain --ws.

AI-assisted-by: muse-spark-1.3-contributor-free

* 🐛 Fix per-instance test database creation on bring-up

psql -c does not reliably mix SQL with psql-only
commands, so the CREATE DATABASE ... WHERE NOT
EXISTS ... \gexec one-liner never ran. Use two
plain SQL round-trips instead (check pg_database,
then CREATE DATABASE) and print what happens, so a
future failure shows up in the bring-up output.

AI-assisted-by: muse-spark-1.3-contributor-free
2026-10-01 19:22:01 +02:00
Belén Albeza
60679bbbcd
🐛 Revert RTL auto-width text growing from its right edge (#12051)
Revert #11775 (commit 142f3d9de8). We are putting this fix on hold
until we settle the UX for RTL auto-width text.

Relates to #11523
2026-10-01 18:29:07 +02:00
Pablo Alba
03e24d18ce
✨ Dual-send audit log archive to Nitrate and Nexus (#12037)
Run the audit archive cron when :nexus or :admin-console is on. Ship
allowlisted events to Admin Console first (with row ids for
idempotency), then the full chunk to Nexus when :nexus is set. Mark
archived_at for the whole chunk on success, including nitrate-only
mode. Rename the gate flag from :audit-log-archive to :nexus.

AI-assisted-by: Composer

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 17:17:11 +02:00
JiMyung Lee
cc2aff6ce8
🐛 Fix typography sample overflow in non-Latin locales (#11487)
* 🐛 Fix typography sample overflow in non-Latin locales

The typography sample glyph sits in a fixed 1.5rem grid column, sized
for the English sample "Ag". Locales that translate it to something
wider wrap it onto several lines and overflow the row: ko translates
the sample as "가나다" and renders it as three stacked characters, ar
as "أسلوب خط النص" and renders it as three stacked words. This affects
the workspace assets panel (list row, detail panel and rename row) and
the dashboard library card.

Let the sample column size to its content and keep the sample on a
single line. The 1.5rem minimum is preserved, so locales whose sample
already fits — English and every locale that keeps "Ag" — render
exactly as before.

Signed-off-by: JiMyung Lee <lee.ji.myung@gmail.com>

* 🐛 Keep spacing between typography sample and name

Follow-up to review on the typography sample overflow fix. Letting
the sample grow removed the fixed-width slack that used to separate it
from the name text, so:

- Dashboard library card: `.library-name-block` no longer assumes a
  24px sample via a hard-coded calc; it flexes to the remaining space
  and the sample does not shrink.
- Typography detail panel, list row and rename/advanced-edit row: add
  an explicit `var(--sp-xs)` gap between the sample and the name, and
  let the name input shrink instead of pushing the action buttons.
- Libraries "Updates" tab: the sample div had no class and still
  wrapped per character; give it a `nowrap` class.

Signed-off-by: JiMyung Lee <lee.ji.myung@gmail.com>

---------

Signed-off-by: JiMyung Lee <lee.ji.myung@gmail.com>
Signed-off-by: Eva Marco <eva.marco@kaleidos.net>
Co-authored-by: Eva Marco <eva.marco@kaleidos.net>
2026-10-01 16:02:21 +02:00
Andrey Antukh
b5fbc4fd8c
✨ Add size limits to profile props and plugin registry (#11596)
* ✨ Add size limits to profile props and plugin registry

Bound the total serialized size of profile settings to 2 MiB
(:profile-props-max-size), checked on the merged result before
persisting, with a controlled :props-too-large error. Profiles
that already exceed the limit can still shrink but cannot grow.

Cap plugin registry entries in the shared schema (code 1 MiB, 50
plugins max, bounded name/host/description/icon) and restore rate
limiting on the plugin RPCs (profile-mutations bucket, one write
at a time per profile). The plugin manager now asks for
confirmation before removal and ignores repeated clicks while a
persist request is in flight.

Closes #11592

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Enforce plugin count cap, byte sizes and removal guard

Enforce the declared 50-plugin cap in add-profile-plugin with a
specific :too-many-plugins error (updates of existing entries
still pass); the cap lives in a shared max-plugins constant.

Measure profile props size in UTF-8 bytes instead of chars so
multibyte content cannot slip past the limit.

Cover install/remove persist logic with mocked-RPC frontend tests
(release semantics, in-flight dedupe, validation vs rollback
split) and add the missing boundary tests in common.

Expose the in-flight persist set from the plugin registry and
disable the remove button of entries being saved.

Closes #11592

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix rollback loops and restore paths in plugin registry

Restore the previous plugin version instead of dropping the entry
when a validation error rejects an update of an installed plugin.

Make compensating writes one-shot with terminal callbacks so a
persistent failure cannot ping-pong between install and remove.
Restores keep the original list position; the unused public
plugin-persisting? predicate is removed.

Pin count-before-size precedence with a dedicated test and fix
translation source refs to their canonical lines.

Closes #11592

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Guard notifications write and fix restore ordering

Route update-profile-notifications through check-props-size! so
oversized profiles cannot grow through that path; document the
exempt system writers. Remove the duplicated stale entries in
en.po, keeping the canonical translation refs.

Restore rejected plugin updates at their original list position
instead of leaving the optimistic move in place.

Closes #11592

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Skip no-op plugin removal and clarify size comments

Return early from remove-profile-plugin when the id is absent:
no wasted write, no size check, and no manufactured :plugins key
that could spuriously fail on oversized profiles.

Clarify that per-field string caps count chars while the byte
budget is enforced by profile-props-max-size.

Closes #11592

AI-assisted-by: muse-spark-1.3-contributor

* 📎 Fix formatting in rlimit.edn for profile operations

Signed-off-by: Andrey Antukh <niwi@niwi.nz>

* 📎 Fix formatting of import-binfile/global entry

Signed-off-by: Andrey Antukh <niwi@niwi.nz>

* ♻️ Simplify props size check and tighten plugin entry caps

Measure props with transit bytes directly instead of the
PGobject string roundtrip.

Rename check-props-size! to check-props-size: single hard limit
on the merged props, no growth comparison, and return props so
writers thread the check into the update.

Move the 2 MiB default into default-props-max-size on the
profile namespace, still overridable with the optional
:profile-props-max-size config entry.

Tighten registry-entry :code and :icon to 500 chars: they hold
manifest paths, not content.

Closes #11592

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix compatibility problems

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-10-01 14:31:30 +02:00
Andrey Antukh
b2c3fd872a
♻️ Scope organization notifications to specific WebSocket topics (#11460)
* ♻️ Scope organization notifications to specific WebSocket topics

Publish team/org notifications to team-id and organization-id topics
instead of broadcasting to all connections via uuid/zero. Dashboard and
workspace now subscribe to their current team and organization on
initialization, receiving only relevant events.

Backend: added subscribe-organization/unsubscribe-organization WebSocket
handlers and updated :close to clean up organization subscriptions.
Modified notify-team-change, notify-organization-deletion, and
notify-organization-change-sso to publish to specific topics.

Frontend: dashboard and workspace now subscribe to team-id and
organization-id (when applicable) on initialization, with nil-guard
in topic filters.

Closes #11455

AI-assisted-by: longcat-2.0

* 🔧 Remove unused session-id binding in unsubscribe-organization

Clj-kondo lint fix.

AI-assisted-by: longcat-2.0

* 🐛 Fix permission check on team ws connection

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-10-01 13:16:12 +02:00
María Valderrama
9111ebb3bf
✨ Add same-subscription option to move-teams permission (#11995) 2026-10-01 13:03:55 +02:00
Alonso Torres
b5d4cd1f0b 🐛 Fix problems with react loops (#11941) 2026-10-01 12:17:27 +02:00
Andrey Antukh
d82f2e1005 Merge remote-tracking branch 'origin/staging' into develop 2026-10-01 12:10:40 +02:00
Andrey Antukh
8db9cb83f3 📎 Update devenv dockerfile 2026-10-01 12:09:57 +02:00
Andrey Antukh
c561a0baff Merge remote-tracking branch 'origin/staging' into develop 2026-10-01 10:59:50 +02:00
Andrey Antukh
38d6f37438 Merge remote-tracking branch 'origin/main' into staging 2026-10-01 10:59:35 +02:00
Andrey Antukh
423cb41132 📚 Update changelog 2.18.1 2026-10-01 10:58:44 +02:00
Shreyash Agare
970948159e
🐛 Fix token display for multi-selected text layers (#11944)
* 🐛 Fix token display for multi-selected text layers

When multiple text layers share the same fill token,
the design panel showed the hex value instead of the
token name. type->token-attrs derived token keys from
type->editable-attrs, which returns empty for text
shapes in the fill group. Fall back to the group's
own attrs when editable-attrs is empty.

Closes #11924

AI-assisted-by: claude-opus-4-6

* 🐛 Take text token attrs from the group attrs

The :text read mode reads values from the group attrs, so its
token attrs now come from those attrs too, instead of falling
back when the editable attrs are empty. type->token-attrs is
restored to its original form.

Add regression tests for fill tokens on multiple selections of
text shapes, and of a rect mixed with a text.

AI-assisted-by: claude-opus-5-5

---------

Co-authored-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-10-01 10:13:42 +02:00
Andrey Antukh
e6983a1e4e
✨ Enforce idle and absolute session expiration (#11654)
* ✨ Enforce idle and absolute session expiration

Sessions now expire on two server-side conditions: an idle window
(PENPOT_AUTH_TOKEN_COOKIE_MAX_AGE, default 7d) and an absolute cap
from creation (PENPOT_AUTH_TOKEN_COOKIE_MAX_AGE_ABSOLUTE, default
30d, enforced by the token :exp claim). The daily session-gc task
deletes rows that exceed either window, so idle sessions can no
longer be replayed and active sessions are not deleted at the idle
window.

Also remove the legacy v1 HTTP sessions: the http_session table and
the string-id / :ver 0 token code paths are gone. Any v1 cookie now
requires a fresh login.

Document the session expiration configuration in the technical guide
and add a backend memory describing the token, renewal and GC model.

Closes #11646

AI-assisted-by: deepseek-v4.1-flash

* 🐛 Address session-expiration review findings F1-F4

Fix the unreadable test (a stray paren broke whole-suite
discovery), enforce idle expiration on every request in
wrap-authz, fail boot fast when the absolute cap sits below
the idle window, and align config defaults with the memory
rule while fixing its migration number and stale reference.

Closes #11646

AI-assisted-by: muse-spark-1.3-contributor
2026-10-01 10:12:05 +02:00
Alonso Torres
d851f82678
🐛 Fix crashing happening on hot reloads (#12005) 2026-10-01 09:21:05 +02:00