npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

veslocker

v1.1.0

Published

Give a short PIN the strength of a hardware security module — without the hardware. A split-key key-server API that removes offline PIN brute-force.

Readme

          ___       ___
         /   \     /   \    VESvault
         \__ /     \ __/    Encrypt Everything without fear of losing the Key
            \\     //                   https://vesvault.com https://ves.host
             \\   //
     ___      \\_//
    /   \     /   \         VESlocker:        Hardware-grade PIN Security API
    \__ /     \ __/
       \\     //                                        https://veslocker.com
        \\   //
         \\_//
         /   \
         \___/

VESlocker — Hardware-grade PIN Security API

CI client: Apache-2.0 server: PolyForm Perimeter live demo

Give a short PIN the strength of a hardware security module — without the hardware.

A 4-digit PIN has only 10,000 combinations. Anywhere it guards data that an attacker can copy (a browser, a file, a backup), they can brute-force it offline in milliseconds. VESlocker removes the offline attack entirely: the decryption key is split between the user's PIN and a secret held by a small key server, and the server hands the key back only under a strict, exponentially-throttled budget. After a few dozen wrong guesses the entry is locked for good — a true lifetime cap on attempts, the way a TPM or a secure enclave behaves, implemented in ~250 lines of vanilla JS and ~110 lines of PHP.

🔎 Try it: there is a live demo at veslocker.com.


How it works

  client                                            key server
  ──────                                            ──────────
  challenge = SHA-256(seed ‖ PIN)  ───── id, challenge ─────▶  look up per-id secret
                                                               throttle check (2^attempts)
  key = AES-GCM key  ◀──── SHA-256(secret ‖ challenge) ──────  return derived key
  ciphertext = AES-GCM(key, data)
  (ciphertext + seed are stored locally; the key is never stored)
  • The data is encrypted client-side with AES-GCM. The key is derived from the server's per-entry secret and a challenge seeded by the user's PIN.
  • The server never receives the seed, the PIN, the plaintext, or the ciphertext — only an id and a challenge (a hash). It returns the derived key, gated by throttling.
  • Each request to an id increments an attempt counter; the next request is refused until 2^attempts seconds have passed (capped at 32). After ~32 attempts the wait exceeds a century — the entry is effectively sealed.

Quickstart

Client

Install from npm (Node 20+, or any bundler):

npm install veslocker
import VESlocker from "veslocker";

…or drop it straight into a page — same API, no build step:

<script src="https://veslocker.com/pub/VESlocker.js"></script>

Then:

// apiUrl defaults to the public veslocker.com key server, so you can try it
// with zero config. Pass apiUrl to point at your own server/VESlocker.php:
const vl = new VESlocker({
  apiUrl: "https://your-key-server.example/VESlocker.php"
});

// Encrypt a secret behind a PIN and stash it in localStorage under a name:
await vl.store("launch-codes", "1234", "the actual launch codes");

// Read it back with the PIN. fetch() returns the value, and on every read
// re-encrypts under a fresh id + seed (resetting the attempt budget) — so it's
// the right call for routine reads:
const secret = await vl.fetch("launch-codes", "1234");          // "the actual launch codes"

// Supply a new PIN and the *same* call also rotates it: fetch() reads with the
// old PIN and re-encrypts under the new one in one step, still returning the value:
const same = await vl.fetch("launch-codes", "1234", "5678");    // "the actual launch codes", now behind 5678

⚠️ Prefer fetch() over peek() for routine reads. peek() decrypts without rekeying: each call spends one of the entry's ~32 lifetime attempts and leaves the counter climbing toward the permanent seal. Reach for peek() only when you deliberately don't want to rotate the entry.

There's a runnable Node example in example/node.mjs and a browser one in example/browser.html.

Prefer to manage storage yourself? encrypt / decrypt are stateless and return / take a self-contained token:

const token = await vl.encrypt("1234", "the actual launch codes");
const back  = await vl.decrypt("1234", token);   // "the actual launch codes"

Get notified when an entry's key is accessed (an early-warning signal that someone is guessing the PIN):

vl.accessFn = (id, count, at) => {
  if (count > 0) console.warn(`VESlocker '${id}' accessed ${count}× (last: ${at})`);
};

Key server

  1. Serve server/VESlocker.php behind any PHP-capable HTTP server.
  2. Create the database table from server/VESlocker.sql.
  3. Set the $DB* connection settings at the top of VESlocker.php.
  4. Point the client's apiUrl at it.

Requirements — Server: PHP + MySQL/MariaDB. Client: any browser with Web Crypto (all current browsers).

Security model

