Version: 3.9.0
Highlights
Native Permission Engine
A brand-new permission engine now governs access across the entire application, replacing ad-hoc role checks with a centralized, fail-closed authorization layer. The engine was rolled out domain by domain — Tickets, Comments, Users, Clients, Settings, Sprints, Wiki, Ideas, Goals, Blueprints, Canvas, Calendar, Timesheets, Files, Reports, and the Projects service — closing numerous cross-user IDORs and information-disclosure oracles along the way. (#3461, #3469, #3471, #3472, #3473)
Unified Blueprints Domain
The 16 separate canvas variant domains have been consolidated into a single unified Blueprints domain with native Laravel controllers, dramatically reducing duplicated code while preserving every canvas type. Legacy Canvas and Strategy code now lives as thin deprecated adapters over Blueprints. (#3483, #3486)
JSON-RPC API
A new JSON-RPC API layer replaces the legacy REST/JSON controllers, with migrated endpoints for Tickets, Users, Ideas, Calendar, Notifications, Reactions, Sessions, Submenu, and more, plus a plugin gate attribute and capability-discovery endpoint for extensions. (#3457)
New Features
- Mobile Push Notifications - Mobile devices can now register Expo/FCM push tokens against access tokens and receive ticket and unread-count notifications via a new dispatcher (#3398, #3401, #3457)
- Logic Model Board - Added a Logic Model canvas board with WorkStructure orchestration (#3455)
- WorkStructure Meta-Model - New WorkStructure meta-model infrastructure underpinning structured work hierarchies (#3454)
- Task Collaborators - Completed multi-collaborator support for tasks, ensuring collaborators always receive ticket notifications and appear with full metadata across list and widget views (#1099)
- "Not Assigned" Milestone Filter - Added a "Not assigned" option to the tickets milestone filter (#3452)
- Stage Row Hover Spotlight - Hovering a stageflow row now spotlights it by dimming non-hovered stages (#3481)
- Blade Component Tiers - Introduced tiered Blade components with a central HTMX event convention (#3441)
Bug Fixes
- Postgres Compatibility - Fixed write limits, sequences, pdo_pgsql handling, and JS escaping for PostgreSQL deployments (#3447)
- Auth & Dashboard 500s - Hardened authentication and the dashboard against server errors and offline hangs (#3448)
- Marketplace 500s - Stopped marketplace errors by coercing API data into typed model properties (#3446)
- Ticket Editor Role on Save - Enforced the editor role when saving and correctly honored milestone project changes (#3445)
- Ticket Modal Milestone Crash - Fixed a crash in the ticket modal when handling milestone-type items
- Milestone & Wiki Dialogs - Restored data binding in the milestone and wiki dialogs (#3444)
- Save Notes from All Notes Grid - Notes saved from the All Notes grid now land in a default notebook (#3451)
- Weekly Timesheet Grid - Blank cells are now skipped when saving the weekly grid (#3449)
- Comment Edit/Reply Box - The edit and reply box now opens above the replies thread (#3450)
- Comments Template Variable - Stopped the comments template from shadowing the controller's comments array (#3459)
- Client Discussion Count - Fixed an inaccurate discussion count on the client detail page (#3426)
- Client Detail 500 - Resolved an undefined constant crash on the client detail page
- Value Proposition Canvas - Fixed an undefined variable on the Value Proposition Canvas (#3402)
- Logic Model Status Filter - Left-aligned the status filter dropdown menu on the Logic Model board (#3484)
- Ticket List Variables - Guarded undefined groupBy/newField variables in ticket list views
- Helm Session Expiration Units - Corrected session expiration units in the Helm chart (#3487, #3378)
- Mobile Responsiveness - Responsive stabilization sweep for phones and tablets (#3442)
Improvements
- Dashboard Performance - Eliminated full-page repaints and cross-widget loading-indicator churn and sped up hot paths via session locking, query dedupe, and improved event patterns (#3439, #3443)
- Thin Controllers - Modernized every domain controller from
run()toget()/post()thin-controller patterns across the entire codebase - Typed Exceptions - Added a typed exception hierarchy and JSON-RPC response types, with RPC-only service methods throwing typed exceptions on denial
- Consolidated Permission Migrations - Merged the 14 permission-engine migrations into a single install migration (#3506)
- Static Analysis - Raised PHPStan static analysis to level 1 (#3440)
Security
- projectIdParam Validation - Project ID parameters are now validated as real positive integers and fail closed when a mandatory ID can't be resolved (#3507, #3509)
- Projects Service Hardening - Gated the Projects god-service with recursion-safe manager+ management checks (#3505)
- Calendar IDOR Fixes - Gated the Calendar domain and closed cross-user IDORs (#3504)
- Reports Surface Reduction - Gated the Reports domain and de-exposed the system/telemetry RPC surface (#3501)
- Files IDOR Fixes - Gated the Files domain and closed RPC IDORs (#3492)
- Timesheets Authorization - Gated the Timesheets domain with own-time vs manage separation and matrix edit (#3490)
- Goals & Canvas Authorization - Gated the Goals and consolidated Canvas domains and closed by-id IDORs (#3483, #3485, #3486)
- Comment Authorization - Scoped comment authorization to the host entity's real project and closed the comment-existence oracle in reaction methods
- Tags IDOR - Closed the Tags
getTagsIDOR and removed dead REST controllers - CSRF Hardening - Excluded install routes from CSRF verification while forms are tokenized
- Comprehensive Remediation - Comprehensive remediation across seven vulnerability families
Dependency Updates
- Bumped shell-quote from 1.8.2 to 1.8.4 to clear a critical advisory (#3502)
- Bumped icalendar and dotlottie-wc, removed Dependabot config (#3438)
- Bumped webpack-cli from 5.1.4 to 7.0.3 (#3413)
- Bumped katex from 0.16.21 to 0.17.0 (#3410)
- Bumped fullcalendar from 6.1.17 to 6.1.20 (#3417)
- Bumped @fullcalendar/luxon3 from 6.1.17 to 6.1.20 (#3409)
- Switched html2canvas and jsPDF to npm-managed dependencies (#3482)
What's Changed
Other Changes
- Fix case sensitivity in secondary color setting by @Delvar in #3348
- Fix syntax for Referrer-Policy header assignment by @Delvar in #3347
- build(deps): bump brace-expansion by @dependabot[bot] in #3340
- Update ko-KR.ini — complete Korean translation by @madrobotnet in #3326
- build(deps-dev): bump yauzl from 3.2.0 to 3.2.1 by @dependabot[bot] in #3320
- make MarketplacePlugin model properties nullable for PHP 8.x by @shaunchokshi in #3356
- Add collaborator support to task assignment and UI (list + kanban) by @juarezsousa-ctrl in #3337
- fix: Postgres compat + plugin install null-safety (bundles 4 issues) by @marcelfolaron in #3359
- fix: moveTicket, KPI linking, backlog filter on Postgres, tiptap list rendering by @marcelfolaron in #3360
- fix: wiki sidebar width, dark-mode editor dropdowns, emoji ESC propagation by @marcelfolaron in #3361
- Backend API surface for Leantime Mobile (TestFlight) by @gloriafolaron in #3395
- Fix three bugs: isset on null param, redundant limit(1), malformed URL by @YoussefMansour9 in #3393
- Implement Arabic language by @Mohd-PH in #3370
- fix: add error403.blade.php to resolve crash on permission-gated pages by @Copilot in #3365
- fix: Resend Invite button silently no-ops due to isset(null) check by @mojotaker in #3392
- Fix favorites star spinning indefinitely after click on Project Hub by @Copilot in #3364
- refactor: migrate all .tpl.php/.sub.php/.inc.php templates to Blade by @marcelfolaron in #3362
- fix: 6 high-impact bugs — PDO crash, commenter role, Wiki Postgres, Ideas canvasId, Kanban scroll by @marcelfolaron in #3397
- fix(calendar): tolerate MySQL zero-date sentinel in getCalendar by @gloriafolaron in #3396
- Security: comprehensive remediation across 7 vulnerability families by @marcelfolaron in #3399
- Modernize all controllers from run() to get()/post() by @marcelfolaron in #3408
- fix: Value Proposition Canvas 500 (Undefined variable $currentCanvas) (#3402) by @marcelfolaron in #3422
- Refactor: thin controllers / fat services across all non-canvas domains by @marcelfolaron in #3420
- refactor: consolidate canvas domains into a unified Blueprints domain (YAML-driven) by @marcelfolaron in #3421
- feat(api): JSON-RPC migration foundation + Tickets pilot by @marcelfolaron in #3424
- feat(api): JSON-RPC migration batch 2 — Submenu, Sessions, Reactions, Notifications by @marcelfolaron in #3425
- Fix 500 on client detail page: undefined constant generalComment by @Roark1138 in #3423
- fix(clients): show accurate discussion count on client detail page by @marcelfolaron in #3428
- feat(api): JSON-RPC migration batch 3 — Calendar (+ delete dead Ideation) by @marcelfolaron in #3426
- feat(api): JSON-RPC migration — Ideas (with project-access authorization) by @marcelfolaron in #3427
- feat(api): JSON-RPC migration — Users JSON endpoints by @marcelfolaron in #3429
- Retire the legacy Api REST controllers (binary/upload → domains, Projects sort → JSON-RPC, delete dead Tags/Timer) by @marcelfolaron in #3430
- refactor(blueprints): native Laravel controllers, drop the dispatch bridge by @marcelfolaron in #3432
- feat(core): typed exception hierarchy + JSON-RPC response types by @marcelfolaron in #3431
- fix(tickets): guard undefined $groupBy/$newField in list views by @marcelfolaron in #3433
- cleanup: remove dead $x = $x; Blade self-assign artifacts by @marcelfolaron in #3434
- refactor: merge Strategy domain into Blueprints by @marcelfolaron in #3435
- feature: RPC-only service methods throw typed exceptions on denial by @marcelfolaron in #3436
- Fix #1099 by @duongynhi000005-oss in #3405
- feat(tickets): finish collaborators feature — notifications, persistence & hardening (#1099) by @marcelfolaron in #3437
- build(deps): bump @fullcalendar/luxon3 from 6.1.17 to 6.1.20 by @dependabot[bot] in #3409
- build(deps): bump fullcalendar from 6.1.17 to 6.1.20 by @dependabot[bot] in #3417
- build(deps): bump katex from 0.16.21 to 0.17.0 by @dependabot[bot] in #3410
- build(deps-dev): bump webpack-cli from 5.1.4 to 7.0.3 by @dependabot[bot] in #3413
- chore(deps): bump icalendar + dotlottie-wc, remove Dependabot by @marcelfolaron in #3438
- perf: speed up dashboard & hot paths (session locking, query dedupe, event patterns) by @marcelfolaron in #3439
- ci(phpstan): raise static analysis to level 1 by @marcelfolaron in #3440
- fix(mobile): responsive stabilization sweep for phones & tablets by @marcelfolaron in #3442
- perf(dashboard): stop full-page repaints & cross-widget loading-indicator churn by @marcelfolaron in #3443
- feat(components): Blade component tiers + central HTMX event convention by @marcelfolaron in #3441
- fix: restore data binding in milestone & wiki dialogs by @marcelfolaron in #3444
- fix: repair #3441 regressions blocking acceptance (app.js bundle syntax + stale install step) by @marcelfolaron in #3453
- fix(tickets): enforce editor role on save + honor milestone project change by @marcelfolaron in #3445
- fix(plugins): stop marketplace 500s by coercing API data to typed model props by @marcelfolaron in #3446
- fix(db): PostgreSQL compatibility (write limits, sequences, pdo_pgsql, JS escaping) by @marcelfolaron in #3447
- fix(ldap): return a normal auth error instead of a 500 on bad credentials by @marcelfolaron in #3448
- fix(comments): open the edit/reply box above the replies thread by @marcelfolaron in #3450
- feat(tickets): add "Not assigned" milestone filter option (#3252) by @marcelfolaron in #3452
- feat(core): WorkStructure meta-model infrastructure by @marcelfolaron in #3454
- fix(timesheets): skip blank cells when saving the weekly grid by @marcelfolaron in #3449
- fix(wiki): save notes from the All Notes grid into a default notebook by @marcelfolaron in #3451
- feat(notifications): mobile push device tokens (Expo + FCM) + getUnreadCount RPC (#3398) by @gloriafolaron in #3401
- feat(logicmodelcanvas): Logic Model board + WorkStructure orchestration by @marcelfolaron in #3455
- feat(notifications): mobile push tokens on access_tokens + FCM/Expo dispatcher by @gloriafolaron in #3457
- fix(comments): stop shadowing controller $comments array in Discussion template by @gloriafolaron in #3459
- feat(core): native permission engine foundation (Phase 0) + console addCommand fix by @marcelfolaron in #3461
- feat(permissions): enforce Tickets + Comments via the engine — full @api coverage by @marcelfolaron in #3469
- feat(permissions): roll the engine across the Users domain (company-scope) by @marcelfolaron in #3471
- feat(permissions): roll the engine across the Clients domain (company-scope) by @marcelfolaron in #3472
- feat(permissions): roll the engine across the Setting domain (company + project scopes) by @marcelfolaron in #3473
- feat(permissions): roll the engine across the Sprints domain (first content domain) by @marcelfolaron in #3474
- feat(api): plugin gate attribute + capability discovery endpoint by @gloriafolaron in #3460
- fix(tickets): ticket modal crashes on milestone type by @gloriafolaron in #3468
- security(comments): scope comment authz to the host entity's real project by @marcelfolaron in #3476
- security(ideas): gate Ideas domain on the native permission engine by @marcelfolaron in #3478
- security(wiki+ideas): gate the Wiki and Ideas domains on the native permission engine by @marcelfolaron in #3479
- security(blueprints): gate the consolidated canvas domain + close by-id IDORs by @marcelfolaron in #3483
- security(goalcanvas): gate the Goals domain + close by-id IDORs by @marcelfolaron in #3485
- security(canvas): gate the legacy Canvas-base controllers (Logicmodelcanvas) + Api\Canvas by @marcelfolaron in #3486
- fix(helm): correct session expiration units in Helm chart (fixes #3378) by @dashitongzhi in #3487
- fix(logicmodelcanvas): left-align status filter dropdown menu by @gloriafolaron in #3484
- feat(stageflow): spotlight non-hovered stages on row hover by @gloriafolaron in #3481
- feat(libs): npm-manage html2canvas + jsPDF by @gloriafolaron in #3482
- security(timesheets): gate the Timesheets domain (own-time vs manage) + matrix edit by @marcelfolaron in #3490
- security(files): gate the Files domain + close RPC IDORs by @marcelfolaron in #3492
- chore(logicmodelcanvas): drop unused snapshot mount div by @gloriafolaron in #3491
- security(reports): gate the Reports domain + de-expose system/telemetry RPC surface by @marcelfolaron in #3501
- fix(deps): bump shell-quote 1.8.2 → 1.8.4 (clear critical advisory) by @marcelfolaron in #3502
- security(calendar): gate the Calendar domain + close cross-user IDORs by @marcelfolaron in #3504
- security(projects): gate the Projects god-service (recursion-safe, manager+ management) by @marcelfolaron in #3505
- refactor(install): consolidate the 14 permission-engine migrations into one by @marcelfolaron in #3506
- security(permissions): fail closed when a mandatory projectIdParam can't be resolved by @marcelfolaron in #3507
- ci(release): one-dispatch release pipeline (version bump, AI changelog, PR gate, auto-publish) by @marcelfolaron in #3508
- security(permissions): validate projectIdParam is a real positive integer by @marcelfolaron in #3509
- ci(release): swap changelog generation from GitHub Models to the Claude API by @marcelfolaron in #3510
- Release v3.9.0 by @marcelfolaron in #3511
- ci(release): set up PHP before make package (fixes v3.9.0 release build) by @marcelfolaron in #3512
New Contributors
- @Delvar made their first contribution in #3348
- @madrobotnet made their first contribution in #3326
- @shaunchokshi made their first contribution in #3356
- @juarezsousa-ctrl made their first contribution in #3337
- @YoussefMansour9 made their first contribution in #3393
- @Mohd-PH made their first contribution in #3370
- @Copilot made their first contribution in #3365
- @mojotaker made their first contribution in #3392
- @Roark1138 made their first contribution in #3423
- @duongynhi000005-oss made their first contribution in #3405
- @dashitongzhi made their first contribution in #3487
Full Changelog: v3.7.3...v3.9.0