typesecure is a classification-first security core for TypeScript projects.
Instead of starting with crypto primitives, it starts with what actually causes most security incidents in web apps: data leaving the boundary it should never cross (logs, analytics, error trackers, headers, client bundles, etc).
You “type” your data as public | pii | secret | token | credential, and typesecure helps you enforce safe handling using TypeScript + runtime checks.
- Classification types:
PublicString,PIIString,SecretString,TokenString,CredentialString. - Runtime validation: Zod-backed constructors (
secretText(),piiText(), ...). - Redaction:
redact()andsafeJsonStringify()prevent secret/PII leakage. - Policy enforcement:
defaultPolicy(),assertAllowed(),audit()help block unsafe crossings.
- You need to stop leaks early: preventing secrets/PII from ending up in logs, analytics, error trackers, or client bundles.
- You want safe defaults: making insecure behavior harder than secure behavior.
- You want guardrails at the boundary: before logging, emitting telemetry, making network calls, or writing to storage.
- You need a full security platform (hosted policy registry, enterprise controls).
typesecureis a library. - You need production-grade crypto primitives. Use well-reviewed, purpose-built libraries and treat crypto carefully.
- You only want compile-time types with zero runtime behavior.
typesecuredeliberately includes runtime checks/redaction.
Requires Node.js >=18.18.0.
# Using npm
npm install typesecure
# Using yarn
yarn add typesecure
# Using pnpm
pnpm add typesecureimport {
piiText,
secretText,
token,
publicText,
redact,
safeJsonStringify,
defaultPolicy,
assertAllowed,
policyLog,
} from "typesecure";
const userEmail = piiText("user@example.com");
const sessionToken = token("abc.def.ghi");
const dbPassword = secretText(process.env.DB_PASSWORD ?? "");
// Redact before logging / serialization
console.log(redact({ userEmail, sessionToken, dbPassword }));
console.log(
safeJsonStringify({ userEmail, sessionToken, dbPassword }, undefined, 2),
);
// Enforce policy before a boundary crossing
const policy = defaultPolicy();
assertAllowed(policy, "network", { sessionToken }); // allowed
// assertAllowed(policy, 'log', { dbPassword }); // throws
// Safe logging helper with enforcement
policyLog(policy, console, "info", publicText("login_ok"), { userEmail });// Express middleware example
import {
safeLoggerAdapter,
defaultPolicy,
assertAllowed,
token,
} from "typesecure";
const log = safeLoggerAdapter(console);
const policy = defaultPolicy();
app.use((req, _res, next) => {
const auth = req.headers.authorization?.replace(/^Bearer\s+/i, "");
if (auth) {
const t = token(auth);
assertAllowed(policy, "network", { t });
log.info({ route: req.path, auth: t }); // will be redacted
}
next();
});publicText(value: string): PublicStringpiiText(value: string): PIIStringsecretText(value: string): SecretStringtoken(value: string): TokenStringcredential(value: string): CredentialStringreveal(value): string(intentionally explicit)
redact(value): value(deep traversal)redactText(value): string(mask sensitive fragments in plain text)detectText(value): StringDetection[](return ranges/kinds for audit workflows)safeJsonStringify(value): stringsafeLoggerAdapter(consoleLike)- Redaction options:
guessByKey(defaulttrue): redact suspicious keys likepassword,token,apiKey.guessByValue(defaulttrue): auto-detect and redact sensitive-looking values.useDefaultValueDetector(defaulttrue): keep built-in rule-based detectors on/off.stringDetectors: add custom detectors (for NER/ML or domain-specific logic).minDetectionConfidence(default0): ignore low-confidence custom detections.
- Value detection masks only the sensitive fragments inside a larger string (instead of replacing the whole text), including:
- PII: email, phone, SSN, date of birth (
YYYY-MM-DD), IPv4 address, payment card numbers (Luhn-validated). - Secrets/tokens: JWTs, private key PEM blocks, GitHub tokens, AWS access keys, Stripe secret keys, OpenAI-style
sk-...keys, credential pairs (user:pass), high-entropy token-like strings.
- PII: email, phone, SSN, date of birth (
Example custom detector (NER/ML-style integration):
const out = redact(
{ text: "Customer Jane Doe uses jane@example.com" },
{
stringDetectors: [
(value) => {
const name = "Jane Doe";
const idx = value.indexOf(name);
return idx >= 0
? [{ start: idx, end: idx + name.length, kind: "pii", confidence: 0.92, source: "ml.ner" }]
: [];
},
],
minDetectionConfidence: 0.8,
},
);defaultPolicy(): PolicyassertAllowed(policy, action, data): voidaudit(policy, action, data): AuditEventpolicyLog(policy, logger, level, ...args): void
Security is as much about preventing leaks as it is about cryptographic correctness. typesecure focuses on preventing accidental secret/PII exposure across common boundaries.
If you need cryptography for production-grade requirements, prefer well-reviewed primitives and consult a security professional. For production applications with high security requirements, consider:
- Consulting a security professional
- Using specialized security libraries
- Keeping dependencies updated
- Implementing proper key management
- Using hardware security modules (HSMs) for key storage when possible
- Conducting regular security audits
- Following the latest NIST recommendations
To contribute to this project:
- Clone the repository
- Install dependencies with
pnpm install - Run tests with
pnpm test - Build the package with
pnpm build - Run Enron dataset integration tests with
pnpm test:data
For larger redaction/policy experiments (Enron + Synthea FHIR), fetch datasets locally:
pnpm data:setupThis command downloads and extracts to:
data/enron-maildirdata/synthea_sample_data_fhir_latest
Notes:
data/is gitignored and not published to npm.pnpm testexcludes dataset suites by default.pnpm test:dataruns Enron dataset tests with verbose output.pnpm test:data:synthearuns Synthea-specific dataset tests.pnpm test:data:allruns all dataset suites.- You can override source URLs with
ENRON_URL=...and/orSYNTHEA_FHIR_URL=.... - You can change destination with
DATA_DIR=/path/to/data.
Dataset sources:
- Enron: https://www.cs.cmu.edu/~enron/
- Synthea: https://github.com/synthetichealth/synthea-sample-data/
This project uses TypeScript for type safety, Jest for testing, and ESLint for code quality.
We use these public datasets for redaction and policy testing:
Personal note: I am especially interested in the historical context around Enron, including how it was able to happen and the improvements in governance and controls that followed.
MIT © Arvid Berndtsson
Contributions are welcome! Please feel free to submit a Pull Request.