--- license: cc-by-nc-4.0 base_model: distilbert-base-multilingual-cased pipeline_tag: text-classification library_name: transformers language: - multilingual - en - de - fr - es - ru tags: - security - secret-detection - credentials - dlp - code metrics: - f1 - precision - recall ---

Prowl

# Prowl secret encoder A multilingual text classifier that scores whether a fragment of text (a line of code, a config value, a Jira comment, a log line, a chat message) **contains a leaked credential**. It is stage 3 of [Prowl](https://github.com/Lercas/prowl), a high-precision secret scanner, and handles the free-form and multilingual tail that regex and the linear model miss. This model is **not a standalone scanner**. It is one stage of an ensemble; on its own it is a recall booster, not a precision oracle. To scan a repo, use Prowl. ## What it does - **Input:** a short text span (≤512 tokens; the tool feeds ~128). - **Output:** two logits → `softmax(...)[1]` is P(text contains a secret). - **Decision:** fires at `P ≥ 0.90`, a threshold calibrated on a held-out validation split to **precision ≥ 0.95** (value-disjoint from the benchmark, no leakage). ## Role in the ensemble Prowl combines three stages by union: a Go regex/checksum/entropy cascade, a char+word TF-IDF logistic regression, and this encoder. Adding the encoder to the other two, measured on [ProwlBench](https://github.com/Lercas/prowlbench) (3,843 leakage-safe cases): | Configuration | Precision | Recall | F1 | |---|:--:|:--:|:--:| | cascade ∪ LR | 0.974 | 0.848 | 0.909 | | **+ this encoder** | **0.971** | **0.970** | **0.970** | The encoder lifts recall by 0.12 at a 0.003 precision cost, and reaches recall **1.00** on German, French, Spanish, and Russian prose passwords - the free-form, multilingual tail that regex and the linear model miss. Standalone recall at a fixed precision of 0.97, by channel: | code | Jira | Confluence | log | |:--:|:--:|:--:|:--:| | 0.984 | 0.996 | 0.998 | 0.947 | ## Usage ```python import torch from transformers import AutoTokenizer, AutoModelForSequenceClassification tok = AutoTokenizer.from_pretrained("Podric/prowl-secret-encoder") model = AutoModelForSequenceClassification.from_pretrained("Podric/prowl-secret-encoder").eval() def secret_score(text: str) -> float: enc = tok(text, return_tensors="pt", truncation=True, max_length=128) with torch.no_grad(): return torch.softmax(model(**enc).logits, -1)[0, 1].item() secret_score('DB_PASSWORD = "tR4!nf0rce-2026-prod"') # high secret_score("the deployment finished without errors") # low # fire at >= 0.90 ``` ## Example output `secret_score(text)` on a few inputs (the model fires at `P ≥ 0.90`): | P(secret) | fires | input | |:--:|:--:|---| | **1.000** | yes | `DB_PASSWORD = "tR4!nf0rce-2026-prod"` | | **1.000** | yes | `Ihr neues Passwort lautet F4QPE91sc6iN ...` (German prose) | | **1.000** | yes | `Пароль от прод-сервера: Zx9!kLmN2k24qP ...` (Russian prose) | | 0.001 | no | `token = os.environ["SERVICE_TOKEN"]` (env reference) | | 0.002 | no | `Развёртывание завершилось без ошибок ...` (Russian log) | | 0.000 | no | `def calculate_total(items): return sum(...)` (benign code) | | 0.000 | no | `API_KEY=your_api_key_here` (placeholder) | It fires on real passwords, including free-form prose in German and Russian that has no fixed prefix for a regex to anchor, and stays near zero on benign code, logs, env-var references, and placeholders. ## Training - **Base:** `distilbert-base-multilingual-cased` (104 languages, 6 layers, 134M params). - **Objective:** binary sequence classification (secret-bearing / not). - **Data:** the [Prowl secrets corpus](https://huggingface.co/datasets/Podric/prowl-secrets-corpus), 503k labeled records across code, tickets, logs, and prose. Real-secret sources (CredData, HF PII sets) are held out by origin so the validation split is not a memorization check. Positives are oversampled and real-origin records up-weighted. - **Calibration:** the operating threshold is fixed post-hoc on validation to a precision target, not learned, so it transfers to the ensemble's union rule. ## Limitations - **Binary, not typed.** It says *secret / not secret*; the secret **type** (AWS vs Stripe vs ...) comes from Prowl's cascade. - **A stage, not a scanner.** High recall comes at a precision that only makes sense inside the ensemble, where the cascade supplies structured-token precision. Do not deploy it alone as a gate. - **Span-level, not token-level.** It flags a span; Prowl localizes the exact value. - **Distilled size.** Chosen for CPU-friendly latency over a larger encoder; the few remaining misses are ambiguous, label-noisy cases. ## Citation ```bibtex @misc{prowl_secret_encoder, title = {Prowl secret encoder}, author = {Prowl}, year = {2026}, howpublished = {Hugging Face}, url = {https://huggingface.co/Podric/prowl-secret-encoder} } ``` ## License Noncommercial use only (CC BY-NC 4.0). Not for use in commercial products. Fine-tuned from `distilbert-base-multilingual-cased` (Apache-2.0). See the [Prowl repository](https://github.com/Lercas/prowl) and the dataset card for data provenance and source licenses.