{"_id":"@nationaldesignstudio/rampart","_rev":"6-dd0ea3e4edb41ca773a811926020b622","name":"@nationaldesignstudio/rampart","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.1":{"name":"@nationaldesignstudio/rampart","version":"0.1.1","keywords":["pii","redaction","privacy","llm","browser","onnx","transformers.js"],"license":"CC-BY-4.0","_id":"@nationaldesignstudio/rampart@0.1.1","maintainers":[{"name":"ed-ndstudio","email":"ed@ndstudio.gov"},{"name":"caelin-ndstudio","email":"caelin@ndstudio.gov"},{"name":"taigrr","email":"groot.tai@gmail.com"}],"homepage":"https://github.com/nationaldesignstudio/rampart#readme","bugs":{"url":"https://github.com/nationaldesignstudio/rampart/issues"},"dist":{"shasum":"480d7ca93fa16e1f68bfb8980f836a43ce5fe9c2","tarball":"https://registry.npmjs.org/@nationaldesignstudio/rampart/-/rampart-0.1.1.tgz","fileCount":62,"integrity":"sha512-293kaVfsJOFVt4zl7lfszdyDNM9jwurSkkQEZ3rz5z9aJNd43y1cfbdIxRUoeufmYmAOV7a8ysgWVOpglIivqQ==","signatures":[{"sig":"MEUCIQDJvNhuOlaXYXI7FzqgC5PUc3AlRDDCLBHGFCG21ciEXgIgbDUzlksK2FZXxCE2wSjEEk9FtX163TbMbdw4tydbwx0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10400120},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./dist":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./worker":{"types":"./dist/src/ner/worker.d.ts","import":"./dist/src/ner/worker.js","default":"./dist/src/ner/worker.js"},"./dist/worker":{"types":"./dist/src/ner/worker.d.ts","import":"./dist/src/ner/worker.js","default":"./dist/src/ner/worker.js"},"./package.json":"./package.json"},"gitHead":"ea2fec01e1132bc04e094e157b6aa87bcfc7ae20","scripts":{"test":"vitest run","bench":"bun eval/bench/run.ts","build":"bun build ./index.ts ./src/ner/worker.ts --outdir ./dist --format esm --sourcemap --target browser && tsc -p tsconfig.build.json","redact":"bun cli/redact.ts","prepack":"bun run build","test:watch":"vitest","type-check":"tsc -p tsconfig.json --noEmit","bench:fetch":"bun eval/bench/fetch.ts","eval:public":"bun eval/run-public-eval.ts","bench:webgpu":"bun eval/bench/webgpu.ts","verify:public":"bun run build && bun test && bun run type-check && bun run eval:public:strict && bun run export:huggingface:verify","prepublishOnly":"bun run verify:public","bench:webgpu:wasm":"bun eval/bench/webgpu.ts --device wasm","eval:public:strict":"bun eval/run-public-eval.ts --strict","export:huggingface":"bun scripts/export-huggingface.ts","publish:huggingface":"bun run export:huggingface:verify && hf repo create nationaldesignstudio/rampart --repo-type model --private --exist-ok && hf upload nationaldesignstudio/rampart hf-export . --repo-type model","export:huggingface:verify":"bun run export:huggingface && bun scripts/verify-huggingface-export.ts"},"_npmUser":{"name":"ed-ndstudio","email":"ed@ndstudio.gov"},"repository":{"url":"git+https://github.com/nationaldesignstudio/rampart.git","type":"git"},"_npmVersion":"11.6.2","description":"Rampart — client-side PII redaction for AI assistants: deterministic recognizers + a 14.7 MB ONNX classifier (transformers.js), default-deny policy, and reversible placeholders. Runs entirely in the browser.","directories":{},"_nodeVersion":"25.0.0","publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"vitest":"4.1.8","@types/bun":"1.3.14","playwright":"1.61.1","typescript":"5.9.2","@huggingface/transformers":"3.7.5"},"peerDependencies":{"@huggingface/transformers":">=3"},"peerDependenciesMeta":{"@huggingface/transformers":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rampart_0.1.1_1782746783372_0.43839053526378713","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@nationaldesignstudio/rampart","version":"0.1.2","keywords":["pii","redaction","privacy","llm","browser","onnx","transformers.js"],"license":"CC-BY-4.0","_id":"@nationaldesignstudio/rampart@0.1.2","maintainers":[{"name":"ed-ndstudio","email":"ed@ndstudio.gov"},{"name":"caelin-ndstudio","email":"caelin@ndstudio.gov"},{"name":"taigrr","email":"groot.tai@gmail.com"}],"homepage":"https://github.com/nationaldesignstudio/rampart#readme","bugs":{"url":"https://github.com/nationaldesignstudio/rampart/issues"},"dist":{"shasum":"bd5ed5f765de1d706897a7286ee393965ee937eb","tarball":"https://registry.npmjs.org/@nationaldesignstudio/rampart/-/rampart-0.1.2.tgz","fileCount":62,"integrity":"sha512-PNSFYxng3ILU/wnIDtUgx3HpeTRok/lLC+UwLeMFdMul8N4sYqX4ofE6TEFlcDxlucNDEbgw9vZ9RlO5MssHDw==","signatures":[{"sig":"MEUCIHPfQ1qxP0Es9Rev2VMlS52a5vUqz7mw+q8Pg+dcKi0+AiEAxlTMEWy689Yqezzw4Uc/kq0kKhL8N1qiZeviCjevJxQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10418488},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./dist":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./worker":{"types":"./dist/src/ner/worker.d.ts","import":"./dist/src/ner/worker.js","default":"./dist/src/ner/worker.js"},"./dist/worker":{"types":"./dist/src/ner/worker.d.ts","import":"./dist/src/ner/worker.js","default":"./dist/src/ner/worker.js"},"./package.json":"./package.json"},"gitHead":"f8fdf68ab8db4cbe947caf269f9ead85fcc7645e","scripts":{"test":"vitest run","bench":"bun eval/bench/run.ts","build":"bun build ./index.ts ./src/ner/worker.ts --outdir ./dist --format esm --sourcemap --target browser && tsc -p tsconfig.build.json","redact":"bun cli/redact.ts","prepack":"bun run build","test:watch":"vitest","type-check":"tsc -p tsconfig.json --noEmit","bench:fetch":"bun eval/bench/fetch.ts","eval:public":"bun eval/run-public-eval.ts","bench:webgpu":"bun eval/bench/webgpu.ts","verify:public":"bun run build && bun test && bun run type-check && bun run eval:public:strict && bun run export:huggingface:verify","prepublishOnly":"bun run verify:public","bench:webgpu:wasm":"bun eval/bench/webgpu.ts --device wasm","eval:public:strict":"bun eval/run-public-eval.ts --strict","export:huggingface":"bun scripts/export-huggingface.ts","publish:huggingface":"bun run export:huggingface:verify && hf repo create nationaldesignstudio/rampart --repo-type model --private --exist-ok && hf upload nationaldesignstudio/rampart hf-export . --repo-type model","export:huggingface:verify":"bun run export:huggingface && bun scripts/verify-huggingface-export.ts"},"_npmUser":{"name":"ed-ndstudio","email":"ed@ndstudio.gov"},"repository":{"url":"git+https://github.com/nationaldesignstudio/rampart.git","type":"git"},"_npmVersion":"11.6.2","description":"Rampart — client-side PII redaction for AI assistants: deterministic recognizers + a 14.7 MB ONNX classifier (transformers.js), default-deny policy, and reversible placeholders. Runs entirely in the browser.","directories":{},"_nodeVersion":"25.0.0","publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"vitest":"4.1.8","@types/bun":"1.3.14","playwright":"1.61.1","typescript":"5.9.2","@huggingface/transformers":"3.7.5"},"peerDependencies":{"@huggingface/transformers":">=3"},"peerDependenciesMeta":{"@huggingface/transformers":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rampart_0.1.2_1782772900573_0.2635053230888176","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@nationaldesignstudio/rampart","version":"0.1.3","description":"Rampart — client-side PII redaction for AI assistants: deterministic recognizers + a 14.7 MB ONNX classifier (transformers.js), default-deny policy, and reversible placeholders. Runs entirely in the browser.","license":"CC-BY-4.0","homepage":"https://github.com/nationaldesignstudio/rampart#readme","repository":{"type":"git","url":"git+https://github.com/nationaldesignstudio/rampart.git"},"bugs":{"url":"https://github.com/nationaldesignstudio/rampart/issues"},"keywords":["pii","redaction","privacy","llm","browser","onnx","transformers.js"],"type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./dist":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./worker":{"types":"./dist/src/ner/worker.d.ts","import":"./dist/src/ner/worker.js","default":"./dist/src/ner/worker.js"},"./dist/worker":{"types":"./dist/src/ner/worker.d.ts","import":"./dist/src/ner/worker.js","default":"./dist/src/ner/worker.js"},"./package.json":"./package.json"},"publishConfig":{"access":"restricted"},"scripts":{"build":"bun build ./index.ts ./src/ner/worker.ts --outdir ./dist --format esm --sourcemap --target browser --external @huggingface/transformers && tsc -p tsconfig.build.json","eval:public":"bun eval/run-public-eval.ts","eval:public:strict":"bun eval/run-public-eval.ts --strict","bench:fetch":"bun eval/bench/fetch.ts","bench":"bun eval/bench/run.ts","bench:webgpu":"bun eval/bench/webgpu.ts","bench:webgpu:wasm":"bun eval/bench/webgpu.ts --device wasm","export:huggingface":"bun scripts/export-huggingface.ts","export:huggingface:verify":"bun run export:huggingface && bun scripts/verify-huggingface-export.ts","publish:huggingface":"bun run export:huggingface:verify && hf repo create nationaldesignstudio/rampart --repo-type model --private --exist-ok && hf upload nationaldesignstudio/rampart hf-export . --repo-type model","prepack":"bun run build","prepublishOnly":"bun run verify:public","redact":"bun cli/redact.ts","test":"vitest run","test:watch":"vitest","type-check":"tsc -p tsconfig.json --noEmit","verify:public":"bun run build && bun test && bun run type-check && bun run eval:public:strict && bun run export:huggingface:verify"},"peerDependencies":{"@huggingface/transformers":">=3"},"peerDependenciesMeta":{"@huggingface/transformers":{"optional":true}},"devDependencies":{"@huggingface/transformers":"3.7.5","@types/bun":"1.3.14","playwright":"1.61.1","typescript":"5.9.2","vitest":"4.1.8"},"packageManager":"bun@1.3.14","gitHead":"763845bf273a876c20db508c9b7414c99ee0b30e","_id":"@nationaldesignstudio/rampart@0.1.3","_nodeVersion":"25.0.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-N3AMnPO1nGxUfMhTm+zJTuhXRIcdR/ETMIMyVr741pgxQcaRNMJVhKgN2nfEmwnXwGsViWpxFQOO0YYs8zZR2w==","shasum":"9d95f9efb5920ddeb883fdb591b2fd3abb562ab6","tarball":"https://registry.npmjs.org/@nationaldesignstudio/rampart/-/rampart-0.1.3.tgz","fileCount":62,"unpackedSize":463386,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDkgR8J2gvKhUVrUlDFO2FThKQtGkGell9RkzKtB0rJWAIgbF/mgl5WyijBZbryEqN5MF57VbFZ8c0daW/xb0+e1qI="}]},"_npmUser":{"name":"ed-ndstudio","email":"ed@ndstudio.gov"},"directories":{},"maintainers":[{"name":"ed-ndstudio","email":"ed@ndstudio.gov"},{"name":"caelin-ndstudio","email":"caelin@ndstudio.gov"},{"name":"taigrr","email":"groot.tai@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rampart_0.1.3_1782828406548_0.29992053925092077"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-29T15:09:20.699Z","modified":"2026-06-30T14:06:47.248Z","0.1.0":"2026-06-29T15:09:21.326Z","0.1.1":"2026-06-29T15:26:23.574Z","0.1.2":"2026-06-29T22:41:40.750Z","0.1.3":"2026-06-30T14:06:46.792Z"},"bugs":{"url":"https://github.com/nationaldesignstudio/rampart/issues"},"license":"CC-BY-4.0","homepage":"https://github.com/nationaldesignstudio/rampart#readme","keywords":["pii","redaction","privacy","llm","browser","onnx","transformers.js"],"repository":{"type":"git","url":"git+https://github.com/nationaldesignstudio/rampart.git"},"description":"Rampart — client-side PII redaction for AI assistants: deterministic recognizers + a 14.7 MB ONNX classifier (transformers.js), default-deny policy, and reversible placeholders. Runs entirely in the browser.","maintainers":[{"name":"ed-ndstudio","email":"ed@ndstudio.gov"},{"name":"caelin-ndstudio","email":"caelin@ndstudio.gov"},{"name":"taigrr","email":"groot.tai@gmail.com"}],"readme":"# Rampart\n\n**Rampart** is a local-first system for removing personally identifiable information\nfrom user-typed text before it leaves the browser. It combines a 14.7 MB ONNX\ntoken-classification model with a deterministic recognizer layer; together they form\na defense-in-depth pipeline released as a complete, reproducible artifact.\n\nThis repository ships the runtime as [`@nationaldesignstudio/rampart`](https://www.npmjs.com/package/@nationaldesignstudio/rampart).\nModel weights load from Hugging Face by default; a copy also lives in `model/` for\nlocal serving and training.\n\n```txt\n\"My name is Alex Rivera and my SSN is 472-81-0094.\"\n→ \"My name is [GIVEN_NAME_1] [SURNAME_1] and my SSN is [SSN_1].\"\n```\n\nThe model provider sees placeholders. The user sees restored values on the client.\nThe session table never leaves the device.\n\nRampart is **harm reduction**, not perfect protection. Personal data the client\nfails to redact is the upper bound on what can leak through a model provider, a\nlogging pipeline, or a future infrastructure compromise. No detector at this size\ncatches everything; we document failure modes openly and ship regression tests so\nfuture training runs surface drops immediately.\n\nSee [WHITEPAPER.md](./WHITEPAPER.md) for the full technical writeup — methodology,\ncandidate sweep, calibration, schema reconciliation, and reproducibility.\n\n## Supported scope\n\nThis release supports **seven Latin-script languages**: English, Spanish, French,\nGerman, Italian, Portuguese, and Dutch. Every headline number below is measured on\nthese languages.\n\nText and names in **non-Latin scripts** (e.g. Chinese, Japanese, Korean, Arabic,\nCyrillic, Devanagari) are **out of scope in this release**: recall drops sharply and\nthe system should not be relied on for them. See [Limitations](#limitations).\n\n## Headline results\n\nOn a 30,000-row held-out test set spanning all **seven supported languages** from\nthe OpenPII 1.5M dataset, the **full system** (model + deterministic layer +\npolicy) achieves:\n\n| Metric | Value |\n| --- | ---: |\n| Private-term recall (7 languages) | **98.42%** (Wilson 95% CI [98.35, 98.49]) |\n| Public-term retention | 91.7% term-presence (>99% policy-aware\\*) |\n| Latency p50 (Node ONNX) | **6.6 ms**† |\n| Shipped artifact size | **14.7 MB** Q4 ONNX (≈15.0 MB with tokenizer) |\n\n\\* OpenPII marks street-line components as public; Rampart redacts the precise\nstreet line (`BUILDING_NUMBER` + `STREET_NAME`) and the secondary-address line\n(`SECONDARY_ADDRESS`) while keeping city, state, and ZIP.\nSee \"Schema reconciliation\" in the whitepaper.\n\n† Latency is hardware-dependent; the committed `eval/bench/runs/sample-900` proof run\nrecords ≈14 ms p50 on CI hardware. Over the held-out slice the browser pipeline runs at\n**3.9 ms p50** on WebGPU (Apple Metal) and 12.6 ms on WASM via `bun run bench:webgpu`.\n\nThese numbers come from the shipped Q4 pipeline scored end-to-end by the committed\n`eval/bench` harness on a pinned held-out slice. The harness was corrected since the\nprevious revision: city/state/ZIP are now scored as kept (matching the runtime policy)\nrather than counted as leaks, which is why public retention rose to ~90% while the\nheadline recall is reported against the larger, harder seven-language slice.\n\nPer supported language, on the same 30,000-row test set:\n\n| Language | Private recall | Public retention |\n| --- | ---: | ---: |\n| English (en) | 98.85% | 90.5% |\n| Spanish (es) | 98.84% | 91.6% |\n| French (fr) | 98.41% | 92.8% |\n| German (de) | 97.94% | 91.7% |\n| Italian (it) | 97.83% | 94.1% |\n| Portuguese (pt) | 97.73% | 92.5% |\n| Dutch (nl) | 97.21% | 91.9% |\n\nOn the English+Spanish slice the full system scores **98.85%** recall — the slice used\nfor the model-selection sweep in the whitepaper.\n\n## Design goals\n\n1. **Local-first privacy.** Remove personal information before it reaches application\n   infrastructure. Data the server never receives cannot be leaked downstream.\n2. **Browser-deployable.** Under 15 MB on the wire — small enough for a low-end phone\n   over a slow connection.\n3. **Recall-biased.** A miss leaks data; over-redaction is the lesser failure mode.\n4. **Domain-aware retention.** The keep-set is policy-driven so assistants retain\n   coarse geography — city, state, ZIP — while the precise street line is redacted,\n   all without retraining.\n\n## Architecture\n\nTwo cooperating layers run in parallel and merge their outputs. Both run entirely in\nthe browser.\n\n### Deterministic recognizer layer\n\nRegular expressions paired with checksum and structural validators. It owns five\nclasses end-to-end:\n\n- **Credit cards** — Luhn-checksummed over the digit projection, so every\n  separator form collapses to one rule and a 16-digit number that fails Luhn is\n  not redacted as a card.\n- **SSNs** — structural rules reject reserved areas (`000`, `666`, `9xx`) and\n  ZIP+4-style false positives.\n- **Email**, **URLs**, and **IP addresses** (IPv4, IPv6, and MAC) — pattern-backed,\n  where the structure lives in the punctuation; near-100% recall, far above the\n  model alone (model-only URL recall is ~5%).\n\nThis layer is synchronous and runs before the model loads; its spans are masked to\nsentinels so the model never re-derives them. Names, phone numbers, account and\nrouting numbers, government identifiers, passports, licenses, and street-address\ncomponents carry no checksum, so they are left to the model rather than guessed at\nwith a regex.\n\n### Token-classification model\n\nA MiniLM-L6-H384 encoder fine-tuned on a 35-label BIO head (17 entity types) covers\ncontextual PII the regex layer can't checksum — split names (`GIVEN_NAME`,\n`SURNAME`), phone numbers, account/routing/tax numbers, government IDs, passports,\nlicenses, and free-form address components — across seven Latin-script languages\n(en, es, fr, de, it, pt, nl). Vocabulary is trimmed to 19,730 WordPieces;\nsingle-character pieces are retained for rare-name fallback.\n\nSpan repair (adjacent merge, bridge-and-merge, capitalized-particle rescue) lifts\nspan-F1 to 0.53 strict (IoU=1.0) and 0.66 relaxed (IoU≥0.5) on the headline test\nset — well above the fragmented spans HuggingFace's default aggregation produces\nfor subword-split names.\n\n### Policy and session table\n\n**Default-deny policy:** every detected label is redacted unless explicitly kept.\nThe default keep-set is `{CITY, STATE, ZIP_CODE}` — coarse geography an assistant\ncan reason about — while the precise street line (`BUILDING_NUMBER` + `STREET_NAME`)\nand the secondary-address line (`SECONDARY_ADDRESS`) are always redacted.\n\n**Session table:** maps each raw value to a stable placeholder (`Maria Garcia → [GIVEN_NAME_1] [SURNAME_1]`).\nPlaceholders are restored locally in assistant responses. The table is never transmitted.\n\nThe npm package exposes a single entry point — `createGuard()` returns a `ChatGuard`\nthat runs the full pipeline (detect → policy → placeholders) and keeps per-conversation\nstate for `reveal()`.\n\n## Install\n\n```bash\nnpm install @nationaldesignstudio/rampart @huggingface/transformers\n```\n\n`@huggingface/transformers` is a peer dependency — your app bundles and serves it.\n\n## Usage\n\nCreate one `ChatGuard` per conversation. `createGuard()` loads the shipped q4 classifier\n(`nationaldesignstudio/rampart`) from Hugging Face by default, caches it on-device, and\npairs it with the deterministic layer.\n\n```ts\nimport { createGuard } from \"@nationaldesignstudio/rampart\";\n\nconst guard = await createGuard();\n\n// Scrub the user message before it reaches your LLM or server.\nconst safe = await guard.protect(userMessage);\n\n// Send safe.text — placeholders, not raw PII — to the model.\nconst reply = await llm(safe.text);\n\n// Restore real values before showing the reply to the user.\nguard.reveal(reply);\n```\n\nStreaming replies: `stream.pipeThrough(guard.revealTransform())`.\n\nScrub model output before logging: `await guard.protectReply(reply)`.\n\n### Options\n\n| Option | Default | Purpose |\n| --- | --- | --- |\n| `model` | `nationaldesignstudio/rampart` | Hugging Face model id or local ONNX directory |\n| `device` | `\"wasm\"` | `\"wasm\"` / `\"webgpu\"` in browsers; `\"cpu\"` in Node |\n| `worker` | — | Worker script URL — run NER off the main thread |\n| `heuristicsOnly` | `false` | Skip the classifier; structured PII only |\n| `keepLabels` | city, state, ZIP | Widen or narrow the default-deny keep-set |\n| `aliases` | `{}` | Display names for tokens, e.g. `{ GIVEN_NAME: \"NAME\" }` |\n| `ner` | — | Inject a custom detector; skips `model` |\n| `minScore` | `0.4` | Drop model spans below this confidence |\n| `noPrefilter` | `false` | Feed raw text to the model (no-prefilter ablation); heuristics still run |\n\n```ts\n// Local weights (e.g. self-hosted or repo `model/` dir)\nconst guard = await createGuard({ model: \"./model\", device: \"cpu\" });\n\n// Heuristics only — no model load\nconst guard = await createGuard({ heuristicsOnly: true });\n\n// Keep inference off the UI thread (browser)\nconst guard = await createGuard({\n  worker: new URL(\"./pii-worker.ts\", import.meta.url),\n});\n```\n\nCustom models must be token-classification ONNX exports (q4) with a label schema\ncompatible with Rampart. Dtype is fixed to q4.\n\nSet `HF_TOKEN` when pulling from a private Hugging Face repo.\n\n### CLI\n\n```bash\nbun run redact   # interactive terminal redactor (Node, device: cpu)\n```\n\n## Limitations\n\nThe most consequential documented gaps:\n\n- **Non-Latin scripts are out of scope.** This release supports the seven\n  Latin-script languages above only. On the fairness suite, Latin-script names —\n  including diacritics — recall ~99.8%, but names in non-Latin scripts recall ~14%\n  in aggregate (Russian 2%, Arabic 5%, Hindi 6%, Han Chinese 9%, Korean 15%,\n  Japanese 46%). There is no checksum for names, so this gap surfaces at the system\n  level. **Do not deploy this release for populations who routinely type non-Latin-script\n  names without compensating controls.** Tracked by a stratified regression test in\n  the eval suite; closing it is the top priority for the next training cycle.\n- **Adversarial robustness.** 86.4% on a 20-case hostile-input suite. Combined\n  attacks can still bypass both layers. The threat model is good-faith user entry,\n  not a motivated adversary smuggling PII past their own filter.\n- **Indirect identifiers.** Rare condition + ZIP-style inferential leaks are out of scope.\n- **Non-text inputs.** Images, audio, and structured form fields are not supported.\n\nSee [MODEL_CARD.md](./MODEL_CARD.md) for per-class statistics and failure modes.\n\n## Evaluation\n\nEverything is evaluated in TypeScript against the shipped `ChatGuard` pipeline — the\nsame code consumers run is the code under test:\n\n- **Unit tests** — deterministic detectors, redaction policy, streaming rehydration,\n  span repair.\n- **Public API end-to-end suite** — chat-style cases across structured identifiers,\n  names from multiple traditions, addresses, government IDs, keep-set behavior, and\n  no-PII controls.\n- **Native benchmark** ([`eval/bench`](./eval/bench)) — runs the real\n  `@nationaldesignstudio/rampart` pipeline over a frozen OpenPII held-out slice and\n  scores recall/retention (Wilson CI), span-F1, and latency. The headline numbers\n  above are regenerated by this harness.\n\n```bash\nbun test                                   # unit + public API suites\nbun run eval:public                        # end-to-end chat cases\n\nbun run bench:fetch --n 30000              # materialise the held-out slice\nbun run bench                              # score the shipped pipeline\n```\n\n## Documentation\n\n| Document | Contents |\n| --- | --- |\n| [WHITEPAPER.md](./WHITEPAPER.md) | Full technical writeup |\n| [MODEL_CARD.md](./MODEL_CARD.md) | Model summary, training data, eval results, limitations |\n| [RELEASE.md](./RELEASE.md) | Verify and publish checklist |\n\n## Distribution\n\n| Channel | Artifact |\n| --- | --- |\n| **npm** | `@nationaldesignstudio/rampart` — TypeScript runtime API |\n| **GitHub** | [`nationaldesignstudio/rampart`](https://github.com/nationaldesignstudio/rampart) — source, tests, eval harness, model weights |\n\n## License\n\nReleased under [CC BY 4.0](./LICENSE) (Creative Commons Attribution 4.0 International).\n\nTraining data: OpenPII 1.5M (CC BY 4.0).\n","readmeFilename":"README.md"}