africc/php-epp2
A PHP client library for the Extensible Provisioning Protocol (EPP), the standard used by domain registrars. It builds and parses EPP XML requests over TLS, with support for domain, contact, and host commands plus common registry extensions.
Architecture fit
africc/php-epp2 is a synchronous, high-level EPP client — it builds RFC 5730-compliant XML command frames, pushes them over a TCP/TLS socket, and parses the response into objects. That shape fits a domain-provisioning back end (registrar, reseller platform, brand-protection/portfolio manager) almost exactly: EPP is a stateful, one-command-at-a-time, per-registry session protocol, and the library models it as such rather than pretending it's REST.
The critical fit question is where it sits in our stack. It is blocking I/O. In a Laravel request cycle — and especially under Octane/Swoole/RoadRunner — a 150–500ms TLS round trip per command will starve workers and blow p99 latency. Architecturally this belongs behind a queued job with a small, per-registry worker pool, not in a controller. If we intend to expose synchronous "check availability" lookups to users, that needs a separate low-latency path (cached/streaming) or strict timeouts.
Second fit question is scope: base EPP only gets us create/check/info/update/transfer/renew/delete. Real registries require extension namespaces — fee:0.12 (pricing), launch:1.0 (sunrise/claims), secDNS:1.1 (DNSSEC), rgp (restore), IDN variants, and registry-specific quirks (Verisign, Nominet, Identity Digital, PIR each differ). The library's extension coverage is the single highest-uncertainty item and must be verified against our target registry list before any commitment.
Integration feasibility
Favorable: Composer-installable, no exotic runtime, standard OpenSSL transport, and a companion Symfony bundle (africc/epp-registry-bundle) exists if we need DI wiring — we would not use that in Laravel, but its existence signals the API is at least framework-agnostic. Integration is a thin service wrapper: config-driven credential/endpoint registry, a connection manager, request/response DTOs, and a job layer.
Unfavorable: the last tagged release is 2022-10-15. As of today that is roughly three PHP minor versions of drift. We must confirm PHP 8.2/8.3/8.4 compatibility, dependency constraints (symfony/*, psr/*), and whether a PHP 8.4 deprecation sweep has ever been run. A stale dependency graph in a modern Laravel app is a routine source of composer update friction.
Technical risk
| Risk | Severity | Note |
|---|---|---|
| GPL-3.0 license | High | Copyleft. Fine for internal/SaaS use where we don't distribute the binary; problematic if this ships in an on-prem appliance, SDK, or distributed agent. Requires legal sign-off before adoption — see containment option below. |
| Extension coverage gaps | High | Must build a registry × extension matrix; likely partial support for fee/launch. Gaps mean hand-rolled XML anyway. |
| Bus factor / maintenance | High | 54 stars, single-maintainer pattern, no visible security policy or CVE process. Assume fork-and-own. |
| Provisioning idempotency | High | Duplicate create on a retry is a billable, customer-visible error. The library won't solve this; we must build an idempotency key + reconciliation layer around it. |
| XML parsing safety | Medium | Verify LIBXML_NONET / entity-loading behavior — XXE on registry responses is unlikely but non-zero, and auditors will ask. |
| Blocking transport | Medium | Mitigable by queue architecture; not mitigable inside a request. |
Key questions
fee:0.12, launch:1.0, secDNS:1.1, and IDN extensions out of the box? For which registries is it field-proven?master vs. tags, and is there an active fork we should track instead?hello/reconnect? A desynchronized EPP session that silently reuses a dead socket is a classic production incident.Stack fit
Laravel-native, but layered:
config/registries.php — endpoint, port, TLS policy, credentials (secrets via Vault/SSM, not .env), registrar client ID.RegistryClientInterface — our own contract with our own DTOs (DomainCheckResult, ProvisionResult). The africc client is one implementation behind it. This is the single most valuable design decision here: it makes the library replaceable.EppConnectionManager — one connection per registry per worker, with explicit connect/greeting/reconnect/close semantics and a health check.CheckDomainJob, RegisterDomainJob, RenewDomainJob, SyncDomainJob, plus a scheduled PollMessagesJob. EPP requires us to poll the registry queue to drain async notifications; if we don't, the registry's queue fills and we lose transfer/expiry events.Migration path
Adapter-first, vendor-second. Ship the interface + a null/fake implementation, wire the africc implementation behind a feature flag per registry, and run it in OT&E until the registry-side event stream matches our expectations. If the GPL proves unworkable, the same interface is implemented by a private fork (still GPL, still contained) or by a commercial registrar API. Either way we are not rewriting application code.
Compatibility
Pin exact versions and commit composer.lock. Verify PHP matrix in CI. If patches are needed and upstream is unresponsive, fork to a private VCS repo — this is likely, given the release cadence, so budget for it rather than discovering it mid-migration.
GPL containment
If legal will not accept GPL-3.0 in the main application, run EPP provisioning as a separate internal service (small PHP daemon or queue worker) that communicates with the Laravel app over an internal HTTP/queue boundary. GPL obligations attach to that service; the proprietary app remains separate. This is a well-trodden pattern and costs roughly one extra deployment unit.
Sequencing
Maintenance
Treat this as owned code from day one, not a dependency. Realistic budget: 0.5 FTE for the first two quarters (fork, patch, extension work, hardening), settling to ~0.15–0.25 FTE ongoing — mostly PHP minor-version compatibility and registry extension revisions. Budget a standing quarterly review of upstream commits and a decision log on whether to keep merging.
Support
Community-only. No SLA, no vendor escalation, GitHub issues with unpredictable response. Registry-side incidents (TLS certificate rotation, endpoint maintenance, schema updates) become our incidents. We need a documented runbook for each registry endpoint and named on-call ownership.
Scaling
The library is not the bottleneck — the registry is. EPP is connection-oriented and serialized per session, and registries cap concurrent connections per registrar account (often single digits). Design accordingly:
Failure modes
| Failure | Impact | Mitigation |
|---|---|---|
| Session desync / truncated response | Commands fail or, worse, appear to succeed | Close socket, reconnect, reconcile state via info before retry |
Duplicate create on retry |
Double billing / conflicting registration | Idempotency keys + pre-flight check, never blind-retry mutations |
| TLS handshake / cert expiry | Full registry outage from our side | Cert-expiry monitoring, alert at 30/14/7 days |
| Idle timeout mid-batch | Batch job stalls | Explicit session TTL, reconnect between batches |
| Unpolled message queue | Missed transfers, silent expiry, registry-side queue overflow | Scheduled poll job with backlog alerting |
| IP not whitelisted | Auth failures that look like credential bugs | Config validation at deploy, not at first request |
| Blocking I/O in web tier | Worker starvation under load | Architectural constraint: EPP only via queue |
Ramp-up
EPP is not hard to learn but it is unforgiving: engineers need fluency in RFC 5730–5734 plus the specific registry's implementation guide, and OT&E credentials are typically gated behind a registrar agreement. Expect 2–3 weeks for a strong PHP engineer to be productive in the sandbox, and 6–8 weeks before they can be trusted with production provisioning unsupervised. The scarce skill is not PHP — it is multi-registry EPP operational scar tissue. Given a 54-star package with a 2022 tag, that knowledge will live with us, not with the upstream maintainer, and should be deliberately cross-trained across at least two engineers.
How can I help you explore Laravel packages today?