puddinhead
v0.1.3
Published
PII detection and anonymization with built-in analyze and sanitize HTTP endpoints.
Downloads
23
Maintainers
Readme
puddinhead
PII detection and anonymization for Node.js apps, HTTP services, and local tooling.
puddinhead ships a stable public API for:
analyze(input, options?)anonymize(input, options?)sanitize(input, options?)POST /analyzePOST /sanitizepuddinheadCLI
Install
npm install puddinheadQuick Start
CommonJS
const { analyze, anonymize, sanitize } = require("puddinhead");
const input = "Email [email protected] or call +1 (415) 555-2671.";
console.log(analyze(input));
console.log(sanitize(input));
console.log(
anonymize(input, {
operators: {
EMAIL_ADDRESS: { type: "replace", newValue: "[EMAIL]" },
PHONE_NUMBER: { type: "mask", charsToMask: 8, fromEnd: true }
}
})
);ESM
import { analyze, anonymize, sanitize } from "puddinhead";
const input = "Visit https://example.com from 192.168.0.10";
console.log(analyze(input));
console.log(sanitize(input));
console.log(
anonymize(input, {
operators: {
URL: { type: "replace", newValue: "[LINK]" },
IP_ADDRESS: "hash"
}
})
);Supported Entities
Built-in recognizers:
EMAIL_ADDRESSPHONE_NUMBERCREDIT_CARDIP_ADDRESS(IPv4andIPv6)URLSSNIBANDATE_OF_BIRTHPASSPORT_NUMBERDRIVER_LICENSE_NUMBERTAX_IDBANK_ACCOUNT_NUMBERNATIONAL_ID
These built-ins cover many common identifiers, but they are not a claim to detect every possible PII format in every country or document system. For anything domain-specific, add your own patterns or recognizers.
Custom detection is also supported through:
patterns: simplestringorRegExpinputsrecognizers: structured recognizer objects withtype,pattern, optionalflags, optionalscore, and optionalvalidate
API Reference
analyze(input, options?)
Detects entities in a string and returns an ordered array of matches:
type AnalysisResult = {
type: string;
start: number;
end: number;
score: number;
text: string;
};Options:
entities: limit analysis to a subset of entity typespatterns: add backward-compatible literal orRegExppatternsrecognizers: add custom recognizer configs
Example:
import { analyze } from "puddinhead";
const entities = analyze("Order ORD-12345 for [email protected]", {
recognizers: [{ type: "ORDER_ID", pattern: "ORD-[0-9]+", score: 0.91 }]
});
console.log(entities);anonymize(input, options?)
Runs analysis, applies operators, and returns both transformed text and the entities that were changed:
type AnonymizeResult = {
text: string;
entities: Array<
AnalysisResult & {
operator: string;
replacement: string;
}
>;
};Options:
- All
analyze()options operators: per-entity operator config, for exampleEMAIL_ADDRESSorDEFAULTdefaultOperator: fallback operator when an entity-specific operator is not configuredreplacement: shortcut for the defaultreplaceoperator valueresults: reuse a previousanalyze()result array
Supported operators:
replaceredactmaskhashkeepcustom
Example:
const result = anonymize("Reach me at [email protected] or +1 (415) 555-2671.", {
operators: {
EMAIL_ADDRESS: { type: "replace", newValue: "[EMAIL]" },
PHONE_NUMBER: { type: "mask", charsToMask: 8, fromEnd: true }
}
});
console.log(result.text);
console.log(result.entities);sanitize(input, options?)
Convenience wrapper around anonymize() that returns only the transformed string.
Example:
sanitize("token abc123", {
patterns: ["abc123"],
replacement: "[MASKED]"
});
// "token [MASKED]"createAnalyzeHandler(options?)
Creates a Node HTTP handler for POST /analyze.
createSanitizeHandler(options?)
Creates a Node HTTP handler for POST /sanitize.
createSanitizeServer(options?)
Creates a Node HTTP server that serves both endpoints. Optional path overrides:
analyzePathsanitizePath
HTTP API
POST /analyze
Request body:
{
"text": "Email [email protected] or call +1 (415) 555-2671",
"entities": ["EMAIL_ADDRESS", "PHONE_NUMBER"]
}You may also send:
- a raw JSON string body
- an object with
inputinstead oftext patternsas an array ofstringorRegExprecognizersto add custom detectors
Success response:
{
"entities": [
{
"type": "EMAIL_ADDRESS",
"start": 6,
"end": 20,
"score": 0.95,
"text": "[email protected]"
},
{
"type": "PHONE_NUMBER",
"start": 29,
"end": 46,
"score": 0.78,
"text": "+1 (415) 555-2671"
}
]
}Errors:
400for invalid JSON or missingtext/input405for non-POSTrequests413when the request body exceedsmaxBodySize
POST /sanitize
Request body:
{
"text": "Email [email protected] or call +1 (415) 555-2671",
"operators": {
"EMAIL_ADDRESS": { "type": "replace", "newValue": "[EMAIL]" },
"PHONE_NUMBER": { "type": "mask", "charsToMask": 8, "fromEnd": true }
}
}Success response:
{
"sanitized": "Email [EMAIL] or call +1 (415) ********",
"entities": [
{
"type": "EMAIL_ADDRESS",
"start": 6,
"end": 20,
"score": 0.95,
"text": "[email protected]",
"operator": "replace",
"replacement": "[EMAIL]"
},
{
"type": "PHONE_NUMBER",
"start": 29,
"end": 46,
"score": 0.78,
"text": "+1 (415) 555-2671",
"operator": "mask",
"replacement": "+1 (415) ********"
}
]
}Errors:
400for invalid JSON or missingtext/input405for non-POSTrequests413when the request body exceedsmaxBodySize
fetch Example
const response = await fetch("http://127.0.0.1:3000/sanitize", {
method: "POST",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
text: "Call me at +1 (415) 555-2671",
operators: {
PHONE_NUMBER: { type: "mask", charsToMask: 8, fromEnd: true }
}
})
});
const body = await response.json();
console.log(body.sanitized);CLI
Start the built-in HTTP server:
npx puddinheadOr set a specific port:
npx puddinhead 4000
PORT=4000 HOST=0.0.0.0 npx puddinheadThe CLI starts a server with:
POST /analyzePOST /sanitize
Local Server Example
import { createSanitizeServer } from "puddinhead";
const server = createSanitizeServer();
server.listen(3000, "127.0.0.1", () => {
console.log("puddinhead listening on http://127.0.0.1:3000");
});