Manages a local PHP dev server for end-to-end development and testing.
Binds to 0.0.0.0:8773, accessible via Caddy at
https://preauth.lyra-dev.devgnome.com.
Features:
- Self-bootstrapping: installs PHP 8.4 + extensions (including APCu,
which is critical for nonce cache, rate limiter, and session storage),
Composer, and project dependencies if missing. Survives terminal
resets/reboots.
- Enables apc.enable_cli=1 for console commands (matches Dockerfile)
- Subcommands: start, stop, status, restart
- Sets APP_SHARE_DIR to var/share for filesystem session persistence
- Clears dev cache on start
No database needed — preauth uses APCu + filesystem cache exclusively.
Port assignment: P-R-E = 7-7-3 → 8773
Security:
- Add SecurityHeadersListener (X-Content-Type-Options, X-Frame-Options,
CSP, Referrer-Policy, HSTS)
- Replace document.write() with document.documentElement.innerHTML
in login JS to avoid CSP violations
- Add CSS escaping (|e('css')) to env color values in _style.html.twig
- Document CSRF protection model: nonce serves as CSRF token for POST
form path (single-use, server-generated, 120s TTL)
- Reduce TOTP verification window from 10 periods (±5 min) to 1 (±30s)
- Remove hardcoded APP_SECRET from bin/franken.sh (now uses env or
generates random)
- Remove backup code values from debug log output
- Add .env to .gitignore
Bug fixes:
- Fix ->json access on possibly-null in LoginListener
(uses null-safe operator ?->)
- Fix validReturn() not checking false from parse_url (could cause
TypeError on malformed URLs)
- Add isHit() race condition check in AcceptListener and AllowListener
- Add try/finally in Kernel::terminate() so parent::terminate() always
runs even if persist() throws
- Add input validation to GenerateBackupCodesCommand (reject count < 1)
- Use Response::HTTP_INTERNAL_SERVER_ERROR constant in GetTotpTrait
instead of literal 500
Docker/CI:
- Explicitly install curl in Docker final image (needed for healthcheck)
- Update workflow tag pattern to v*.*.* (standardize on v-prefix)
- Extract version without v-prefix for Docker image tag
- Remove stale develop branch from CI triggers
- Fix publish.yaml git remote add to use set-url on re-runs
Code quality:
- Add declare(strict_types=1) to all interface files
- Add #[AsCommand] attribute to GenerateBackupCodesCommand
- Fix BackupCodeInterface default count to match implementation (10)
- Lowercase host before TLD lookup in DomainManager
- Expand TLD list with many missing multi-part TLDs (.com.au, .co.jp,
.com.br, .co.kr, .com.tw, .co.za, etc.) to prevent open redirect
vulnerabilities
- Disable unused Symfony sessions in framework.yaml
Tests:
- Update DomainManagerTest for corrected TLD parsing (.com.au, .co.jp,
.com.br now correctly recognized as multi-part)
- Update GetTotpTraitTest for corrected error message
- Update GenerateBackupCodesCommandTest: zero count now throws exception
- Add .dockerignore to exclude .git, vendor, var, tests, docs, .env
and other non-build files from Docker context
- Fix broken base64url padding in src/Data/Payload.php: str_pad was
a no-op because the length argument was always < string length.
Replaced with correct str_repeat approach
- Fix typo in bin/franken.sh: digtialadapt → digitaladapt
- Add comment to bin/franken.sh noting it's a dev utility
- Remove config/reference.php from git tracking (auto-generated file)
and add to .gitignore
- Fix readme.md: env.example → example.env (matches actual filename)
- Add phpunit/phpunit ^13.2, symfony/browser-kit and symfony/css-selector
to require-dev, plus the autoload-dev mapping for App\Tests- Add phpunit.dist.xml (strict deprecation/notice/warning failures,
APP_ENV=test forced) and .env.test / bin/phpunit / tests/bootstrap.php
from the PHPUnit recipe
- Add tests/Support/TotpTestHelper providing a deterministic TOTP
fixture, frozen clock and ConfigBag/cache-pool helpers
- Add 121 unit tests covering Clock, ConfigBag, Data/Payload, Enum/Scope,
MonitorCacheKeys, PersistCache, Utilities, all five Traits and the
three Service managers (BackupCode, Domain, Login)
- Fix LoginManagerTest nonce lookups to use makeCacheKey() so the cache
key matches the one the manager actually reads/writes
- Gitignore bin/.phpunit.result.cache