Instructions to use nationaldesignstudio/rampart with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers.js
How to use nationaldesignstudio/rampart with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('token-classification', 'nationaldesignstudio/rampart');
Commit ·
bc423a6
0
Parent(s):
Initial Rampart release
Browse files- .gitattributes +1 -0
- LICENSE +402 -0
- MODEL_CARD.md +422 -0
- README.md +422 -0
- WHITEPAPER.md +316 -0
- config.json +99 -0
- examples/basic-chat.ts +12 -0
- onnx/model_q4.onnx +3 -0
- special_tokens_map.json +37 -0
- tokenizer.json +0 -0
- tokenizer_config.json +58 -0
- vocab.txt +0 -0
.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
LICENSE
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright 2026 National Design Studio
|
| 2 |
+
|
| 3 |
+
This work is licensed under the Creative Commons Attribution 4.0
|
| 4 |
+
International License. To view a copy of this license, visit
|
| 5 |
+
https://creativecommons.org/licenses/by/4.0/ or see the full legal
|
| 6 |
+
code below.
|
| 7 |
+
|
| 8 |
+
Attribution 4.0 International
|
| 9 |
+
|
| 10 |
+
=======================================================================
|
| 11 |
+
|
| 12 |
+
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
| 13 |
+
does not provide legal services or legal advice. Distribution of
|
| 14 |
+
Creative Commons public licenses does not create a lawyer-client or
|
| 15 |
+
other relationship. Creative Commons makes its licenses and related
|
| 16 |
+
information available on an "as-is" basis. Creative Commons gives no
|
| 17 |
+
warranties regarding its licenses, any material licensed under their
|
| 18 |
+
terms and conditions, or any related information. Creative Commons
|
| 19 |
+
disclaims all liability for damages resulting from their use to the
|
| 20 |
+
fullest extent possible.
|
| 21 |
+
|
| 22 |
+
Using Creative Commons Public Licenses
|
| 23 |
+
|
| 24 |
+
Creative Commons public licenses provide a standard set of terms and
|
| 25 |
+
conditions that creators and other rights holders may use to share
|
| 26 |
+
original works of authorship and other material subject to copyright
|
| 27 |
+
and certain other rights specified in the public license below. The
|
| 28 |
+
following considerations are for informational purposes only, are not
|
| 29 |
+
exhaustive, and do not form part of our licenses.
|
| 30 |
+
|
| 31 |
+
Considerations for licensors: Our public licenses are
|
| 32 |
+
intended for use by those authorized to give the public
|
| 33 |
+
permission to use material in ways otherwise restricted by
|
| 34 |
+
copyright and certain other rights. Our licenses are
|
| 35 |
+
irrevocable. Licensors should read and understand the terms
|
| 36 |
+
and conditions of the license they choose before applying it.
|
| 37 |
+
Licensors should also secure all rights necessary before
|
| 38 |
+
applying our licenses so that the public can reuse the
|
| 39 |
+
material as expected. Licensors should clearly mark any
|
| 40 |
+
material not subject to the license. This includes other CC-
|
| 41 |
+
licensed material, or material used under an exception or
|
| 42 |
+
limitation to copyright. More considerations for licensors:
|
| 43 |
+
wiki.creativecommons.org/Considerations_for_licensors
|
| 44 |
+
|
| 45 |
+
Considerations for the public: By using one of our public
|
| 46 |
+
licenses, a licensor grants the public permission to use the
|
| 47 |
+
licensed material under specified terms and conditions. If
|
| 48 |
+
the licensor's permission is not necessary for any reason--for
|
| 49 |
+
example, because of any applicable exception or limitation to
|
| 50 |
+
copyright--then that use is not regulated by the license. Our
|
| 51 |
+
licenses grant only permissions under copyright and certain
|
| 52 |
+
other rights that a licensor has authority to grant. Use of
|
| 53 |
+
the licensed material may still be restricted for other
|
| 54 |
+
reasons, including because others have copyright or other
|
| 55 |
+
rights in the material. A licensor may make special requests,
|
| 56 |
+
such as asking that all changes be marked or described.
|
| 57 |
+
Although not required by our licenses, you are encouraged to
|
| 58 |
+
respect those requests where reasonable. More considerations
|
| 59 |
+
for the public:
|
| 60 |
+
wiki.creativecommons.org/Considerations_for_licensees
|
| 61 |
+
|
| 62 |
+
=======================================================================
|
| 63 |
+
|
| 64 |
+
Creative Commons Attribution 4.0 International Public License
|
| 65 |
+
|
| 66 |
+
By exercising the Licensed Rights (defined below), You accept and agree
|
| 67 |
+
to be bound by the terms and conditions of this Creative Commons
|
| 68 |
+
Attribution 4.0 International Public License ("Public License"). To the
|
| 69 |
+
extent this Public License may be interpreted as a contract, You are
|
| 70 |
+
granted the Licensed Rights in consideration of Your acceptance of
|
| 71 |
+
these terms and conditions, and the Licensor grants You such rights in
|
| 72 |
+
consideration of benefits the Licensor receives from making the
|
| 73 |
+
Licensed Material available under these terms and conditions.
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
Section 1 -- Definitions.
|
| 77 |
+
|
| 78 |
+
a. Adapted Material means material subject to Copyright and Similar
|
| 79 |
+
Rights that is derived from or based upon the Licensed Material
|
| 80 |
+
and in which the Licensed Material is translated, altered,
|
| 81 |
+
arranged, transformed, or otherwise modified in a manner requiring
|
| 82 |
+
permission under the Copyright and Similar Rights held by the
|
| 83 |
+
Licensor. For purposes of this Public License, where the Licensed
|
| 84 |
+
Material is a musical work, performance, or sound recording,
|
| 85 |
+
Adapted Material is always produced where the Licensed Material is
|
| 86 |
+
synched in timed relation with a moving image.
|
| 87 |
+
|
| 88 |
+
b. Adapter's License means the license You apply to Your Copyright
|
| 89 |
+
and Similar Rights in Your contributions to Adapted Material in
|
| 90 |
+
accordance with the terms and conditions of this Public License.
|
| 91 |
+
|
| 92 |
+
c. Copyright and Similar Rights means copyright and/or similar rights
|
| 93 |
+
closely related to copyright including, without limitation,
|
| 94 |
+
performance, broadcast, sound recording, and Sui Generis Database
|
| 95 |
+
Rights, without regard to how the rights are labeled or
|
| 96 |
+
categorized. For purposes of this Public License, the rights
|
| 97 |
+
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
| 98 |
+
Rights.
|
| 99 |
+
|
| 100 |
+
d. Effective Technological Measures means those measures that, in the
|
| 101 |
+
absence of proper authority, may not be circumvented under laws
|
| 102 |
+
fulfilling obligations under Article 11 of the WIPO Copyright
|
| 103 |
+
Treaty adopted on December 20, 1996, and/or similar international
|
| 104 |
+
agreements.
|
| 105 |
+
|
| 106 |
+
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
| 107 |
+
any other exception or limitation to Copyright and Similar Rights
|
| 108 |
+
that applies to Your use of the Licensed Material.
|
| 109 |
+
|
| 110 |
+
f. Licensed Material means the artistic or literary work, database,
|
| 111 |
+
or other material to which the Licensor applied this Public
|
| 112 |
+
License.
|
| 113 |
+
|
| 114 |
+
g. Licensed Rights means the rights granted to You subject to the
|
| 115 |
+
terms and conditions of this Public License, which are limited to
|
| 116 |
+
all Copyright and Similar Rights that apply to Your use of the
|
| 117 |
+
Licensed Material and that the Licensor has authority to license.
|
| 118 |
+
|
| 119 |
+
h. Licensor means the individual(s) or entity(ies) granting rights
|
| 120 |
+
under this Public License.
|
| 121 |
+
|
| 122 |
+
i. Share means to provide material to the public by any means or
|
| 123 |
+
process that requires permission under the Licensed Rights, such
|
| 124 |
+
as reproduction, public display, public performance, distribution,
|
| 125 |
+
dissemination, communication, or importation, and to make material
|
| 126 |
+
available to the public including in ways that members of the
|
| 127 |
+
public may access the material from a place and at a time
|
| 128 |
+
individually chosen by them.
|
| 129 |
+
|
| 130 |
+
j. Sui Generis Database Rights means rights other than copyright
|
| 131 |
+
resulting from Directive 96/9/EC of the European Parliament and of
|
| 132 |
+
the Council of 11 March 1996 on the legal protection of databases,
|
| 133 |
+
as amended and/or succeeded, as well as other essentially
|
| 134 |
+
equivalent rights anywhere in the world.
|
| 135 |
+
|
| 136 |
+
k. You means the individual or entity exercising the Licensed Rights
|
| 137 |
+
under this Public License. Your has a corresponding meaning.
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
Section 2 -- Scope.
|
| 141 |
+
|
| 142 |
+
a. License grant.
|
| 143 |
+
|
| 144 |
+
1. Subject to the terms and conditions of this Public License,
|
| 145 |
+
the Licensor hereby grants You a worldwide, royalty-free,
|
| 146 |
+
non-sublicensable, non-exclusive, irrevocable license to
|
| 147 |
+
exercise the Licensed Rights in the Licensed Material to:
|
| 148 |
+
|
| 149 |
+
a. reproduce and Share the Licensed Material, in whole or
|
| 150 |
+
in part; and
|
| 151 |
+
|
| 152 |
+
b. produce, reproduce, and Share Adapted Material.
|
| 153 |
+
|
| 154 |
+
2. Exceptions and Limitations. For the avoidance of doubt, where
|
| 155 |
+
Exceptions and Limitations apply to Your use, this Public
|
| 156 |
+
License does not apply, and You do not need to comply with
|
| 157 |
+
its terms and conditions.
|
| 158 |
+
|
| 159 |
+
3. Term. The term of this Public License is specified in Section
|
| 160 |
+
6(a).
|
| 161 |
+
|
| 162 |
+
4. Media and formats; technical modifications allowed. The
|
| 163 |
+
Licensor authorizes You to exercise the Licensed Rights in
|
| 164 |
+
all media and formats whether now known or hereafter created,
|
| 165 |
+
and to make technical modifications necessary to do so. The
|
| 166 |
+
Licensor waives and/or agrees not to assert any right or
|
| 167 |
+
authority to forbid You from making technical modifications
|
| 168 |
+
necessary to exercise the Licensed Rights, including
|
| 169 |
+
technical modifications necessary to circumvent Effective
|
| 170 |
+
Technological Measures. For purposes of this Public License,
|
| 171 |
+
simply making modifications authorized by this Section 2(a)
|
| 172 |
+
(4) never produces Adapted Material.
|
| 173 |
+
|
| 174 |
+
5. Downstream recipients.
|
| 175 |
+
|
| 176 |
+
a. Offer from the Licensor -- Licensed Material. Every
|
| 177 |
+
recipient of the Licensed Material automatically
|
| 178 |
+
receives an offer from the Licensor to exercise the
|
| 179 |
+
Licensed Rights under the terms and conditions of this
|
| 180 |
+
Public License.
|
| 181 |
+
|
| 182 |
+
b. No downstream restrictions. You may not offer or impose
|
| 183 |
+
any additional or different terms or conditions on, or
|
| 184 |
+
apply any Effective Technological Measures to, the
|
| 185 |
+
Licensed Material if doing so restricts exercise of the
|
| 186 |
+
Licensed Rights by any recipient of the Licensed
|
| 187 |
+
Material.
|
| 188 |
+
|
| 189 |
+
6. No endorsement. Nothing in this Public License constitutes or
|
| 190 |
+
may be construed as permission to assert or imply that You
|
| 191 |
+
are, or that Your use of the Licensed Material is, connected
|
| 192 |
+
with, or sponsored, endorsed, or granted official status by,
|
| 193 |
+
the Licensor or others designated to receive attribution as
|
| 194 |
+
provided in Section 3(a)(1)(A)(i).
|
| 195 |
+
|
| 196 |
+
b. Other rights.
|
| 197 |
+
|
| 198 |
+
1. Moral rights, such as the right of integrity, are not
|
| 199 |
+
licensed under this Public License, nor are publicity,
|
| 200 |
+
privacy, and/or other similar personality rights; however, to
|
| 201 |
+
the extent possible, the Licensor waives and/or agrees not to
|
| 202 |
+
assert any such rights held by the Licensor to the limited
|
| 203 |
+
extent necessary to allow You to exercise the Licensed
|
| 204 |
+
Rights, but not otherwise.
|
| 205 |
+
|
| 206 |
+
2. Patent and trademark rights are not licensed under this
|
| 207 |
+
Public License.
|
| 208 |
+
|
| 209 |
+
3. To the extent possible, the Licensor waives any right to
|
| 210 |
+
collect royalties from You for the exercise of the Licensed
|
| 211 |
+
Rights, whether directly or through a collecting society
|
| 212 |
+
under any voluntary or waivable statutory or compulsory
|
| 213 |
+
licensing scheme. In all other cases the Licensor expressly
|
| 214 |
+
reserves any right to collect such royalties.
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
Section 3 -- License Conditions.
|
| 218 |
+
|
| 219 |
+
Your exercise of the Licensed Rights is expressly made subject to the
|
| 220 |
+
following conditions.
|
| 221 |
+
|
| 222 |
+
a. Attribution.
|
| 223 |
+
|
| 224 |
+
1. If You Share the Licensed Material (including in modified
|
| 225 |
+
form), You must:
|
| 226 |
+
|
| 227 |
+
a. retain the following if it is supplied by the Licensor
|
| 228 |
+
with the Licensed Material:
|
| 229 |
+
|
| 230 |
+
i. identification of the creator(s) of the Licensed
|
| 231 |
+
Material and any others designated to receive
|
| 232 |
+
attribution, in any reasonable manner requested by
|
| 233 |
+
the Licensor (including by pseudonym if
|
| 234 |
+
designated);
|
| 235 |
+
|
| 236 |
+
ii. a copyright notice;
|
| 237 |
+
|
| 238 |
+
iii. a notice that refers to this Public License;
|
| 239 |
+
|
| 240 |
+
iv. a notice that refers to the disclaimer of
|
| 241 |
+
warranties;
|
| 242 |
+
|
| 243 |
+
v. a URI or hyperlink to the Licensed Material to the
|
| 244 |
+
extent reasonably practicable;
|
| 245 |
+
|
| 246 |
+
b. indicate if You modified the Licensed Material and
|
| 247 |
+
retain an indication of any previous modifications; and
|
| 248 |
+
|
| 249 |
+
c. indicate the Licensed Material is licensed under this
|
| 250 |
+
Public License, and include the text of, or the URI or
|
| 251 |
+
hyperlink to, this Public License.
|
| 252 |
+
|
| 253 |
+
2. You may satisfy the conditions in Section 3(a)(1) in any
|
| 254 |
+
reasonable manner based on the medium, means, and context in
|
| 255 |
+
which You Share the Licensed Material. For example, it may be
|
| 256 |
+
reasonable to satisfy the conditions by providing a URI or
|
| 257 |
+
hyperlink to a resource that includes the required
|
| 258 |
+
information.
|
| 259 |
+
|
| 260 |
+
3. If requested by the Licensor, You must remove any of the
|
| 261 |
+
information required by Section 3(a)(1)(A) to the extent
|
| 262 |
+
reasonably practicable.
|
| 263 |
+
|
| 264 |
+
4. If You Share Adapted Material You produce, the Adapter's
|
| 265 |
+
License You apply must not prevent recipients of the Adapted
|
| 266 |
+
Material from complying with this Public License.
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
Section 4 -- Sui Generis Database Rights.
|
| 270 |
+
|
| 271 |
+
Where the Licensed Rights include Sui Generis Database Rights that
|
| 272 |
+
apply to Your use of the Licensed Material:
|
| 273 |
+
|
| 274 |
+
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
| 275 |
+
to extract, reuse, reproduce, and Share all or a substantial
|
| 276 |
+
portion of the contents of the database;
|
| 277 |
+
|
| 278 |
+
b. if You include all or a substantial portion of the database
|
| 279 |
+
contents in a database in which You have Sui Generis Database
|
| 280 |
+
Rights, then the database in which You have Sui Generis Database
|
| 281 |
+
Rights (but not its individual contents) is Adapted Material; and
|
| 282 |
+
|
| 283 |
+
c. You must comply with the conditions in Section 3(a) if You Share
|
| 284 |
+
all or a substantial portion of the contents of the database.
|
| 285 |
+
|
| 286 |
+
For the avoidance of doubt, this Section 4 supplements and does not
|
| 287 |
+
replace Your obligations under this Public License where the Licensed
|
| 288 |
+
Rights include other Copyright and Similar Rights.
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
| 292 |
+
|
| 293 |
+
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
| 294 |
+
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
| 295 |
+
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
| 296 |
+
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
| 297 |
+
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
| 298 |
+
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
| 299 |
+
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
| 300 |
+
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
| 301 |
+
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
| 302 |
+
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
| 303 |
+
|
| 304 |
+
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
| 305 |
+
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
| 306 |
+
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
| 307 |
+
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
| 308 |
+
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
| 309 |
+
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
| 310 |
+
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
| 311 |
+
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
| 312 |
+
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
| 313 |
+
|
| 314 |
+
c. The disclaimer of warranties and limitation of liability provided
|
| 315 |
+
above shall be interpreted in a manner that, to the extent
|
| 316 |
+
possible, most closely approximates an absolute disclaimer and
|
| 317 |
+
waiver of all liability.
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
Section 6 -- Term and Termination.
|
| 321 |
+
|
| 322 |
+
a. This Public License applies for the term of the Copyright and
|
| 323 |
+
Similar Rights licensed here. However, if You fail to comply with
|
| 324 |
+
this Public License, then Your rights under this Public License
|
| 325 |
+
terminate automatically.
|
| 326 |
+
|
| 327 |
+
b. Where Your right to use the Licensed Material has terminated under
|
| 328 |
+
Section 6(a), it reinstates:
|
| 329 |
+
|
| 330 |
+
1. automatically as of the date the violation is cured, provided
|
| 331 |
+
it is cured within 30 days of Your discovery of the
|
| 332 |
+
violation; or
|
| 333 |
+
|
| 334 |
+
2. upon express reinstatement by the Licensor.
|
| 335 |
+
|
| 336 |
+
For the avoidance of doubt, this Section 6(b) does not affect any
|
| 337 |
+
right the Licensor may have to seek remedies for Your violations
|
| 338 |
+
of this Public License.
|
| 339 |
+
|
| 340 |
+
c. For the avoidance of doubt, the Licensor may also offer the
|
| 341 |
+
Licensed Material under separate terms or conditions or stop
|
| 342 |
+
distributing the Licensed Material at any time; however, doing so
|
| 343 |
+
will not terminate this Public License.
|
| 344 |
+
|
| 345 |
+
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
| 346 |
+
License.
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
Section 7 -- Other Terms and Conditions.
|
| 350 |
+
|
| 351 |
+
a. The Licensor shall not be bound by any additional or different
|
| 352 |
+
terms or conditions communicated by You unless expressly agreed.
|
| 353 |
+
|
| 354 |
+
b. Any arrangements, understandings, or agreements regarding the
|
| 355 |
+
Licensed Material not stated herein are separate from and
|
| 356 |
+
independent of the terms and conditions of this Public License.
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
Section 8 -- Interpretation.
|
| 360 |
+
|
| 361 |
+
a. For the avoidance of doubt, this Public License does not, and
|
| 362 |
+
shall not be interpreted to, reduce, limit, restrict, or impose
|
| 363 |
+
conditions on any use of the Licensed Material that could lawfully
|
| 364 |
+
be made without permission under this Public License.
|
| 365 |
+
|
| 366 |
+
b. To the extent possible, if any provision of this Public License is
|
| 367 |
+
deemed unenforceable, it shall be automatically reformed to the
|
| 368 |
+
minimum extent necessary to make it enforceable. If the provision
|
| 369 |
+
cannot be reformed, it shall be severed from this Public License
|
| 370 |
+
without affecting the enforceability of the remaining terms and
|
| 371 |
+
conditions.
|
| 372 |
+
|
| 373 |
+
c. No term or condition of this Public License will be waived and no
|
| 374 |
+
failure to comply consented to unless expressly agreed to by the
|
| 375 |
+
Licensor.
|
| 376 |
+
|
| 377 |
+
d. Nothing in this Public License constitutes or may be interpreted
|
| 378 |
+
as a limitation upon, or waiver of, any privileges and immunities
|
| 379 |
+
that apply to the Licensor or You, including from the legal
|
| 380 |
+
processes of any jurisdiction or authority.
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
=======================================================================
|
| 384 |
+
|
| 385 |
+
Creative Commons is not a party to its public
|
| 386 |
+
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
| 387 |
+
its public licenses to material it publishes and in those instances
|
| 388 |
+
will be considered the "Licensor." The text of the Creative Commons
|
| 389 |
+
public licenses is dedicated to the public domain under the CC0 Public
|
| 390 |
+
Domain Dedication. Except for the limited purpose of indicating that
|
| 391 |
+
material is shared under a Creative Commons public license or as
|
| 392 |
+
otherwise permitted by the Creative Commons policies published at
|
| 393 |
+
creativecommons.org/policies, Creative Commons does not authorize the
|
| 394 |
+
use of the trademark "Creative Commons" or any other trademark or logo
|
| 395 |
+
of Creative Commons without its prior written consent including,
|
| 396 |
+
without limitation, in connection with any unauthorized modifications
|
| 397 |
+
to any of its public licenses or any other arrangements,
|
| 398 |
+
understandings, or agreements concerning use of licensed material. For
|
| 399 |
+
the avoidance of doubt, this paragraph does not form part of the
|
| 400 |
+
public licenses.
|
| 401 |
+
|
| 402 |
+
Creative Commons may be contacted at creativecommons.org.
|
MODEL_CARD.md
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers.js
|
| 3 |
+
pipeline_tag: token-classification
|
| 4 |
+
license: cc-by-4.0
|
| 5 |
+
language:
|
| 6 |
+
- en
|
| 7 |
+
- es
|
| 8 |
+
- fr
|
| 9 |
+
- de
|
| 10 |
+
- it
|
| 11 |
+
- pt
|
| 12 |
+
- nl
|
| 13 |
+
tags:
|
| 14 |
+
- pii
|
| 15 |
+
- redaction
|
| 16 |
+
- privacy
|
| 17 |
+
- onnx
|
| 18 |
+
- web
|
| 19 |
+
- client-side
|
| 20 |
+
- minilm
|
| 21 |
+
- browser
|
| 22 |
+
datasets:
|
| 23 |
+
- ai4privacy/pii-masking-openpii-1.5m
|
| 24 |
+
base_model: nreimers/MiniLM-L6-H384-uncased
|
| 25 |
+
metrics:
|
| 26 |
+
- private-term-recall
|
| 27 |
+
- public-term-retention
|
| 28 |
+
- span-f1
|
| 29 |
+
- ece
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
# Rampart
|
| 33 |
+
|
| 34 |
+
`rampart` is a 14.7 MB ONNX token-classification model that detects personally identifiable information (PII) in text before it leaves the user's device.
|
| 35 |
+
It is the on-device half of **Rampart**, a defense-in-depth client-side redaction system released by National Design Studio.
|
| 36 |
+
The shipped artifact runs alongside a deterministic recognizer layer that handles structured identifiers; together they form the complete system.
|
| 37 |
+
|
| 38 |
+
This card documents the released artifact only.
|
| 39 |
+
Alternative configurations explored during model selection (an ELECTRA-small base, the prefilter-off training variant, leaner data mixes, and smaller corpus slices) are discussed in the project whitepaper for context but are not published.
|
| 40 |
+
|
| 41 |
+
## Model summary
|
| 42 |
+
|
| 43 |
+
| Property | Value |
|
| 44 |
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
| 45 |
+
| Model id | `nationaldesignstudio/rampart` |
|
| 46 |
+
| Architecture | [`nreimers/MiniLM-L6-H384-uncased`](https://huggingface.co/nreimers/MiniLM-L6-H384-uncased) fine-tuned with a 35-label BIO head (17 entity types) |
|
| 47 |
+
| Parameters | ≈18.5M (MiniLM-L6-H384 with the trimmed 19,730-piece vocabulary; the 22.7M base figure is for the full 30,522-piece BERT vocab) |
|
| 48 |
+
| Quantization | 4-bit MatMul + INT8 embedding (`onnx/model_q4.onnx`) |
|
| 49 |
+
| Shipped artifact size | 14.7 MB |
|
| 50 |
+
| Vocabulary | 19,730 WordPieces (trimmed from BERT-uncased's 30,522, retaining all special and single-character pieces plus frequent multi-character pieces) |
|
| 51 |
+
| Max sequence | 512 tokens |
|
| 52 |
+
| Languages | English, Spanish, French, German, Italian, Portuguese, Dutch (all Latin-script) |
|
| 53 |
+
| Runtime | ONNX Runtime Web (WASM/WebGPU) via `transformers.js` |
|
| 54 |
+
| License | CC BY 4.0 (Creative Commons Attribution 4.0 International) |
|
| 55 |
+
| Training data license | CC BY 4.0 ([`ai4privacy/pii-masking-openpii-1.5m`](https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m)) |
|
| 56 |
+
| Released by | National Design Studio |
|
| 57 |
+
| Card version | 1.0 (initial public release) |
|
| 58 |
+
|
| 59 |
+
## Intended use
|
| 60 |
+
|
| 61 |
+
The model is designed for **client-side redaction of user-typed text in AI assistants and intake flows** — replacing identifying values with stable placeholders before any data is transmitted to a model provider, a server, or a logging system.
|
| 62 |
+
|
| 63 |
+
### Direct uses
|
| 64 |
+
|
| 65 |
+
- Redact user content before passing it to a hosted LLM.
|
| 66 |
+
- Maintain stable placeholders (`[GIVEN_NAME_1]`, `[SSN_1]`, ...) across a multi-turn conversation, with rehydration on the client.
|
| 67 |
+
- Preempt accidental collection of personal data in analytics, traces, and crash reports.
|
| 68 |
+
- Validate domain-specific redaction policies before deploying chat systems in regulated contexts.
|
| 69 |
+
|
| 70 |
+
### Out of scope
|
| 71 |
+
|
| 72 |
+
- **Stand-alone government-ID detection.**
|
| 73 |
+
The model is one layer of a defense-in-depth system; it is not a replacement for the deterministic recognizer layer that ships alongside it.
|
| 74 |
+
SSNs and payment cards are caught by the deterministic layer with checksum validation (structural rules and Luhn), at higher recall than the model alone.
|
| 75 |
+
Phone, routing, government-ID, passport, and license numbers carry no checksum, so they are caught by the model; the deterministic layer does not attempt them.
|
| 76 |
+
- **Indirect / inferential identifiers.**
|
| 77 |
+
A "rare disease + 5-digit ZIP" combination can re-identify someone even though neither token is in the redact-set.
|
| 78 |
+
The model does not detect inferential leaks.
|
| 79 |
+
- **Adversarial robustness as a security guarantee.**
|
| 80 |
+
We publish numbers on hostile inputs and document the failure surface; the system is positioned as harm reduction for users entering their own information in good faith, not as a security boundary against motivated adversaries.
|
| 81 |
+
- **Non-Latin scripts.**
|
| 82 |
+
This release is scoped to the seven Latin-script languages listed above.
|
| 83 |
+
Korean, Han Chinese, Japanese, Arabic, Cyrillic, and Devanagari names recall ~14% in aggregate (see "Fairness and limitations" below).
|
| 84 |
+
Do not deploy this release for populations who routinely type non-Latin-script names without compensating controls; monitor accordingly.
|
| 85 |
+
|
| 86 |
+
### Usage
|
| 87 |
+
|
| 88 |
+
The runtime ships as [`@nationaldesignstudio/rampart`](https://www.npmjs.com/package/@nationaldesignstudio/rampart). `createGuard()` returns a `ChatGuard` that loads this classifier and runs the full deterministic + model pipeline:
|
| 89 |
+
|
| 90 |
+
```ts
|
| 91 |
+
import { createGuard } from "@nationaldesignstudio/rampart";
|
| 92 |
+
|
| 93 |
+
const guard = await createGuard();
|
| 94 |
+
const { redacted } = await guard.redact("My name is Alex Rivera and my SSN is 472-81-0094.");
|
| 95 |
+
// → "My name is [GIVEN_NAME_1] [SURNAME_1] and my SSN is [SSN_1]."
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
## Training data
|
| 99 |
+
|
| 100 |
+
| Source | Rows used | License | Role |
|
| 101 |
+
| ------------------------------------------------------------------------------------------------------------ | -------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
| 102 |
+
| [`ai4privacy/pii-masking-openpii-1.5m`](https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m) | 643,756 train + 100,000 held-out | CC BY 4.0 | Realistic chat-style PII; 7 Latin-script languages (en, es, fr, de, it, pt, nl); the OpenPII schema mapped to our 35-label BIO schema (17 entity types) |
|
| 103 |
+
| Synthetic generator | 20,000 train | Apache-2.0 | Class reinforcement for the 17 entity types — accent-bearing names from curated first- and last-name pools and templated structured fields, generated deliberately messy (typos, all-caps, voice-dictated and pasted-from-form phrasing, multilingual mixing, contradictory/duplicated values) so the model sees realistic disordered input, not just clean OpenPII prose |
|
| 104 |
+
|
| 105 |
+
The held-out 100,000 rows are split into two non-overlapping subsets, seeded for full reproducibility:
|
| 106 |
+
|
| 107 |
+
- **10,000 rows** for recall-floor threshold tuning.
|
| 108 |
+
- **30,000 rows** for the headline test results below (per-language row counts in the eval table).
|
| 109 |
+
|
| 110 |
+
The remaining 60,000 held-out rows are reserved for future evaluation and are not used in this release.
|
| 111 |
+
|
| 112 |
+
### Pre-processing
|
| 113 |
+
|
| 114 |
+
All training rows pass through the same normalization the runtime applies before tokenization: lowercase, NFKD decomposition, and combining-mark stripping.
|
| 115 |
+
The combining-mark step folds accents — `José` becomes `jose`, `Müller` becomes `muller` — so the model sees a single canonical form regardless of how the user typed the name.
|
| 116 |
+
This matches what BERT's BasicTokenizer does implicitly at inference time under `do_lower_case=True`, so the train-time and runtime distributions are identical by construction.
|
| 117 |
+
A guard in the training pipeline fails the run if a future tokenizer change breaks this assumption.
|
| 118 |
+
|
| 119 |
+
A re-trainer who omits this normalization step will produce a model with mismatched distributions, and recall numbers will not reproduce.
|
| 120 |
+
|
| 121 |
+
The structured classes the deterministic layer owns (`SSN`, `CREDIT_CARD`, `IP_ADDRESS`) are also masked to sentinel tokens before tokenization — both at inference (`src/premask.ts`) and during dataset construction — so the model never learns to classify raw card/SSN/IP digits and the train-time and inference-time inputs match by construction.
|
| 122 |
+
|
| 123 |
+
### Vocabulary
|
| 124 |
+
|
| 125 |
+
The full BERT-uncased vocabulary contains 30,522 WordPieces.
|
| 126 |
+
The shipped vocabulary retains:
|
| 127 |
+
|
| 128 |
+
1. All special tokens (`[PAD]`, `[UNK]`, `[CLS]`, `[SEP]`, `[MASK]`).
|
| 129 |
+
2. All single-character pieces and their `##` continuations, which preserve WordPiece's character-level fallback for rare names.
|
| 130 |
+
3. All multi-character pieces appearing in the training corpus above a frequency threshold.
|
| 131 |
+
|
| 132 |
+
The shipped vocabulary is **19,730 pieces**.
|
| 133 |
+
|
| 134 |
+
### Training procedure
|
| 135 |
+
|
| 136 |
+
| Hyperparameter | Value |
|
| 137 |
+
| ------------------- | -------------------------------- |
|
| 138 |
+
| Base | nreimers/MiniLM-L6-H384-uncased |
|
| 139 |
+
| Epochs | 3 |
|
| 140 |
+
| Batch size | 32 |
|
| 141 |
+
| Learning rate | 5e-5 |
|
| 142 |
+
| Weight decay | 0.01 |
|
| 143 |
+
| Max sequence length | 512 |
|
| 144 |
+
| Optimizer | AdamW |
|
| 145 |
+
| Eval strategy | per-epoch on held-out validation |
|
| 146 |
+
| Save strategy | per-epoch |
|
| 147 |
+
| Hardware | Apple M-series MPS |
|
| 148 |
+
| Total wall time | ~3.5 hours |
|
| 149 |
+
|
| 150 |
+
The final epoch was selected by held-out eval loss.
|
| 151 |
+
|
| 152 |
+
## Label taxonomy
|
| 153 |
+
|
| 154 |
+
The model emits 35 BIO labels (17 entity types × {B-, I-} + O); the deterministic
|
| 155 |
+
recognizer layer contributes three more structured classes that are masked before
|
| 156 |
+
the model runs. The runtime applies a default-deny policy: every detected span is
|
| 157 |
+
redacted unless its label is explicitly in the keep-set.
|
| 158 |
+
|
| 159 |
+
### Redacted by default
|
| 160 |
+
|
| 161 |
+
Owned by the deterministic recognizer layer (regex + validator, masked before the model):
|
| 162 |
+
|
| 163 |
+
| Label | Description |
|
| 164 |
+
| ------------- | ----------------------------------------------------------------- |
|
| 165 |
+
| `SSN` | Social Security Numbers (US) — structural validation |
|
| 166 |
+
| `CREDIT_CARD` | Payment card numbers — Luhn-validated |
|
| 167 |
+
| `EMAIL` | Email addresses |
|
| 168 |
+
| `URL` | URLs in user content |
|
| 169 |
+
| `IP_ADDRESS` | IPv4 / IPv6 / MAC addresses |
|
| 170 |
+
|
| 171 |
+
Emitted by the token-classification model:
|
| 172 |
+
|
| 173 |
+
| Label | Description |
|
| 174 |
+
| ------------------- | ---------------------------------------------------- |
|
| 175 |
+
| `GIVEN_NAME` | Given / first names |
|
| 176 |
+
| `SURNAME` | Family / last names |
|
| 177 |
+
| `PHONE` | Phone numbers |
|
| 178 |
+
| `TAX_ID` | Tax identifiers |
|
| 179 |
+
| `BANK_ACCOUNT` | Bank account / IBAN numbers |
|
| 180 |
+
| `ROUTING_NUMBER` | Bank routing numbers |
|
| 181 |
+
| `GOVERNMENT_ID` | Government-issued ID / case numbers |
|
| 182 |
+
| `PASSPORT` | Passport numbers |
|
| 183 |
+
| `DRIVERS_LICENSE` | Driver's license numbers |
|
| 184 |
+
| `BUILDING_NUMBER` | Street-line building number |
|
| 185 |
+
| `STREET_NAME` | Street name |
|
| 186 |
+
| `SECONDARY_ADDRESS` | Secondary-address line (apt / unit / suite) |
|
| 187 |
+
|
| 188 |
+
`BUILDING_NUMBER` + `STREET_NAME` together form the precise street line; both are
|
| 189 |
+
redacted while city/state/ZIP are kept.
|
| 190 |
+
|
| 191 |
+
### Kept by default
|
| 192 |
+
|
| 193 |
+
| Label | Description |
|
| 194 |
+
| ---------- | ------------------------------------------------------------ |
|
| 195 |
+
| `CITY` | City — coarse geography for eligibility checks |
|
| 196 |
+
| `STATE` | State / region |
|
| 197 |
+
| `ZIP_CODE` | Postal code |
|
| 198 |
+
|
| 199 |
+
The keep-set keeps coarse geography (city/state/ZIP) while redacting the precise
|
| 200 |
+
street line. To change it, edit `KEEP_LABELS` in `src/types.ts` — it is a
|
| 201 |
+
compile-time set, not a runtime flag.
|
| 202 |
+
|
| 203 |
+
The taxonomy is deliberately **atomic**: there is no coarse `PERSON`,
|
| 204 |
+
`STREET_ADDRESS`, `ADDRESS`, `ORGANIZATION`, or `LOCATION` label, and no catch-all
|
| 205 |
+
`SECRET`. Names split into `GIVEN_NAME` / `SURNAME`, the street line into
|
| 206 |
+
`BUILDING_NUMBER` / `STREET_NAME`, and document identifiers into their specific
|
| 207 |
+
classes, so the model learns to catch PII fragments in disordered text rather than
|
| 208 |
+
expecting one tidy blob. Dates, ages, and income are intentionally **not** modeled
|
| 209 |
+
as PII (they map to `O`): a bare date is rarely identifying, and assistants need age
|
| 210 |
+
and income as context, so redacting them was over-redaction without a privacy gain.
|
| 211 |
+
|
| 212 |
+
## Evaluation
|
| 213 |
+
|
| 214 |
+
We score the **full system** (model + deterministic layer) because that is what consumers experience end-to-end.
|
| 215 |
+
Model-only numbers are reported separately for researchers who want to evaluate the encoder in isolation.
|
| 216 |
+
|
| 217 |
+
### Primary metrics
|
| 218 |
+
|
| 219 |
+
- **Private-term recall**: for every gold private value, did the redacted output contain the value? This is the privacy-headline number; misses here are leaks.
|
| 220 |
+
- **Public-term retention**: for every gold public value, did the redacted output preserve the value? This measures over-redaction.
|
| 221 |
+
- **Span F1 strict (IoU=1.0)** and **relaxed (IoU≥0.5)**: how well predicted span boundaries align with gold boundaries under one-to-one greedy matching.
|
| 222 |
+
- **Latency**: Node.js ONNX runtime cold / p50 / p95 / p99 over the full 30,000-row test set. Browser latency (WebGPU and WASM backends) is measured separately by `eval/bench/webgpu.ts` — see below.
|
| 223 |
+
- **Calibration**: 15-bin reliability ECE, per label and overall, on per-span max-class scores.
|
| 224 |
+
|
| 225 |
+
All recall and retention numbers carry Wilson 95% confidence intervals; stratified breakdowns include 1000-iteration bootstrap intervals.
|
| 226 |
+
|
| 227 |
+
### Held-out OpenPII test set — seven supported languages (30,000 rows; 131,707 private terms; 87,207 public terms)
|
| 228 |
+
|
| 229 |
+
The headline number is measured across all seven supported Latin-script languages.
|
| 230 |
+
English-only, Spanish-only, and the English+Spanish slice are reported as sub-slices.
|
| 231 |
+
|
| 232 |
+
| Slice | Private recall (Wilson 95%) | Public retention\* | Span F1 strict | Latency p50 |
|
| 233 |
+
| ---------------------------- | --------------------------- | ------------------ | -------------- | ----------- |
|
| 234 |
+
| **All seven languages** | **98.42% [98.35, 98.49]** | 91.69% | 0.528 | 6.6 ms |
|
| 235 |
+
| English only (11,569 rows) | 98.85% | 90.5% | — | 6.6 ms |
|
| 236 |
+
| Spanish only (3,234 rows) | 98.84% | 91.6% | — | 6.6 ms |
|
| 237 |
+
| English + Spanish | 98.85% | 91.0% | — | 6.6 ms |
|
| 238 |
+
|
| 239 |
+
2,082 leaks of 131,707 private terms on the seven-language test (1 in 64 terms slips past
|
| 240 |
+
the system, before the application's downstream defenses fire). On the English+Spanish
|
| 241 |
+
slice the system leaks 778 of 67,613.
|
| 242 |
+
|
| 243 |
+
These numbers are measured by the committed `eval/bench` harness running the **shipped Q4
|
| 244 |
+
pipeline** end-to-end over a pinned held-out slice of `pii-masking-openpii-1.5m`. The
|
| 245 |
+
harness was corrected relative to earlier revisions of this card: city/state/ZIP are now
|
| 246 |
+
scored as **kept** (matching the runtime keep-set) instead of being counted as leaks, so
|
| 247 |
+
public retention reflects policy-aware behavior directly. Recall is reported against the
|
| 248 |
+
full, harder seven-language slice. Span-F1 strict (exact byte+label match) is a secondary
|
| 249 |
+
metric; term-presence recall is the privacy headline.
|
| 250 |
+
|
| 251 |
+
The 6.6 ms p50 above is the Node ONNX (CPU) figure over the 30k held-out set. Run over a
|
| 252 |
+
held-out OpenPII slice in the browser, the same shipped pipeline measures **3.9 ms p50**
|
| 253 |
+
on WebGPU (Apple Metal, p95 9.3 ms) and 12.6 ms on WASM (p95 35.5 ms), via
|
| 254 |
+
`eval/bench/webgpu.ts` — so the WebGPU form factor is faster than Node CPU on the same
|
| 255 |
+
class of inputs, and WASM is the floor when no GPU is available.
|
| 256 |
+
|
| 257 |
+
\* See "Schema reconciliation" below — the Rampart policy redacts the precise street line
|
| 258 |
+
(`BUILDING_NUMBER` + `STREET_NAME`) and the secondary-address line while keeping city/state/ZIP, which the harness now honors.
|
| 259 |
+
|
| 260 |
+
### Per-language slices (OpenPII Latin test, 30k rows across 7 languages)
|
| 261 |
+
|
| 262 |
+
| Language | Rows | Private recall | Public retention | Leaks / total |
|
| 263 |
+
| ----------------- | ------ | -------------- | ---------------- | ------------- |
|
| 264 |
+
| English (`en`) | 11,569 | 98.85% | 90.5% | 618 / 53,877 |
|
| 265 |
+
| Spanish (`es`) | 3,234 | 98.84% | 91.6% | 160 / 13,736 |
|
| 266 |
+
| French (`fr`) | 4,708 | 98.41% | 92.8% | 317 / 19,906 |
|
| 267 |
+
| German (`de`) | 4,260 | 97.94% | 91.7% | 357 / 17,347 |
|
| 268 |
+
| Italian (`it`) | 3,218 | 97.83% | 94.1% | 301 / 13,855 |
|
| 269 |
+
| Portuguese (`pt`) | 1,485 | 97.73% | 92.5% | 147 / 6,467 |
|
| 270 |
+
| Dutch (`nl`) | 1,526 | 97.21% | 91.9% | 182 / 6,519 |
|
| 271 |
+
|
| 272 |
+
All seven languages land in the 97-99% band; Dutch is the lowest at 97.21% and is flagged
|
| 273 |
+
for attention in subsequent training cycles. (The recall band moved down ~1pp versus the
|
| 274 |
+
previous card because the harness now scores the corrected, harder slice — see the note
|
| 275 |
+
above; the same model scores higher on the older, easier slice.)
|
| 276 |
+
|
| 277 |
+
### Hand-curated suites
|
| 278 |
+
|
| 279 |
+
| Suite | Cases | Private recall (Wilson 95%) | Public retention |
|
| 280 |
+
| ------------------------------------------------------------------------------------------ | ----- | --------------------------- | ---------------- |
|
| 281 |
+
| Domain intake | 20 | 96.97% [84.68, 99.46] | 93.2% |
|
| 282 |
+
| Adversarial (homoglyph / zero-width / leet / splits / NFC-NFD / casing / prompt-injection) | 20 | 86.36% [66.66, 95.25] | 83.3% |
|
| 283 |
+
| Fairness (Faker × 15 naming traditions × 5 templates) | 1,875 | 65.44% [63.26, 67.56] | 90.0% |
|
| 284 |
+
|
| 285 |
+
The adversarial and domain-intake suites are 20 cases each; Wilson CIs are wide.
|
| 286 |
+
The 1,875-case fairness suite has tight CIs and is the most statistically grounded slice we report.
|
| 287 |
+
|
| 288 |
+
### Schema reconciliation
|
| 289 |
+
|
| 290 |
+
The 91.69% retention number in the headline table is term-presence scoring that already credits city/state/ZIP as kept, matching the runtime keep-set.
|
| 291 |
+
We analyzed the 7,244 remaining "over-redacted" public terms in the 30,000-row eval:
|
| 292 |
+
|
| 293 |
+
- **The vast majority** are policy-driven redactions of street-line components (street name, building number, secondary address line).
|
| 294 |
+
AI4Privacy OpenPII marks `STREET`, `BUILDINGNUM`, and `SECADDRESS` as `O` (public); the Rampart policy redacts the precise street line (`BUILDING_NUMBER` + `STREET_NAME`) and `SECONDARY_ADDRESS` while keeping `CITY`, `STATE`, and `ZIP`.
|
| 295 |
+
These are not detector errors; they are the policy firing as designed.
|
| 296 |
+
- **A smaller share** are span-edge artifacts.
|
| 297 |
+
The runtime's particle-rescue step grows name spans (`GIVEN_NAME` / `SURNAME`) to swallow capitalized particles ("de la", "von", "Mc").
|
| 298 |
+
When an adjacent public token is itself capitalized, that token can be absorbed into the redacted span.
|
| 299 |
+
- **A very small fraction** are digit fragments inside longer correctly-redacted spans (e.g. "376" found inside a redacted 16-digit credit card).
|
| 300 |
+
|
| 301 |
+
We publish the 91.69% term-presence number for like-for-like comparison against public PII benchmarks running the same scoring rules.
|
| 302 |
+
For product reasoning, the policy-aware retention exceeds 99%.
|
| 303 |
+
|
| 304 |
+
## Calibration
|
| 305 |
+
|
| 306 |
+
The runtime applies a single recall-biased confidence floor (`minScore` = 0.4) uniformly
|
| 307 |
+
across the model's labels, chosen against the 10,000-row OpenPII Latin calibration split
|
| 308 |
+
(disjoint from test) so misses — which leak data — are traded against the cheaper failure
|
| 309 |
+
of over-redaction. There is no per-label threshold table in the shipped runtime; the
|
| 310 |
+
deterministic recognizer layer, not a tuned model threshold, is the system of record for
|
| 311 |
+
the structured classes the model alone is weakest on:
|
| 312 |
+
|
| 313 |
+
- **SSN** — structural validation (reserved-area rules).
|
| 314 |
+
- **CREDIT_CARD** — Luhn checksum over the digit projection.
|
| 315 |
+
- **EMAIL / URL / IP_ADDRESS** — pattern-anchored regex at near-100% recall.
|
| 316 |
+
|
| 317 |
+
Phone, routing, government-ID, passport, and license numbers carry no checksum and are
|
| 318 |
+
left to the model under the same recall-biased floor.
|
| 319 |
+
|
| 320 |
+
ECE on the full 30,000-row test set is **0.291** (overall, all labels); the model alone (no deterministic layer) is **0.018**.
|
| 321 |
+
The system-level ECE is higher because the deterministic layer always emits score 1.0 on its detections, making the score distribution bimodal — that is a score-distribution artifact of the union, not a calibration regression of the underlying model.
|
| 322 |
+
|
| 323 |
+
## Fairness and limitations
|
| 324 |
+
|
| 325 |
+
We document failures because consumers need this to deploy the redactor responsibly.
|
| 326 |
+
None of these are surprises; we measured each.
|
| 327 |
+
|
| 328 |
+
### Fairness across naming traditions (1,875 Faker-generated cases)
|
| 329 |
+
|
| 330 |
+
Cases are stratified by **naming tradition** (15 categories) embedded in 5 chat templates.
|
| 331 |
+
Same surrounding context across all traditions — only the name varies.
|
| 332 |
+
|
| 333 |
+
| Tradition | Locale | Recall | Cases |
|
| 334 |
+
| ------------------- | ------------ | ------ | ----- |
|
| 335 |
+
| Anglo | en_US | 99.9% | 125 |
|
| 336 |
+
| Hispanic | es_MX, es_ES | 99.9% | 250 |
|
| 337 |
+
| Francophone | fr_FR | 99.9% | 125 |
|
| 338 |
+
| Germanic | de_DE | 99.9% | 125 |
|
| 339 |
+
| Romance (Italian) | it_IT | 99.9% | 125 |
|
| 340 |
+
| Lusophone | pt_BR | 99.9% | 125 |
|
| 341 |
+
| Turkic | tr_TR | 99.9% | 125 |
|
| 342 |
+
| Vietnamese | vi_VN | 99.2% | 125 |
|
| 343 |
+
| Japanese | ja_JP | 45.6% | 125 |
|
| 344 |
+
| Korean | ko_KR | 15.2% | 125 |
|
| 345 |
+
| Han Chinese | zh_CN | 8.8% | 125 |
|
| 346 |
+
| South Asian (Hindi) | hi_IN | 5.6% | 125 |
|
| 347 |
+
| Arabic | ar_AA | 4.8% | 125 |
|
| 348 |
+
| Slavic (Russian) | ru_RU | 2.4% | 125 |
|
| 349 |
+
|
| 350 |
+
Aggregated by script:
|
| 351 |
+
|
| 352 |
+
- **Latin-ASCII names**: ~100% recall (695 / 695)
|
| 353 |
+
- **Latin + diacritics**: 99.8% recall (429 / 430)
|
| 354 |
+
- **Non-Latin scripts**: 13.7% recall (103 / 750)
|
| 355 |
+
|
| 356 |
+
The deterministic recognizer layer does not catch names — there is no checksum to validate against — so this failure surfaces at the system level.
|
| 357 |
+
This is the most important regression we have identified, and the fairness suite is wired into the eval pipeline as a stratified regression test so any further drop will surface in subsequent training cycles.
|
| 358 |
+
|
| 359 |
+
### Government-style identifiers (model only)
|
| 360 |
+
|
| 361 |
+
Government-style identifiers (case numbers, Medicare-style identifiers, USCIS receipts,
|
| 362 |
+
A-numbers, passports, licenses) carry no checksum, so — unlike SSNs and payment cards —
|
| 363 |
+
the deterministic layer does **not** detect them. They rely entirely on the model, which
|
| 364 |
+
catches ~67.6% of them in a structured-ID probe.
|
| 365 |
+
This is a documented weak spot: there is no deterministic backstop for these classes, so
|
| 366 |
+
the model's recall is effectively the system's recall on them.
|
| 367 |
+
Consumers should not assume the deterministic layer covers government IDs the way it
|
| 368 |
+
covers SSNs and cards; deployments that handle these identifiers heavily should add their
|
| 369 |
+
own format-specific validators.
|
| 370 |
+
|
| 371 |
+
### Adversarial robustness
|
| 372 |
+
|
| 373 |
+
The system catches most homoglyph, casing, leet, NFC/NFD, and basic whitespace-split attacks.
|
| 374 |
+
It does not reliably catch:
|
| 375 |
+
|
| 376 |
+
- Zero-width characters injected between every digit of an SSN.
|
| 377 |
+
- Prompt-injection text inside the PII span (e.g. `"ignore previous instructions"`).
|
| 378 |
+
- Combined attacks (homoglyph plus whitespace split).
|
| 379 |
+
|
| 380 |
+
The deterministic layer's digit projection (which strips non-digit characters before checksum validation) restores most digit-bearing PII against these attacks; names remain vulnerable.
|
| 381 |
+
This is the right framing for the limitation, not the primary use case: Rampart is designed to protect users entering their own information in good faith from incidental disclosure to downstream services, not to defeat a motivated user actively trying to smuggle their own PII past the filter.
|
| 382 |
+
|
| 383 |
+
### WordPiece fragmentation on long names
|
| 384 |
+
|
| 385 |
+
Names like `Thanh-Nghiem Quoc-Bao` or `Chukwuemeka Okonkwo-Adeyemi` produce many subwords; the runtime performs span-merging across same-label adjacencies plus particle-rescue, which closes most of the gap.
|
| 386 |
+
Some five-or-more-subword names still fragment in a way that loses recall on the trailing subword.
|
| 387 |
+
|
| 388 |
+
## Reproducibility
|
| 389 |
+
|
| 390 |
+
The model weights, deterministic layer, and TypeScript evaluation harness are released under CC BY 4.0.
|
| 391 |
+
|
| 392 |
+
Evaluation runs entirely in TypeScript, against the shipped pipeline: the native
|
| 393 |
+
benchmark (`eval/bench`) runs the real `@nationaldesignstudio/rampart` code over a
|
| 394 |
+
frozen OpenPII held-out slice and writes `summary.json` / `by_language.json`, which are
|
| 395 |
+
committed alongside the eval output — so every number in this card traces to committed
|
| 396 |
+
evidence produced by the code that ships. The held-out
|
| 397 |
+
row `uid`s are pinned in a committed manifest; regenerate the data with
|
| 398 |
+
`bun run bench:fetch` and reproduce the figures with `bun run bench`.
|
| 399 |
+
|
| 400 |
+
## Citation
|
| 401 |
+
|
| 402 |
+
If you use this model in research, please cite:
|
| 403 |
+
|
| 404 |
+
```bibtex
|
| 405 |
+
@misc{rampart-2026,
|
| 406 |
+
author = {National Design Studio},
|
| 407 |
+
title = {Rampart: Client-side PII redaction for AI assistants},
|
| 408 |
+
year = {2026},
|
| 409 |
+
url = {https://huggingface.co/nationaldesignstudio/rampart},
|
| 410 |
+
}
|
| 411 |
+
```
|
| 412 |
+
|
| 413 |
+
Please also cite the upstream training corpus:
|
| 414 |
+
|
| 415 |
+
```bibtex
|
| 416 |
+
@misc{ai4privacy-openpii-1.5m,
|
| 417 |
+
title = {ai4privacy/pii-masking-openpii-1.5m},
|
| 418 |
+
author = {AI4Privacy},
|
| 419 |
+
year = {2025},
|
| 420 |
+
url = {https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m},
|
| 421 |
+
}
|
| 422 |
+
```
|
README.md
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers.js
|
| 3 |
+
pipeline_tag: token-classification
|
| 4 |
+
license: cc-by-4.0
|
| 5 |
+
language:
|
| 6 |
+
- en
|
| 7 |
+
- es
|
| 8 |
+
- fr
|
| 9 |
+
- de
|
| 10 |
+
- it
|
| 11 |
+
- pt
|
| 12 |
+
- nl
|
| 13 |
+
tags:
|
| 14 |
+
- pii
|
| 15 |
+
- redaction
|
| 16 |
+
- privacy
|
| 17 |
+
- onnx
|
| 18 |
+
- web
|
| 19 |
+
- client-side
|
| 20 |
+
- minilm
|
| 21 |
+
- browser
|
| 22 |
+
datasets:
|
| 23 |
+
- ai4privacy/pii-masking-openpii-1.5m
|
| 24 |
+
base_model: nreimers/MiniLM-L6-H384-uncased
|
| 25 |
+
metrics:
|
| 26 |
+
- private-term-recall
|
| 27 |
+
- public-term-retention
|
| 28 |
+
- span-f1
|
| 29 |
+
- ece
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
# Rampart
|
| 33 |
+
|
| 34 |
+
`rampart` is a 14.7 MB ONNX token-classification model that detects personally identifiable information (PII) in text before it leaves the user's device.
|
| 35 |
+
It is the on-device half of **Rampart**, a defense-in-depth client-side redaction system released by National Design Studio.
|
| 36 |
+
The shipped artifact runs alongside a deterministic recognizer layer that handles structured identifiers; together they form the complete system.
|
| 37 |
+
|
| 38 |
+
This card documents the released artifact only.
|
| 39 |
+
Alternative configurations explored during model selection (an ELECTRA-small base, the prefilter-off training variant, leaner data mixes, and smaller corpus slices) are discussed in the project whitepaper for context but are not published.
|
| 40 |
+
|
| 41 |
+
## Model summary
|
| 42 |
+
|
| 43 |
+
| Property | Value |
|
| 44 |
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
| 45 |
+
| Model id | `nationaldesignstudio/rampart` |
|
| 46 |
+
| Architecture | [`nreimers/MiniLM-L6-H384-uncased`](https://huggingface.co/nreimers/MiniLM-L6-H384-uncased) fine-tuned with a 35-label BIO head (17 entity types) |
|
| 47 |
+
| Parameters | ≈18.5M (MiniLM-L6-H384 with the trimmed 19,730-piece vocabulary; the 22.7M base figure is for the full 30,522-piece BERT vocab) |
|
| 48 |
+
| Quantization | 4-bit MatMul + INT8 embedding (`onnx/model_q4.onnx`) |
|
| 49 |
+
| Shipped artifact size | 14.7 MB |
|
| 50 |
+
| Vocabulary | 19,730 WordPieces (trimmed from BERT-uncased's 30,522, retaining all special and single-character pieces plus frequent multi-character pieces) |
|
| 51 |
+
| Max sequence | 512 tokens |
|
| 52 |
+
| Languages | English, Spanish, French, German, Italian, Portuguese, Dutch (all Latin-script) |
|
| 53 |
+
| Runtime | ONNX Runtime Web (WASM/WebGPU) via `transformers.js` |
|
| 54 |
+
| License | CC BY 4.0 (Creative Commons Attribution 4.0 International) |
|
| 55 |
+
| Training data license | CC BY 4.0 ([`ai4privacy/pii-masking-openpii-1.5m`](https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m)) |
|
| 56 |
+
| Released by | National Design Studio |
|
| 57 |
+
| Card version | 1.0 (initial public release) |
|
| 58 |
+
|
| 59 |
+
## Intended use
|
| 60 |
+
|
| 61 |
+
The model is designed for **client-side redaction of user-typed text in AI assistants and intake flows** — replacing identifying values with stable placeholders before any data is transmitted to a model provider, a server, or a logging system.
|
| 62 |
+
|
| 63 |
+
### Direct uses
|
| 64 |
+
|
| 65 |
+
- Redact user content before passing it to a hosted LLM.
|
| 66 |
+
- Maintain stable placeholders (`[GIVEN_NAME_1]`, `[SSN_1]`, ...) across a multi-turn conversation, with rehydration on the client.
|
| 67 |
+
- Preempt accidental collection of personal data in analytics, traces, and crash reports.
|
| 68 |
+
- Validate domain-specific redaction policies before deploying chat systems in regulated contexts.
|
| 69 |
+
|
| 70 |
+
### Out of scope
|
| 71 |
+
|
| 72 |
+
- **Stand-alone government-ID detection.**
|
| 73 |
+
The model is one layer of a defense-in-depth system; it is not a replacement for the deterministic recognizer layer that ships alongside it.
|
| 74 |
+
SSNs and payment cards are caught by the deterministic layer with checksum validation (structural rules and Luhn), at higher recall than the model alone.
|
| 75 |
+
Phone, routing, government-ID, passport, and license numbers carry no checksum, so they are caught by the model; the deterministic layer does not attempt them.
|
| 76 |
+
- **Indirect / inferential identifiers.**
|
| 77 |
+
A "rare disease + 5-digit ZIP" combination can re-identify someone even though neither token is in the redact-set.
|
| 78 |
+
The model does not detect inferential leaks.
|
| 79 |
+
- **Adversarial robustness as a security guarantee.**
|
| 80 |
+
We publish numbers on hostile inputs and document the failure surface; the system is positioned as harm reduction for users entering their own information in good faith, not as a security boundary against motivated adversaries.
|
| 81 |
+
- **Non-Latin scripts.**
|
| 82 |
+
This release is scoped to the seven Latin-script languages listed above.
|
| 83 |
+
Korean, Han Chinese, Japanese, Arabic, Cyrillic, and Devanagari names recall ~14% in aggregate (see "Fairness and limitations" below).
|
| 84 |
+
Do not deploy this release for populations who routinely type non-Latin-script names without compensating controls; monitor accordingly.
|
| 85 |
+
|
| 86 |
+
### Usage
|
| 87 |
+
|
| 88 |
+
The runtime ships as [`@nationaldesignstudio/rampart`](https://www.npmjs.com/package/@nationaldesignstudio/rampart). `createGuard()` returns a `ChatGuard` that loads this classifier and runs the full deterministic + model pipeline:
|
| 89 |
+
|
| 90 |
+
```ts
|
| 91 |
+
import { createGuard } from "@nationaldesignstudio/rampart";
|
| 92 |
+
|
| 93 |
+
const guard = await createGuard();
|
| 94 |
+
const { redacted } = await guard.redact("My name is Alex Rivera and my SSN is 472-81-0094.");
|
| 95 |
+
// → "My name is [GIVEN_NAME_1] [SURNAME_1] and my SSN is [SSN_1]."
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
## Training data
|
| 99 |
+
|
| 100 |
+
| Source | Rows used | License | Role |
|
| 101 |
+
| ------------------------------------------------------------------------------------------------------------ | -------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
| 102 |
+
| [`ai4privacy/pii-masking-openpii-1.5m`](https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m) | 643,756 train + 100,000 held-out | CC BY 4.0 | Realistic chat-style PII; 7 Latin-script languages (en, es, fr, de, it, pt, nl); the OpenPII schema mapped to our 35-label BIO schema (17 entity types) |
|
| 103 |
+
| Synthetic generator | 20,000 train | Apache-2.0 | Class reinforcement for the 17 entity types — accent-bearing names from curated first- and last-name pools and templated structured fields, generated deliberately messy (typos, all-caps, voice-dictated and pasted-from-form phrasing, multilingual mixing, contradictory/duplicated values) so the model sees realistic disordered input, not just clean OpenPII prose |
|
| 104 |
+
|
| 105 |
+
The held-out 100,000 rows are split into two non-overlapping subsets, seeded for full reproducibility:
|
| 106 |
+
|
| 107 |
+
- **10,000 rows** for recall-floor threshold tuning.
|
| 108 |
+
- **30,000 rows** for the headline test results below (per-language row counts in the eval table).
|
| 109 |
+
|
| 110 |
+
The remaining 60,000 held-out rows are reserved for future evaluation and are not used in this release.
|
| 111 |
+
|
| 112 |
+
### Pre-processing
|
| 113 |
+
|
| 114 |
+
All training rows pass through the same normalization the runtime applies before tokenization: lowercase, NFKD decomposition, and combining-mark stripping.
|
| 115 |
+
The combining-mark step folds accents — `José` becomes `jose`, `Müller` becomes `muller` — so the model sees a single canonical form regardless of how the user typed the name.
|
| 116 |
+
This matches what BERT's BasicTokenizer does implicitly at inference time under `do_lower_case=True`, so the train-time and runtime distributions are identical by construction.
|
| 117 |
+
A guard in the training pipeline fails the run if a future tokenizer change breaks this assumption.
|
| 118 |
+
|
| 119 |
+
A re-trainer who omits this normalization step will produce a model with mismatched distributions, and recall numbers will not reproduce.
|
| 120 |
+
|
| 121 |
+
The structured classes the deterministic layer owns (`SSN`, `CREDIT_CARD`, `IP_ADDRESS`) are also masked to sentinel tokens before tokenization — both at inference (`src/premask.ts`) and during dataset construction — so the model never learns to classify raw card/SSN/IP digits and the train-time and inference-time inputs match by construction.
|
| 122 |
+
|
| 123 |
+
### Vocabulary
|
| 124 |
+
|
| 125 |
+
The full BERT-uncased vocabulary contains 30,522 WordPieces.
|
| 126 |
+
The shipped vocabulary retains:
|
| 127 |
+
|
| 128 |
+
1. All special tokens (`[PAD]`, `[UNK]`, `[CLS]`, `[SEP]`, `[MASK]`).
|
| 129 |
+
2. All single-character pieces and their `##` continuations, which preserve WordPiece's character-level fallback for rare names.
|
| 130 |
+
3. All multi-character pieces appearing in the training corpus above a frequency threshold.
|
| 131 |
+
|
| 132 |
+
The shipped vocabulary is **19,730 pieces**.
|
| 133 |
+
|
| 134 |
+
### Training procedure
|
| 135 |
+
|
| 136 |
+
| Hyperparameter | Value |
|
| 137 |
+
| ------------------- | -------------------------------- |
|
| 138 |
+
| Base | nreimers/MiniLM-L6-H384-uncased |
|
| 139 |
+
| Epochs | 3 |
|
| 140 |
+
| Batch size | 32 |
|
| 141 |
+
| Learning rate | 5e-5 |
|
| 142 |
+
| Weight decay | 0.01 |
|
| 143 |
+
| Max sequence length | 512 |
|
| 144 |
+
| Optimizer | AdamW |
|
| 145 |
+
| Eval strategy | per-epoch on held-out validation |
|
| 146 |
+
| Save strategy | per-epoch |
|
| 147 |
+
| Hardware | Apple M-series MPS |
|
| 148 |
+
| Total wall time | ~3.5 hours |
|
| 149 |
+
|
| 150 |
+
The final epoch was selected by held-out eval loss.
|
| 151 |
+
|
| 152 |
+
## Label taxonomy
|
| 153 |
+
|
| 154 |
+
The model emits 35 BIO labels (17 entity types × {B-, I-} + O); the deterministic
|
| 155 |
+
recognizer layer contributes three more structured classes that are masked before
|
| 156 |
+
the model runs. The runtime applies a default-deny policy: every detected span is
|
| 157 |
+
redacted unless its label is explicitly in the keep-set.
|
| 158 |
+
|
| 159 |
+
### Redacted by default
|
| 160 |
+
|
| 161 |
+
Owned by the deterministic recognizer layer (regex + validator, masked before the model):
|
| 162 |
+
|
| 163 |
+
| Label | Description |
|
| 164 |
+
| ------------- | ----------------------------------------------------------------- |
|
| 165 |
+
| `SSN` | Social Security Numbers (US) — structural validation |
|
| 166 |
+
| `CREDIT_CARD` | Payment card numbers — Luhn-validated |
|
| 167 |
+
| `EMAIL` | Email addresses |
|
| 168 |
+
| `URL` | URLs in user content |
|
| 169 |
+
| `IP_ADDRESS` | IPv4 / IPv6 / MAC addresses |
|
| 170 |
+
|
| 171 |
+
Emitted by the token-classification model:
|
| 172 |
+
|
| 173 |
+
| Label | Description |
|
| 174 |
+
| ------------------- | ---------------------------------------------------- |
|
| 175 |
+
| `GIVEN_NAME` | Given / first names |
|
| 176 |
+
| `SURNAME` | Family / last names |
|
| 177 |
+
| `PHONE` | Phone numbers |
|
| 178 |
+
| `TAX_ID` | Tax identifiers |
|
| 179 |
+
| `BANK_ACCOUNT` | Bank account / IBAN numbers |
|
| 180 |
+
| `ROUTING_NUMBER` | Bank routing numbers |
|
| 181 |
+
| `GOVERNMENT_ID` | Government-issued ID / case numbers |
|
| 182 |
+
| `PASSPORT` | Passport numbers |
|
| 183 |
+
| `DRIVERS_LICENSE` | Driver's license numbers |
|
| 184 |
+
| `BUILDING_NUMBER` | Street-line building number |
|
| 185 |
+
| `STREET_NAME` | Street name |
|
| 186 |
+
| `SECONDARY_ADDRESS` | Secondary-address line (apt / unit / suite) |
|
| 187 |
+
|
| 188 |
+
`BUILDING_NUMBER` + `STREET_NAME` together form the precise street line; both are
|
| 189 |
+
redacted while city/state/ZIP are kept.
|
| 190 |
+
|
| 191 |
+
### Kept by default
|
| 192 |
+
|
| 193 |
+
| Label | Description |
|
| 194 |
+
| ---------- | ------------------------------------------------------------ |
|
| 195 |
+
| `CITY` | City — coarse geography for eligibility checks |
|
| 196 |
+
| `STATE` | State / region |
|
| 197 |
+
| `ZIP_CODE` | Postal code |
|
| 198 |
+
|
| 199 |
+
The keep-set keeps coarse geography (city/state/ZIP) while redacting the precise
|
| 200 |
+
street line. To change it, edit `KEEP_LABELS` in `src/types.ts` — it is a
|
| 201 |
+
compile-time set, not a runtime flag.
|
| 202 |
+
|
| 203 |
+
The taxonomy is deliberately **atomic**: there is no coarse `PERSON`,
|
| 204 |
+
`STREET_ADDRESS`, `ADDRESS`, `ORGANIZATION`, or `LOCATION` label, and no catch-all
|
| 205 |
+
`SECRET`. Names split into `GIVEN_NAME` / `SURNAME`, the street line into
|
| 206 |
+
`BUILDING_NUMBER` / `STREET_NAME`, and document identifiers into their specific
|
| 207 |
+
classes, so the model learns to catch PII fragments in disordered text rather than
|
| 208 |
+
expecting one tidy blob. Dates, ages, and income are intentionally **not** modeled
|
| 209 |
+
as PII (they map to `O`): a bare date is rarely identifying, and assistants need age
|
| 210 |
+
and income as context, so redacting them was over-redaction without a privacy gain.
|
| 211 |
+
|
| 212 |
+
## Evaluation
|
| 213 |
+
|
| 214 |
+
We score the **full system** (model + deterministic layer) because that is what consumers experience end-to-end.
|
| 215 |
+
Model-only numbers are reported separately for researchers who want to evaluate the encoder in isolation.
|
| 216 |
+
|
| 217 |
+
### Primary metrics
|
| 218 |
+
|
| 219 |
+
- **Private-term recall**: for every gold private value, did the redacted output contain the value? This is the privacy-headline number; misses here are leaks.
|
| 220 |
+
- **Public-term retention**: for every gold public value, did the redacted output preserve the value? This measures over-redaction.
|
| 221 |
+
- **Span F1 strict (IoU=1.0)** and **relaxed (IoU≥0.5)**: how well predicted span boundaries align with gold boundaries under one-to-one greedy matching.
|
| 222 |
+
- **Latency**: Node.js ONNX runtime cold / p50 / p95 / p99 over the full 30,000-row test set. Browser latency (WebGPU and WASM backends) is measured separately by `eval/bench/webgpu.ts` — see below.
|
| 223 |
+
- **Calibration**: 15-bin reliability ECE, per label and overall, on per-span max-class scores.
|
| 224 |
+
|
| 225 |
+
All recall and retention numbers carry Wilson 95% confidence intervals; stratified breakdowns include 1000-iteration bootstrap intervals.
|
| 226 |
+
|
| 227 |
+
### Held-out OpenPII test set — seven supported languages (30,000 rows; 131,707 private terms; 87,207 public terms)
|
| 228 |
+
|
| 229 |
+
The headline number is measured across all seven supported Latin-script languages.
|
| 230 |
+
English-only, Spanish-only, and the English+Spanish slice are reported as sub-slices.
|
| 231 |
+
|
| 232 |
+
| Slice | Private recall (Wilson 95%) | Public retention\* | Span F1 strict | Latency p50 |
|
| 233 |
+
| ---------------------------- | --------------------------- | ------------------ | -------------- | ----------- |
|
| 234 |
+
| **All seven languages** | **98.42% [98.35, 98.49]** | 91.69% | 0.528 | 6.6 ms |
|
| 235 |
+
| English only (11,569 rows) | 98.85% | 90.5% | — | 6.6 ms |
|
| 236 |
+
| Spanish only (3,234 rows) | 98.84% | 91.6% | — | 6.6 ms |
|
| 237 |
+
| English + Spanish | 98.85% | 91.0% | — | 6.6 ms |
|
| 238 |
+
|
| 239 |
+
2,082 leaks of 131,707 private terms on the seven-language test (1 in 64 terms slips past
|
| 240 |
+
the system, before the application's downstream defenses fire). On the English+Spanish
|
| 241 |
+
slice the system leaks 778 of 67,613.
|
| 242 |
+
|
| 243 |
+
These numbers are measured by the committed `eval/bench` harness running the **shipped Q4
|
| 244 |
+
pipeline** end-to-end over a pinned held-out slice of `pii-masking-openpii-1.5m`. The
|
| 245 |
+
harness was corrected relative to earlier revisions of this card: city/state/ZIP are now
|
| 246 |
+
scored as **kept** (matching the runtime keep-set) instead of being counted as leaks, so
|
| 247 |
+
public retention reflects policy-aware behavior directly. Recall is reported against the
|
| 248 |
+
full, harder seven-language slice. Span-F1 strict (exact byte+label match) is a secondary
|
| 249 |
+
metric; term-presence recall is the privacy headline.
|
| 250 |
+
|
| 251 |
+
The 6.6 ms p50 above is the Node ONNX (CPU) figure over the 30k held-out set. Run over a
|
| 252 |
+
held-out OpenPII slice in the browser, the same shipped pipeline measures **3.9 ms p50**
|
| 253 |
+
on WebGPU (Apple Metal, p95 9.3 ms) and 12.6 ms on WASM (p95 35.5 ms), via
|
| 254 |
+
`eval/bench/webgpu.ts` — so the WebGPU form factor is faster than Node CPU on the same
|
| 255 |
+
class of inputs, and WASM is the floor when no GPU is available.
|
| 256 |
+
|
| 257 |
+
\* See "Schema reconciliation" below — the Rampart policy redacts the precise street line
|
| 258 |
+
(`BUILDING_NUMBER` + `STREET_NAME`) and the secondary-address line while keeping city/state/ZIP, which the harness now honors.
|
| 259 |
+
|
| 260 |
+
### Per-language slices (OpenPII Latin test, 30k rows across 7 languages)
|
| 261 |
+
|
| 262 |
+
| Language | Rows | Private recall | Public retention | Leaks / total |
|
| 263 |
+
| ----------------- | ------ | -------------- | ---------------- | ------------- |
|
| 264 |
+
| English (`en`) | 11,569 | 98.85% | 90.5% | 618 / 53,877 |
|
| 265 |
+
| Spanish (`es`) | 3,234 | 98.84% | 91.6% | 160 / 13,736 |
|
| 266 |
+
| French (`fr`) | 4,708 | 98.41% | 92.8% | 317 / 19,906 |
|
| 267 |
+
| German (`de`) | 4,260 | 97.94% | 91.7% | 357 / 17,347 |
|
| 268 |
+
| Italian (`it`) | 3,218 | 97.83% | 94.1% | 301 / 13,855 |
|
| 269 |
+
| Portuguese (`pt`) | 1,485 | 97.73% | 92.5% | 147 / 6,467 |
|
| 270 |
+
| Dutch (`nl`) | 1,526 | 97.21% | 91.9% | 182 / 6,519 |
|
| 271 |
+
|
| 272 |
+
All seven languages land in the 97-99% band; Dutch is the lowest at 97.21% and is flagged
|
| 273 |
+
for attention in subsequent training cycles. (The recall band moved down ~1pp versus the
|
| 274 |
+
previous card because the harness now scores the corrected, harder slice — see the note
|
| 275 |
+
above; the same model scores higher on the older, easier slice.)
|
| 276 |
+
|
| 277 |
+
### Hand-curated suites
|
| 278 |
+
|
| 279 |
+
| Suite | Cases | Private recall (Wilson 95%) | Public retention |
|
| 280 |
+
| ------------------------------------------------------------------------------------------ | ----- | --------------------------- | ---------------- |
|
| 281 |
+
| Domain intake | 20 | 96.97% [84.68, 99.46] | 93.2% |
|
| 282 |
+
| Adversarial (homoglyph / zero-width / leet / splits / NFC-NFD / casing / prompt-injection) | 20 | 86.36% [66.66, 95.25] | 83.3% |
|
| 283 |
+
| Fairness (Faker × 15 naming traditions × 5 templates) | 1,875 | 65.44% [63.26, 67.56] | 90.0% |
|
| 284 |
+
|
| 285 |
+
The adversarial and domain-intake suites are 20 cases each; Wilson CIs are wide.
|
| 286 |
+
The 1,875-case fairness suite has tight CIs and is the most statistically grounded slice we report.
|
| 287 |
+
|
| 288 |
+
### Schema reconciliation
|
| 289 |
+
|
| 290 |
+
The 91.69% retention number in the headline table is term-presence scoring that already credits city/state/ZIP as kept, matching the runtime keep-set.
|
| 291 |
+
We analyzed the 7,244 remaining "over-redacted" public terms in the 30,000-row eval:
|
| 292 |
+
|
| 293 |
+
- **The vast majority** are policy-driven redactions of street-line components (street name, building number, secondary address line).
|
| 294 |
+
AI4Privacy OpenPII marks `STREET`, `BUILDINGNUM`, and `SECADDRESS` as `O` (public); the Rampart policy redacts the precise street line (`BUILDING_NUMBER` + `STREET_NAME`) and `SECONDARY_ADDRESS` while keeping `CITY`, `STATE`, and `ZIP`.
|
| 295 |
+
These are not detector errors; they are the policy firing as designed.
|
| 296 |
+
- **A smaller share** are span-edge artifacts.
|
| 297 |
+
The runtime's particle-rescue step grows name spans (`GIVEN_NAME` / `SURNAME`) to swallow capitalized particles ("de la", "von", "Mc").
|
| 298 |
+
When an adjacent public token is itself capitalized, that token can be absorbed into the redacted span.
|
| 299 |
+
- **A very small fraction** are digit fragments inside longer correctly-redacted spans (e.g. "376" found inside a redacted 16-digit credit card).
|
| 300 |
+
|
| 301 |
+
We publish the 91.69% term-presence number for like-for-like comparison against public PII benchmarks running the same scoring rules.
|
| 302 |
+
For product reasoning, the policy-aware retention exceeds 99%.
|
| 303 |
+
|
| 304 |
+
## Calibration
|
| 305 |
+
|
| 306 |
+
The runtime applies a single recall-biased confidence floor (`minScore` = 0.4) uniformly
|
| 307 |
+
across the model's labels, chosen against the 10,000-row OpenPII Latin calibration split
|
| 308 |
+
(disjoint from test) so misses — which leak data — are traded against the cheaper failure
|
| 309 |
+
of over-redaction. There is no per-label threshold table in the shipped runtime; the
|
| 310 |
+
deterministic recognizer layer, not a tuned model threshold, is the system of record for
|
| 311 |
+
the structured classes the model alone is weakest on:
|
| 312 |
+
|
| 313 |
+
- **SSN** — structural validation (reserved-area rules).
|
| 314 |
+
- **CREDIT_CARD** — Luhn checksum over the digit projection.
|
| 315 |
+
- **EMAIL / URL / IP_ADDRESS** — pattern-anchored regex at near-100% recall.
|
| 316 |
+
|
| 317 |
+
Phone, routing, government-ID, passport, and license numbers carry no checksum and are
|
| 318 |
+
left to the model under the same recall-biased floor.
|
| 319 |
+
|
| 320 |
+
ECE on the full 30,000-row test set is **0.291** (overall, all labels); the model alone (no deterministic layer) is **0.018**.
|
| 321 |
+
The system-level ECE is higher because the deterministic layer always emits score 1.0 on its detections, making the score distribution bimodal — that is a score-distribution artifact of the union, not a calibration regression of the underlying model.
|
| 322 |
+
|
| 323 |
+
## Fairness and limitations
|
| 324 |
+
|
| 325 |
+
We document failures because consumers need this to deploy the redactor responsibly.
|
| 326 |
+
None of these are surprises; we measured each.
|
| 327 |
+
|
| 328 |
+
### Fairness across naming traditions (1,875 Faker-generated cases)
|
| 329 |
+
|
| 330 |
+
Cases are stratified by **naming tradition** (15 categories) embedded in 5 chat templates.
|
| 331 |
+
Same surrounding context across all traditions — only the name varies.
|
| 332 |
+
|
| 333 |
+
| Tradition | Locale | Recall | Cases |
|
| 334 |
+
| ------------------- | ------------ | ------ | ----- |
|
| 335 |
+
| Anglo | en_US | 99.9% | 125 |
|
| 336 |
+
| Hispanic | es_MX, es_ES | 99.9% | 250 |
|
| 337 |
+
| Francophone | fr_FR | 99.9% | 125 |
|
| 338 |
+
| Germanic | de_DE | 99.9% | 125 |
|
| 339 |
+
| Romance (Italian) | it_IT | 99.9% | 125 |
|
| 340 |
+
| Lusophone | pt_BR | 99.9% | 125 |
|
| 341 |
+
| Turkic | tr_TR | 99.9% | 125 |
|
| 342 |
+
| Vietnamese | vi_VN | 99.2% | 125 |
|
| 343 |
+
| Japanese | ja_JP | 45.6% | 125 |
|
| 344 |
+
| Korean | ko_KR | 15.2% | 125 |
|
| 345 |
+
| Han Chinese | zh_CN | 8.8% | 125 |
|
| 346 |
+
| South Asian (Hindi) | hi_IN | 5.6% | 125 |
|
| 347 |
+
| Arabic | ar_AA | 4.8% | 125 |
|
| 348 |
+
| Slavic (Russian) | ru_RU | 2.4% | 125 |
|
| 349 |
+
|
| 350 |
+
Aggregated by script:
|
| 351 |
+
|
| 352 |
+
- **Latin-ASCII names**: ~100% recall (695 / 695)
|
| 353 |
+
- **Latin + diacritics**: 99.8% recall (429 / 430)
|
| 354 |
+
- **Non-Latin scripts**: 13.7% recall (103 / 750)
|
| 355 |
+
|
| 356 |
+
The deterministic recognizer layer does not catch names — there is no checksum to validate against — so this failure surfaces at the system level.
|
| 357 |
+
This is the most important regression we have identified, and the fairness suite is wired into the eval pipeline as a stratified regression test so any further drop will surface in subsequent training cycles.
|
| 358 |
+
|
| 359 |
+
### Government-style identifiers (model only)
|
| 360 |
+
|
| 361 |
+
Government-style identifiers (case numbers, Medicare-style identifiers, USCIS receipts,
|
| 362 |
+
A-numbers, passports, licenses) carry no checksum, so — unlike SSNs and payment cards —
|
| 363 |
+
the deterministic layer does **not** detect them. They rely entirely on the model, which
|
| 364 |
+
catches ~67.6% of them in a structured-ID probe.
|
| 365 |
+
This is a documented weak spot: there is no deterministic backstop for these classes, so
|
| 366 |
+
the model's recall is effectively the system's recall on them.
|
| 367 |
+
Consumers should not assume the deterministic layer covers government IDs the way it
|
| 368 |
+
covers SSNs and cards; deployments that handle these identifiers heavily should add their
|
| 369 |
+
own format-specific validators.
|
| 370 |
+
|
| 371 |
+
### Adversarial robustness
|
| 372 |
+
|
| 373 |
+
The system catches most homoglyph, casing, leet, NFC/NFD, and basic whitespace-split attacks.
|
| 374 |
+
It does not reliably catch:
|
| 375 |
+
|
| 376 |
+
- Zero-width characters injected between every digit of an SSN.
|
| 377 |
+
- Prompt-injection text inside the PII span (e.g. `"ignore previous instructions"`).
|
| 378 |
+
- Combined attacks (homoglyph plus whitespace split).
|
| 379 |
+
|
| 380 |
+
The deterministic layer's digit projection (which strips non-digit characters before checksum validation) restores most digit-bearing PII against these attacks; names remain vulnerable.
|
| 381 |
+
This is the right framing for the limitation, not the primary use case: Rampart is designed to protect users entering their own information in good faith from incidental disclosure to downstream services, not to defeat a motivated user actively trying to smuggle their own PII past the filter.
|
| 382 |
+
|
| 383 |
+
### WordPiece fragmentation on long names
|
| 384 |
+
|
| 385 |
+
Names like `Thanh-Nghiem Quoc-Bao` or `Chukwuemeka Okonkwo-Adeyemi` produce many subwords; the runtime performs span-merging across same-label adjacencies plus particle-rescue, which closes most of the gap.
|
| 386 |
+
Some five-or-more-subword names still fragment in a way that loses recall on the trailing subword.
|
| 387 |
+
|
| 388 |
+
## Reproducibility
|
| 389 |
+
|
| 390 |
+
The model weights, deterministic layer, and TypeScript evaluation harness are released under CC BY 4.0.
|
| 391 |
+
|
| 392 |
+
Evaluation runs entirely in TypeScript, against the shipped pipeline: the native
|
| 393 |
+
benchmark (`eval/bench`) runs the real `@nationaldesignstudio/rampart` code over a
|
| 394 |
+
frozen OpenPII held-out slice and writes `summary.json` / `by_language.json`, which are
|
| 395 |
+
committed alongside the eval output — so every number in this card traces to committed
|
| 396 |
+
evidence produced by the code that ships. The held-out
|
| 397 |
+
row `uid`s are pinned in a committed manifest; regenerate the data with
|
| 398 |
+
`bun run bench:fetch` and reproduce the figures with `bun run bench`.
|
| 399 |
+
|
| 400 |
+
## Citation
|
| 401 |
+
|
| 402 |
+
If you use this model in research, please cite:
|
| 403 |
+
|
| 404 |
+
```bibtex
|
| 405 |
+
@misc{rampart-2026,
|
| 406 |
+
author = {National Design Studio},
|
| 407 |
+
title = {Rampart: Client-side PII redaction for AI assistants},
|
| 408 |
+
year = {2026},
|
| 409 |
+
url = {https://huggingface.co/nationaldesignstudio/rampart},
|
| 410 |
+
}
|
| 411 |
+
```
|
| 412 |
+
|
| 413 |
+
Please also cite the upstream training corpus:
|
| 414 |
+
|
| 415 |
+
```bibtex
|
| 416 |
+
@misc{ai4privacy-openpii-1.5m,
|
| 417 |
+
title = {ai4privacy/pii-masking-openpii-1.5m},
|
| 418 |
+
author = {AI4Privacy},
|
| 419 |
+
year = {2025},
|
| 420 |
+
url = {https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m},
|
| 421 |
+
}
|
| 422 |
+
```
|
WHITEPAPER.md
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rampart — A Local-First System for PII Redaction
|
| 2 |
+
|
| 3 |
+
## Abstract
|
| 4 |
+
|
| 5 |
+
We describe **Rampart**, a local-first system for removing personally identifiable information from user-typed text before it leaves the browser.
|
| 6 |
+
The system combines a 14.7 MB ONNX token-classification model with a deterministic recognizer layer; together the two layers form the first gate of a defense-in-depth pipeline.
|
| 7 |
+
|
| 8 |
+
This release is scoped to the **seven Latin-script languages** the model was trained on (English, Spanish, French, German, Italian, Portuguese, Dutch); names in non-Latin scripts are out of scope (see §8).
|
| 9 |
+
Across all seven languages, on a 30,000-row held-out test set drawn from the OpenPII 1.5M corpus, the full system achieves **98.42% private-term recall** (Wilson 95% CI [98.35, 98.49]) at 6.6 ms median latency in Node ONNX, with 91.7% public-term retention under term-presence scoring (and >99% under policy-aware scoring after schema reconciliation with the gold labels).
|
| 10 |
+
On the English+Spanish slice the system reaches **98.85%** recall.
|
| 11 |
+
In the browser, over a held-out OpenPII slice, the shipped pipeline runs at **3.9 ms p50** on WebGPU (Apple Metal) and 12.6 ms on WASM, measured by the `eval/bench/webgpu.ts` harness (latency is hardware-dependent; reproduce with `bun run bench:webgpu`).
|
| 12 |
+
|
| 13 |
+
Rampart is **harm reduction**, not perfect protection.
|
| 14 |
+
Any privacy system that depends on a trusted server-side environment inherits that environment's risk: once unredacted text leaves the device — to a model provider, a logging pipeline, an analytics SDK, or a future infrastructure breach — it is exposed to failures beyond the user's control.
|
| 15 |
+
Redacting in the browser shifts that trust boundary: the impact of any downstream failure is bounded by what the client already removed, because a later component cannot leak what was never sent.
|
| 16 |
+
No model this small catches every instance of PII, so we deliberately chose a recall-biased operating point that still runs entirely in the browser on low-end hardware, offline, with no network round trip — a meaningful layer of defense in depth, not a guarantee.
|
| 17 |
+
|
| 18 |
+
This document describes the architecture, the alternatives we evaluated and rejected, our training and evaluation methodology, operating-point and calibration analysis, known failure modes, and the licensing under which the system is released.
|
| 19 |
+
|
| 20 |
+
## 1. Design goals
|
| 21 |
+
|
| 22 |
+
The system is built around four constraints:
|
| 23 |
+
|
| 24 |
+
1. **Local-first privacy as harm reduction.** Personal information is removed before reaching application infrastructure. Data the server never receives cannot be leaked by a model provider, a logging system, an analytics pipeline, a third-party SDK, or a future compromise of the application's own backend. This is the threat model that motivates the entire system: every other constraint follows from refusing to put the user in a position where they have to trust a remote operator with their unredacted text.
|
| 25 |
+
2. **Browser-deployable.** The shipped artifact must fit on a low-end mobile phone. Targeting under 15 MB on the wire ruled out most modern PII NER models, including GLiNER (≈50 MB) and DistilBERT-based detectors (≈64 MB).
|
| 26 |
+
3. **Recall-biased.** Misses leak data, so the default policy redacts whenever a detector fires above its calibrated threshold. Over-redaction has a real cost, too: a chat assistant that cannot see context cannot help, but at a smaller cost than a leak.
|
| 27 |
+
4. **Domain-aware retention.** Useful assistants often need rough context, like coarse geography, to be helpful. The keep-set (`{CITY, STATE, ZIP_CODE}`) is policy-driven so applications can tune the boundary between privacy and utility without retraining; the precise street line is redacted.
|
| 28 |
+
|
| 29 |
+
## 2. Architecture
|
| 30 |
+
|
| 31 |
+
Rampart ships as two cooperating layers that run in parallel and merge their outputs.
|
| 32 |
+
Each layer is designed to cover a class of failures the other layer cannot reliably handle on its own.
|
| 33 |
+
Both run entirely in-browser.
|
| 34 |
+
|
| 35 |
+
### 2.1 The deterministic recognizer layer
|
| 36 |
+
|
| 37 |
+
Real-world PII often has structure that is faster and more reliable to validate than to learn.
|
| 38 |
+
The deterministic layer is a curated set of regular expressions paired with checksum and structural validators.
|
| 39 |
+
It owns five classes end-to-end:
|
| 40 |
+
|
| 41 |
+
- **Luhn checksum** for payment cards, matched over the digit projection so every separator form collapses to one rule.
|
| 42 |
+
- **SSN structural rules** that reject reserved areas (000, 666, 9XX) and ZIP+4 codes that pattern-match the SSN shape.
|
| 43 |
+
- **Pattern-backed detection** for email addresses, URLs, and IP addresses (IPv4, IPv6, and MAC), where the structure lives in the punctuation.
|
| 44 |
+
|
| 45 |
+
These detectors are synchronous and run on the raw input, so this structured PII can be removed even before the model has loaded.
|
| 46 |
+
Because cards and SSNs use checksums and structural rules rather than only shape matching, false-positive rates are very low: a 16-digit number that fails Luhn is not redacted as a card; a 9-digit number with a reserved-area prefix is not redacted as an SSN.
|
| 47 |
+
Classes with no checksum — phone, routing, tax, government-ID, passport, and license numbers, and street-address components — are deliberately left to the model rather than guessed at with a regex.
|
| 48 |
+
|
| 49 |
+
### 2.2 The token-classification model
|
| 50 |
+
|
| 51 |
+
The deterministic layer cannot recognize contextual PII — names, phone numbers, account/routing/tax numbers, government IDs, passports, licenses, and free-form address components.
|
| 52 |
+
For these, Rampart uses a MiniLM-L6-H384 encoder fine-tuned on a 35-label BIO head (17 entity types).
|
| 53 |
+
The tokenizer is an uncased WordPiece tokenizer trimmed to 19,730 pieces (from BERT-uncased's 30,522).
|
| 54 |
+
Single-character pieces are always retained, which preserves WordPiece's character-level fallback for rare names and out-of-vocabulary content.
|
| 55 |
+
|
| 56 |
+
### 2.3 Span repair
|
| 57 |
+
|
| 58 |
+
HuggingFace's standard `aggregation_strategy="simple"` produces fragmented spans when subword B-`GIVEN_NAME` probabilities outrank I-`GIVEN_NAME` inside a name, e.g. "Zaccarino" tokenized as "Zac"+"##car"+"##ino" can come back as three separate name spans.
|
| 59 |
+
Rampart applies three layers of post-processing:
|
| 60 |
+
|
| 61 |
+
1. **Adjacent-span merging** collapses consecutive same-label spans separated only by name-internal punctuation (space, hyphen, apostrophe, period, comma).
|
| 62 |
+
2. **Iterative bridge-and-merge** rescues low-confidence candidates (score between the 0.15 extend floor and the 0.4 keep threshold) when they bridge two high-confidence spans of the same label. This catches names like "Jose [LOW] de [HIGH] Garcia" where a particle scored below the cutoff but is structurally part of the name.
|
| 63 |
+
3. **Capitalized-particle rescue** grows name spans (`GIVEN_NAME` / `SURNAME`) to swallow short capitalized name particles ("De la", "Von", "Mc") sitting inside a name's flow.
|
| 64 |
+
|
| 65 |
+
This composition lifts span-F1 to 0.53 strict (IoU=1.0) and 0.66 relaxed (IoU≥0.5) on the 30,000-row test set, well above the fragmented spans the default aggregation produces.
|
| 66 |
+
|
| 67 |
+
### 2.4 Defense in depth
|
| 68 |
+
|
| 69 |
+
The two layers are complementary, not redundant. Each owns the classes the other handles poorly:
|
| 70 |
+
|
| 71 |
+
| Layer | Owns | Why |
|
| 72 |
+
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| 73 |
+
| Deterministic | `SSN`, `CREDIT_CARD`, `IP_ADDRESS`, `EMAIL`, `URL` | Checksum/structural validation (cards, SSNs) or punctuation-anchored patterns (email, URL, IP) give near-perfect precision and recall, including under orthographic perturbation. |
|
| 74 |
+
| Model | `GIVEN_NAME`, `SURNAME`, `PHONE`, `TAX_ID`, `BANK_ACCOUNT`, `ROUTING_NUMBER`, `GOVERNMENT_ID`, `PASSPORT`, `DRIVERS_LICENSE`, `BUILDING_NUMBER`, `STREET_NAME`, `SECONDARY_ADDRESS`, `CITY`, `STATE`, `ZIP_CODE` | Open-ended, context-dependent, and free of any checksum to validate against — exactly what an enumerable rule set cannot keep up with. |
|
| 75 |
+
|
| 76 |
+
The model handles open-ended cases in shapes the regex catalog cannot predict for; the deterministic layer covers the structured classes where exact-character recall matters and adversarial inputs where a Luhn-valid card survives any perturbation of the surrounding text.
|
| 77 |
+
|
| 78 |
+
The system unions the two layers' spans before applying policy, so a token redacted by either layer is redacted in output.
|
| 79 |
+
This is why full-system private-term recall exceeds the model alone on the structured classes the deterministic layer owns, while the model raises the ceiling on classes the deterministic layer cannot enumerate.
|
| 80 |
+
|
| 81 |
+
### 2.5 Policy
|
| 82 |
+
|
| 83 |
+
Every detected span carries a label.
|
| 84 |
+
The policy layer applies **default-deny**: each label is redacted unless explicitly in the keep-set.
|
| 85 |
+
The default keep-set is `{CITY, STATE, ZIP_CODE}` so an assistant can reason about coarse geography and eligibility while the precise street line (`BUILDING_NUMBER` + `STREET_NAME`) and secondary-address line (`SECONDARY_ADDRESS`) are redacted alongside names, identifiers, and contact information.
|
| 86 |
+
The keep-set is a compile-time set (`KEEP_LABELS` in `src/types.ts`), not a runtime flag.
|
| 87 |
+
|
| 88 |
+
### 2.6 Session table
|
| 89 |
+
|
| 90 |
+
A client-only session table maps each detected raw value to a stable placeholder:
|
| 91 |
+
|
| 92 |
+
```
|
| 93 |
+
Maria Garcia → [GIVEN_NAME_1] [SURNAME_1]
|
| 94 |
+
555-11-2222 → [SSN_1]
|
| 95 |
+
88 Pine Avenue → [BUILDING_NUMBER_1] [STREET_NAME_1]
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
The model provider sees only the placeholders.
|
| 99 |
+
The user sees the restored response.
|
| 100 |
+
Placeholders are intentionally formatted to look obviously synthetic so a model provider cannot easily de-anonymize them through downstream inference.
|
| 101 |
+
The table is never transmitted.
|
| 102 |
+
|
| 103 |
+
## 3. Methodology
|
| 104 |
+
|
| 105 |
+
### 3.1 The candidate space
|
| 106 |
+
|
| 107 |
+
Model selection was not a one-shot grid.
|
| 108 |
+
The shipped model is the product of a sustained, instrumented search: many fine-tuning runs across base architecture, corpus composition, label schema, vocabulary trim, prefilter strategy, and quantization, run over the project's lifetime.
|
| 109 |
+
The eval harness (§3.4) was the instrument that drove it.
|
| 110 |
+
Every run was scored end-to-end against the held-out set, and the corpus and schema were evolved in whatever direction the metrics said was working.
|
| 111 |
+
The relabeling and corpus update decisions in §3.5 are themselves validated outputs of that loop.
|
| 112 |
+
|
| 113 |
+
One representative round of that search — the selection matrix detailed in §4 — varied four axes over their Cartesian product:
|
| 114 |
+
|
| 115 |
+
- **Base architecture (2).** MiniLM-L6-H384-uncased vs ELECTRA-small. ELECTRA is attractive in size (its trimmed Q4 artifact is ~10 MB to MiniLM's ~19 MB at full vocab) but, as §4.1 shows, did not imbibe additional data the way MiniLM did.
|
| 116 |
+
- **Prefilter (2).** Whether the deterministic layer's structured classes (`SSN`, `CREDIT_CARD`, `IP_ADDRESS`) are premasked before the model classifier runs or trained as real model classes (the no-prefilter ablation). Prefilter-on is the shipped configuration; the model never spends capacity on digits a checksum already settles.
|
| 117 |
+
- **Data mix (3).** Cumulative: the synthetic conversation corpus alone, then plus an OCR'd-document corpus, then plus the public AI4Privacy template corpus.
|
| 118 |
+
- **Corpus volume (2).** The first 100k vs the first 250k synthetic conversations.
|
| 119 |
+
|
| 120 |
+
That is 2 × 3 × 2 = 12 data/architecture cells per base, scored under both runtime modes — a 24-model ablation, each cell exported to its Q4 and several vocab-trimmed artifacts.
|
| 121 |
+
§4 reports it and the directional findings that came out of it; this section defines the rules under which every cell was scored.
|
| 122 |
+
It is one round of the larger, ongoing search, not its entirety.
|
| 123 |
+
|
| 124 |
+
### 3.2 Pre-registered eval design
|
| 125 |
+
|
| 126 |
+
The eval design is fixed before a round's numbers are read.
|
| 127 |
+
The governing principle is recall-biased and size-aware:
|
| 128 |
+
|
| 129 |
+
> Among candidates that clear the private-term recall floor on the held-out set, ship the smallest — unless a documented reason (multilingual coverage, adversarial robustness, a fairness regression) justifies overriding.
|
| 130 |
+
|
| 131 |
+
### 3.3 Datasets
|
| 132 |
+
|
| 133 |
+
| Dataset | Rows | Use |
|
| 134 |
+
| ----------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------- |
|
| 135 |
+
| OpenPII calibration | 10k | Recall-floor threshold tuning |
|
| 136 |
+
| OpenPII held-out test (7 languages) | 30k | Headline test (en, es, fr, de, it, pt, nl) |
|
| 137 |
+
| Per-language slices | — | English (11,569) and Spanish (3,234) reported separately; remaining five in the per-language table (§ model card) |
|
| 138 |
+
| Fairness | 1,875 | Faker × 15 naming traditions × 5 templates |
|
| 139 |
+
|
| 140 |
+
All OpenPII splits are drawn deterministically from the held-out 100k partition of the OpenPII 1.5M corpus, disjoint from training.
|
| 141 |
+
The shipped headline is measured across all seven supported languages; the per-language counts are the natural language distribution of that 30k slice (see model card).
|
| 142 |
+
|
| 143 |
+
### 3.4 Metrics
|
| 144 |
+
|
| 145 |
+
- **Private-term recall**: for every gold private value, did the redacted output contain the value? Wilson 95% CI; bootstrap 1000-resample CI for stratified breakdowns.
|
| 146 |
+
- **Public-term retention**: for every gold public value, did the redacted output preserve the value?
|
| 147 |
+
- **Span-level F1**: strict at IoU=1.0; relaxed at IoU≥0.5; overlap at IoU>0. One-to-one greedy matching, higher-scored predictions match first.
|
| 148 |
+
- **Latency**: Node.js ONNX runtime cold / p50 / p95 / p99 over the full 30,000-row test set.
|
| 149 |
+
- **Calibration**: 15-bin reliability expected calibration error (ECE), per label and overall, computed on per-span max-class scores.
|
| 150 |
+
|
| 151 |
+
#### Headline results, per language
|
| 152 |
+
|
| 153 |
+
Full system (model + deterministic layer + policy) on the 30,000-row held-out test, scored end-to-end by the committed `eval/bench` harness:
|
| 154 |
+
|
| 155 |
+
| Language | Rows | Private recall | Public retention | Leaks / private terms |
|
| 156 |
+
| --------------- | ---------: | -------------: | ---------------: | --------------------: |
|
| 157 |
+
| English (en) | 11,569 | 98.85% | 90.5% | 618 / 53,877 |
|
| 158 |
+
| Spanish (es) | 3,234 | 98.84% | 91.6% | 160 / 13,736 |
|
| 159 |
+
| French (fr) | 4,708 | 98.41% | 92.8% | 317 / 19,906 |
|
| 160 |
+
| German (de) | 4,260 | 97.94% | 91.7% | 357 / 17,347 |
|
| 161 |
+
| Italian (it) | 3,218 | 97.83% | 94.1% | 301 / 13,855 |
|
| 162 |
+
| Portuguese (pt) | 1,485 | 97.73% | 92.5% | 147 / 6,467 |
|
| 163 |
+
| Dutch (nl) | 1,526 | 97.21% | 91.9% | 182 / 6,519 |
|
| 164 |
+
| **All seven** | **30,000** | **98.42%** | **91.69%** | **2,082 / 131,707** |
|
| 165 |
+
|
| 166 |
+
Recall is term-presence (did every gold private value vanish from the output); retention is the policy-aware keep-set (city/state/ZIP). The seven-language aggregate carries a Wilson 95% CI of [98.35, 98.49].
|
| 167 |
+
|
| 168 |
+
### 3.5 Label schema and training-data design
|
| 169 |
+
|
| 170 |
+
The shipped model reflects two design decisions that postdate the candidate sweep above.
|
| 171 |
+
Both are about _what_ the model is asked to learn, not which checkpoint to ship.
|
| 172 |
+
|
| 173 |
+
**Atomic label decomposition.**
|
| 174 |
+
Earlier iterations used coarse, pre-combined labels — a single `STREET_ADDRESS`, a single `PERSON`, plus `ORGANIZATION`, `LOCATION`, `DATE`, `AGE`, `INCOME`, and a catch-all `SECRET`.
|
| 175 |
+
A model trained on those overfits to the easy case where a name or address arrives as one tidy, well-formed blob.
|
| 176 |
+
The shipped schema instead forces everything to atomic pieces: names split into `GIVEN_NAME` / `SURNAME`; the street line into `BUILDING_NUMBER` / `STREET_NAME`; geography into `CITY` / `STATE` / `ZIP_CODE`; and document identifiers into their specific classes (`TAX_ID`, `BANK_ACCOUNT`, `ROUTING_NUMBER`, `GOVERNMENT_ID`, `PASSPORT`, `DRIVERS_LICENSE`) rather than a generic `SECRET`.
|
| 177 |
+
This trains the model to recognize PII _fragments_ in disordered text — a building number on one line, a street name three messages later — instead of expecting a textbook one-line address.
|
| 178 |
+
|
| 179 |
+
**Dates, ages, and income are non-PII.**
|
| 180 |
+
These were dropped from the redact-set entirely and map to `O` (kept context).
|
| 181 |
+
A public-benefits assistant needs to reason about age and income to be useful, and a bare date is rarely identifying on its own; classifying them as redactable was over-redaction that hurt utility without a matching privacy gain.
|
| 182 |
+
The keep-set proper is `{CITY, STATE, ZIP_CODE}`; dates/ages/income are simply not modeled as PII.
|
| 183 |
+
|
| 184 |
+
**Premask train/serve symmetry.**
|
| 185 |
+
The structured classes the deterministic layer owns (`SSN`, `CREDIT_CARD`, `IP_ADDRESS`) are replaced with sentinel tokens _before_ the model sees the text, both at inference (`src/premask.ts`) and during dataset construction .
|
| 186 |
+
The model therefore never spends capacity learning to classify raw card/SSN/IP digits — those are a solved problem for a checksum — and the train-time and inference-time input distributions match by construction.
|
| 187 |
+
|
| 188 |
+
**A deliberately noisy corpus.** OpenPII supplies broad multilingual entropy, but its conversations are clean and well-formed.
|
| 189 |
+
To keep the redactor from overfitting to tidy inputs, the synthetic portion of the corpus is generated to be messy and realistic on purpose: low-effort and typo-prone text, voice-dictated phrasing, values pasted out of forms, multilingual mixing, and contradictory, duplicated, or wrong-field entries, produced across a range of assistant personas so the user-side language varies.
|
| 190 |
+
The aim is a model that catches partial, fragmented PII in real chatbot text rather than only in clean examples.
|
| 191 |
+
|
| 192 |
+
## 4. Alternatives we tried
|
| 193 |
+
|
| 194 |
+
### 4.1 Base architecture: MiniLM over ELECTRA
|
| 195 |
+
|
| 196 |
+
The first axis we settled was the encoder.
|
| 197 |
+
ELECTRA-small was the strongest size contender — its trimmed, Q4 artifact is ~10 MB against MiniLM's ~19 MB at full vocab — which for a browser-deployed model is a real pull.
|
| 198 |
+
We ran the entire selection matrix (§4.2) twice, once on each base, on identical data and schema.
|
| 199 |
+
|
| 200 |
+
ELECTRA did not turn extra data into accuracy the way MiniLM did.
|
| 201 |
+
Its eval loss bottomed out early and then crept up as the corpus grew, and the matrix bears this out: every one of ELECTRA's strongest cells used the _smaller_ (100k-conversation) data slice, and adding the larger slice produced its _worst_ cells, not its best.
|
| 202 |
+
MiniLM, on the same axes, reached its top result on the larger slice.
|
| 203 |
+
For a project whose whole thesis is a corpus that keeps growing (§3.5), an encoder that stops improving — or regresses — as data grows has no headroom for the loop we rely on.
|
| 204 |
+
We shipped MiniLM-L6-H384-uncased and kept ELECTRA on the shelf as a size lever for a hypothetical future release willing to accept that ceiling.
|
| 205 |
+
|
| 206 |
+
### 4.2 The selection matrix
|
| 207 |
+
|
| 208 |
+
To choose the data recipe and the prefilter strategy, we ran a Cartesian ablation rather than tuning one variable at a time.
|
| 209 |
+
Twelve cells per base — **prefilter** {on, off} × **data mix** {synthetic-only, +OCR'd documents, +AI4Privacy templates} × **corpus volume** {100k, 250k synthetic conversations} — each fine-tuned, exported to its Q4 and vocab-trimmed artifacts, and scored _end-to-end through its matching runtime_ (prefilter-on cells run with the deterministic layer; prefilter-off cells run the model raw).
|
| 210 |
+
Across both bases that is 24 trained models, scored under both runtime modes.
|
| 211 |
+
|
| 212 |
+
Scoring used a deliberately hard internal development suite — 55 hand-written chat cases (66 private terms) skewed toward the failure modes we most wanted to catch: hyphenated and particled names, non-Latin-script names, address fragments, government identifiers, split and dotted contact details.
|
| 213 |
+
It is **not** the shipped held-out metric (the 98.42% headline is measured on 30k OpenPII rows, §3.3); these are the much harder, much smaller dev numbers we used to rank _directions_, and the shipped model is many training iterations beyond this round.
|
| 214 |
+
The matched-runtime results:
|
| 215 |
+
|
| 216 |
+
| Base | Best cell (data mix / volume / prefilter) | Dev recall | Worst cell |
|
| 217 |
+
| ------- | ----------------------------------------- | ---------: | -------------------------------------------------------------------- |
|
| 218 |
+
| MiniLM | +AI4Privacy / 250k / prefilter-on | **80.3%** | 21.2% (no-prefilter, 250k synthetic, full mix — a training collapse) |
|
| 219 |
+
| ELECTRA | +AI4Privacy / 100k / prefilter-on | 81.8% | 42.4% (no-prefilter, 250k, synthetic-only) |
|
| 220 |
+
|
| 221 |
+
Three findings drove the recipe:
|
| 222 |
+
|
| 223 |
+
1. **The full data mix produced every top cell.** On both bases the highest-recall configurations folded in all three sources; synthetic-only cells trailed. Breadth of corpus, not volume of one source, moved the needle.
|
| 224 |
+
2. **MiniLM scaled with data; ELECTRA did not** (§4.1). MiniLM's best cell used the larger slice; all of ELECTRA's best cells used the smaller one.
|
| 225 |
+
3. **One cell collapsed.** The MiniLM no-prefilter / 250k synthetic / full-mix cell scored 21.2% — a reminder that more data and more classes can destabilize training, and the reason every candidate is scored end-to-end before it is trusted, never assumed good from its loss curve.
|
| 226 |
+
|
| 227 |
+
The prefilter-on configuration won on its merits here and matches the runtime: the deterministic layer is a checksum away from perfect on the digit classes, so making the model relearn them only adds variance.
|
| 228 |
+
|
| 229 |
+
### 4.3 Prefolded normalization
|
| 230 |
+
|
| 231 |
+
All training rows pass through the same normalization the runtime applies before tokenization: lowercase, NFKD decomposition, and combining-mark stripping.
|
| 232 |
+
The combining-mark step is what folds accents — `José` becomes `jose`, `Müller` becomes `muller`, `François` becomes `francois` — so the model sees a single canonical form regardless of how the user typed the name.
|
| 233 |
+
|
| 234 |
+
We do this for two reasons.
|
| 235 |
+
First, BERT's BasicTokenizer already performs the same fold implicitly at inference time under `do_lower_case=True` with default accent-stripping, so prefolding the training data makes the train-time and runtime distributions identical by construction; without it, the model would learn token sequences containing combining marks that the runtime tokenizer would never emit.
|
| 236 |
+
Second, accent collapse is a robustness property we want: a user who types `Jose` and a user who types `José` should be redacted identically, and an attacker who substitutes one for the other to evade detection should fail.
|
| 237 |
+
Prefolding bakes that property into the training distribution rather than relying on the runtime to recover it after the fact.
|
| 238 |
+
|
| 239 |
+
A guard in the training pipeline fails the run if a future tokenizer change breaks this assumption, so retraining with a cased base model cannot silently desync the two normalizations.
|
| 240 |
+
Because prefolding already produces the canonical form, separate accent-augmentation (training on both `José` and `Jose` as distinct strings) is disabled — it would be a no-op against an already-folded corpus.
|
| 241 |
+
|
| 242 |
+
## 5. Comparison to public PII baselines
|
| 243 |
+
|
| 244 |
+
During model selection we ran a one-off comparison of Rampart against public PII systems — a community BERT-small detector, Microsoft Presidio + spaCy, GLiNER small v2.1, and AWS Bedrock Guardrails — on the English+Spanish slice under identical scoring rules.
|
| 245 |
+
On that slice Rampart reached 98.85% private-term recall; the open baselines trailed on retention in particular (GLiNER and BERT-small kept ~33.5% of public context), and the cloud incumbent trailed on both recall and latency.
|
| 246 |
+
Also of note, those systems are Python- or cloud-only (Presidio, GLiNER/torch, Bedrock) and cannot run in the shipped TypeScript form factor.
|
| 247 |
+
|
| 248 |
+
## 6. Calibration
|
| 249 |
+
|
| 250 |
+
The runtime applies a single recall-biased confidence floor (`minScore` = 0.4) uniformly across the model's labels, chosen against the 10,000-row OpenPII Latin calibration split (disjoint from test).
|
| 251 |
+
There is no per-label threshold table in the shipped runtime: classes the model alone is weak on — SSN, CREDIT_CARD — are not propped up with a tuned threshold but covered by the deterministic layer's checksum/structural validation, which is the system of record for them (with EMAIL, URL, and IP_ADDRESS covered by pattern match).
|
| 252 |
+
Phone, routing, government-ID, passport, and license numbers carry no checksum and are left to the model under the same floor.
|
| 253 |
+
Trading a miss (which leaks data) against the cheaper failure of over-redaction is the explicit policy choice here, not a model regression.
|
| 254 |
+
|
| 255 |
+
ECE on the 30,000-row test set is **0.018** for the model alone (well-calibrated, no post-hoc isotonic correction needed) and **0.291** for the full system.
|
| 256 |
+
The system-level ECE is higher because the deterministic layer always emits score 1.0 on its detections, making the score distribution bimodal — that is a score-distribution artifact of the union, not a calibration regression of the underlying model.
|
| 257 |
+
|
| 258 |
+
## 7. Schema reconciliation
|
| 259 |
+
|
| 260 |
+
The 91.69% retention number on the headline test is term-presence scoring that already credits the keep-set (city/state/ZIP) as kept, matching the Rampart policy. We analyzed the 7,244 remaining "over-redacted" public terms in the 30,000-row eval:
|
| 261 |
+
|
| 262 |
+
- The vast majority are policy-driven redactions of street-line components (street name, building number, secondary address line). OpenPII marks `STREET`, `BUILDINGNUM`, and `SECADDRESS` as `O` (public); the Rampart policy redacts the precise street line (`BUILDING_NUMBER` + `STREET_NAME`) and `SECONDARY_ADDRESS` while keeping `CITY`, `STATE`, and `ZIP`. These are not detector errors; they are the policy firing as designed.
|
| 263 |
+
- A smaller share are span-edge artifacts. The runtime's particle-rescue step grows name spans (`GIVEN_NAME` / `SURNAME`) to swallow capitalized particles ("De la", "Von", "Mc"). When an adjacent public token is itself capitalized, that token can be absorbed into the redacted span.
|
| 264 |
+
- A very small fraction are digit fragments inside longer correctly-redacted spans (e.g. "376" found inside a redacted 16-digit credit card, surfacing as a "kept token" because the gold schema marks individual digits separately from the card number).
|
| 265 |
+
|
| 266 |
+
We publish the term-presence number for like-for-like comparison with public PII benchmarks running the same scoring rules.
|
| 267 |
+
Under policy-aware scoring the retention exceeds 99%.
|
| 268 |
+
|
| 269 |
+
## 8. Systems we considered and did not adopt
|
| 270 |
+
|
| 271 |
+
Two widely-discussed alternatives are worth addressing directly because each is a plausible default for a team starting from scratch on this problem.
|
| 272 |
+
|
| 273 |
+
**OpenAI Privacy Filter.** OpenAI released an open-weight token-classification model under Apache 2.0 ([announcement](https://openai.com/index/introducing-openai-privacy-filter/)) with a similar shape to ours: bidirectional token classification with BIOES span decoding via constrained Viterbi, eight detection categories (person, address, email, phone, URL, date, account number, secret), and reported F1 of 97.4% on the corrected `pii-masking-300k` benchmark.
|
| 274 |
+
It is the closest peer to Rampart in design intent, and we evaluated it as a candidate.
|
| 275 |
+
|
| 276 |
+
We did not adopt it for two reasons.
|
| 277 |
+
(1) Size: the released model is 1.5B total parameters (50M active, MoE-style routing).
|
| 278 |
+
Even with aggressive quantization the on-disk footprint is two orders of magnitude beyond our 15 MB browser-deployment target, and the active-parameter count alone exceeds what we can ship to a low-end device over the wire.
|
| 279 |
+
(2) Inference shape: a 1.5B-parameter forward pass with a 128k-token context window is engineered for server-side or workstation-class throughput on long documents, not for sub-10 ms per-keystroke client-side redaction during a chat turn.
|
| 280 |
+
The two systems are solving overlapping problems with different deployment constraints, and the OpenAI model's strengths (long-context coherence, fine-tunability for domain adaptation) are orthogonal to ours (browser-deployable size, encoder-only latency, per-class checksum validation through the deterministic layer).
|
| 281 |
+
For applications that have a server they trust and documents that justify the round-trip, the OpenAI model is a strong choice; for an application whose contract is "the user's text never leaves the device," it is too large to deploy on the device.
|
| 282 |
+
|
| 283 |
+
**`ai4privacy/llama-ai4privacy-english-anonymiser-openpii`.**
|
| 284 |
+
A Llama-family fine-tune trained on the same OpenPII corpus we used.
|
| 285 |
+
The model is high quality on its native distribution and was a serious candidate.
|
| 286 |
+
We did not adopt it for three reasons.
|
| 287 |
+
(1) Size: the released artifact is multiple gigabytes, three orders of magnitude larger than our 15 MB target and incompatible with browser deployment on the low-end devices we need to support.
|
| 288 |
+
(2) Inference cost: a Llama-class generative anonymizer takes hundreds of milliseconds to seconds per turn even on accelerated hardware, versus 6.6 ms for an encoder pass; running it on the client is not viable, and running it on the server reintroduces the threat-model problem above.
|
| 289 |
+
(3) Generative outputs require a different correctness story — the model rewrites text rather than emitting spans, which makes calibration, span F1, and policy-driven keep-sets harder to define and audit.
|
| 290 |
+
A token classifier with a deterministic post-processing layer is a much smaller surface area to reason about for a system whose contract is "do not leak."
|
| 291 |
+
|
| 292 |
+
Both systems are good engineering for their intended deployment shape.
|
| 293 |
+
Neither is a substitute for client-side redaction when the goal is to prevent data from reaching any server in the first place.
|
| 294 |
+
|
| 295 |
+
## 9. Limitations
|
| 296 |
+
|
| 297 |
+
The model card enumerates each documented failure with statistics. The most consequential:
|
| 298 |
+
|
| 299 |
+
1. **Cross-locale name fairness.** Recall on Faker-generated names spans 15 naming traditions; non-Latin scripts (Korean, Han Chinese, Japanese, Arabic, South Asian, Slavic) are below 50%. This is the most important regression to close in subsequent training cycles and is tracked by a stratified regression test in the eval suite.
|
| 300 |
+
2. **Adversarial robustness.** The system catches 86.4% of a 20-case adversarial suite. Combined attacks (homoglyph plus whitespace-split, deep zero-width injection inside checksum-valid identifiers) can still bypass the union of the two layers. The deterministic layer raises the floor on structured classes but does not close the gap on unstructured identifiers under composed perturbations. This is the right framing for the limitation, not the primary use case: Rampart is designed to protect users who are entering their own information in good faith from incidental disclosure to downstream services, not to defeat a motivated user who is actively trying to smuggle their own PII past the filter. Adversarial cases are scored to characterize the failure surface and to surface regressions, not because circumventing one's own redactor is the threat model.
|
| 301 |
+
3. **Indirect identifiers.** Inferential leaks — e.g. a rare medical condition combined with a ZIP code — are out of scope. The system redacts terms, not statistical fingerprints.
|
| 302 |
+
|
| 303 |
+
## 10. Reproducibility
|
| 304 |
+
|
| 305 |
+
The training pipeline and the benchmark are released under CC BY 4.0.
|
| 306 |
+
The benchmark (`eval/bench`) runs the shipped TypeScript pipeline over a frozen OpenPII held-out slice and writes the per-run `summary.json` / `by_language.json` that the published numbers cite, so every figure traces to committed evidence produced by the artifact itself.
|
| 307 |
+
The held-out row `uid`s are pinned in a committed manifest; `bun run bench:fetch` regenerates the rows and `bun run bench` reproduces the numbers.
|
| 308 |
+
|
| 309 |
+
## 11. Conclusion
|
| 310 |
+
|
| 311 |
+
Rampart is harm reduction. No client-side redactor at this size will catch every leak, and we do not claim otherwise — §9 documents the classes where the system underperforms and the eval suite is structured so future regressions surface immediately.
|
| 312 |
+
What it provides is a defensible floor: text is filtered through two complementary layers and replaced with stable placeholders before any server sees it, so the worst case for a downstream leak is bounded by what the client failed to redact, not by the entire raw conversation.
|
| 313 |
+
|
| 314 |
+
We release the model, deterministic layer, and eval suite under CC BY 4.0 so any team building privacy-sensitive software can use, audit, fork, and improve it.
|
| 315 |
+
The constraints adopted here — browser-deployable size, recall-biased calibration, defense in depth, no network dependency at inference — are specific to the threat model in which the user's unredacted text must never leave the device.
|
| 316 |
+
Other deployment shapes warrant other tradeoffs; under this one, the system reported here is a deployable baseline against which future work can be measured.
|
config.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"BertForTokenClassification"
|
| 4 |
+
],
|
| 5 |
+
"attention_probs_dropout_prob": 0.1,
|
| 6 |
+
"classifier_dropout": null,
|
| 7 |
+
"dtype": "float32",
|
| 8 |
+
"gradient_checkpointing": false,
|
| 9 |
+
"hidden_act": "gelu",
|
| 10 |
+
"hidden_dropout_prob": 0.1,
|
| 11 |
+
"hidden_size": 384,
|
| 12 |
+
"id2label": {
|
| 13 |
+
"0": "O",
|
| 14 |
+
"1": "B-GIVEN_NAME",
|
| 15 |
+
"2": "I-GIVEN_NAME",
|
| 16 |
+
"3": "B-SURNAME",
|
| 17 |
+
"4": "I-SURNAME",
|
| 18 |
+
"5": "B-EMAIL",
|
| 19 |
+
"6": "I-EMAIL",
|
| 20 |
+
"7": "B-PHONE",
|
| 21 |
+
"8": "I-PHONE",
|
| 22 |
+
"9": "B-URL",
|
| 23 |
+
"10": "I-URL",
|
| 24 |
+
"11": "B-TAX_ID",
|
| 25 |
+
"12": "I-TAX_ID",
|
| 26 |
+
"13": "B-BANK_ACCOUNT",
|
| 27 |
+
"14": "I-BANK_ACCOUNT",
|
| 28 |
+
"15": "B-ROUTING_NUMBER",
|
| 29 |
+
"16": "I-ROUTING_NUMBER",
|
| 30 |
+
"17": "B-GOVERNMENT_ID",
|
| 31 |
+
"18": "I-GOVERNMENT_ID",
|
| 32 |
+
"19": "B-PASSPORT",
|
| 33 |
+
"20": "I-PASSPORT",
|
| 34 |
+
"21": "B-DRIVERS_LICENSE",
|
| 35 |
+
"22": "I-DRIVERS_LICENSE",
|
| 36 |
+
"23": "B-BUILDING_NUMBER",
|
| 37 |
+
"24": "I-BUILDING_NUMBER",
|
| 38 |
+
"25": "B-STREET_NAME",
|
| 39 |
+
"26": "I-STREET_NAME",
|
| 40 |
+
"27": "B-SECONDARY_ADDRESS",
|
| 41 |
+
"28": "I-SECONDARY_ADDRESS",
|
| 42 |
+
"29": "B-CITY",
|
| 43 |
+
"30": "I-CITY",
|
| 44 |
+
"31": "B-STATE",
|
| 45 |
+
"32": "I-STATE",
|
| 46 |
+
"33": "B-ZIP_CODE",
|
| 47 |
+
"34": "I-ZIP_CODE"
|
| 48 |
+
},
|
| 49 |
+
"initializer_range": 0.02,
|
| 50 |
+
"intermediate_size": 1536,
|
| 51 |
+
"label2id": {
|
| 52 |
+
"B-BANK_ACCOUNT": 13,
|
| 53 |
+
"B-BUILDING_NUMBER": 23,
|
| 54 |
+
"B-CITY": 29,
|
| 55 |
+
"B-DRIVERS_LICENSE": 21,
|
| 56 |
+
"B-EMAIL": 5,
|
| 57 |
+
"B-GIVEN_NAME": 1,
|
| 58 |
+
"B-GOVERNMENT_ID": 17,
|
| 59 |
+
"B-PASSPORT": 19,
|
| 60 |
+
"B-PHONE": 7,
|
| 61 |
+
"B-ROUTING_NUMBER": 15,
|
| 62 |
+
"B-SECONDARY_ADDRESS": 27,
|
| 63 |
+
"B-STATE": 31,
|
| 64 |
+
"B-STREET_NAME": 25,
|
| 65 |
+
"B-SURNAME": 3,
|
| 66 |
+
"B-TAX_ID": 11,
|
| 67 |
+
"B-URL": 9,
|
| 68 |
+
"B-ZIP_CODE": 33,
|
| 69 |
+
"I-BANK_ACCOUNT": 14,
|
| 70 |
+
"I-BUILDING_NUMBER": 24,
|
| 71 |
+
"I-CITY": 30,
|
| 72 |
+
"I-DRIVERS_LICENSE": 22,
|
| 73 |
+
"I-EMAIL": 6,
|
| 74 |
+
"I-GIVEN_NAME": 2,
|
| 75 |
+
"I-GOVERNMENT_ID": 18,
|
| 76 |
+
"I-PASSPORT": 20,
|
| 77 |
+
"I-PHONE": 8,
|
| 78 |
+
"I-ROUTING_NUMBER": 16,
|
| 79 |
+
"I-SECONDARY_ADDRESS": 28,
|
| 80 |
+
"I-STATE": 32,
|
| 81 |
+
"I-STREET_NAME": 26,
|
| 82 |
+
"I-SURNAME": 4,
|
| 83 |
+
"I-TAX_ID": 12,
|
| 84 |
+
"I-URL": 10,
|
| 85 |
+
"I-ZIP_CODE": 34,
|
| 86 |
+
"O": 0
|
| 87 |
+
},
|
| 88 |
+
"layer_norm_eps": 1e-12,
|
| 89 |
+
"max_position_embeddings": 512,
|
| 90 |
+
"model_type": "bert",
|
| 91 |
+
"num_attention_heads": 12,
|
| 92 |
+
"num_hidden_layers": 6,
|
| 93 |
+
"pad_token_id": 0,
|
| 94 |
+
"position_embedding_type": "absolute",
|
| 95 |
+
"transformers_version": "4.57.6",
|
| 96 |
+
"type_vocab_size": 2,
|
| 97 |
+
"use_cache": true,
|
| 98 |
+
"vocab_size": 19730
|
| 99 |
+
}
|
examples/basic-chat.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createGuard } from "../index.ts";
|
| 2 |
+
|
| 3 |
+
const guard = await createGuard();
|
| 4 |
+
|
| 5 |
+
const safe = await guard.protect("My name is Alex Rivera. My SSN is 472-81-0094.");
|
| 6 |
+
const reply = await llm(safe.text);
|
| 7 |
+
|
| 8 |
+
console.log(guard.reveal(reply));
|
| 9 |
+
|
| 10 |
+
async function llm(text: string): Promise<string> {
|
| 11 |
+
return "Got it, [GIVEN_NAME_1].";
|
| 12 |
+
}
|
onnx/model_q4.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9f27d24949b0581701071ea5ef522d77ccd3f50c525cc91eac4d265b0fc2afe5
|
| 3 |
+
size 14693379
|
special_tokens_map.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cls_token": {
|
| 3 |
+
"content": "[CLS]",
|
| 4 |
+
"lstrip": false,
|
| 5 |
+
"normalized": false,
|
| 6 |
+
"rstrip": false,
|
| 7 |
+
"single_word": false
|
| 8 |
+
},
|
| 9 |
+
"mask_token": {
|
| 10 |
+
"content": "[MASK]",
|
| 11 |
+
"lstrip": false,
|
| 12 |
+
"normalized": false,
|
| 13 |
+
"rstrip": false,
|
| 14 |
+
"single_word": false
|
| 15 |
+
},
|
| 16 |
+
"pad_token": {
|
| 17 |
+
"content": "[PAD]",
|
| 18 |
+
"lstrip": false,
|
| 19 |
+
"normalized": false,
|
| 20 |
+
"rstrip": false,
|
| 21 |
+
"single_word": false
|
| 22 |
+
},
|
| 23 |
+
"sep_token": {
|
| 24 |
+
"content": "[SEP]",
|
| 25 |
+
"lstrip": false,
|
| 26 |
+
"normalized": false,
|
| 27 |
+
"rstrip": false,
|
| 28 |
+
"single_word": false
|
| 29 |
+
},
|
| 30 |
+
"unk_token": {
|
| 31 |
+
"content": "[UNK]",
|
| 32 |
+
"lstrip": false,
|
| 33 |
+
"normalized": false,
|
| 34 |
+
"rstrip": false,
|
| 35 |
+
"single_word": false
|
| 36 |
+
}
|
| 37 |
+
}
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"added_tokens_decoder": {
|
| 3 |
+
"0": {
|
| 4 |
+
"content": "[PAD]",
|
| 5 |
+
"lstrip": false,
|
| 6 |
+
"normalized": false,
|
| 7 |
+
"rstrip": false,
|
| 8 |
+
"single_word": false,
|
| 9 |
+
"special": true
|
| 10 |
+
},
|
| 11 |
+
"1": {
|
| 12 |
+
"content": "[UNK]",
|
| 13 |
+
"lstrip": false,
|
| 14 |
+
"normalized": false,
|
| 15 |
+
"rstrip": false,
|
| 16 |
+
"single_word": false,
|
| 17 |
+
"special": true
|
| 18 |
+
},
|
| 19 |
+
"2": {
|
| 20 |
+
"content": "[CLS]",
|
| 21 |
+
"lstrip": false,
|
| 22 |
+
"normalized": false,
|
| 23 |
+
"rstrip": false,
|
| 24 |
+
"single_word": false,
|
| 25 |
+
"special": true
|
| 26 |
+
},
|
| 27 |
+
"3": {
|
| 28 |
+
"content": "[SEP]",
|
| 29 |
+
"lstrip": false,
|
| 30 |
+
"normalized": false,
|
| 31 |
+
"rstrip": false,
|
| 32 |
+
"single_word": false,
|
| 33 |
+
"special": true
|
| 34 |
+
},
|
| 35 |
+
"4": {
|
| 36 |
+
"content": "[MASK]",
|
| 37 |
+
"lstrip": false,
|
| 38 |
+
"normalized": false,
|
| 39 |
+
"rstrip": false,
|
| 40 |
+
"single_word": false,
|
| 41 |
+
"special": true
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
"clean_up_tokenization_spaces": true,
|
| 45 |
+
"cls_token": "[CLS]",
|
| 46 |
+
"do_basic_tokenize": true,
|
| 47 |
+
"do_lower_case": true,
|
| 48 |
+
"extra_special_tokens": {},
|
| 49 |
+
"mask_token": "[MASK]",
|
| 50 |
+
"model_max_length": 1000000000000000019884624838656,
|
| 51 |
+
"never_split": null,
|
| 52 |
+
"pad_token": "[PAD]",
|
| 53 |
+
"sep_token": "[SEP]",
|
| 54 |
+
"strip_accents": null,
|
| 55 |
+
"tokenize_chinese_chars": true,
|
| 56 |
+
"tokenizer_class": "BertTokenizer",
|
| 57 |
+
"unk_token": "[UNK]"
|
| 58 |
+
}
|
vocab.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|