Redactor

How it works

Rules and checksums. No model, no guessing, no copies.

The whole engine is decidable from the text in front of it. That is a limit — it is why person names are absent — and it is also the entire reason the numbers on this site can be checked.

01

Nine detectors run over the text

Each one is a rule or a checksum — Luhn for cards, mod-97 for IBANs, a base64 header that has to decode to JSON with an alg field, a published vendor prefix for a credential. There is no model in this path and no network call, so the same input always gives you the same output, and every figure we publish about it means something.

02

Overlaps resolve whole, never trimmed

A JWT contains things that look like phone numbers. A PEM block contains things that look like everything. So spans are ranked — private key, then JWT, then credential, then IBAN, card, SSN, email, address, phone — and a lower-priority span that overlaps an accepted one is dropped entirely. Never shortened, because half a redacted card number is a leaked card number.

03

Values become tokens, or markers

In tokenize mode each distinct value gets a stable numbered token, shared across every document in the call, and you get a mapping back. In mask mode it becomes a fixed per-type marker and there is no mapping at all. If your text already contained something shaped like one of our tokens, new ones are numbered above it and the collision is reported rather than absorbed.

04

The near-misses are reported, not removed

A Luhn-valid number with no card-network prefix. Ten bare digits in North American dialling shape. A high-entropy string next to a secret-shaped label. Each of those is counted in unsure and handed back with offsets — left in the text, because removing them would wreck precision on every order number you own.

05

The response ends and the text is gone

What gets written is a row of counts: characters, findings by type, unsure, credits. Not your text, not the output, not the mapping, and not in the logs — errors are recorded as identifiers rather than messages, because a message can quote its input.

The closed enum

Every type, with the exact rule it applies.

email

Email address

An RFC-shaped local part, an @, a domain with at least one dot and a 2–24 character TLD. Bounded on both sides so an address inside a longer token is not half-matched.

precision 100%
recall 100%
n = 14
phone

Phone number

Either an E.164 number (a leading + and 8–15 digits) or a North American number written with separators or parentheses, with the area code and exchange code both starting 2–9. A bare run of ten digits with no separators is NOT treated as a phone number.

precision 100%
recall 100%
n = 9
credit_card

Payment card number

A 13–19 digit run that passes the Luhn checksum AND carries a known network prefix (Visa, Mastercard, Amex, Discover, JCB, Diners, UnionPay, Maestro). Luhn alone is reported as unsure, never redacted.

precision 100%
recall 100%
n = 8
iban

IBAN

Two letters, two check digits, then the exact body length that country publishes, validated with the ISO 7064 mod-97 checksum.

precision 100%
recall 100%
n = 8
us_ssn

US Social Security number

AAA-GG-SSSS with a dash or space separator, rejecting the ranges the SSA never issues (area 000/666/900-999, group 00, serial 0000). Also matched when nine bare digits directly follow the words 'SSN' or 'social security'.

precision 100%
recall 100%
n = 5
ip_address

IP address

A dotted quad with every octet in 0–255, or an IPv6 address parsed structurally (never regex-guessed). A quad written directly after a 'v' is treated as a version string and left alone.

precision 100%
recall 100%
n = 9
api_key

API key or token

A credential carrying a published vendor prefix — OpenAI, Anthropic, GitHub, AWS, Google, Slack, Stripe, SendGrid, GitLab, DigitalOcean, Hugging Face, npm, Shopify, Twilio, Notion, Linear. High-entropy strings with no known prefix are reported as unsure, never redacted.

precision 100%
recall 100%
n = 8
jwt

JSON Web Token

Three base64url segments where the first one actually decodes to JSON containing an `alg` field. A string that merely looks like a JWT is not one.

precision 100%
recall 100%
n = 4
private_key

Private key block

A complete PEM block, from a BEGIN … PRIVATE KEY line to its matching END line, including the body.

precision 100%
recall 100%
n = 2
Measured against our own labelled corpus of 26 documents and 67 marked instances, most of which is text that resembles personal data and is not. It is evidence that the detectors work on the cases we could think of, including a set of near-misses that must not fire. It is not an independent benchmark, and it is not a promise about your documents. The corpus is 26 documents and 67 labelled instances, and a test recomputes this table from it on every build.

Mask or tokenize

One of these has a way back.

WHAT YOU SENDRefunddana@halberd.example, card4111 1111 1111 1111.MASKRefund[EMAIL], card[CREDIT_CARD].no mappingNothing to restore from. That is the mode.TOKENIZERefund[EMAIL_1], card[CREDIT_CARD_1].mapping → rehydrateReturned to you. We never had a copy.

What it will not do

The absences are the specification.

not detected

Person names

There is no rule that separates a person's name from any other pair of capitalised words, and this engine deliberately runs no model. A name-detector built on a list would miss every name not on the list while looking like it worked. Names stay visible in the output — see the unsure count and the hero on the homepage.

not detected

Postal addresses

Street addresses vary by country in ways no regex settles, and the plausible patterns collide with ordinary prose. A partial address redaction is worse than none: it removes the street and leaves the city, so the record still identifies one household.

not detected

Dates of birth

A date is only a birth date because of what surrounds it. Redacting every date would gut a document; redacting some of them would be a guess about which.

not detected

Passport and national ID numbers

Outside the US SSN there is no checksum we can rely on across issuing countries, and the formats overlap with ordinary reference numbers.

not detected

Bare account and routing numbers

US ABA routing numbers do carry a checksum, but nine bare digits with no label match far too many internal identifiers. Nine-digit runs in the SSA's issued range are reported in `unsure` rather than removed.

not detected

Generic high-entropy secrets

A random-looking string is as likely to be a hash, a content id or a base64 payload as a credential. Only credentials with a published vendor prefix are redacted; the rest are counted in `unsure` so you can look.

not detected

Anything inside an image, a PDF or an audio file

This app takes text. It does no OCR and no transcription — use Extractor or Transcriber first, then send the text here.

Limits and price

per call

200,000

characters, across documents

per call

50

documents, one token namespace

price

1 cr

per 10,000 characters, rounded up

Read the docsPricing