Connor Yoh 06f2e0f0b2 feat(licensing): enforce the user cap and surface capacity
Groundwork for capping the Server plan at 100 users per server. The licensing
rule itself needs no change: calculateMaxAllowedUsers already returns
licenseMaxUsers when it is positive and unlimited when it is zero, which is
exactly the behaviour we want once licences start carrying a real number. An
un-upgraded instance therefore enforces a capped licence correctly.

What did need work is the paths around it.

Closes two ways past the limit. Invite links were checked when the link was
generated, not when it was redeemed, so a link minted while slots were free
could still create the user over the cap; redemption now re-checks and returns
409 with a message naming the limit. And saveUserCore gained a final guard, so
a seventh creation path cannot be added without one.

The guard needs an exemption. INTERNAL_API_USER is excluded from
getTotalUsersCount, so charging its creation against the limit would strand an
installation already at its cap - it could not create the account it needs to
function. SaveUserRequest carries an explicit bypassUserLimit flag, set only by
the three InitialSecuritySetup bootstrap paths.

UserService reaches UserLicenseSettingsService through an ObjectProvider because
that service already injects UserService to count users; this is the same cycle
break it uses for LicenseKeyChecker.

Also surfaces what the capacity UI will need: server_quantity and
user_block_size read from licence metadata at all three verification paths
through one helper so they cannot drift, persisted so they survive the weekly
refresh, and exposed on /license-info and the admin payload. Both are
presentation only; maxUsers stays the enforced limit. The admin payload also
gains pendingInvites, so the UI can show what is consuming capacity - disabled
accounts and unredeemed invites both hold a slot - and offer a way to reclaim
it rather than making payment the only exit.

No behaviour changes for any licence in the wild.
2026-08-13 16:00:12 +01:00
2026-07-24 09:37:10 +00:00
2026-07-11 12:48:53 +01:00
2026-03-25 11:00:40 +00:00
2026-07-17 10:16:06 +00:00
2026-03-25 11:00:40 +00:00

Stirling PDF logo

Stirling PDF - The Open-Source PDF Platform

Stirling PDF is a powerful, open-source PDF editing platform. Run it as a personal desktop app, in the browser, or deploy it on your own servers with a private API. Edit, sign, redact, convert, and automate PDFs without sending documents to external services.

Docker Pulls Discord OpenSSF Scorecard GitHub Repo stars

Stirling PDF - Dashboard

Key Capabilities

  • Everywhere you work - Desktop client, browser UI, and self-hosted server with a private API.
  • 50+ PDF tools - Edit, merge, split, sign, redact, convert, OCR, compress, and more.
  • Automation & workflows - No-code pipelines direct in UI with APIs to process millions of PDFs.
  • Enterprisegrade - SSO, auditing, and flexible onprem deployments.
  • Developer platform - REST APIs available for nearly all tools to integrate into your existing systems.
  • Global UI - Interface available in 40+ languages.

For a full feature list, see the docs: https://docs.stirlingpdf.com

Quick Start

docker run -p 8080:8080 docker.stirlingpdf.com/stirlingtools/stirling-pdf

Then open: http://localhost:8080

For full installation options (including desktop and Kubernetes), see our Documentation Guide.

Resources

Support

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

This project uses Task as a unified command runner for all build, dev, and test commands. Run task dev to get started running the editor, run task to see the most common commands, or see the Developer Guide for full details.

For adding translations, see the Translation Guide.

License

Stirling PDF is open-core. See LICENSE for details.

S
Description
#1 Locally hosted web application that allows you to perform various operations on PDF files
Readme MIT
751 MiB
Languages
Java 45.9%
TypeScript 44.7%
Python 3.9%
CSS 2.4%
Shell 0.8%
Other 2.2%