RepoPilotOpen in app →

kelektiv/node.bcrypt.js

bcrypt for NodeJs

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 4w ago
  • 21+ active contributors
  • MIT licensed
Show 3 more →
  • CI configured
  • Tests present
  • Concentrated ownership — top contributor handles 62% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/kelektiv/node.bcrypt.js)](https://repopilot.app/r/kelektiv/node.bcrypt.js)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/kelektiv/node.bcrypt.js on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: kelektiv/node.bcrypt.js

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/kelektiv/node.bcrypt.js shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 4w ago
  • 21+ active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Concentrated ownership — top contributor handles 62% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live kelektiv/node.bcrypt.js repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/kelektiv/node.bcrypt.js.

What it runs against: a local clone of kelektiv/node.bcrypt.js — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in kelektiv/node.bcrypt.js | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 55 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>kelektiv/node.bcrypt.js</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of kelektiv/node.bcrypt.js. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/kelektiv/node.bcrypt.js.git
#   cd node.bcrypt.js
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of kelektiv/node.bcrypt.js and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "kelektiv/node.bcrypt.js(\\.git)?\\b" \\
  && ok "origin remote is kelektiv/node.bcrypt.js" \\
  || miss "origin remote is not kelektiv/node.bcrypt.js (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "bcrypt.js" \\
  && ok "bcrypt.js" \\
  || miss "missing critical file: bcrypt.js"
test -f "promises.js" \\
  && ok "promises.js" \\
  || miss "missing critical file: promises.js"
test -f "binding.gyp" \\
  && ok "binding.gyp" \\
  || miss "missing critical file: binding.gyp"
test -f "src/bcrypt.cc" \\
  && ok "src/bcrypt.cc" \\
  || miss "missing critical file: src/bcrypt.cc"
test -f "src/bcrypt_node.cc" \\
  && ok "src/bcrypt_node.cc" \\
  || miss "missing critical file: src/bcrypt_node.cc"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 55 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~25d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/kelektiv/node.bcrypt.js"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

node.bcrypt.js is a native Node.js binding for bcrypt, a password hashing algorithm designed to be computationally expensive and resistant to brute-force attacks. It provides both synchronous and asynchronous APIs for hashing passwords and comparing plaintext passwords against stored hashes, with support for cost factors to control computation time. Hybrid native + JavaScript bridge: C++ core (src/bcrypt.cc, src/blowfish.cc, src/bcrypt_node.cc) handles cryptographic operations and is compiled by node-gyp; bcrypt.js and promises.js are thin JavaScript wrappers exposing callback and Promise APIs respectively; binding.gyp defines the native build configuration.

👥Who it's for

Backend developers and DevOps engineers building Node.js applications (v18+) who need to securely hash and verify user passwords. Authentication library maintainers and anyone implementing user credential storage also rely on this.

🌱Maturity & risk

Highly mature and production-ready. The project has a comprehensive test suite (async.test.js, promise.test.js, sync.test.js, implementation.test.js), CI/CD pipelines via GitHub Actions (.github/workflows/ci.yaml and build-pack-publish.yml), and version 6.0.0 indicates stable API. Active maintenance with security documentation (SECURITY.md) and version compatibility table up to Node 18+.

Low risk for production use, but note: only 2 dependencies (node-addon-api, node-gyp-build) but requires native compilation via node-gyp, which can fail on some platforms. The security-critical Blowfish implementation is hand-written C++ (src/blowfish.cc, src/bcrypt.cc) requiring expert review. Previous versions had wrap-around bugs (< 5.0.0), so staying on v6.0.0+ is mandatory.

Active areas of work

The repo maintains active CI via .github/workflows/ with build and publish automation. Recent focus appears to be on Node 18+ support (engine constraint in package.json), pre-built binary distribution (prebuildify in build script), and Alpine Linux Docker support (Dockerfile-alpine). Security advisories are handled per SECURITY.md.

🚀Get running

git clone https://github.com/kelektiv/node.bcrypt.js.git && cd node.bcrypt.js && npm install && npm test

Daily commands: npm test runs Jest suite. npm run build creates pre-built binaries. For development: make targets available (see Makefile). npm install will auto-build via node-gyp-build.

🗺️Map of the codebase

  • bcrypt.js — Main entry point exporting all public API (hash, compare, genSalt methods); all consumers depend on this interface.
  • promises.js — Promise-based wrapper around async methods; required reading for understanding modern usage patterns.
  • binding.gyp — Build configuration for native C++ bindings; must understand to compile or troubleshoot native module failures.
  • src/bcrypt.cc — Core C++ bcrypt algorithm implementation; performance and security bottleneck for all password hashing.
  • src/bcrypt_node.cc — Node.js native addon bindings bridging JavaScript and C++; critical for async execution and memory management.
  • package.json — Declares minimum Node.js v18+ requirement and build dependencies; version constraints affect compatibility across environments.
  • test/async.test.js — Test suite validating async behavior; reference for expected API contracts and edge cases.

🧩Components & responsibilities

  • bcrypt.js (Public API) (JavaScript, Node.js module system) — Router between sync, async callback, and error handling; exports hash, compare, genSalt, getRounds methods.
    • Failure mode: Invalid arguments not caught; reliant on native addon for actual work.
  • promises.js (Promise Wrapper) (JavaScript Promises) — Promisifies async callback methods for async/await consumption.
    • Failure mode: Upstream failures from bcrypt.js propagate; no additional error transformation.
  • bcrypt_node.cc (NAPI Binding) (Node.js NAPI, libuv) — Bridges JavaScript calls to C++ bcrypt logic; manages work queue, threads, and callbacks.
    • Failure mode: Memory leaks, race conditions, or segfaults if NAPI lifecycle not properly managed; affects entire Node process.
  • bcrypt.cc (Core Algorithm) (C++, Blowfish cipher) — Implements bcrypt salt generation, key expansion, and hash computation; invoked from NAPI binding.
    • Failure mode: Algorithmic correctness issues, weak salts, or timing vulnerabilities affect all password security.
  • blowfish.cc (Cipher Primitive) — Implements Blowfish encrypt/decrypt operations used by bcrypt; critical for constant

🛠️How to make changes

Add support for a new hashing algorithm variant

  1. Implement algorithm logic in a new .cc file alongside src/bcrypt.cc (src/bcrypt.cc)
  2. Add NAPI wrapper functions in src/bcrypt_node.cc for the new algorithm (src/bcrypt_node.cc)
  3. Export new methods from bcrypt.js main module (bcrypt.js)
  4. Add corresponding promise wrappers in promises.js (promises.js)
  5. Write tests in test/implementation.test.js validating the new algorithm (test/implementation.test.js)

Optimize performance for a specific platform or scenario

  1. Review C++ implementation in src/bcrypt.cc or src/blowfish.cc for optimization opportunities (src/bcrypt.cc)
  2. Adjust work factor constants or algorithm parameters if needed (src/node_blf.h)
  3. Add platform-specific build flags or conditions in binding.gyp (binding.gyp)
  4. Run performance benchmarks via test/repetitions.test.js to validate improvements (test/repetitions.test.js)

Add a new high-level API method (e.g., hashSync with custom options)

  1. Implement the underlying C++ logic in src/bcrypt_node.cc (src/bcrypt_node.cc)
  2. Export the method from bcrypt.js with callback/sync variants (bcrypt.js)
  3. Add promise wrapper in promises.js if async (promises.js)
  4. Write comprehensive tests in appropriate test file (async.test.js, sync.test.js, or promise.test.js) (test/async.test.js)

🔧Why these technologies

  • C++ with NAPI bindings — Bcrypt requires intensive cryptographic computation; native code avoids JavaScript overhead and enables libuv thread pool async execution without blocking the event loop.
  • Blowfish cipher (src/blowfish.cc) — Bcrypt is built on Blowfish; providing the primitive in native code ensures constant-time comparison and prevents timing attacks.
  • node-gyp-build for compilation — Enables prebuilt binaries across Node.js versions and platforms (Windows, macOS, Linux) without requiring users to compile from source.
  • Jest for testing — Modern async test framework well-suited for testing callback, promise, and synchronous APIs across different Node.js versions.

⚖️Trade-offs already made

  • Offer sync, async callback, and promise APIs

    • Why: Backward compatibility with older callback-based code while supporting modern async/await patterns.
    • Consequence: Larger API surface, more code paths to maintain, potential for misuse (blocking the event loop with sync methods).
  • Synchronous hash API (hashSync) available

    • Why: Some scenarios (e.g., CLI tools, initialization) require synchronous blocking behavior.
    • Consequence: Risk of developers accidentally calling hashSync in request handlers, freezing the entire application during expensive hash computation.
  • Work factor (rounds) configurable up to ~31

    • Why: Allows tuning cost as hardware evolves and security requirements change.
    • Consequence: User error: too low rounds = weak passwords; too high = DoS vector or unresponsive application.
  • Native addon dependency

    • Why: Necessary for performance and algorithm correctness.
    • Consequence: Compilation required on platforms without prebuilt binaries; complexity in CI/CD and deployment; native code security responsibility.

🚫Non-goals (don't propose these)

  • Does not provide authentication or session management; only password hashing and comparison.
  • Does not implement key derivation for encryption keys; specialized to bcrypt password hashing.
  • Does not support GPU acceleration; CPU-bound libuv threading only.
  • Does not provide audit logging or rate limiting; delegated to application layer.
  • Does not handle NodeJS versions below v18; targets modern LTS releases only.

🪤Traps & gotchas

Native build requirement: npm install performs native compilation and will fail without a C++ compiler (gcc/clang on Linux, MSVC on Windows, xcode on macOS). Node.js version must be >= 18 (per package.json engines field). The 72-byte truncation of passwords is cryptographically intentional but may surprise developers with long passwords or UTF-8 multibyte characters. Pre-built binaries are architecture and libc-specific; Docker builds use both Dockerfile and Dockerfile-alpine because Alpine's musl libc is incompatible with glibc binaries.

🏗️Architecture

💡Concepts to learn

  • Bcrypt (password hashing algorithm) — The entire purpose of this repo; understanding bcrypt's design (adaptive cost factor, salt rounds) is essential to use node.bcrypt.js correctly
  • Blowfish cipher — Bcrypt is built on Blowfish; the src/blowfish.cc implementation is the cryptographic foundation, and knowing Blowfish's 64-bit block size and key schedule explains bcrypt's behavior
  • Node.js native addons (node-gyp) — node.bcrypt.js is a native addon compiled via node-gyp; understanding binding.gyp, V8 APIs, and the build process is required to modify or debug the C++ core
  • Cost factor (work factor) in bcrypt — The genSalt(rounds) function in bcrypt.js uses a cost factor to control hashing time; misunderstanding this leads to either weak hashes (low cost) or DoS vulnerabilities (high cost)
  • UTF-8 byte encoding vs. character count — The 72-byte truncation documented in SECURITY.md is measured in bytes, not characters; emojis and multibyte UTF-8 sequences can cause confusion about which characters are actually hashed
  • Constant-time comparison — The compare() function in bcrypt.js must prevent timing attacks; comparing hashes in constant time (regardless of match position) is critical to password verification security
  • dcodeIO/bcrypt.js — Pure JavaScript bcrypt implementation (no native compilation required); alternative when native modules cannot be used
  • pyca/bcrypt — Python reference implementation of bcrypt; useful for understanding the algorithm and cross-language validation
  • OpenBSD/src — Original OpenBSD bcrypt source code; this node.bcrypt.js directly derives from and ports the OpenBSD implementation
  • nodejs/node-gyp — Build tool used by node.bcrypt.js to compile C++ extensions; essential for understanding the binding.gyp configuration
  • nodejs/node-addon-api — C++ API abstraction layer for Node.js native addons; directly used in src/bcrypt_node.cc for bridging C++ and JavaScript

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive error handling and edge case tests for bcrypt operations

The test suite (test/async.test.js, test/sync.test.js, test/promise.test.js) lacks coverage for error conditions such as invalid salt rounds, null inputs, extremely large inputs, and concurrent operation race conditions. The native C++ binding (src/bcrypt.cc, src/bcrypt_node.cc) needs validated error paths. This is critical for a security-sensitive library where unexpected behavior could expose vulnerabilities.

  • [ ] Review existing test files (test/async.test.js, test/sync.test.js, test/promise.test.js) to identify missing error scenarios
  • [ ] Add tests for invalid salt rounds (negative, zero, > 31)
  • [ ] Add tests for null/undefined inputs and boundary conditions
  • [ ] Add tests for concurrent hash/compare operations to detect race conditions
  • [ ] Add tests validating C++ error propagation from src/bcrypt.cc and src/bcrypt_node.cc
  • [ ] Ensure error messages are consistent across async, sync, and promise APIs

Add GitHub Actions workflow for testing across Node 18+ LTS versions and multiple platforms

The .github/workflows/ci.yaml exists but the current CI setup may not be comprehensively testing all supported Node versions (>= 18 per package.json engines) or all major platforms. The repo has Dockerfile and Dockerfile-alpine, suggesting multi-platform support should be tested. Adding explicit matrix testing would catch platform-specific native binding issues early.

  • [ ] Review current .github/workflows/ci.yaml configuration
  • [ ] Create or extend CI workflow to test against Node 18.x, 20.x, 21.x (latest LTS versions)
  • [ ] Add matrix for multiple platforms: ubuntu-latest, windows-latest, macos-latest
  • [ ] Add build testing for Alpine Linux variant (leverage Dockerfile-alpine)
  • [ ] Include prebuilt binary validation using prebuildify --napi --tag-libc
  • [ ] Document platform-specific test results in CHANGELOG.md or README.md

Create performance benchmarking suite and document results

The repo lacks a formal performance testing framework despite bcrypt being computationally intensive and round counts heavily impacting performance. The binding.gyp and native code (src/bcrypt.cc, src/blowfish.cc) would benefit from documented performance characteristics. test/repetitions.test.js exists but is not a comprehensive benchmark. A structured benchmark would help contributors understand performance trade-offs and catch regressions.

  • [ ] Create test/benchmark.js or similar file with structured performance tests
  • [ ] Benchmark hash generation across salt rounds 4-12 to establish baseline metrics
  • [ ] Benchmark compare operations (success and failure cases)
  • [ ] Benchmark async vs sync vs promise API performance differences
  • [ ] Include memory profiling for large-scale operations
  • [ ] Document results in BENCHMARKS.md with hardware specifications
  • [ ] Integrate benchmark into CI to track performance regressions (e.g., via build-pack-publish.yml workflow)

🌿Good first issues

  • Add TypeScript definitions: The bcrypt.js and promises.js exports lack .d.ts type definition files; a junior could create src/bcrypt.d.ts defining interfaces for async functions, Promise variants, and genSalt/hash/compare signatures.
  • Expand examples/: The examples/ directory has only 2 files (async_compare.js, forever_gen_salt.js); missing are examples for the Promise API (examples/promise_usage.js), cost factor comparison (examples/benchmark_costs.js), and error handling patterns.
  • Improve test coverage for edge cases: test/sync.test.js and test/async.test.js exist but likely lack explicit tests for the documented 72-byte truncation behavior; add a test validating that hash('very_long_password_over_72_bytes_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') behaves identically to hash('very_long_password_over_72_bytes_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx').

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 24aa3c5 — Merge pull request #1216 from veksen/exclude-tests-from-npm (recrsn)
  • d7d2e84 — Merge pull request #1215 from mshradtya/docs/fix-broken-timing-attack-link (recrsn)
  • b3d2701 — Exclude test files from npm package (veksen)
  • 007c65a — docs: update broken link for timing attacks article (mshradtya)
  • 0d9aae8 — Merge pull request #1202 from noritaka1166/fix-typos (recrsn)
  • d465bde — Merge pull request #1207 from awrreny/readme-timing-note (recrsn)
  • 2d6fc63 — clarified timing attack note in README (awrreny)
  • 50ef446 — chore: fix typos in docs (noritaka1166)
  • 86c2b66 — Merge pull request #1200 from kelektiv/update-readme (recrsn)
  • 366f2ce — Update README (recrsn)

🔒Security observations

The bcrypt.js library has a solid security foundation as a well-maintained cryptographic library. No critical vulnerabilities were identified. Main concerns are: (1) dependency version management could be stricter for a crypto library, (2) Node.js version support should be updated to LTS versions with longer support windows, (3) Docker build reproducibility needs digest pinning, and (4) prebuilt binary integrity verification could be enhanced. The library maintains a responsible disclosure policy and has clear supported version guidance. Overall security posture is good with minor hardening opportunities.

  • Medium · Outdated Node.js Version Requirement — package.json - engines field. The package requires Node.js >= 18, but Node.js 18 is approaching end-of-life (April 2025). The project should consider updating minimum requirements to Node.js 20 LTS or later to ensure users have access to security patches. Fix: Update minimum Node.js requirement to >= 20 LTS or >= 22 depending on project maturity and support timeline.
  • Medium · Dependency Version Pinning Not Strict — package.json - dependencies. Dependencies use caret (^) version constraints rather than exact pinning. 'node-addon-api': '^8.3.0' and 'node-gyp-build': '^4.8.4' allow minor/patch version upgrades that could introduce unexpected behavior in a cryptographic library where stability is critical. Fix: Consider using exact version pinning (remove ^) for production dependencies, especially for cryptographic libraries. At minimum, use tilde (~) to restrict to patch versions only.
  • Low · Incomplete Security Policy Documentation — SECURITY.md and README.md. The SECURITY.md file references 'maintainers listed in the README' for vulnerability reporting, but the README snippet provided appears truncated. This could lead to confusion about proper security disclosure procedures. Fix: Ensure SECURITY.md contains complete, direct contact information (email addresses) for security researchers without relying on external README references. Provide a clear responsible disclosure timeline.
  • Low · Docker Image Base Not Pinned — Dockerfile - FROM statement. Dockerfile uses 'node:18-bullseye' without a specific digest hash. This allows automatic pulls of potentially different image versions, reducing reproducibility and auditability of builds. Fix: Pin the Docker base image to a specific digest hash (e.g., 'node:18-bullseye@sha256:...') to ensure reproducible builds and detect unauthorized image modifications.
  • Low · Missing Native Module Security Validation — binding.gyp, build-all.sh, package.json build scripts. The project uses node-gyp-build for compiling native C++ code (bcrypt.cc, blowfish.cc). There's no apparent validation that prebuilt binaries haven't been tampered with beyond npm's basic verification. Fix: Implement checksum verification for prebuilt binaries. Consider publishing SHA-256 checksums alongside prebuilt artifacts and document verification procedures for users.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · kelektiv/node.bcrypt.js — RepoPilot