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

@tunghus193/pii-crypto

v0.2.1

Published

Deterministic PII encryption for Rino services

Readme

@tunghus193/pii-crypto

Deterministic PII encryption for Rino services (Node >=10.13.0).

This package is designed to work with the KMS v2 key provider (vh_account) which returns two 32-byte keys:

  • aes_key (pii-aes-key): AES-256-GCM encrypt/decrypt payload
  • search_key (pii-search-key): derive deterministic IV via HMAC-SHA256(search_key, plaintext)[0:12]

Install

npm install @tunghus193/pii-crypto

No GitLab token or custom registry — published on npmjs.com.

Publish (maintainers)

Requires npm account — publishes under your personal scope @tunghus193.

npm login                  # token lưu vào ~/.npmrc — không tạo .npmrc trong repo
npm whoami                 # phải in đúng username (tunghus193)
npm test
npm publish --access public

CI: set NPM_TOKEN in GitLab variables (CI tự tạo .npmrc trong job).

Environment variables

  • KMS_URL: base URL of vh_account (e.g. https://account-api.example.com)
  • KMS_CLIENT_ID / KMS_CLIENT_SECRET: credentials stored in kms_service_clients
  • PII_WRITE_ENABLED: 1 to write encrypted values, otherwise keep plaintext (dual-read always enabled)

Quickstart (Core only)

const { createPiiCrypto, FIELD_TYPES } = require('@tunghus193/pii-crypto');

async function main() {
  const pii = createPiiCrypto({
    kms: {
      baseUrl: process.env.KMS_URL,
      clientId: process.env.KMS_CLIENT_ID,
      clientSecret: process.env.KMS_CLIENT_SECRET,
    },
  });

  await pii.warmUp(); // GET /kms/v1/keys/pii (once at startup)

  const enc = pii.encrypt('0912345678', { fieldType: FIELD_TYPES.PHONE });
  const plain = pii.decrypt(enc);

  console.log({ enc, plain });
}

main().catch((e) => {
  console.error(e);
  process.exit(1);
});

Express + Sequelize (recommended for vh_api)

const { createPiiCrypto } = require('@tunghus193/pii-crypto');
const { attachSequelize } = require('@tunghus193/pii-crypto/sequelize');

async function initPii(models) {
  const pii = createPiiCrypto({
    kms: {
      baseUrl: process.env.KMS_URL,
      clientId: process.env.KMS_CLIENT_ID,
      clientSecret: process.env.KMS_CLIENT_SECRET,
      pollIntervalMs: 5 * 60 * 1000,
    },
    flags: {
      getWriteEnabled: async () => process.env.PII_WRITE_ENABLED === '1',
    },
  });

  await pii.warmUp();

  attachSequelize(pii, models, {
    User: ['mobile', 'email'],
    Contact: ['mobile'],
  });

  return pii;
}

What you get:

  • Write path: beforeSave encrypts configured fields when PII_WRITE_ENABLED=1
  • Read path: afterFind decrypts enc.1.… values (and legacy enc$1$…); plaintext passes through
  • Envelope format: enc.1.<iv>.<payload> (dot-separated). The legacy $ separator broke Sequelize bound queries (Named bind parameter ... has no value) because Sequelize scans the whole SQL for $word tokens. Legacy envelopes remain readable and are lazy-upgraded on save; searches dual-match both formats.
  • Search: beforeFind rewrites where to IN (raw, detEncrypt(raw)) so queries keep working during migration

Sequelize search behavior

Exact-match predicates on mapped PII fields support scalar values, Op.eq, array shorthand, and Op.in. The transformer follows Sequelize symbol containers such as Op.and and Op.or, but rewrites only mapped PII fields.

Non-PII values and non-plain objects are preserved by reference, including Date, Buffer, RegExp, Sequelize.literal, Fn, Col, and custom class instances. Operators that cannot perform deterministic exact matching, such as Op.like, ranges, and regexp, are left unchanged.

Express middleware (non-Sequelize)

const express = require('express');
const { expressPiiMiddleware } = require('@tunghus193/pii-crypto/express');

async function main() {
  const app = express();
  app.use(await expressPiiMiddleware({
    kms: {
      baseUrl: process.env.KMS_URL,
      clientId: process.env.KMS_CLIENT_ID,
      clientSecret: process.env.KMS_CLIENT_SECRET,
    },
  }));

  app.get('/demo', (req, res) => {
    res.json({ enc: req.pii.encrypt('0912345678') });
  });

  app.listen(3000);
}

Next.js (server-only helper)

Create a singleton getter (only import this from server code):

// lib/pii.js
const { createNextPiiGetter } = require('@tunghus193/pii-crypto/next');

module.exports = createNextPiiGetter({
  kms: {
    baseUrl: process.env.KMS_URL,
    clientId: process.env.KMS_CLIENT_ID,
    clientSecret: process.env.KMS_CLIENT_SECRET,
  },
});

Use it in route handlers:

const getPii = require('./lib/pii');

async function handler(req, res) {
  const pii = await getPii();
  const enc = pii.encrypt(req.query.mobile);
  res.json({ enc });
}

KMS endpoints used

  • GET /kms/v1/keys/pii (startup fetch, returns key material)
  • GET /kms/v1/keys/config (polling versions, optional)

Development

npm test
npm run build