T0
Always runs. Cannot be disabled.
An open-source project by FlowX.AI
It runs on CPU with the network interface down. An embeddable Python library: two functions, a policy file a reviewer can read without knowing Python, and an evidence record that holds hashes rather than user text.
Built for teams that run models inside their own perimeter, in more languages than English, and have to prove what was checked.
There is no client to construct, no gateway to run, and nothing wraps your model call. You call two functions and decide what to do with what they return.
Hashes, the resolved policy hash, and which model revision produced each finding. Never the text. The record is canonical JSON, so the hash and the signature reproduce on another machine.
Three fields carry the weight: input_hash proves what was scanned without storing it, policy_hash proves which rulebook was in force, and the model revision and weight hash prove which exact model made the call.
Check hard where text crosses the boundary, and do not re-inspect inside. A message passing between two services that already trust each other has been checked once, and checking it again costs latency without changing the answer. The cost of inspecting everything everywhere is the reason teams end up turning inspection off.
The design is borrowed from the Schengen model: internal checks could be abolished only because the external checks became strong, uniform, and governed by one shared rulebook. Check hard at the perimeter, then let traffic move freely inside the area of trust.
| Border | The library. Where checks happen. |
|---|---|
| Code | The policy file, border-code.yaml. The shared rulebook, named after the Schengen Borders Code. |
| Crossing | A single scan, inbound or outbound. |
| Stamp | The signed evidence record attached to a crossing. |
| Area | The trust domain. Services that accept each other's stamps. |
Tiers decide what runs when, so the expensive checks only run once something cheaper has found a reason.
Reading the table. Runs today means installed and callable now. Trained means the model exists but the detector is not yet callable; a policy that asks it to block or redact raises before any scan happens. Needs dependency means an optional extra must be installed. Needs network means the check makes outbound requests and is off unless a policy turns it on. In the F1 column, not a classifier marks rule-based detectors, which are deterministic and measured on latency only. No corpus yet means no evaluation set we trust exists; the cost column says whether its figure is measured or a budget.
| Detector | Tier | What it does | Needs | Backed by | Mean F1 | Cost | Status |
|---|---|---|---|---|---|---|---|
| disclosureoutputruns today | T0 | Reports whether an AI disclosure is present in the output, in 26 languages, and records the affirmative as well as the absence. Recording the affirmative as well as the absence is the artifact a transparency review under Article 50 of the EU AI Act asks for. | CPU | rule | not a classifier | 0.038 msmeasured | Runs today |
| invisible_textinput, outputruns today | T0 | Characters that are in the text but not on the screen: bidirectional controls, tag characters used to smuggle instructions, zero-width characters used to evade filters. | CPU | rule | not a classifier | 0.038 msmeasured | Runs today |
| secretsinputruns today | T0 | Credentials in text on its way to the model: named key formats, plus a deliberately conservative entropy rule. | CPU | rule | not a classifier | 0.048 msmeasured | Runs today |
| banned_termsinput, outputruns today | T1 | Terms the deploying organisation has decided must not appear, matched correctly in 26 languages. The list is policy; none ships. | CPU | rule | not a classifier | 0.168 msmeasured | Runs today |
| code_presentinput, outputruns today | T1 | Source code in text that should be prose, reported as one finding per shape found: a fence, a shebang, a definition, an import, a script tag, a shell invocation. Each carries its own confidence so a policy can act on a fenced block without acting on a line that merely ends in a brace. | CPU | rule | not a classifier | 0.009 msmeasured | Runs today |
| gibberishinputruns today | T1 | Input that is not meaningful text. | CPU | classifier | 0.966 | 27.528 msmeasured | Runs today |
| internal_domainsoutputruns today | T1 | Internal hostnames appearing in an answer meant for someone outside, in both their Unicode and punycode spellings. | CPU | rule | not a classifier | 0.234 msmeasured | Runs today |
| json_schemaoutputruns today · needs dependency | T1 | Output that does not satisfy a JSON Schema the policy carries. Point it at the OpenAPI meta-schema and it validates an OpenAPI document. | dependency | rule | not a classifier | 0.001 msmeasured | Runs today |
| language_idinput, outputruns today | T1 | Which of the 26 supported languages the text is in, whether that is one the policy permits, and whether an answer is in the same language as the prompt. Reports uncertain rather than guessing on short or mixed text. | CPU | rule | not a classifier | 0.362 msmeasured | Runs today |
| markup_injectioninput, outputruns today | T1 | Markup in the text that a browser would execute rather than display, found through case folding, entity decoding and compatibility folding. | CPU | rule | not a classifier | 0.239 msmeasured | Runs today |
| output_formatoutputruns today | T1 | Shape assertions a policy states: JSON, HTML, URL presence, length in graphemes, word count, case, choices, ranges, a regex, reading time. | CPU | rule | not a classifier | 0.001 msmeasured | Runs today |
| output_leakageoutputruns today | T1 | Personal data in the output that the user did not supply, which is the narrower and more useful question than whether any is present. | CPU | ner | per entity | 27.991 msmeasured | Runs today |
| piiinput, outputruns today | T1 | Personal data in input or output, as named entity spans with checksum validation where the identifier has one. | CPU | ner | per entity | 27.636 msmeasured | Runs today |
| postal_codeoutputruns today | T1 | Postal codes that cannot exist in the countries the product serves: the wrong shape, or outside a published province or department range. | CPU | rule | not a classifier | 0.002 msmeasured | Runs today |
| repetitionoutputruns today | T1 | Sentences the answer says twice, compared over folded text so a change of case or diacritic spelling does not hide a repeat. | CPU | rule | not a classifier | 0.497 msmeasured | Runs today |
| sql_injectionoutputruns today · needs dependency | T1 | Generated SQL that does more than the product asked for: a second statement, a forbidden statement kind, a tautology, an unexpected UNION. | dependency | rule | not a classifier | 0.238 msmeasured | Runs today |
| summary_supportoutputruns today | T1 | Whether each sentence of a summary appears in the source it summarises, by string overlap rather than by judgement. Useful for an extractive summary, and not a groundedness check: it says so in its own docstring. | CPU | rule | not a classifier | 0.921 msmeasured | Runs today |
| system_prompt_leakageoutputruns today | T1 | Whether the answer gave away the instructions the model was operating under, by containment against the system prompt and by phrase match in 26 languages. | CPU | rule | not a classifier | 5 msbudget | Runs today |
| token_limitinput, outputruns today | T1 | Text longer than the token budget of the model it is going to, counted with a tokenizer the policy names and pins: a local file, whose hash is reported as the revision, or an id already carrying a commit. A bare repo id is refused, because an unpinned count cannot be reproduced. | CPU | rule | not a classifier | 5 msbudget | Runs today |
| biasoutputruns today | T2 | Output carrying bias related to a protected characteristic. | CPU | classifier | 0.977 | 21.327 msmeasured | Runs today |
| injectioninputruns today | T2 | Attempts to talk the model out of its instructions. | CPU | classifier | 0.970 | 27.622 msmeasured | Runs today |
| moderationinput, outputtrained | T2 | Thirteen hazard categories in one pass, from violent crime to election misinformation. Replaces the capability Llama Guard and ShieldGemma provide, with weights this project can ship. | CPU | classifier | no corpus yet | 150 msbudget | trained |
| nsfwinput, outputruns today | T2 | Sexual or otherwise not-safe-for-work content. | CPU | classifier | 0.934 | 27.544 msmeasured | Runs today |
| politenessoutputruns today | T2 | Whether the tone of an answer is acceptable. | CPU | classifier | 0.962 | 30.319 msmeasured | Runs today |
| regulated_adviceoutputruns today | T2 | Output that reads as regulated financial, legal or medical advice. | CPU | classifier | 0.995 | 30.43 msmeasured | Runs today |
| toxicityinput, outputruns today | T2 | Abusive or hateful language, in input or output. | CPU | classifier | 0.992 | 27.224 msmeasured | Runs today |
| groundednessoutputruns today | T3 | Whether the claims in an answer are supported by the sources it was given. | CPU | classifier | no corpus yet | 300 msbudget | Runs today |
| topic_scopeinputruns today | T3 | Whether a request is inside the subject matter the product covers. | CPU | classifier | no corpus yet | 46.298 msmeasured | Runs today |
| url_reachabilityoutputruns today · needs network | T3 | Whether links in the answer resolve to something that answers, with a deadline and a refusal to request private addresses. | network | rule | not a classifier | 3000 msbudget | Runs today |
Always runs. Cannot be disabled.
Runs on the standard path.
Runs on the standard path. Can be disabled by policy.
Runs only when a lower tier flags, or when the policy asks for it.
Three claims, each with its measurement underneath it.
CPU is the reference target, not a fallback. Weights are fetched once and cached, and after that a scan works with the network interface down, which a test asserts by making a socket call raise. The cost of that choice is latency you can see, and the benefit is the row below that sends nothing anywhere.
The PII model is trained across all 26 target languages at once, with dates as a seventh entity type, and each country's national identifier generated so it passes its own checksum. Not an English model with translation bolted on: span-level F1 0.998, weakest language French at 0.977, and the full table ships in the model card.
Each of the 7 classifiers is scored separately in all 26 languages rather than in aggregate, and every row is published, including the ones that fail. All 7 tables, per language.
| Language | Models |
|---|---|
| en | 15 |
| ro | 14 |
| hu | 11 |
| pl | 11 |
| de | 10 |
| fr | 10 |
| az | 9 |
| bg | 9 |
| cs | 9 |
| da | 9 |
| el | 9 |
| es | 9 |
| et | 9 |
| fi | 9 |
| ga | 9 |
| hr | 9 |
| it | 9 |
| lt | 9 |
| lv | 9 |
| mt | 9 |
| nl | 9 |
| pt | 9 |
| sk | 9 |
| sl | 9 |
| sv | 9 |
| tr | 9 |
Every crossing produces a stamp: which detectors ran, which model revision and weight hash each used, the resolved policy hash, and hashes of the text. Optionally signed with a key you hold and the library never stores. The record above in section 01 is the whole of it.
Every latency figure here describes 87 tokens of prose on 1 thread, the library default, so a scan does not take cores from the application it runs inside. Cost is close to linear at 1.663 ms per token, and every model-backed detector shares one base model, so they cost the same at the same length.
The models this library ships: the PII tagger and eight classifiers, one per model-backed detector. All on Hugging Face under Apache-2.0, each carrying its per-language evaluation in its card, and small enough to run on a CPU you already own. Browse our models.
Sequence classification
Backs the bias detector on the output side.
Apache-2.0
Sequence classification
Backs the gibberish detector: unreadable input short-circuits the tiers above it.
Apache-2.0
Sequence classification
Backs the injection detector, for the text a retrieval tool fetched as much as the text a user typed.
Apache-2.0
Sequence classification
Backs the nsfw detector on input and output.
Apache-2.0
Token classification, PII spans
Seven entity types: card, date, email, IBAN, national ID, person, phone. Identifiers are generated checksum-valid in training, so an IBAN that fails mod-97 is not reported as an IBAN.
Apache-2.0
pii: { model: piiguard }
Sequence classification
Backs the politeness detector on the output side.
Apache-2.0
Sequence classification
Backs the regulated_advice detector: the line between explaining an instrument and recommending it.
Apache-2.0
Sequence classification
A bi-encoder for the topic_scope retrieval task. Runs today at 46 ms p95 against a 300 ms budget.
Apache-2.0
Sequence classification
Backs the toxicity detector on both sides of the boundary.
Apache-2.0
It does not sit in front of your model, it does not hold your traffic, and it does not wrap your model call. If it stops working, your application still runs, it just stops producing evidence.
Nothing here is certified, and no library can be. Obligations under the EU AI Act sit with the provider or deployer of a system, not with a dependency it installs. What this produces is an auditable record of which checks ran and what they found, which supports the evidence requirements of a governance process you run yourself. What it does support: the disclosure detector records whether an AI disclosure was present in each output, in 26 languages, which is evidence a transparency process can file.
It reads text and reports on text. It knows nothing about your authentication, your tool permissions, or what your agent is allowed to do with the answer it got.
A detector whose model is not published raises an error naming what is missing rather than returning an empty result, because a check that silently passes is worse than one that is absent.
Weights are fetched once and cached. After that a scan needs no network, and nothing you scan reaches FlowX.AI.
Python 3.11 or newer. Apache-2.0.
The package is flowx-border, the import is flowx_border, the GitHub org is flowx-ai, and the models live under flowxai on Hugging Face. Four spellings, one project.