jgxvx/cilician
Cilician is a Laravel/PHP package that provides a lightweight, configurable foundation for common application needs. It emphasizes a clean API, straightforward setup, and simple integration into Laravel projects while keeping code maintainable and easy to work with.
Architecture fit. Cilician occupies a narrow, low-coupling position in the stack: it is an HTTP client with no persistence, no queue worker, no middleware, and no domain model beyond request/response DTOs. Whether we treat it as a generic "framework-native toolkit" (per description.md) or — as the two engineering assessments independently concluded — a thin wrapper over the Have I Been Pwned (HIBP) REST API, the architectural verdict is the same: it is a swappable transport detail that should sit behind an internal interface (BreachLookup, PasswordRangeLookup) rather than be called directly from controllers or models. The advertised Laravel surface (auto-discovered service provider, optional facade, publishable config, typed PHP 8 API, extensible contracts, PHPUnit/Pest coverage) is exactly the shape we want for a framework-native integration, and it is the main reason this is worth evaluating at all. But the advertised shape and the observed shape do not agree, and that gap is itself the finding.
Integration feasibility. Mechanically trivial: composer require, instantiate the client with an API key, call a method. The friction is entirely in the metadata:
description.md describes a general-purpose "domain-specific integrations" toolkit. Both engineering assessments read it as a HIBP client. A package whose own summary does not match its code is a documentation-quality signal, and it means the PM cannot rely on the README for scope, cost, or compliance claims.Technical risk. High risk, low reward. This is a security-adjacent integration (it handles user email addresses and an API key), and the trust surface is unattractive:
Retry-After handling, jittered backoff, hibp-api-key header injection, User-Agent requirements, Add-Padding on password ranges, response caching for breach metadata, and — critically — whether request URLs, emails, or keys are ever logged or surfaced in traces. All unverified.Key questions for the maintainer / spike.
jgxvx? What is the release cadence, and is there a stated support or disclosure policy?Stack fit. Assume a PHP 8.1+ / Laravel application. Cilician is unverified on both the Laravel-native axis and the PSR-18 axis, and the two assessments disagree on whether the framework wiring exists. Either way, we must not couple domain code to its types. The integration contract we own is:
PasswordRangeLookup::isPwned(string $password): bool — k-anonymity, keyless.BreachLookup::breachesFor(string $email): Collection — authenticated, paid key.The package, a fork of it, or a first-party client is an implementation detail behind those two interfaces. This is the single most important design decision, because it makes the build-vs-buy question reversible at any point.
Migration path — do not adopt directly. Three options, in recommended order:
Illuminate\Support\Facades\Http (or Guzzle) with explicit connect/read timeouts, retry with jittered exponential backoff, Retry-After respect, k-anonymity range handling with prefix caching, and a single config block. Test with Http::fake() against HIBP's documented test vectors covering 200, 404, 429, 503, and timeout. Estimated 1–2 engineer-days including tests — cheaper than the provenance review required to adopt an unmaintained third party.composer require. Not recommended for a security-adjacent integration on these adoption signals.Compatibility. Target PHP 8.1+ (the typed API implies 8.0+, but 8.1 is our floor) and the Laravel versions in our support matrix. Confirm no transitive dependency conflicts with our lockfile; an HTTP client that reuses the framework's own transport (or is zero-dependency Guzzle) is the ideal shape. Pin the version explicitly and mirror behind our namespace to insulate against the Codeberg repo disappearing.
Sequencing.
src/ end to end, and run the keyless Pwned Passwords endpoint (count('password123')) to confirm autoloading, transport, and error behaviour. Resolve the description-drift question here.PasswordRangeLookup, BreachLookup) and implement, with contract tests against Http::fake().HIBP_API_KEY, metrics on lookup latency and error rate, alerting on 429/5xx spikes, and a documented fail-open/fail-closed policy per call site.Maintenance. Effectively zero upstream maintenance. 0 stars and 0 dependents means no community patch flow: any bug, PHP 9 incompatibility, or HIBP API change is ours. The package also carries a nonzero chance of never being updated again. Owning ~150 lines of our own client is strictly cheaper than owning a fork of someone else's, which is the decisive economic argument against adoption.
Support. No SLA, no disclosure policy, no observable issue-tracker responsiveness, single maintainer on a non-mainstream forge. Our escalation path terminates at our own team regardless — so we should choose the option where that is true by design rather than by accident. The paid HIBP API key carries HIBP's own commercial terms; the package does not remove that cost or that contract.
Scaling. The binding constraints are upstream and architectural, not in the client:
Failure modes to design and test for.
Retry-After, add jittered exponential backoff, and cap retries. Define fail-open vs fail-closed per call site: fail open for signup password screening (log, allow, alert) and fail closed only where compliance policy explicitly demands it. Make this configurable, not hard-coded.Ramp-up. Technically trivial — an engineer unfamiliar with HIBP is productive in under an hour, and the whole integration is a 1–2 day build. The real ramp-up cost is trust and compliance onboarding: provenance review, licence confirmation, data-handling assessment, and documenting a deviation from our "prefer maintained dependencies" policy if we adopt it. That review overhead exceeds the cost of writing the client ourselves, which is why the recommendation is unambiguous.
Recommendation. Reject Cilician as a production dependency. Build a thin internal client behind PasswordRangeLookup / BreachLookup; fork and pin only if the spike shows the API shape is materially better than what we would write. Ship password screening at registration/reset first behind a flag — it is keyless, high-visibility, and directly answers enterprise security questionnaires. Revisit the package only if it gains tags, tests, a disclosure policy, and observable adoption.
How can I help you explore Laravel packages today?