cr/hashcli
cr/hashcli provides a lightweight command-line interface for hashing and verifying values in Laravel and PHP projects. Generate secure hashes, check existing hashes, and streamline password or token workflows directly from your terminal.
Architecture fit. cr/hashcli is a standalone PHP CLI binary, not a Laravel library. It has no service provider, facade, config surface, or Artisan registration — integration happens via composer require --dev plus shelling out to vendor/bin/hashcli. That makes it a fit for out-of-process developer and ops workflows (seeders, deploy scripts, CI integrity checks, local fixture generation) and a poor fit for request-path hashing, queue workers doing high-volume hashing, or anything on the authentication critical path. The right architectural framing is "internal developer tool wrapped behind a HashCliAdapter interface," not "dependency added to the application kernel." Native Hash::make() / hash() already cover in-app needs; this package only earns a slot where a named shell command is meaningfully more ergonomic than php -r or Tinker.
Integration feasibility. Low effort in the happy path. Composer install is standard, and the binary surface is small enough that --help resolves any ambiguity in a few minutes. The real integration work is around the tool, not in it: process escaping, stdin vs. argv handling (argv is visible in ps, so piping secrets is mandatory), output parsing discipline (prefer exit codes and machine-readable output over scraping human text), and fixture-based tests that pin behavior. Expect the wrapper and its tests to cost more than the tool itself.
Technical risk. Two distinct categories. Code risk is probably low — it's described as wrapping native PHP hashing (hash(), password_hash(), password_verify()), and if that's literally all it does, there's little to go wrong cryptographically. This must be verified by reading the source, not assumed; any bespoke crypto logic is an immediate stop. Adoption risk is high and asymmetric: 2 stars, 0 dependents, score 0.14, opportunity 22.58, no visible release cadence, single maintainer. There is no community signal to inherit, no ecosystem stress-testing, and no one to escalate to. For a security-adjacent tool, the supply-chain question — "do we want an unaudited, unmaintained dependency in a workflow that touches credentials and token generation?" — answers itself. The blast radius if misapplied (someone uses it for real password storage, or leaks a secret via argv) is non-trivial relative to the half-day it saves.
Key questions. Which algorithms are exposed — generic hash() digests, password_hash() outputs, or both? Does it support stdin and file-input (hash_file-style streaming) or only string args? Which PHP/Laravel versions and extensions are required, and is Composer install the only distribution path? Is output deterministic and machine-parseable, with reliable nonzero exit codes on failure? Is there a test suite, a security policy, a declared license, and any commit activity in the last 12 months? Who owns this internally in 18 months when it breaks?
Verdict from a TPM lens: evaluate-and-decline as a Composer dependency. Treat the source as a reference implementation at most; if the ergonomics are genuinely wanted, vendor a pinned copy into the internal toolbox or — better — write the ~30-line internal equivalent and own it.
Stack fit. Compatible with any Laravel/PHP stack where the CLI runtime matches the application runtime (same PHP major/minor, same extensions). The appropriate integration shape is an internal wrapper: an HashCliAdapter (or equivalent) that owns process invocation, escaping, timeouts, and output validation, so the underlying implementation can be swapped for native hash() / password_hash() without touching call sites. Do not sprinkle shell_exec('vendor/bin/hashcli ...') through seeders and deploy scripts.
Migration path. Start with a single non-critical use case — a scheduled artifact-checksum job or a fixture-generation helper — pinned to a specific version or commit. Instrument it, confirm output stability, and write tests against known-answer vectors. If it holds, expand to batch work behind the adapter. If it's abandoned or a PHP upgrade breaks it, the adapter boundary means the replacement (native PHP or symfony/password-hasher) is a one-file change. Do not attempt a big-bang rollout, and do not sequence this anywhere near authentication, session tokens, or customer-facing flows.
Compatibility. Verify before committing: supported PHP versions, required extensions (e.g., hash, sodium, openssl), OS/container support, binary path behavior under vendor/bin on Linux vs. macOS vs. Windows, and whether Laravel's Hash config (driver, bcrypt rounds, Argon2 params) is honored or bypassed. The trap here is divergence: the CLI's notion of a correct hash must match the application's, or you'll generate fixtures and seeded credentials the app can't verify. Test this explicitly with a round-trip (Hash::check() on CLI output and vice versa). Also confirm output encoding is stable across PHP versions — bcrypt strings are, but any custom digest formatting may not be.
Sequencing. (1) Inventory the actual use cases and confirm native PHP one-liners don't already cover them. (2) Security/source review of the package. (3) Isolated pilot in one non-critical command. (4) Build the adapter, escaping, timeout, and fixture tests. (5) Package into CI/container images with a pinned version. (6) Feature-flag rollout to additional commands/jobs. (7) Document the runbook and the pre-agreed fallback. Parallel track: draft the internal adoption-bar policy for security-adjacent dependencies using this package as the worked example — that policy is likely more valuable than the tool.
Maintenance. Low code surface, low community support — the worst combination for a dependency, because the cost of carrying it is entirely in upkeep you can't defer to upstream. Assign a named internal owner, pin the version, run composer audit on schedule, watch advisories, and refresh container images. Budget for the realistic outcome: an internal fork or a swap to native PHP within a year. If the answer to "who fixes this in 18 months?" is "we do," skip the dependency and write the 30 lines now — the fork is cheaper before it's urgent.
Support. Assume self-support. Zero dependents and minimal stars means no ecosystem validation, no Stack Overflow corpus, no one else hitting the bug first. Documentation, tests, and an escalation path are all internal artifacts. Ramp-up for a developer is measured in hours (it's a small CLI); the real onboarding cost is the security review and the process-wrapping conventions, which is a moderate, one-time investment.
Scaling. Hashing is CPU-bound, and this adds process-startup overhead per invocation on top. Fine for one-off developer tasks, seeders, and low-frequency scheduled jobs. Not fine for batch or latency-sensitive work without deliberate design: queue workers with bounded concurrency, streaming file input rather than loading into memory, and timeouts on every invocation. For sustained throughput, native PHP functions in-process or a dedicated hashing service beat shelling out to a CLI on every call — the process boundary is pure overhead once volume rises.
Failure modes. Missing or misplaced binary after a Composer update; permission errors in container images; PHP version or extension mismatch between CLI and app; shell-injection via unescaped arguments; secrets leaking through argv (visible in ps) or through logs capturing command lines; timeouts on large files; memory exhaustion on non-streaming input; encoding mismatches producing hashes the app can't verify; silent failures if exit codes aren't checked and human-readable output is parsed instead. Mitigations are standard but non-optional: always pipe secrets via stdin, always escape, always check exit codes, always set timeouts, always have a documented fallback path to native Hash::make().
Ramp-up. Fast for usage, moderate for safe operation. The developer learning curve is trivial; the TPM-relevant cost is the security review, the adapter and test scaffolding, and the runbook — which is precisely the argument for declining the dependency and building the internal equivalent instead.
How can I help you explore Laravel packages today?