One endpoint,
every model.
Tare is an OpenAI-compatible gateway in front of every model you use — ours and the ones you bring yourself. One base URL, one key, and every token accounted for.
Data protection (DLP)
Every call to chat completions, embeddings, images and video generation is scanned for sensitive content before it leaves Tare — secrets that would let someone act as you, and personal data that shouldn't reach a model unnecessarily. What happens to a match is entirely your decision. Calls made directly on a Local Node are scanned on the node, with the same rules; that page says what the node needs for it.
What gets checked
The whole request body is scanned, not a fixed list of fields — every string value is a candidate
except structural fields (model, role, type, ids) and binary payloads (base64 image/audio/video
data).
Secrets (cloud credentials, private keys, platform tokens like ghp_…/sk_live_…/xox…,
database connection strings, crypto wallet keys and recovery phrases, auth tokens, and values that
look like a secret from their variable name alone) are checked by default on any key with protection
turned on.
Personal data (email, phone number, national ID, bank card, passport, and a handful of other structured formats) is masked, not blocked, by default: the call still goes through, with the value replaced. IP and MAC addresses are off by default — they come up constantly in ordinary ops and support conversations, and masking every one would break a lot of normal traffic for little benefit. Name, location and organization detection is also off by default, for a different reason: it costs real CPU time per call. Turn either on if your traffic needs it.
Older-format national ID numbers, bare passport numbers and bare 9-digit numbers are masked by
default like any other personal data — what's gated isn't whether they're detected, but whether
they're recognized at all. A bare 9-digit string, or a 15-digit ID number with no checksum, is
indistinguishable from an ordinary number on its own, so detection only fires with a real context
word nearby (身份证, "SSN", "passport").
What happens to a match
- Blocked: the call is rejected —
400, not billed, not sent anywhere. The error message names what was found and roughly where (field, line, a short prefix), plus about 25 characters of context on each side of the match with the match itself — and anything else sensitive that also falls in that window — replaced by[TYPE_REDACTED]. For mnemonics, private keys and contextual secrets, even that much is withheld: the message names only the type. The same evidence is attached to the Call Traces failure record and the audit event. - Masked: the matched text is replaced with a placeholder like
[EMAIL_REDACTED]before the request continues. The call is billed normally; there is no error and no special header — the model simply receives the redacted version.
Which one happens for a given type of content is your setting, not a property of the type. Console → Security → What counts as sensitive lists every built-in type with what the platform suggests and a choice of your own: ignore, replace with a placeholder, fail the request, or alert me (the request is forwarded unchanged, the call shows up under Recent events as detected, and a notification goes to your alert channels, at most once per silence window for the same kind of content). The same card has the switch for the whole account (on, off, or follow the platform default). Person names, locations and organization names are off unless you turn them on there. Resolution order when more than one setting could apply: built-in default ← platform default ← a recommended rule's own pin ← your account's choice ← a per-key override, each later one winning if set. A call made directly on a Local Node has no key, so it stops at your account's choice.
Recommended rules
Beyond what ships built in, new detection rules are occasionally published to a shared, continuously-maintained list — a newly-seen credential format, for instance. Whether they ever touch your traffic is entirely your choice, with four settings:
| Choice | What it does |
|---|---|
| Don't use them | These rules never run against your traffic |
| Just tell me (default) | They run and count matches, but never change or reject a request — the Security page shows what would have happened |
| Hide it automatically | Matches are masked, the same as a built-in personal-data match |
| Block the request | Raises the ceiling to BLOCK, but only for rules already published as a blocking recommendation. A rule published as a masking recommendation still only masks |
New tenants start on Just tell me: nothing about your traffic changes until you choose otherwise. Any single rule can also be pinned to its own choice regardless of the account-wide setting. The Security page shows, for each rule, how many of your last 7/30 days' calls it would have matched, so you can see the impact before turning enforcement on.
Skipping a check on purpose
Sometimes content that looks like a secret genuinely is not one (a test fixture, a redacted example already safe to send). If your key has this turned on:
- A header,
X-DLP-Skip: all(or a comma list of specific types), skips checking for the whole request. Read once and never forwarded upstream. - An inline marker at the very start of a message,
[[dlp:skip:<secret>]](or[[dlp:skip:<secret>:EMAIL,PHONE_CN]]for specific types), skips checking for just that message. The secret is generated once from the console and shown only then; a marker with the wrong secret is left as plain text and scanned normally rather than silently honored. The marker itself is stripped before the model ever sees it.
⚠️ By default a skip only waives masking, not blocking — a secret is still stopped even on a key with skipping turned on, unless the key is explicitly configured to waive that too. Turning that on is a real reduction in protection: anyone who can set the header or the marker can then push a real secret through unchecked.
Configuring this via API
Console-session only — no API key, including a provisioning key, can call any of these:
| Method | Path | What it does |
|---|---|---|
GET/PUT | /v1/dlp/policy | Read or change the adoption policy: pack mode, the ceiling, per-type actions |
GET | /v1/dlp/pack | List recommended rules, paginated and filterable |
GET | /v1/dlp/pack/summary | How many of your calls each rule would have matched, over the last N days |
PUT/DELETE | /v1/dlp/pack/{ruleKey}/pin | Pin one rule to its own choice, or remove the pin |
POST | /v1/dlp/pack/{ruleKey}/feedback | Flag a rule as wrong or too noisy |
GET | /v1/dlp/notices | Recent adoption-related notices |
GET/PUT | /v1/dlp/keys/{keyId}/skip | Read or change one key's skip settings |
POST/DELETE | /v1/dlp/keys/{keyId}/skip/secret | Generate or clear that key's skip secret. The plaintext is returned once, in this response, and never again |
GET /v1/api-keys/dlp-summary is the aggregate behind the Security page's headline number: how many
of your keys have protection turned on, counted in one query rather than one per key.
If a scan cannot finish in time
An oversized or adversarial input can make a rule time out. The response depends on which half of the scan was affected:
- If the optional name/location detection cannot complete, that part is skipped and the rest of the scan still applies; missing it for one call isn't a reason to fail the call.
- If a required rule (built-in or a pinned recommended rule) cannot complete, the whole call is blocked, not billed, with a message asking you to shorten the input. Treating a timeout as "assume clean" would turn it into a way to bypass the rule.
Adjacent controls that live elsewhere
DLP is content-level protection — it looks at what's inside a call. Two related controls sit outside it, each documented where the rest of that feature is: restricting a key to certain source IP addresses (see API keys), and the account-wide audit log, which records every price, routing, billing-mode and credential change regardless of who made it (see Call Traces).