Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cilician Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Despite the marketing copy describing a "framework-native toolkit" with an auto-discovered service provider and facade, Cilician is a thin, framework-agnostic HTTP client for the Have I Been Pwned (HIBP) REST API. Expect to instantiate it manually — do not assume app(Cilician::class) resolves out of the box. Verify the constructor and whether it ships a facade before writing anything else.

  1. Install

    composer require jgxvx/cilician
    

    If it is not on Packagist (likely, given it's hosted on Codeberg with 0 dependents), add a VCS repository entry pinned to a commit SHA to composer.json.

  2. Get an HIBP API key from https://haveibeenpwned.com/API/Key. This is a paid, per-key subscription. Only the Pwned Passwords range endpoint works without a key. Store it in .env as HIBP_API_KEY and never commit it.

  3. Read the source first. With 0 stars and 0 dependents, src/ is the documentation. Check three things before committing to it:

    • Constructor signature — does it take a PSR-18/PSR-17 transport, Guzzle client, or build its own?
    • Exception types thrown — can you distinguish 404 (not pwned) from 429 (rate-limited) from a timeout?
    • Whether any code path logs request URLs, emails, or the API key.
  4. Smoke test against the cheapest, keyless endpoint before burning paid API calls:

    $client = new \Jgxvx\Cilician\Client(config('services.hibp.key'));
    
    // k-anonymity range query — no API key required
    $count = $client->pwnedPasswords()->count('password123');
    // Expect > 0; confirms autoloading, transport, and response parsing
    
  5. First real use case: password screening at signup and reset. Check the candidate password against the Pwned Passwords range API. Send only the 5-character SHA-1 prefix (k-anonymity — no plaintext or full hash leaves your infrastructure). Wire it into a FormRequest rule or a dedicated PasswordNotPwned validation rule. That's the highest-leverage, lowest-risk entry point and doesn't require the paid key.

Implementation Patterns

Wrap it behind your own interface. Do not let domain code reference Jgxvx\Cilician\* types directly. Define:

interface BreachLookup {
    public function breachesFor(string $email): array;
    public function isPasswordPwned(string $password): bool;
}

Implement it once with Cilician. When the package is abandoned (see Gotchas) you swap one adapter class instead of chasing call sites across the codebase.

Split the free and paid paths. The password range endpoint is free and self-contained; the breach-by-account and paste endpoints require the paid key. Model these as separate methods, separate config keys, and separate failure semantics. Many teams ship the password check first, validate the UX, then add breach lookup.

Move breach-account lookups off the request path. A registration request should never block on an authenticated HIBP call. Dispatch a queued job:

// Registration, inline — fail open, strict timeout
if (config('services.hibp.screen_passwords')) {
    try {
        $pwned = app(BreachLookup::class)->isPasswordPwned($pw);
    } catch (ConnectionException $e) {
        report($e);
        $pwned = false; // fail open, log, continue
    }
}

// Async, after the fact
ScanAccountForBreaches::dispatch($user)->onQueue('security');

Cache aggressively. Breach metadata changes slowly; Pwned Passwords prefixes are stable for long periods.

  • Cache the full SHA-1 prefix responses from the range endpoint (cache()->remember("hibp:pw:{$prefix}", now()->addDay(), ...)). The response contains hundreds of suffixes per prefix — you're amortizing a large payload across every password that shares the prefix.
  • Cache breach-by-email results with a 24h TTL. HIBP updates continuously; longer than that and you're reporting stale "not pwned" verdicts.
  • Cache negative results too — otherwise a safe-but-unlisted email hammers the API on every page load.

Feature-flag the enforcement decision. Warning vs. hard block on a pwned password is a product call, not an engineering one. Ship behind HIBP_BLOCK_PWNED_PASSWORDS=false and flip it after the warning copy is validated with users.

Test with Http::fake(). If the package uses Laravel's Http facade under the hood, the usual fakes work. If it uses its own Guzzle transport, you'll need to inject a mocked PSR-18 client. Cover at least: 200 with bodies, 404, 429 with Retry-After, 503, and a timeout. Pin the request assertions to HIBP's documented test prefix (5BAA6 → suffix 1E4C9B93F3F0682250B6CF8331B7EE68FD8 is a well-known example).

Config as a first-class concern. Even if the package ships no publishable config (as appears likely), introduce your own config/services.php entry:

'hibp' => [
    'key'      => env('HIBP_API_KEY'),
    'timeout'  => env('HIBP_TIMEOUT', 3),
    'retries'  => env('HIBP_RETRIES', 2),
    'screen_passwords' => env('HIBP_SCREEN_PASSWORDS', true),
],

This isolates every environment-specific knob and makes the "do we have a key in staging?" question a config-diff rather than a code search.

Gotchas and Tips

The description and reality disagree. The package blurb promises an auto-discovered service provider, optional facade, and publishable config. The technical assessment could not confirm any of these. Assume you're getting a bare client, not a Laravel package in the usual sense. Verify before you plan around framework helpers.

This package is effectively unmaintained. 0 stars, 0 dependents, single maintainer, hosted on Codeberg. There is no CVE history, no disclosure process, no community regression coverage. If you adopt it, treat it as source code you've adopted, not a dependency you consume — pin the commit, fork it into your org if it stays, and budget for owning it indefinitely.

Do not log emails, hashes, or the API key. The logging behavior is unverified. Before shipping, grep the source for Log::, logger(, dump(, and ray(. Add a test that asserts Log::shouldReceive('error') is never called with the email in the payload, or use a Monolog processor to scrub PII on the security channel. Error reporters (Sentry, Bugsnag) will happily capture request bodies by default — verify with a thrown exception test.

Rate limits will bite you at scale. HIBP enforces per-key limits; the free Pwned Passwords endpoint has its own throttling. Handle 429 with Retry-After respect plus jittered exponential backoff. Never retry in a tight loop. If the package doesn't expose retry configuration, wrap the call yourself or override the transport.

Fail open or fail closed — decide once, per path. Password checks at registration should fail open (allow signup, log, continue) unless policy says otherwise — you do not want an HIBP outage to block all new users. Breach-exposure reporting for enterprise customers may be the opposite: better to show "unavailable, retry" than a false "safe" verdict. Encode this explicitly in the adapter.

Enforce strict timeouts on the request path. 3s connect and 5s read are reasonable ceilings for an inline password check. Anything longer and a slow HIBP response becomes a slow registration form. If the package builds a default Guzzle client without timeouts, replace the transport.

The 404 case is not an error. HIBP returns 404 for "no breaches found" and for "password not in range data." If the client throws a generic exception on any non-2xx, you will misclassify every clean user as an error. Verify how it distinguishes 404 from 429 and 5xx, and catch NotFoundException separately — or don't trust the client and inspect the status code yourself.

Confirm k-anonymity actually holds. The whole privacy argument rests on the client sending only the 5-character SHA-1 prefix. Read the source and assert it in a test that inspects the outbound request URL/body. If it sends the full hash or the plaintext, stop — that's a compliance incident waiting to happen.

Cache the prefix space, not the password. The k-anonymity range response contains hundreds of suffixes for a given prefix. Cache the full response keyed by prefix, then look up suffixes in your own code. This cuts egress and lets you serve safe-verdicts from local state.

Verify the User-Agent header. HIBP requires a meaningful User-Agent. If the client omits it, requests may be rate-limited more aggressively or rejected. Set it explicitly via the transport if the package doesn't.

Pin the version. Whatever you do, do not track dev-main. Use a commit SHA in composer.json and a lockfile. If the maintainer force-pushes, your composer.lock preserves the exact code you tested against.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route