31 Commits

Author SHA1 Message Date
Andrey Antukh
eec06a5987
🐛 Allow registration with disabled public registration (#11912)
* 🐛 Allow invitation-based registration when disable-registration is set (#5178)

Per documentation, disable-registration 'disables registration
(still enabled for invitations only)'. Two bugs prevented this:

1. verify_token.clj: when processing an invitation token for a
   non-logged-in user with no member-id, the redirect included
   registration-disabled? in its condition, sending invited users
   to the login page instead of the register page.

2. auth.clj validate-register-attempt!: the registration-disabled
   check fired unconditionally before the invitation-token check,
   rejecting the actual register RPC even with a valid invitation.

Fix: in verify_token.clj remove registration-disabled? from the
redirect condition for new-user invitations. In auth.clj restructure
the check as an if/else: with an invitation token, validate the token
and allow registration; without one, enforce the flag as before.

* 🐛 Allow registration with disabled public registration

Allow valid team invitations to create new profiles when public
registration is disabled, while keeping password login and invitation
validation required.

Add backend regression coverage for flag combinations and verify-token
redirects, frontend route coverage, and configuration documentation.
Closes #5178

AI-assisted-by: space-bunny-free

* 🐛 Revalidate active invitation during registration

Require a live, unexpired team invitation before using the
registration exception, and recheck it before creating a profile.
Reuse the same lookup in invitation token verification.

Add regression tests for canceled and expired invitations, the
registration race, and explicit redirect contracts. Update docs
and backend auth guidance.

AI-assisted-by: Space Bunny Free

* 🐛 Lock and normalize invitation registration checks

Lock active invitation rows during transactional registration and
acceptance so cancellations cannot race with profile or membership
creation.

Normalize invitation emails before comparisons and database lookups.
Add concurrency, email casing, and final flag regression tests.

AI-assisted-by: Space Bunny Free

---------

Co-authored-by: Sumit Ridhal <sridhal@redhat.com>
2026-09-25 14:13:12 +02:00
Andrey Antukh
acd146f6f4
🐛 Restore rate-limit headers and add Retry-After on 429 (#11895)
* 🐛 Restore rate-limit headers and add Retry-After

The account-lockout change replaced the header-forwarding 429 handler
with a body-only one, so existing RPC rate-limit responses lost their
x-rate-limit-remaining and x-rate-limit-reset headers. Account lockout
never sent Retry-After either.

Make handle-error :rate-limit preserve ::http/headers and add a
retry-after header when the exception carries a non-nil :ttl in
seconds, keeping the current JSON body. Add focused tests for both the
lockout and the RPC limiter paths.

Document activation, defaults, password/LDAP scope, Redis fail-open
behavior, and the lockout risk, and record the final HTTP contract in
the backend auth memory.

Refs #11397

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

* 🐛 Add Retry-After to RPC 429 and expose headers in CORS

Address review follow-ups on the account-lockout 429 contract:

- The RPC limiter now sets retry-after in its 429 headers (seconds
  until the longest rejecting limit resets), so it matches the
  account-lockout response and the HTTP standard.
- CORS exposes retry-after, x-rate-limit-remaining, and
  x-rate-limit-reset so browser clients can read them.
- Use backticks for Retry-After and account-locked in the docs for
  consistency with nearby sections.

Refs #11397

AI-assisted-by: deepseek-v4.1-flash
2026-09-24 12:19:29 +02:00
Dr. Dominik Jain
6031f6c318
⬆️ Upgrade MCP SDK to v2, removing SSE support and HTTP session management (#11841)
* ⬆️ Upgrade MCP SDK to v2 and remove HTTP sessions

MCP's per-request protocol removes the need to retain HTTP sessions.
Use the v2 handler to manage each request's transport and lifecycle,
so requests can reach any server instance without session affinity
or the workaround that adopts sessions through private SDK fields.

Keep legacy SSE support and the shared plugin and Redis bridges.
Remove the shared expiry checker, including legacy SSE idle expiry;
SSE connections now remain until disconnection or server shutdown.
Verify stateless requests, token isolation, and legacy compatibility.

Resolves #11827

AI-assisted-by: gpt-6-astra

* 🔥 Remove legacy MCP SSE support

Use Streamable HTTP as the sole MCP client transport so the server no
longer needs a separate SSE connection registry or lifecycle.

Remove /sse and /messages, their nginx routes, and the server-legacy
dependency. Legacy SSE clients must switch to /mcp; older Streamable
HTTP clients remain supported. Document the migration and verify that
the removed endpoints return 404.

Resolves #11846

AI-assisted-by: gpt-6-astra
2026-09-23 15:07:19 +02:00
David Barragán Merino
b402637fe4 🐳 Allow extending the CSP directives without replacing the policy
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-18 19:05:37 +02:00
David Barragán Merino
1e8acbb2db 🐳 Cover the inline scripts of the served pages with CSP hashes
The frontend build now emits the sha256 hashes of the inline scripts of every page it writes into resources/public, the image moves them out of the document root, and the entrypoint splices them into the default script-src. This removes one of the two reasons why enforcing mode was not usable.

The hashes are computed on the rendered output rather than on the mustache templates, since the digest covers the exact bytes served between the script tags. All four served pages contribute, not just index.html: challenge.html handles the redirect, render.html is loaded by the exporter in a headless browser, and rasterizer.html is initialised by the frontend itself, so leaving any of them out would have broken those paths under enforcing mode. The storybook previews are excluded because that container does not serve them.

A bundle predating this change yields no hashes and the policy stays as it was, so older bundles keep building.

The three external locations were also passing through the security headers of their upstreams. raw.githubusercontent.com returns its own Content-Security-Policy and both it and fonts.googleapis.com return Strict-Transport-Security. Browsers enforce the intersection of every policy they receive, so the upstream one takes precedence on those responses, and the HSTS one lands on our own host, meaning a deployment that deliberately disables HSTS would get it set anyway by a third party. Hide all three at the proxy.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-18 18:55:19 +02:00
elhombretecla
a91d81c695 🎉 Add link preview metadata for shared links 2026-09-15 17:10:54 +02:00
David Barragán Merino
52723b3851 🐳 Add configurable CSP and HSTS headers to the frontend image
Ship both headers from the image so that every deployment starts from a sensible default instead of each installation deriving its own policy. Report-only mode never blocks a request, so this changes no behaviour for existing deployments, and HSTS stays absent unless PENPOT_PUBLIC_URI declares an https scheme.

The policy can be narrow because the frontend already reverse proxies its own external dependencies, so 'self' covers them. What it must permit beyond that comes from the code: 'wasm-unsafe-eval' for the render engine, 'unsafe-inline' styles for the inline style attributes of the UI, and blob:/data: for thumbnails, exports and fonts.

Closes #11374
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
AI-assisted-by: Claude
2026-09-15 13:41:51 +02:00
Shreyash
3f1c88fca2
📚 Document MCP and internal resolver environment variables (#11572)
Add the frontend MCP variables PENPOT_MCP_URI and PENPOT_MCP_URI_WS,
the PENPOT_INTERNAL_RESOLVER variable used by nginx to resolve the
internal service names, and the enable-mcp flag to the configuration
guide. All three were used by the frontend docker image but were
missing from the docs.

Closes #11318

AI-assisted-by: claude-opus-5

Signed-off-by: Shreyash Agare <envisiontechdevelopers@gmail.com>
2026-09-09 08:16:04 +02:00
David Barragán Merino
ddd32670b3 📚 Remove architectural constraints related to MCP Server HA 2026-08-18 19:15:27 +02:00
Sebastien MALOT
7ac61e0597
🐛 Fix typo in auto-file-snapshot timeout setting (#10909)
Corrected a typo in the configuration documentation regarding the auto-file-snapshot timeout setting.

Signed-off-by: Sebastien MALOT <sebastien.malot@pm.gouv.fr>
2026-08-17 22:14:01 +02:00
Andrey Antukh
e73aa9e981
✨ Add PENPOT_INTERNAL_URI to exporter for separate internal and public URI handling (#10630)
Add PENPOT_INTERNAL_URI environment variable to the exporter. This allows
separating the URI used for internal communication (headless browser to
frontend) from the public URI used for resource references in exported SVGs.

Previously, PENPOT_PUBLIC_URI served both purposes, which caused exported
SVGs to contain broken font URLs when the internal Docker address was used.

Changes:
- Add :internal-uri to exporter config schema with fallback to :public-uri
- Add get-internal-uri helper function
- Use internal-uri for browser navigation in SVG/PDF/bitmap renderers
- Post-process SVG output to replace internal URI with public URI
- Use internal-uri for backend API calls in resource handler
- Log both URIs on startup
- Update docker-compose.yaml to use both variables
- Document the new variable in configuration.md

Closes #10627

AI-assisted-by: mimo-v2.5-pro
2026-07-10 10:41:49 +02:00
Andrey Antukh
b4532486e3
✨ Add configurable resource usage limits for imagemagick (#10240)
* 🐳 Add ImageMagick policy.xml resource limits to backend Docker image

Add a restrictive policy.xml to the backend Docker image that caps
ImageMagick resource usage: 256MiB memory, 512MiB map, 128MP area,
30s time limit, 16KP max dimensions. Blocks PS/EPS/PDF/XPS coders
to prevent Ghostscript attack surface.

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* ✨ Add timeout support to shell/exec!

Add optional :timeout parameter (in seconds) that uses
Process.waitFor(long, TimeUnit). On timeout, the process is
destroyed forcibly and an :internal/:process-timeout exception
is raised. Stdout/stderr readers handle IOException from closed
streams when the process is killed.

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* ♻️ Rename ::wrk/netty-executor to ::wrk/executor with cached pool

Replace DefaultEventExecutorGroup (fixed Netty thread pool) with a
cached thread pool (px/cached-executor) for general async task
offloading. The cached pool creates threads on demand and reuses
idle ones, which is more appropriate for blocking I/O workloads
(shell commands, message bus, rate limiting, etc.).

Changes:
- Rename ::wrk/netty-executor to ::wrk/executor in worker/executor.clj
- Switch implementation from DefaultEventExecutorGroup to px/cached-executor
- Update all ig/ref wiring in main.clj (msgbus, tmp cleaner, climit, rlimit, rpc)
- Remove ::wrk/netty-executor from redis.clj (let lettuce create its own
  eventExecutorGroup instead of sharing a Netty executor)
- Assert executor is present in shell/exec! to prevent silent nil usage
- Remove executor-threads config (no longer needed for cached pool)

The ::wrk/netty-io-executor (NioEventLoopGroup) remains unchanged as it
handles actual non-blocking network I/O for Redis and S3.

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* 🔥 Remove im4java dependency and replace with direct ImageMagick CLI calls

- Replace im4java Java library with direct 'magick' CLI calls via shell/exec!
- Add PENPOT_IMAGEMAGICK_* config env vars for resource limits (thread, memory, map, area, disk, time, width, height)
- Use configurable ImageMagick environment with sensible defaults matching policy.xml
- Remove -Dim4java.useV7=true JVM flag from startup scripts
- Remove org.im4java/im4java from deps.edn
- All ImageMagick commands now use shell/exec! with 60s timeout and resource limits

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* 💄 Rename imagemagick env functions and optimize config reads

- Rename imagemagick-defaults -> imagemagick-default-env
- Rename imagemagick-env -> get-imagemagick-env
- Optimize to avoid double cf/get calls per config key

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* ✨ Add tests for shell/exec! timeout and media processing

- Add shell_test.clj: tests for exec! timeout, env vars, stdin, stderr
- Add media_test.clj: tests for info, generic-thumbnail, profile-thumbnail
- Fix generic-process to prefer explicit format over input mtype
- Fix shell/exec! to use cached executor when system has no executor
- Fix reduce-kv accumulator in set-env (must return penv)

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* ♻️ Refactor media/process to take system as first argument

- Change (defmulti process :cmd) -> (defmulti process (fn [_system params] (:cmd params)))
- Change (run params) -> (run system params)
- All process methods now receive [system params]
- Update all callers: rpc/commands/media, profile, auth, fonts
- Revert shell/exec! to require system with executor (no fallback)
- Fix lint warnings and formatting

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* 🔥 Remove unused app.svgo namespace

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* 🔥 Remove Node.js from backend Docker image

- Delete unused svgo-cli.js script
- Remove Node.js installation from Dockerfile.backend
- Remove svgo-cli.js copy from backend build script

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* 🔥 Remove unused process-error multimethod

- Remove process-error multimethod and its default handler
- Simplify media/run to directly call process
- Fix alignment in main.clj

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

* 📚 Add ImageMagick resource limits configuration to technical guide

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>

---------

Co-authored-by: mimo-v2.5-pro <mimo-v2.5-pro@penpot.app>
2026-06-18 17:52:01 +02:00
ruizterce
e6f5b270de
💄 Fix typos in configuration.md (#9975)
Corrected typos in the configuration documentation.

Signed-off-by: ruizterce <127963868+ruizterce@users.noreply.github.com>
2026-06-02 16:32:34 +02:00
Francis Santiago
106b10e971
📚 Clarify self-hosted OIDC configuration for containerized deployments (#9758)
Signed-off-by: Francis Santiago <francis.santiago@kaleidos.net>
2026-05-20 13:05:40 +02:00
Andrey Antukh
d78074307f Merge remote-tracking branch 'origin/main' into staging 2026-05-14 11:07:42 +02:00
Yamila Moreno
328efd4e16
📚 Add notice regarding architectural constraints with MCP Server (#9423) 2026-05-12 10:06:13 +02:00
wdeveloper16
d772632b08
✨ Allow customising the OIDC login button label (#9026)
* ✨ Allow customising the OIDC login button label (#7027)

* 📚 Add CHANGES entry and docs for PENPOT_OIDC_NAME (#7027)

---------

Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
2026-04-17 16:56:29 +02:00
Yamila Moreno
084ca401fd
📚 Improve recommended settings for self-host (#8846) 2026-03-31 15:11:58 +02:00
Andrey Antukh
8252bc485e 📚 Fix oidc callback related documentation issue 2026-01-20 16:24:12 +01:00
André Carvalhais
54451608dc 💄 Fix spelling of 'smtp' in email configuration section
Corrected the spelling of 'smtp' in the documentation.

Signed-off-by: André Carvalhais <carvalhais@live.com>
2025-11-26 08:11:27 +01:00
David Barragán Merino
f32913adcf
📚 Adapt doc with the storage settings changes (#7607) 2025-10-31 08:56:06 +01:00
Yamila Moreno
a9f4fe84fa 📎 Improve gh actions 2025-08-07 17:51:20 +02:00
David Barragán Merino
b385f055e0
🐳 Migrate from Redis to Valkey (#6666) 2025-06-10 10:08:10 +02:00
Yamila Moreno
6fc63f14a0
✨ Add configuration for air gapped installations (#6567) 2025-05-29 10:34:47 +02:00
Ramiro Andres Sanchez Balo
5fc2208c16
📚 Improve metadata descriptions (#6457) 2025-05-13 08:09:59 +02:00
Yamila Moreno
6067e438a3
📚 Document auto file snapshot (#6085) 2025-03-18 11:40:58 +01:00
Yamila Moreno
4b5d304a40
📚 Improve technical guide
* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide

* 📚 Improve technical guide
2025-02-19 14:35:40 +01:00
Yamila Moreno
31bc7e7c86 🐳 add advice for unsecure configuration 2025-01-22 13:34:48 +01:00
Yamila Moreno
b3a5e6710f 🐳 improve docs about custom configuration 2025-01-22 12:21:13 +01:00
adi-lb-phoenix
94976aa2b1 📚 Fix incorrect flag on configuration.md 2025-01-16 12:19:03 +01:00
David Barragán Merino
88296480ec 📚 Merge penpot/penpot-docs repository 2024-10-30 13:30:02 +01:00