What's Changed
High level overview
This release adds picture password login, a way for learners to sign in by selecting a sequence of pictures instead of typing a password. It is configured per facility and is currently available only when the interface language is set to English.
It also adds the ability to import and update channels from the community library directly within Kolibri.
The rest of the release is mostly internal: a round of security improvements, behind-the-scenes work to keep the codebase maintainable, and early groundwork for an upcoming Courses feature that is not yet available to use.
Added
New Feature: Picture Password login
Learners can sign in by selecting a sequence of pictures instead of typing a password. Picture password login is off by default, and can be turned on per facility in the facility settings. It is currently available only when the interface language is English. Key workflows:
- Admins enable picture password login from a facility's settings.
- Each learner is assigned a picture password automatically; coaches and admins can view and print them from a dedicated page.
- Learners sign in by selecting their pictures from a grid on the sign-in page.
- Picture passwords are assigned and reconciled automatically as learners are created, synced between devices, or change role.
List of supporting PRs
- Add new picture_password field to FacilityUser by @AlexVelezLl in #14365
- Add new facility setting fields to support picture password login. by @AllanOXDi in #14400
- Add picture password API endpoint with role-based access by @akolson in #14462
- Implement logic to assign picture password to learners by @ozer550 in #14469
- Add modal for reaching picture-password learner limit by @bjester in #14526
- Refactored facility settings page with picture password settings by @bjester in #14495
- Add base AllPasswordsPage component and coach routing for picture passwords by @akolson in #14547
- Update CreateSessionSerializer to support learners logging in with a picture password by @AllanOXDi in #14550
- Auto-assign picture_password to new learners and clear on role assignment by @rtibblesbot in #14551
- Add utility for picture passwords exhaustion by @bjester in #14560
- Assign picture password when user's last role is deleted by @rtibblesbot in #14566
- Implement PicturePasswordGrid component by @AlexVelezLl in #14546
- Update user edit page and user table for picture login by @bjester in #14573
- Register task to assign picture password by @ozer550 in #14565
- Update UserCreateSidePanel to display info about picture password assignment by @AllanOXDi in #14600
- Learner summary page picture password by @AlexVelezLl in #14632
- Implement full icon display and print functionality for AllPasswordsPage by @akolson in #14617
- Add facility plugin route and ClassEditPage entry point for picture passwords by @akolson in #14652
- Update Facility Settings Page with informational modals by @AllanOXDi in #14641
- Picture password sign-in integration by @bjester in #14649
- Add picture password row to learner Profile page by @nucleogenesis in #14679
- Show Change password row for admins; respect facility's show_icon_text by @rtibbles in #14692
- Responsive picture password sign in by @AlexVelezLl in #14682
- Add picture password confirm modal by @akolson in #14691
- Fix new user creation when picture password is enabled by @rtibbles in #14693
- Add route wrapper components for Coach and Facility picture password page by @akolson in #14701
- Implement sign-up/join picture password confirmation modal by @bjester in #14698
- Resolve picture_password sync collisions via PicturePasswordCollisionOperation by @rtibblesbot in #14699
- Assign picture passwords to synced learners missing them on receiver by @rtibblesbot in #14650
- Picture Password Animations by @DXCanas in #14690
- Use class summary password settings instead of useFacility by @AlexVelezLl in #14737
- Show picture password modal on home page after signup/facility change by @akolson in #14725
- Gate picture-password UI on isPictureLoginFeatureEnabled by @rtibbles in #14739
- Use KListbox by @AlexVelezLl in #14745
- Fix picture password icons broken on Coach page refresh by @akolson in #14751
- Add visible error notification for wrong picture password by @akolson in #14741
- ensure picture password modal appears on LOD join by @marcellamaki in #14753
Community library import
Kolibri can now import and update channels from the community library directly, including importing a channel at a particular version and importing channels that are still drafts on Studio.
List of supporting PRs
- Add channel token support to importchannel management command by @rtibbles in #14056
- Thread token-resolved metadata (library, version) through channel import pipeline by @rtibblesbot in #14598
- Fetch versioned channel DB URL when importing with a version token by @rtibblesbot in #14607
- Use Studio v2 API for community library channel update checks by @rtibblesbot in #14608
- Allow channel downgrade when version explicitly requested via token by @rtibblesbot in #14609
- Pass channel token through to remotechannelimport task params by @rtibblesbot in #14605
- Handle draft channels with no published Studio version by @rtibblesbot in #14638
- Fix content tree hidden and 404 error when importing from draft channels by @rtibblesbot in #14664
- Set prop for import modal to ensure devices have facilities by @bjester in #14749
- Fix token-based channel import: redirect to new version page and support draft channels by @rtibblesbot in #14747
Course tools (foundational work, not yet available)
Groundwork for an upcoming Courses feature — course content download, learning-objective reports, and the pre/post test flow. Courses cannot yet be created in Studio, so this is not usable in this release.
List of supporting PRs
- Pre/Post test renderer by @AlexVelezLl in #14389
- Use useGoBack for course back navigation instead of hardcoded HOME by @nucleogenesis in #14403
- Exclude Courses in Search, Resume, Library & plugin_data flag for courses by @nucleogenesis in #14132
- Add unit learning objectives report API endpoint by @AllanOXDi in #14422
- Refined prev/next buttons in course viewer by @nucleogenesis in #14399
- Create content download requests and removal requests for courses descendants by @AlexVelezLl in #14448
- Add Learning Objectives report tab to CourseSummaryPage by @nucleogenesis in #14440
- Fix unit report to credit all mapped LOs for multi-LO items by @rtibblesbot in #14472
- Prioritize course content download based on learner progress by @AlexVelezLl in #14496
- Add Learners report tab to CourseSummaryPage and Learner SidePanel View by @AllanOXDi in #14497
- Add course-level learner progress notifications by @rtibblesbot in #14523
- Add Learning Objective side panel detail view by @nucleogenesis in #14454
- Handle deleted course content gracefully on courses pages by @nucleogenesis in #14521
- Add unit lesson progress API endpoint by @rtibblesbot in #14535
- Add URL routing for tab and side panel state on Course Summary page by @rtibblesbot in #14606
- Update course title by @AlexVelezLl in #14557
- Course lod content update by @AlexVelezLl in #14619
- Reinstate learner interaction blocking for course pre/post test flow by @nucleogenesis in #14562
Other additions
- Add Python 3.14 support by @rtibblesbot in #14395
- Add official container image with best practices by @bjester in #14450
- Enable postgres SSL configuration through Kolibri options by @bjester in #14702
- Add locally-triggered impact-stories prompt for super admins by @rtibbles in #14729
Changed
Security hardening
A pass over the platform to reduce attack surface and tighten input handling:
- API endpoints that fetch from a caller-supplied URL are now restricted, reducing server-side request forgery risk.
- Error responses and logs no longer disclose internal details such as file paths.
- App-initialisation redirects are validated before use.
- GitHub Actions workflows were given least-privilege permissions and protected against artifact poisoning.
- Dependency updates now wait for a cooldown period to reduce supply-chain risk.
List of supporting PRs
- Fix/remove pickle protocol cache defaults v0.19.x by @Swoyamjeetcodes in #14198
- Add minReleaseAge to pnpm settings to reduce likelihood of supply chain compromises by @bjester in #14475
- Add excludes for dep cool downs by @bjester in #14477
- Automigrate allow_other_browsers_to_connect to True as it was previously always wrong by @rtibbles in #14596
- Harden workflow_run comment pipelines against artifact poisoning by @rtibblesbot in #14715
- Restructure InitializeAppView redirect to validate-then-use, drop HTTP_REFERER fallback by @rtibblesbot in #14710
- Fix fork-PR lookup in findPrByHeadSha by @rtibbles in #14719
- Replace execSync with execFileSync argv array in kolibri-build by @rtibblesbot in #14713
- Sweep str(e) information disclosure and log idioms from API paths by @rtibblesbot in #14717
- Add least-privilege permissions blocks to all GitHub workflow files by @rtibblesbot in #14716
- Drop expanduser from filesystem helpers; echo submitted path in API by @rtibblesbot in #14718
- Update CodeQL workflow to allow running on prs from forks by @rtibbles in #14721
- Extract npm version report artifact to temp dir by @rtibbles in #14720
- Authorization scopes with enhanced test coverage by @bjester in #14706
- Tighten API endpoints that fetch from a caller-supplied remote URL by @rtibbles in #14705
- Bypass facility ID filter for superusers, but require it for all other users by @bjester in #14730
Frontend architecture
Continued migration away from Vuex: the remaining shared state — page loading, errors, notifications, routing, and page visibility — now lives in composables, the plugin event bus has been removed in favour of explicit hooks, and webpack bundles can load RTL stylesheets dynamically.
List of supporting PRs
- Refactor facilityConfig Vuex module into useFacilityEditor composable by @bjester in #14449
- Replace samePageCheckGenerator with route identity checks by @rtibblesbot in #14518
- Move setUnspecifiedPassword from core Vuex to user_auth utility by @rtibblesbot in #14528
- Remove dead Vuex notification state and consumers by @rtibblesbot in #14529
- Replace Vuex pageVisibility state with reactive ref in browserInfo by @rtibblesbot in #14530
- Move fetchUserSyncStatus to coach plugin, add notification dismiss, and relocate setUnspecifiedPassword by @rtibblesbot in #14531
- Move allowRemoteAccess from core to per-plugin plugin_data by @rtibblesbot in #14532
- Remove Vuex page loading state, use shared composable by @rtibblesbot in #14527
- Replace Vuex error state with appError utility module by @rtibblesbot in #14536
- Remove Vue event bus from pluginMediator by @rtibblesbot in #14540
- Move usePageLoading composable to kolibri-common by @rtibbles in #14544
- Add dynamic RTL CSS loading support for webpack bundles by @rtibbles in #14479
- Remove vuex-router-sync; replace store.state.route with useRoute() by @rtibblesbot in #14541
- Double down on hooks, remove app plugin in favour of individual hooks for individual capabilities by @rtibbles in #12879
Developer tooling and build
Frontend tests migrated to Vue Testing Library, ESLint moved to flat config with stricter rules, npm package publishing automated, and webpack transpilation switched to swc.
List of supporting PRs
- Update dependabot to monthly schedule with 7-day cooldown by @rtibbles in #14020
- Remove call-holiday-message github action by @LianaHarris360 in #14048
- fix(docs): correct spelling mistakes in documentation files by @himanshujays29 in #14059
- Update Node.js version in getting_started docs from 18.x to 20.x by @curiouscoder-cmd in #14232
- Refactor epub navigation button tests to use Vue Testing Library by @curiouscoder-cmd in #14237
- Converted user auth page unit tests to Vue Testing Library by @AadarshM07 in #14245
- Convert epub viewer bar unit tests to Vue Testing Library by @sharma-anushka in #14225
- Convert epub viewer settings unit tests to Vue Testing Library for SettingsButton.spec.js and SettingsSideBar.spec.js by @ketan0919 in #14280
- Refactor user auth layout tests to use Vue Testing Library by @curiouscoder-cmd in #14329
- Add workflow for pull request events by @MisRob in #14333
- Add tests for allow_other_browsers_to_connect remote access setting by @rtibblesbot in #14334
- Add jest-fail-on-console and fix console noise in frontend tests by @rtibblesbot in #14337
- Remove stale Python 2.7 references from comments and docstrings by @ketan0919 in #14342
- Migrate epub viewer search tests to Vue Testing Library by @sahibsiddiqui in #14321
- test: migrate setup_wizard tests to Vue Testing Library by @rohitt-pandey in #14219
- Convert PDF viewer unit tests to Vue Testing Library #14194 by @rohitt-pandey in #14287
- Migrate ESLint config from legacy .eslintrc to flat config (ESLint 9) by @rtibblesbot in #14346
- Migrate plugin tests to Vue Testing Library by @Swoyamjeetcodes in #14197
- refactor TaskPanel and PinAuthenticationModal to use vue test library by @curiouscoder-cmd in #14393
- Add explicit run mode setting to smoke test. by @rtibbles in #14442
- Add FUNDING.yml by @bjester in #14446
- Refactor device plugin test files to use Vue Testing Library by @curiouscoder-cmd in #14376
- Fix broken pnpm lock file. by @rtibbles in #14509
- Remove quotes and generalize workflow names by @bjester in #14520
- Automate npm package publishing by @rtibbles in #14561
- Use VTL v1 syntax for stubs. by @rtibbles in #14568
- Add npm package publish check tooling and bump versions by @rtibbles in #14576
- Exclude frontend/ directories from sdist instead of old assets/ by @rtibbles in #14575
- Fix npm publish workflow permissions for provenance by @rtibbles in #14577
- Drop --provenance and --access public from npm publish by @rtibbles in #14578
- Use pnpm pack + npm publish for OIDC trusted publishing by @rtibbles in #14581
- Revert pnpm pack change, use Node 24 for npm publish by @rtibbles in #14582
- Remove pnpm install from npm publish workflow by @rtibbles in #14583
- Use lightweight install for npm publish workflow by @rtibbles in #14584
- Add published packages summary for Slack notifications by @rtibbles in #14586
- Strengthen frontend linting with dependency, Node.js, and test rules by @rtibbles in #14594
- Replace babel-loader with swc-loader for webpack transpilation by @rtibbles in #14595
- Update android installer version to v0.1.7.1 by @rtibbles in #14602
- Update to android app version with APK build fix by @rtibbles in #14604
- Convert setup wizard step unit tests to Vue Testing Library by @AadarshM07 in #14616
- Narrow npm publish triggers and add Slack notifications by @rtibbles in #14615
- Migrate epub viewer TOC tests from vue test utils to Vue testing library by @curiouscoder-cmd in #14542
- Update browserslist config to new support matrix by @rtibblesbot in #14627
- Add new guidance and linter rules for Testing Library tests by @MisRob in #14579
- Enable import-x module-system ESM enforcement rules by @rtibblesbot in #14639
- Add 'opened' to pull-request-target trigger types by @rtibbles in #14672
- Conditionalize webpack cache by hot/transpile to prevent errors. by @rtibbles in #14678
- Replace add_contributor workflow with monthly updater and backfill AUTHORS.md by @rtibbles in #14686
- Cancel outdated CI runs when a PR is updated by @rtibbles in #14694
- Bump kolibri-app workflow refs to v0.5.1 by @rtibbles in #14744
Other changes
- Consolidate deprovision to reuse facility deletion utilities by @rtibblesbot in #14343
- Optimize Role and Membership bulk creation with bulk_create by @rtibblesbot in #14341
- Upgrade KDS from v5.5.2 to v5.6.1 by @MisRob in #14517
- Upgrade morango and pin setuptools by @bjester in #14567
- UI cleanup by @marcellamaki in #14533
- Bump morango version to v0.8.11 by @bjester in #14655
- Upgrade morango to resolve dirty-bit issue and cleanup by @bjester in #14683
Fixed
List of supporting PRs
- Prevent crash in content cleanup when no eligible user downloads are available by @remo-lab in #14054
- Fix UnboundLocalError in ClassroomNotificationsViewset.list when filter_queryset raises DatabaseError by @TheGreatPratyush in #14345
- Add better error message for deleted learners in assignments by @AllanOXDi in #14209
- Fix selected value handling in select components by @Abhishek-Punhani in #14250
- Use update_dirty_bit_to in pre_save to allow null user fields during deserialization by @ozer550 in #14234
- Fix ResourceLayout remounting default slot on side panel toggle by @rtibbles in #14397
- Handle frontend loaded zip archive content (h5p, bloom(pub/d), perseus, kpub) that contains large files by @rtibbles in #12805
- Fix benchmark command Session import and macOS psutil crash by @rtibblesbot in #14447
- Remove duplicate page name definitions which was causing errors by @marcellamaki in #14463
- Remove remaining duplicate PageNames definitions in coach plugin by @rtibbles in #14467
- Ensure Attendance Tracking confirmation snackbar is displayed by @LianaHarris360 in #14478
- Fix flash of unstyled text (FOUT) on login page by @rtibbles in #14476
- Check progress before notifications in coach lesson report status by @rtibbles in #14516
- Fix progress bar resetting during import/export operations by @rtibbles in #14057
- Do early resolution of language redirect for redirect views to prevent multihop redirects. by @rtibbles in #14597
- fix(qti-viewer): accessibility and maxChoices fixes for choice and text-entry interactions by @rajeshuchil in #14374
- Fix mark-all-present switch requiring two clicks after modal cancel by @akolson in #14552
- Skip download when file already exists in fallback storage dir by @rtibblesbot in #14630
- Fix UI freeze when cancelling Edit Attendance by @akolson in #14653
- A11y fixes by @marcellamaki in #14645
- Miscellaneous bug fixes by @rtibbles in #14688
- Fixes for global facility state handling by @bjester in #14703
- Reload on back/forward navigation to prevent stale auth state by @akolson in #14704
- Bundle colorama so Click works on Windows by @rtibbles in #14757
- Set up Django app registry before building WSGI applications by @rtibbles in #14760
Dependencies
List of supporting PRs
- Bump lodash from 4.17.21 to 4.17.23 by @dependabot[bot] in #14096
- Bump axios from 1.13.2 to 1.13.5 by @dependabot[bot] in #14157
- Bump qs from 6.14.1 to 6.14.2 by @dependabot[bot] in #14175
- Bump markdown-it from 14.1.0 to 14.1.1 by @dependabot[bot] in #14174
- Bump dompurify from 3.3.1 to 3.3.2 by @dependabot[bot] in #14331
- Bump underscore from 1.4.4 to 1.13.8 by @dependabot[bot] in #14326
- Bump the github group with 3 updates by @dependabot[bot] in #14480
- Bump pnpm/action-setup from 4 to 5 by @dependabot[bot] in #14481
- Bump the babel group with 4 updates by @dependabot[bot] in #14482
- Bump sass-loader from 16.0.5 to 16.0.7 by @dependabot[bot] in #14484
- Bump eslint-plugin-jest from 29.12.1 to 29.15.1 by @dependabot[bot] in #14485
- Bump babel-loader from 10.0.0 to 10.1.1 by @dependabot[bot] in #14486
- Bump dompurify from 3.3.2 to 3.3.3 by @dependabot[bot] in #14487
- Bump @crowdin/cli from 4.12.0 to 4.14.1 by @dependabot[bot] in #14489
- Bump dayjs from 1.11.19 to 1.11.20 by @dependabot[bot] in #14490
- Bump babel-jest from 30.2.0 to 30.3.0 by @dependabot[bot] in #14491
- Bump commander from 14.0.2 to 14.0.3 by @dependabot[bot] in #14499
- Bump enhanced-resolve from 5.18.4 to 5.20.1 by @dependabot[bot] in #14500
- Bump webpack-cli from 6.0.1 to 7.0.2 by @dependabot[bot] in #14503
- Bump kolibri-constants from 0.2.13 to 0.2.16 by @dependabot[bot] in #14504
- Bump csv-parse from 6.1.0 to 6.2.1 by @dependabot[bot] in #14505
- Bump axios from 1.13.5 to 1.15.0 by @dependabot[bot] in #14612
- Bump dompurify from 3.3.3 to 3.4.0 by @dependabot[bot] in #14634
- Bump actions/github-script from 8 to 9 in the github group by @dependabot[bot] in #14668
- Bump docker/setup-qemu-action from 3 to 4 by @dependabot[bot] in #14669
- Bump pnpm/action-setup from 5 to 6.0.3 by @dependabot[bot] in #14670
- Bump ad-m/github-push-action from 1.0.0 to 1.1.0 by @dependabot[bot] in #14671
- Bump axios from 1.15.0 to 1.15.2 by @dependabot[bot] in #14685
- Bump webpack-dev-server from 5.2.3 to 5.2.4 by @dependabot[bot] in #14724
New Contributors
- @himanshujays29 made their first contribution in #14059
- @remo-lab made their first contribution in #14054
- @Swoyamjeetcodes made their first contribution in #14198
- @TheGreatPratyush made their first contribution in #14345
- @sahibsiddiqui made their first contribution in #14321
- @rohitt-pandey made their first contribution in #14219
- @rajeshuchil made their first contribution in #14374
Full Changelog: v0.19.3...v0.19.4