Fresh prod install ran Grav rc.10 (stale .env.prod GRAV_VERSION), so GPM would not serve the api plugin (needs >=2.0.4); admin2 (auth via /api/v1) then 404'd login silently. Documents the dead ends (deploying api config, gpm index -f, same-channel assumption) and the fix (self-upgrade core + reinstall + bump .env.prod). Adds reciprocal 'same 2026-07-04 cutover' cross-links across the three sibling deploy gotchas. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Np4cMQLF77i664CAQXySzU
5.6 KiB
title, date, category, module, problem_type, component, symptoms, root_cause, resolution_type, severity, related_components, tags
| title | date | category | module | problem_type | component | symptoms | root_cause | resolution_type | severity | related_components | tags | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Grav login new-user grants api.* but not admin.* on Admin2-only installs | 2026-07-04 | docs/solutions/test-failures | testing / account provisioning | test_failure | authentication |
|
missing_permission | tooling_addition | medium |
|
|
Grav login new-user grants api.* but not admin.* on Admin2-only installs
Problem
When the Playwright suite was moved onto a dedicated local testrunner account, every /gpx-manager spec started failing — the account could authenticate but was treated as unauthorized for the manager page. The account had been created with bin/plugin login new-user ... -P b (Admin + Site access) but without --admin-type, and on this Admin2-only install that grants api.* permissions and no admin.* permissions.
Symptoms
- The
/gpx-managerPlaywright specs fail after switching from the real user to thetestrunneraccount (they passed as the real user). - An authenticated
testrunnerstill gets the Login plugin's login form at/gpx-managerinstead of the manager UI. - The generated
user/accounts/testrunner.yamlcontains anaccess.apiblock (login: true,super: true) but noaccess.adminblock.
What Didn't Work
- Assuming
-P bwas enough.-P/--permissions bselects the category of access (Admin + Site), but the type of admin permission — classicadmin.*vs Admin2api.*— is a separate axis controlled by--admin-type, which defaults to auto-detect.-P balone does not guaranteeadmin.login. - Blaming the wrong specs. In the same push, the home
H1/map specs were also red, which looked like it might be the same auth problem. It was not — those were gated bysite.yamltravelling: falsehiding the active-trip view, a completely separate cause. Conflating the two delayed pinning the permission root cause.
Solution
Create the account with an explicit --admin-type both, and bake it into the idempotent make test-account target so every recreation is faithful:
test-account:
@docker exec intotheeast_grav sh -c 'test -f /var/www/html/user/accounts/$(GRAV_TEST_USER).yaml \
|| php bin/plugin login new-user -u $(GRAV_TEST_USER) -p "$(GRAV_TEST_PASS)" \
-e $(GRAV_TEST_USER)@example.test -N "Test Runner" -P b --admin-type both -s enabled -n'
Verify the resulting permissions actually include admin.login:
docker exec intotheeast_grav rm -f /var/www/html/user/accounts/testrunner.yaml
make test-account
docker exec intotheeast_grav sh -c 'grep -A6 "^access:" /var/www/html/user/accounts/testrunner.yaml'
# access:
# admin:
# login: true
# super: true
# api:
# login: true
# super: true
Why This Works
The login new-user help text spells out the axis:
--admin-type— Which admin permission type to grant when permissions include Admin:admin(classic Admin plugin,admin.*),api(Admin2,api.*), orboth. If omitted, auto-detects from which admin plugin is installed.
This site runs Admin2 only (the classic admin plugin is disabled), so auto-detect resolves to api and emits api.* alone. /gpx-manager is gated by access.admin.login: true in its page frontmatter (enforced by the Login plugin), and that check looks specifically for the admin.login permission — api.login does not satisfy it. Passing --admin-type both forces both namespaces into the account, so the admin-gated page accepts the session.
Prevention
- On Admin2-only Grav installs, always pass
--admin-type both(oradmin) tologin new-userwhen the account must reach any page gated byaccess.admin.login(e.g./gpx-manager). Auto-detect will otherwise silently give you api-only. - Assert the permission, not the exit code. After provisioning an account for admin-gated pages, check that
access.admin.loginexists in the generated YAML rather than trusting that account creation "succeeded." - Keep provisioning in one idempotent place. The
make test-accounttarget is the single source of truth;tests/global-setup.jscalls it, somake testand a barenpx playwright testboth get identical permissions. Don't hand-create the account out-of-band with different flags — that reintroduces the drift this fix removed.
Related Issues
docs/working/plans/2026-07-04-grav-2.0.4-upgrade.md— the self-contained test-account infrastructure shipped alongside the Grav 2.0.4 upgrade.- Sibling gotchas from the same 2026-07-04 Grav 2.0.4 production cutover (all surface around admin2/api but with distinct root causes):
docs/solutions/integration-issues/stale-grav-version-blocks-api-plugin-install.md(staleGRAV_VERSION→ rc core → GPM won't serve theapiplugin → login 404s) anddocs/solutions/integration-issues/grav-double-content-encoding-garbage-page.md(doubleContent-Encodingheader → garbage page). docs/solutions/architecture-patterns/dual-repo-submodule-workflow.md— accounts live in theuser/repo; thetestrunneraccount is gitignored so it never reaches production.- GPX manager auth model (
access.admin.login: truefrontmatter + Login plugin) — see the project's GPX manager notes.