Adds a disclosed, reversible attribution-integrity check (AGPL-3.0 §7(b)).
The "Vateron Media · AGPL-3.0" credit now has a single source
(AdminHelpers::getAttribution(), reused by getFooter). AttributionGuard
verifies its markers; AttributionVerificationStage runs the check on the
Admin (UI) boot profile only — admin/reseller/player panels — and calls
generateError('ATTRIBUTION_REMOVED') when the notice is gone.
Scope is deliberately UI-only: streaming is NOT gated, so removing a UI
footer cannot cut off a reseller's end-viewers (that is the licence
layer's concern). The lock is non-destructive and auto-reverses — the CLI
stays usable and restoring the notice unlocks the panel on the next
request. Enforcement is documented in README (License Enforcement).
This is a readable-PHP deterrent (a determined actor can remove the check
with the notice); tamper-resistant enforcement belongs in the compiled
xcvm_core extension (planned).
Tests: attribution markers, hasMarkers tamper detection, CLI self-skip,
UI-only wiring, error-code registration. Full suite + gates + CRAP green.
Three fixes from the automated PR review:
- ErrorResponder::respondError() used `$httpCode === null` where the legacy
generateError() used `!$rCode`; a caller passing 0 now falls through to the 404
page again instead of emitting http_response_code(0). (+ test)
- StageProfiles::for(BootContext::WebApi) now throws instead of silently building
a wrong stage list from the common prefix/suffix — WebApi boots via
WebApiBootstrap, never the kernel. (+ test)
- LegacyCoreStage reads enable_cache via SettingsManager::getBool() (the typed
getter the Web API path used), rather than the raw get(). Behaviour is
equivalent (`!` already coerces) but the intent is clearer.
Replace the fully-static XC_Bootstrap god-class with a stage pipeline so the boot
logic becomes unit-testable and the per-context sequences are explicit.
- BootState replaces the 8 static readiness flags with a value object threaded
through the pipeline; stages read/write it instead of static state.
- BootStageInterface + BootPipeline run an ordered stage list and abort loudly
on a throwing stage.
- 16 stages under Core/Bootstrap/Stage/ hold one subsystem each, extracted
verbatim from the old private methods (constants, config, flood, host, session,
database, legacy core, redis, process title, admin API, translator, admin
shutdown, status constants, admin globals, container populate, health check).
- StageProfiles builds the ordered list per context, mirroring the exact previous
sequence; BootKernel resolves options, sets up the container and runs it.
- XC_Bootstrap is now a thin BC facade delegating to BootKernel; its getters read
the returned BootState. reset() also clears EventDispatcher and the new
DatabaseFactory::reset() (a side-effect-free registry clear for test isolation).
The DB-touching contexts (Cli/Stream/Admin) still require a live MySQL and the
xcvm_core extension, so they are verified on a canary rather than in CI; the
Minimal context and the pipeline/profile composition are covered by new tests.