KELDRIN REMOTE PORTAL SIGNED UPDATE PACKAGE — SCHEMA 2

Trust: ECDSA P-256 with SHA-256. portal-manifest.sig is standard Base64 of a
DER/RFC 3279 ECDSA signature over the exact UTF-8 bytes of
portal-manifest.json. SigningKeyId is lowercase SHA-256 of the DER
SubjectPublicKeyInfo public key. Production private keys remain offline.

The ZIP is at most 2 MiB compressed and expanded, contains the exact reviewed
payload allowlist plus portal-manifest.json and portal-manifest.sig, and is
ordered by ordinal entry name. The manifest never lists itself or its detached
signature. Its compact JSON properties are ordered exactly as follows:
PackageType, SchemaVersion, DisplayName, Version, MinimumCurrentVersion,
MaximumCurrentVersion, UpdaterProtocol, SigningAlgorithm, SigningKeyId, Files.
Each Files row is ordered Path, Length, Sha256, Install.

PackageType: KeldrinRemotePortalDeployment
SchemaVersion: 2
DisplayName: Keldrin Remote Portal
UpdaterProtocol: keldrin-portal-update-v1
SigningAlgorithm: ECDSA-P256-SHA256

Every public PHP front controller, .htaccess, lib/bootstrap.php and
lib/maintenance.php is an immutable protocol-v1 boundary with Install=false.
The installer verifies those packaged files exactly match the installed
boundary before loading or activating mutable code. A release that must change
that boundary requires a deliberately new updater protocol/manual bootstrap;
it cannot silently replace the recovery or request-locking code online.

All data/ entries have Install=false. Runtime stores, users, passwords, API
keys, audit/replay/command/presence state, trusted-proxy policy and the private
external-target policy are forbidden from the package and remain untouched.