VESlocker is a key oracle gated by throttling, not a vault. Reason about it honestly before you deploy:

  • The server cannot read your data. It never sees plaintext or ciphertext — only an id and a challenge hash.
  • No offline brute force. An attacker who copies the ciphertext cannot test PINs offline: the key also requires the server's secret, which they don't have.
  • Hard lifetime cap on online guesses. Exponential per-id throttling limits an attacker (and the legitimate user) to roughly 32 attempts, ever.
  • ⚠️ Availability dependency. Decryption requires the key server to be reachable. This is by design — it is what makes the throttle unavoidable.
  • ⚠️ Two-secret compromise. If an attacker obtains both the server-side secret store and the ciphertext, the PIN can be brute-forced offline (bounded only by PIN entropy). Protect the key server's database accordingly, and treat the PIN's entropy as your last line of defense in that scenario.

Found a vulnerability? See SECURITY.md — please report privately.

How it compares

VESlocker isn't a new cryptographic protocol — it's a deliberately minimal take on a known idea: harden a low-entropy secret (a PIN) with the help of a key server, so it can't be brute-forced offline. It's worth saying plainly how it relates to the rigorous prior art, and where it consciously trades rigor for simplicity.

The server is unauthenticated by design — and that is the zero-knowledge property. There are no accounts, no API keys, no per-user auth. The only credential is the entry's id: 32 cryptographically-random bytes (256 bits) minted on the client. You cannot throttle-attack — or even address — an entry whose id you don't hold, and guessing one costs 2^256. The server stores only (id → secret, attempt-counter) and learns nothing that links an id to a person, a PIN, or any ciphertext. The id is a bearer capability, not an identity.

| | What it's for | Trust anchor | Hard lifetime guess cap | No special hardware | Survives a malicious operator | |---|---|---|---|---|---| | VESlocker | Decrypting local data behind a PIN | Server operator + availability | ✅ exponential → ~century | ✅ any PHP host + Web Crypto | ❌ by design | | OPAQUE / aPAKE | Login / session key agreement | Server operator | ❌ needs bolt-on rate limiting | ✅ | partial | | Signal SVR | PIN-recovering a stored secret | Hardware enclave (SGX) + attestation | ✅ destroy-after-N | ❌ needs SGX | ✅ | | Pythia / PHE | Hardening + rotatable password keys | Server key (rotatable) | ❌ focus is rotation | ✅ | partial (oblivious + rotation) |

Read the table by its one standout cell: among the systems compared here, VESlocker is the only row that is ✅ in both "hard lifetime guess cap" and "no special hardware." Signal's SVR gets the cap by putting the throttle inside a secure enclave; OPAQUE and Pythia run on commodity servers but have no built-in attempt limit. VESlocker gets both by enforcing the throttle in ordinary application code — the trade-off being the last column: it trusts the operator, where an enclave would not.

  • vs. OPAQUE and PAKEs. OPAQUE proves knowledge of a password and agrees a session key without the server learning the password. It's built for authentication, and online guessing is still possible unless you add your own rate limiting. VESlocker does no handshake (one round trip: id + challenge = H(seed ‖ PIN)H(secret ‖ challenge)) and makes the throttle itself the headline feature — an exponential, lifetime-capped attempt budget — for the narrower job of unlocking local data.
  • vs. Signal's Secure Value Recovery. SVR is the closest cousin: a PIN, a server-side hard attempt limit, and a secret that becomes unrecoverable after too many tries. The difference is the trust anchor. SVR enforces the limit inside an SGX enclave with remote attestation, so it holds even against a malicious operator — at the cost of requiring that hardware and attestation infrastructure. VESlocker enforces the throttle in ordinary code on any commodity host, so it does not defend against the operator — it buys most of SVR's user-facing behavior for roughly none of the operational complexity, by trusting the operator and the network.
  • vs. Pythia / Password-Hardened Encryption. Pythia is a verifiable, oblivious PRF service with server-key rotation, so the server can't dictionary-attack the input even with its own key, and a stolen database can be invalidated by rotating. VESlocker's challenge is a plain hash, not blinded — the server could attack the PIN if it also had the seed, which it never does (the seed is client-only and never sent). VESlocker has no obliviousness and no rotation; in exchange it's SHA-256 and a counter column in ~110 lines of PHP, with nothing to install.

The honest one-liner: VESlocker gives a PIN the behavior of a hardware attempt-limiter — rate-limited, lifetime-capped, no offline attack — by trusting a reachable server instead of a chip. If your threat model includes a malicious server operator, reach for SVR or an OPRF service. If it's "someone copied my browser storage / my backup," VESlocker covers it at a fraction of the cost — and unlike a TPM, the protected data is portable to any device that has the token and the PIN.

License

VESlocker is licensed per component:

| Component | License | | |-----------|---------|---| | client/ | Apache License 2.0 | Embed it in your app with no copyleft obligations. | | server/ | PolyForm Perimeter 1.0.1 | Use, modify, and self-host for any purpose — except offering a product that competes with VESlocker. |

Contributing

Contributions are welcome under the DCO — see CONTRIBUTING.md. In short: sign off your commits with git commit -s.


Part of the VESvault project — Encrypt Everything without fear of losing the Key.