porro / php-core
Shared core for Porro connectors: site keys, signing, lock-file reading, sending reports and local site health checks.
Requires
- php: ^8.2
- composer-runtime-api: ^2.2
- composer/semver: ^3.4
- paragonie/sodium_compat: ^1.21|^2.0
Requires (Dev)
- laravel/pint: ^1.18
- pestphp/pest: ^3.0|^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-23 15:49:05 UTC
README
Shared core for the Porro connectors: parsing site keys, signing reports, reading composer.lock, sending reports to Porro and the site health checks the connectors show in the control panel.
You don't install this directly. It comes with a connector:
| Connector | For |
|---|---|
porro/laravel-connector |
Laravel 11–13 |
porro/filament-connector |
Filament 3–5 |
porro/statamic-connector |
Statamic 5–6 |
porro/craft-connector |
Craft 4–5 |
How reports are signed
A site key looks like porro_{site id}_{seed}. The seed is the private half of an Ed25519 key pair; Porro stores only the public half. Each report is signed over:
{timestamp}\n{nonce}\n{sha256 of body}
and sent with X-Porro-Site, X-Porro-Timestamp, X-Porro-Nonce and X-Porro-Signature headers. Porro rejects a report outside a 5-minute window or if it has seen the nonce before. If ext-sodium isn't available, paragonie/sodium_compat provides it in pure PHP.
What a report says about the server
Each report includes a system block, so Porro can flag an end-of-life OS and match Ubuntu and Debian security notices against the server's installed packages:
os:ID,VERSION_ID,VERSION_CODENAMEandPRETTY_NAMEfrom/etc/os-releasekernel: the running kernel (php_uname('r'))reboot_required: whether/var/run/reboot-requiredexists (Debian and Ubuntu)fingerprint: an HMAC-SHA256 of/etc/machine-idunder a Porro-specific key, so sites on one server share one record. The machine id itself is never sent.packages: the installed packages from/var/lib/dpkg/status(statusinstall ok installedorhold ok installed), each withname,version,source(the source package Ubuntu and Debian notices name; the package's own name when it has noSource:field) andarch, plussource_versionwhen the source's version differs from the package's, andoriginwhen the package declares onepackages_hash: a SHA-256 of that list. The list itself is only sent when Porro asks for it, because the hash is new to it (the same idea as thecomposer.lockhash):Client::send()seesneed_system_packagesin Porro's reply and sends the report once more with the list, so connectors don't need to do anythingunattended_upgrades.enabled: whetherAPT::Periodic::Unattended-Upgradeis set to anything but"0"in/etc/apt/apt.conf.d(20auto-upgradeson a stock server) and theunattended-upgradespackage is installedunattended_upgrades.last_run_at: when it last started, from the end of/var/log/unattended-upgrades/unattended-upgrades.log, in ISO 8601 with the server's offset. That log is often readable by root only, in which case this isnull
These are plain file reads, with no exec. Anything the PHP process can't read, such as files outside open_basedir on shared hosting, is left out: null, or false for reboot_required.
Site health checks
Porro\Core\Health runs the checks behind each connector's Site health panel, on the site itself and without a Porro account:
EndOfLife: PHP, Laravel, Craft and Statamic versions past, or within 90 days of, the end of security support. Dates come from endoflife.date, fetched once a day, with a copy indata/endoflife.jsonfor when it can't be reached.Advisories: known security advisories for the packages incomposer.lock, from Packagist's security advisories API (the onecomposer audituses). Only package names are sent; versions are matched on the site withcomposer/semver. High and critical advisories rank like Porro's "Act now"; low and medium ones, and anything in a dev dependency, as notices.Updates: the newest release of a few key packages on the major version the site runs, from Packagist.ReportChecks: debug mode in production, failed jobs, pending migrations, a stuck queue, stale backups, failing health checks and whether cron runs, from what the connector already collects. The same rules Porro runs on each report.PorroFindings: once connected, the site's open findings in Porro and its server's, read fromGET /api/v1/findingswith a request signed by the site key (over an empty body).SiteHealthputs them together for the panel, leaving out local issues Porro already raises once its findings are in.
Network results go through the connector's own cache via Sources (failures for ten minutes), so a control panel page rarely waits on the network and an unreachable API never slows every page.
Developing
composer install
composer test
Security
Please email security@porro.dev rather than opening an issue. We reply within one working day.
License
MIT