Built to be cut down. This release makes removing a feature a supported operation rather than an archaeology exercise, and fixes two broken routes that shipped in v3.1.0.
Fixes you'll want
Two route() calls named routes that have never existed. Both threw at runtime for anyone who reached them:
- Magic-link registration posted to
magic.register; the route ismagic.store. Submitting the form threw. - "Log out all other devices" posted to
user.sessions.destroy— plural, nonexistent. The real route isuser.session.logout, and it's a POST. (user.session.destroyis the DELETE for a single session, used correctly ten lines above, which is how the typo survived.)
Both were found by a new test rather than by a user, which is the point of it.
php artisan guacpanel:remove
php artisan guacpanel:remove # list what's removable
php artisan guacpanel:remove charts --dry-run # show the plan
php artisan guacpanel:remove charts # apply itDeletes the feature's files and tests, strips its route statements (and any group they leave empty), removes its entries from the navigation manifest and settings page, deletes its permissions from the seeder and protected list, drops its npm packages and CSS imports, and forgets the feature so it stops being offered.
It won't rewrite a route group or a page you're keeping that merely references the feature — it prints those as follow-ups, because a half-correct automatic edit to a file you keep is worse than a clear instruction.
Its undo is git, so it refuses to run with uncommitted changes and prints the exact restore command when it finishes. Removing several in a row, it offers to commit between them — one commit per feature.
Covers charts, backups, health, audits and impersonation. Config-flagged features are .env switches, not deletions.
Guardrails
RouteReferenceTestcross-checks everyroute('…')inresources/jsagainst Laravel's route table. Delete a route and forget a reference and it's a red build naming the file, instead of a Ziggy throw in production.- One navigation manifest. The sidebar and command palette kept separate lists and had drifted — both offered "Notifications", pointing at different pages. Now
resources/js/navigation.js, read by both. - Manifest-drift tests fail the build if the remove command's paths, routes or feature tests go stale.
Security
- Password policy was
min:8and nothing else —Password::default()was used whilePassword::defaults()was never configured. Now 12 characters plus a Have I Been Pwned check by k-anonymity (only the first five characters of the hash leave the server, never the password). - Admin pages now mirror the permissions the server enforces. Three controllers check per-action permissions with
abort_unless, but no page reflected it, so a view-only admin saw Edit, Delete, Restore and Save and got a 403 on click. - The notification scheduler no longer runs jobs for a disabled feature.
CI
npm install and npm run build were the only checks. ESLint, Prettier and all 72 vitest tests now run too. Note npm run lint is eslint . --fix — a new non-mutating lint:check is what CI uses, since the fixing variant would repair problems and report success.
Docs
REMOVING.md documents the removal path per feature. README leads with what this kit actually is rather than a description that fit every starter kit.
Full Changelog: v3.1.0...v3.2.0