The four remaining checks are all deliberately out of scope — three need .gitea/workflows/ changes (blocked by the pre-receive hook, see below) and one needs a container build test.
What changed
PHP toolchain (§1)
require.php: >=8.4 → ^8.5. The old constraint also permitted PHP 9, which isn't a promise we can keep.
config.platform pinned to 8.5.0.
composer.lock regenerated with --lock: content-hash + platform-overrides only, zero dependency version movement.
friendsofphp/php-cs-fixer: * → ^3.95. A wildcard meant CI wasn't reproducible.
PHPStan (§2.2)
Vendored the shared phpstan.neon.dist (level 6) plus a generated baseline. 187 errors are captured, not fixed — the baseline should only shrink from here. Ratcheting the level up is a follow-up.
Code style (§8.2)
Vendored the shared .php-cs-fixer.dist.php (@Symfony + @Symfony:risky + declare_strict_types) and applied it: 59 of 67 files reformatted.
I verified this is formatting, not behaviour: all 313 tests pass afterwards, every in_array() already passed strict: true, and the rest is @Symfony:risky idiom (yoda conditions, \count(), self:: over the class name).
Only three files gained a declare(strict_types=1): config/bundles.php, config/preload.php, tests/bootstrap.php. preload.php is inert (not referenced by the Caddyfile or Dockerfile).
Repository layout (§4.4)
docs/{Caddyfile,compose.yaml,example.env} → docs/examples/, with example.env becoming the conventional .env.example. GUIDING-LIGHT §4.3 already cites preauth as the one repo doing this correctly — it just needed renaming.
Updated the four readme.md references and a stale comment in compose.yaml.
docs/v1.1-plan.md left alone on purpose: it's a historical plan recording what was decided at the time, not live documentation.
preauth was the one app already past the font-size precondition — controls render at 21.6px (0.9em × 24px), well over the 16px auto-zoom threshold. So the zoom lock was suppressing a symptom that doesn't exist here, while genuinely blocking pinch-zoom on Android. The doc says exactly this: "preauth: already fine at 21.6px — just remove maximum-scale=1 so Android users can zoom."
Conformance tooling (§8.2)
Vendored .ci/conformance.sh + .ci/css-control-size.py so the check runs from a checkout instead of fetching from the LAN-only private/ci.
.editorconfig synced from the version that retains the [Caddyfile] indent_style = tab rule (see dependency below).
Verification
Gate
Result
vendor/bin/phpunit
OK (313 tests, 738 assertions)
vendor/bin/php-cs-fixer --dry-run
0 of 66 files
vendor/bin/phpstan analyse
No errors (187 baselined)
composer validate --strict --no-check-publish
valid
composer audit
no advisories
./.ci/conformance.sh --profile=auth-gateway
30/34
Dependencies and deliberate omissions
Depends on private/ci#1 — the [Caddyfile] rule in the shared .editorconfig. Without that merged first, syncing this repo's .editorconfig would re-drop the tab rule. I generated this branch's copy from the fixed version.
Not included — blocked by the .gitea/workflows/ pre-receive hook.ci-composer-audit, ci-composer-validate and ci-reusable-workflows all need workflow changes, and workflow files may only arrive via a trusted ref (main / v*). The four replacement caller files are staged in the working tree but not committed (191 lines → 78), validated against the shared workflows' declared inputs and secrets. They need to be applied to main directly.
Not included — needs a container test.dockerfile-nonroot (§6.4). Adding USER to an image declaring VOLUME ["/config", "/data"] changes volume ownership; a drive-by edit here risks breaking a running auth gateway. Separate change, with an actual build and run.
Note on CI behaviour
This PR touches no workflow files, so preauth's existing CI keeps running unchanged. Once the caller files land on main, ci-composer-audit and ci-composer-validate come for free from the shared workflow — but only if the cross-repo uses: form actually resolves. AGENTS.md in context-shuttle and task-loom records that it has never worked on this instance (workflow registers as active, zero runs appear). That needs proving before the swap, or preauth loses CI silently.
Phase 1 + 3 + 5 of the Guiding Light rollout for preauth: shared leaf configs, PHP version declarations, and the viewport fix.
**Conformance: 18/34 → 30/34** (`profile: auth-gateway`)
The four remaining checks are all deliberately out of scope — three need `.gitea/workflows/` changes (blocked by the pre-receive hook, see below) and one needs a container build test.
## What changed
**PHP toolchain (§1)**
- `require.php`: `>=8.4` → `^8.5`. The old constraint also permitted PHP 9, which isn't a promise we can keep.
- `config.platform` pinned to `8.5.0`.
- `composer.lock` regenerated with `--lock`: content-hash + `platform-overrides` only, **zero dependency version movement**.
- `friendsofphp/php-cs-fixer`: `*` → `^3.95`. A wildcard meant CI wasn't reproducible.
**PHPStan (§2.2)**
- Vendored the shared `phpstan.neon.dist` (level 6) plus a generated baseline. **187 errors are captured, not fixed** — the baseline should only shrink from here. Ratcheting the level up is a follow-up.
**Code style (§8.2)**
- Vendored the shared `.php-cs-fixer.dist.php` (`@Symfony` + `@Symfony:risky` + `declare_strict_types`) and applied it: **59 of 67 files reformatted**.
- I verified this is formatting, not behaviour: all 313 tests pass afterwards, every `in_array()` already passed `strict: true`, and the rest is `@Symfony:risky` idiom (yoda conditions, `\count()`, `self::` over the class name).
- Only three files gained a `declare(strict_types=1)`: `config/bundles.php`, `config/preload.php`, `tests/bootstrap.php`. `preload.php` is inert (not referenced by the Caddyfile or Dockerfile).
**Repository layout (§4.4)**
- `docs/{Caddyfile,compose.yaml,example.env}` → `docs/examples/`, with `example.env` becoming the conventional `.env.example`. GUIDING-LIGHT §4.3 already cites preauth as the one repo doing this correctly — it just needed renaming.
- Updated the four `readme.md` references and a stale comment in `compose.yaml`.
- `docs/v1.1-plan.md` left alone on purpose: it's a historical plan recording what was decided at the time, not live documentation.
**Licence and security policy (§7)**
- Added `LICENSE` (uniform MIT, matching `composer.json`).
- Added `SECURITY.md` describing the actual threat model rather than boilerplate.
**Mobile accessibility (§3.3a)**
- `templates/base.html.twig`: dropped `maximum-scale=1`, added `viewport-fit=cover`.
preauth was **the one app already past the font-size precondition** — controls render at 21.6px (`0.9em` × 24px), well over the 16px auto-zoom threshold. So the zoom lock was suppressing a symptom that doesn't exist here, while genuinely blocking pinch-zoom on Android. The doc says exactly this: *"preauth: already fine at 21.6px — just remove `maximum-scale=1` so Android users can zoom."*
**Conformance tooling (§8.2)**
- Vendored `.ci/conformance.sh` + `.ci/css-control-size.py` so the check runs from a checkout instead of fetching from the LAN-only `private/ci`.
- `.editorconfig` synced from the version that retains the `[Caddyfile] indent_style = tab` rule (see dependency below).
## Verification
| Gate | Result |
|---|---|
| `vendor/bin/phpunit` | **OK (313 tests, 738 assertions)** |
| `vendor/bin/php-cs-fixer --dry-run` | 0 of 66 files |
| `vendor/bin/phpstan analyse` | No errors (187 baselined) |
| `composer validate --strict --no-check-publish` | valid |
| `composer audit` | no advisories |
| `./.ci/conformance.sh --profile=auth-gateway` | **30/34** |
## Dependencies and deliberate omissions
**Depends on `private/ci#1`** — the `[Caddyfile]` rule in the shared `.editorconfig`. Without that merged first, syncing this repo's `.editorconfig` would re-drop the tab rule. I generated this branch's copy from the fixed version.
**Not included — blocked by the `.gitea/workflows/` pre-receive hook.** `ci-composer-audit`, `ci-composer-validate` and `ci-reusable-workflows` all need workflow changes, and workflow files may only arrive via a trusted ref (`main` / `v*`). The four replacement caller files are **staged in the working tree but not committed** (191 lines → 78), validated against the shared workflows' declared inputs and secrets. They need to be applied to `main` directly.
**Not included — needs a container test.** `dockerfile-nonroot` (§6.4). Adding `USER` to an image declaring `VOLUME ["/config", "/data"]` changes volume ownership; a drive-by edit here risks breaking a running auth gateway. Separate change, with an actual build and run.
## Note on CI behaviour
This PR touches no workflow files, so preauth's existing CI keeps running unchanged. Once the caller files land on `main`, `ci-composer-audit` and `ci-composer-validate` come for free from the shared workflow — but only if the cross-repo `uses:` form actually resolves. `AGENTS.md` in `context-shuttle` and `task-loom` records that it has never worked on this instance (workflow registers as active, zero runs appear). **That needs proving before the swap**, or preauth loses CI silently.
Brings preauth from 18/34 to 30/34 conformance (auth-gateway profile). The
remaining four checks all depend on files this branch cannot change (see below).
PHP toolchain (§1)
- require.php >=8.4 -> ^8.5, and pin config.platform to 8.5.0. The old
constraint also permitted PHP 9, which is not a promise we can keep.
composer.lock regenerated with --lock: content-hash + platform-overrides
only, zero dependency version movement.
- friendsofphp/php-cs-fixer * -> ^3.95. A wildcard meant CI was not
reproducible.
PHPStan (§2.2)
- vendor the shared phpstan.neon.dist (level 6) + a generated baseline.
187 errors are captured rather than fixed; the baseline should only shrink
from here.
- add phpstan/phpstan:^2.1 to require-dev.
Code style (§8.2)
- vendor the shared .php-cs-fixer.dist.php (@Symfony + @Symfony:risky +
declare_strict_types) and apply it: 59 of 67 files reformatted.
- Verified this is a formatting change, not a behaviour change: all 313 tests
pass after the reformat, all in_array() calls already passed strict=true,
and the remaining edits are @Symfony:risky idiom (yoda conditions, \count(),
self:: over the class name).
Repository layout (§4.4)
- docs/{Caddyfile,compose.yaml,example.env} -> docs/examples/, with
example.env becoming the conventional .env.example. This is the layout
GUIDING-LIGHT already cites preauth as doing correctly — it just needed
renaming.
- update the four readme.md references and a stale compose.yaml comment.
- docs/v1.1-plan.md references are left alone deliberately: it is a historical
plan recording what was done at the time, not live documentation.
Licence and security policy (§7)
- add LICENSE (uniform MIT, matching composer.json).
- add SECURITY.md describing the actual threat model: per-request
allow/intercept, no caching of the login flow, app-set security headers,
TOTP, and the fact that REMOTE_USER is trusted input.
Mobile accessibility (§3.3a)
- templates/base.html.twig: drop maximum-scale=1 and add viewport-fit=cover.
preauth was the one app already past the font-size precondition (controls
render at 21.6px = 0.9em x 24px), so removing the lock is safe here and
restores pinch-zoom for Android users.
Conformance tooling (§8.2)
- vendor .ci/conformance.sh and .ci/css-control-size.py so the check runs
from a checkout rather than fetching from the LAN-only private/ci.
- .editorconfig synced from the version that keeps the Caddyfile tab rule.
Not included (blocked by the .gitea/workflows pre-receive hook):
- ci-composer-audit, ci-composer-validate, ci-reusable-workflows.
Workflow files may only change via a trusted ref, so the caller files are
staged but not committed.
Also not included: dockerfile-nonroot (§6.4). Adding USER to an image with
VOLUME [/config, /data] changes volume ownership and needs an actual container
build/run to verify, so it goes in its own change.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Phase 1 + 3 + 5 of the Guiding Light rollout for preauth: shared leaf configs, PHP version declarations, and the viewport fix.
Conformance: 18/34 → 30/34 (
profile: auth-gateway)The four remaining checks are all deliberately out of scope — three need
.gitea/workflows/changes (blocked by the pre-receive hook, see below) and one needs a container build test.What changed
PHP toolchain (§1)
require.php:>=8.4→^8.5. The old constraint also permitted PHP 9, which isn't a promise we can keep.config.platformpinned to8.5.0.composer.lockregenerated with--lock: content-hash +platform-overridesonly, zero dependency version movement.friendsofphp/php-cs-fixer:*→^3.95. A wildcard meant CI wasn't reproducible.PHPStan (§2.2)
phpstan.neon.dist(level 6) plus a generated baseline. 187 errors are captured, not fixed — the baseline should only shrink from here. Ratcheting the level up is a follow-up.Code style (§8.2)
.php-cs-fixer.dist.php(@Symfony+@Symfony:risky+declare_strict_types) and applied it: 59 of 67 files reformatted.in_array()already passedstrict: true, and the rest is@Symfony:riskyidiom (yoda conditions,\count(),self::over the class name).declare(strict_types=1):config/bundles.php,config/preload.php,tests/bootstrap.php.preload.phpis inert (not referenced by the Caddyfile or Dockerfile).Repository layout (§4.4)
docs/{Caddyfile,compose.yaml,example.env}→docs/examples/, withexample.envbecoming the conventional.env.example. GUIDING-LIGHT §4.3 already cites preauth as the one repo doing this correctly — it just needed renaming.readme.mdreferences and a stale comment incompose.yaml.docs/v1.1-plan.mdleft alone on purpose: it's a historical plan recording what was decided at the time, not live documentation.Licence and security policy (§7)
LICENSE(uniform MIT, matchingcomposer.json).SECURITY.mddescribing the actual threat model rather than boilerplate.Mobile accessibility (§3.3a)
templates/base.html.twig: droppedmaximum-scale=1, addedviewport-fit=cover.preauth was the one app already past the font-size precondition — controls render at 21.6px (
0.9em× 24px), well over the 16px auto-zoom threshold. So the zoom lock was suppressing a symptom that doesn't exist here, while genuinely blocking pinch-zoom on Android. The doc says exactly this: "preauth: already fine at 21.6px — just removemaximum-scale=1so Android users can zoom."Conformance tooling (§8.2)
.ci/conformance.sh+.ci/css-control-size.pyso the check runs from a checkout instead of fetching from the LAN-onlyprivate/ci..editorconfigsynced from the version that retains the[Caddyfile] indent_style = tabrule (see dependency below).Verification
vendor/bin/phpunitvendor/bin/php-cs-fixer --dry-runvendor/bin/phpstan analysecomposer validate --strict --no-check-publishcomposer audit./.ci/conformance.sh --profile=auth-gatewayDependencies and deliberate omissions
Depends on
private/ci#1— the[Caddyfile]rule in the shared.editorconfig. Without that merged first, syncing this repo's.editorconfigwould re-drop the tab rule. I generated this branch's copy from the fixed version.Not included — blocked by the
.gitea/workflows/pre-receive hook.ci-composer-audit,ci-composer-validateandci-reusable-workflowsall need workflow changes, and workflow files may only arrive via a trusted ref (main/v*). The four replacement caller files are staged in the working tree but not committed (191 lines → 78), validated against the shared workflows' declared inputs and secrets. They need to be applied tomaindirectly.Not included — needs a container test.
dockerfile-nonroot(§6.4). AddingUSERto an image declaringVOLUME ["/config", "/data"]changes volume ownership; a drive-by edit here risks breaking a running auth gateway. Separate change, with an actual build and run.Note on CI behaviour
This PR touches no workflow files, so preauth's existing CI keeps running unchanged. Once the caller files land on
main,ci-composer-auditandci-composer-validatecome for free from the shared workflow — but only if the cross-repouses:form actually resolves.AGENTS.mdincontext-shuttleandtask-loomrecords that it has never worked on this instance (workflow registers as active, zero runs appear). That needs proving before the swap, or preauth loses CI silently.Brings preauth from 18/34 to 30/34 conformance (auth-gateway profile). The remaining four checks all depend on files this branch cannot change (see below). PHP toolchain (§1) - require.php >=8.4 -> ^8.5, and pin config.platform to 8.5.0. The old constraint also permitted PHP 9, which is not a promise we can keep. composer.lock regenerated with --lock: content-hash + platform-overrides only, zero dependency version movement. - friendsofphp/php-cs-fixer * -> ^3.95. A wildcard meant CI was not reproducible. PHPStan (§2.2) - vendor the shared phpstan.neon.dist (level 6) + a generated baseline. 187 errors are captured rather than fixed; the baseline should only shrink from here. - add phpstan/phpstan:^2.1 to require-dev. Code style (§8.2) - vendor the shared .php-cs-fixer.dist.php (@Symfony + @Symfony:risky + declare_strict_types) and apply it: 59 of 67 files reformatted. - Verified this is a formatting change, not a behaviour change: all 313 tests pass after the reformat, all in_array() calls already passed strict=true, and the remaining edits are @Symfony:risky idiom (yoda conditions, \count(), self:: over the class name). Repository layout (§4.4) - docs/{Caddyfile,compose.yaml,example.env} -> docs/examples/, with example.env becoming the conventional .env.example. This is the layout GUIDING-LIGHT already cites preauth as doing correctly — it just needed renaming. - update the four readme.md references and a stale compose.yaml comment. - docs/v1.1-plan.md references are left alone deliberately: it is a historical plan recording what was done at the time, not live documentation. Licence and security policy (§7) - add LICENSE (uniform MIT, matching composer.json). - add SECURITY.md describing the actual threat model: per-request allow/intercept, no caching of the login flow, app-set security headers, TOTP, and the fact that REMOTE_USER is trusted input. Mobile accessibility (§3.3a) - templates/base.html.twig: drop maximum-scale=1 and add viewport-fit=cover. preauth was the one app already past the font-size precondition (controls render at 21.6px = 0.9em x 24px), so removing the lock is safe here and restores pinch-zoom for Android users. Conformance tooling (§8.2) - vendor .ci/conformance.sh and .ci/css-control-size.py so the check runs from a checkout rather than fetching from the LAN-only private/ci. - .editorconfig synced from the version that keeps the Caddyfile tab rule. Not included (blocked by the .gitea/workflows pre-receive hook): - ci-composer-audit, ci-composer-validate, ci-reusable-workflows. Workflow files may only change via a trusted ref, so the caller files are staged but not committed. Also not included: dockerfile-nonroot (§6.4). Adding USER to an image with VOLUME [/config, /data] changes volume ownership and needs an actual container build/run to verify, so it goes in its own change.