Source-available crisis safety infrastructure for AI apps.
Detects distress, finds local help. Zero tracking. Works offline.
34 countries · 67 resource records · 94 contact methods · 647 safety tests · 0 permissions
Live Site · Test Inspector · Partner Overview · Get Help Now · Community · FAMTEC
SafeChat is an international register and toolkit for chat safety protocol. Free for everyone. Built for developers, health professionals, and communities.
- Crisis Detection — Regex-based detection runs locally. No API calls. No data leaves the device. Catches misspellings, text-speak, indirect warning signs, and passive suicidality.
- Geo-Location (No GPS) — Finds the user's country from timezone and locale. No permissions needed. 7-layer cascade.
- Maintained Helpline Database — 67 resource records across 34 countries, providing 94 phone, text, chat, email, WhatsApp, and web contact methods. CC0 public domain.
- AI Prompt Override — System prompt injections that tell your LLM to show crisis resources. Works with any AI provider.
- Drop-in UI — Modal, banner, and full-page popup. One script tag. PWA-capable. Works offline.
- Configurable Routing — Verified helplines by default, or hand off to a human moderator, safeguarding lead, or your own escalation path — the deployment decides where a signal goes.
- Safety-oriented — Designed to support crisis-routing implementations and informed by VERA-MH and Samaritans guidance; compliance and clinical suitability remain the integrator's responsibility.
SafeChat works as a standalone app on any device:
- iPhone/iPad: Open the popup in Safari → tap Share → "Add to Home Screen"
- Android: Open in Chrome → menu → "Install App"
- Desktop: Open in Chrome/Edge → click install icon in address bar
Open the popup: rob-e-graham.github.io/safechat/app/popup.html
<script src="https://cdn.jsdelivr.net/gh/rob-e-graham/safechat@main/src/browser.js"></script>
<script>
Safechat.protect(); // auto-monitor all text inputs
</script><script src="https://cdn.jsdelivr.net/gh/rob-e-graham/safechat@main/src/embed.js"
data-safechat-monitor="true"></script>npm install safechatconst safechat = require('safechat');
const safety = safechat.check(userMessage);
if (safety.level === 'high') {
systemPrompt = safechat.promptOverride('high', safety.country)
+ '\n\n' + systemPrompt;
}const safechat = require('safechat');
app.use(safechat.middleware());
app.post('/api/chat', (req, res) => {
const safety = req.safechat.check(req.body.message);
if (safety.action === 'crisis_intervention') {
systemPrompt = req.safechat.promptOverride(safety.level) + '\n\n' + systemPrompt;
}
});<iframe src="https://rob-e-graham.github.io/safechat/app/popup.html"
style="width:100%;max-width:480px;height:700px;border:none;border-radius:16px;">
</iframe>Detection uses local pattern matching with four pieces:
- Input normalisation — Smart quotes → ASCII, whitespace collapse, misspelling correction, slang/code-word expansion
- Core pattern matching — HIGH signals (explicit suicidal language, methods) and LOW signals (hopelessness, worthlessness)
- Reviewed signal pack — source-linked signal families distilled from public or peer-reviewed crisis-language resources
- False-positive guards — Context-aware filtering skips figurative language ("cut my hair", "suicide squeeze play", "overdosed on coffee")
| Level | Triggers | Action |
|---|---|---|
| high | Suicidal ideation, self-harm, explicit intent | Show crisis resources immediately |
| low | Hopelessness, worthlessness, feeling trapped | Soft safety response with helpline link |
| none | No crisis signals | Normal operation |
Threat, hate-speech, and protected-class slur detection are exposed separately through detectModeration().
They do not trigger crisis-resource routing by default; they return local moderation
signals that an app can use for review, de-escalation, or harm prevention.
SafeChat also exposes an evidence-linked reviewed signal pack through
detectReviewed(). This pack distils signal families from public or
peer-reviewed crisis-language resources such as C-SSRS, CLPsych, eRisk,
VERA-MH, MindGuard, MentalLLaMA, and MentalChat16K without embedding raw restricted
posts or transcripts. The pack is local, auditable, versioned, and designed to
be updated as expert review improves.
ConversationTracker assigns transparent routing weights (1-3) to contextual
signals that are not sufficient on their own. For example, a method-information
query has weight 3 but does not trigger by itself; a separate overwhelm signal
adds 1, crossing the configurable LOW threshold of 4. Broader combinations
that reach 8 trigger HIGH routing. Only categories, weights, and timestamps are
held in memory for 30 minutes; message text is not stored.
These weights are deterministic routing controls, not probabilities, diagnoses, or a clinically validated suicide-risk score. Applications can configure the thresholds, but changes require their own safety evaluation.
See docs/living-signal-database.md for the compression and governance model behind slang, misspellings, slurs, countries, and social-context expansion. See docs/vera-mh-signal-integration.md for the VERA-MH provenance, integration boundary, and multi-turn scoring example.
SafeChat finds the user's country without location permissions:
| Priority | Method | Permissions |
|---|---|---|
| 1 | Browser locale (navigator.language) |
None |
| 2 | Timezone (Intl.DateTimeFormat) |
None |
| 3 | CDN headers (CF-IPCountry, X-Vercel-IP-Country) |
None |
| 4 | Accept-Language header | None |
| 5 | Manual override | None |
| 6 | Global fallback (findahelpline.com) | None |
1. jsDelivr CDN (latest data) ← primary
2. GitHub raw (same data, different CDN) ← if jsDelivr down
3. localStorage cache ← if offline
4. Inline emergency numbers ← if never loaded
Automated checks run twice monthly for data structure, phone formatting, and chat-link reachability. Service details still require human verification.
SafeChat is designed for trust:
- Zero data collection — all detection runs locally, nothing is transmitted
- Content Security Policy — CSP headers on all pages
- HTML escaping — all dynamic content is escaped to prevent XSS
- Input validation — type checking, ReDoS protection, JSON structure validation
- Scoped service worker — only intercepts same-origin requests
- No cookies, no analytics, no tracking
- 647 automated tests including security/adversarial inputs
- Referrer policy —
no-referreron all pages
safechat.check("I can't go on anymore", { country: "AU" });
// { level: "low", matched: "can't go on", country: "AU", action: "soft_warning", resources: {...} }safechat.detect("I want to kill myself") // { level: "high", matched: "kill myself" }
safechat.detect("I feel worthless") // { level: "low", matched: "worthless" }
safechat.detect("great day today") // { level: "none", matched: null }safechat.detectModeration("I want to kill you")
// { level: "high", category: "threat", matched: "i want to kill you" }
safechat.detectModeration("great day today")
// { level: "none", category: null, matched: null }safechat.detectReviewed("I wish I could go to sleep and never wake up")
// { level: "low", sourceId: "cssrs_2011", family: "wish_to_be_dead", ... }SafeChat v1.2 introduces an optional cross-classifier module that runs local ML models alongside the regex engine. The regex layer stays as the fast, deterministic, always-on first pass. The cross-classifier provides a second opinion.
Suggested by Professor Stevie Chancellor (University of Minnesota) and informed by research from the VERA-MH evaluation framework.
- MindGuard (Sword Health) -- 4B/8B crisis classifier. Safe, self-harm, harm-to-others.
- MentalLLaMA -- 7B/13B mental health analysis. Depression, stress, suicidal ideation.
- MentalChat16K -- Benchmark dataset for evaluation.
- Any local model via Ollama, LM Studio, or a custom function.
The classifier never downgrades a regex detection. If regex says HIGH and the model says safe, it stays HIGH. False negatives cost lives.
const safechat = require('safechat');
const cc = safechat.createCrossClassifier({
backend: 'ollama',
model: 'mindguard-8b',
endpoint: 'http://localhost:11434',
});
const shield = safechat.createShield({
crisis: true,
subtle: true,
crossClassifier: cc,
});
// Async processing with cross-classifier verification
const result = await shield.processAsync(userMessage);
// result.crossClassifier.mergeAction = 'confirmed' | 'escalated' | 'regex_override'Everything runs locally. No data leaves the device.
SafeChat v1.3 adds a semantic layer: an embedding-similarity tier light enough to run in any modern browser — including installed PWAs on phones — fully offline after first load. It catches metaphorical distress that keyword patterns can't express ("I just want the noise to stop") by comparing each message against a curated set of crisis exemplar phrases in embedding space.
- ~25 MB instead of gigabytes — works with small sentence-embedding models like all-MiniLM-L6-v2
- Same merge contract as the cross-classifier: confirm or escalate, never downgrade
- Plain-text exemplars — auditable, replaceable, community-ownable for culturally specific language packs
- Zero new dependencies — the embedder is injected; SafeChat works identically without it
// Browser / PWA with Transformers.js
import { pipeline } from '@huggingface/transformers';
const extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
const sem = safechat.createSemanticLayer({
backend: 'custom',
embed: async (texts) => {
const out = await extractor(texts, { pooling: 'mean', normalize: true });
return out.tolist();
},
});
// Or server-side via Ollama
const sem = safechat.createSemanticLayer({
backend: 'ollama',
model: 'nomic-embed-text',
});
const shield = safechat.createShield({ crisis: true, semanticLayer: sem });
const result = await shield.processAsync(userMessage);
// result.semantic.mergeAction = 'confirmed' | 'escalated' | 'regex_override' | 'passthrough'Every layer examines the message independently — no layer sees another's verdict, so no layer can be anchored by it or argue it down. A deterministic merge (~20 lines of auditable code, not a model) takes the most cautious opinion. The safety floor is architectural: a flaky model can only fail toward "no worse than regex alone."
| Tier | Layer | Footprint | Runs on |
|---|---|---|---|
| 0 | Regex engine + ConversationTracker | KBs | Everything, always, offline |
| 1 | Semantic layer (embedding similarity) | ~25 MB | Any modern browser, PWA |
| 2 | Distilled crisis classifier (planned) | ~50 MB | Browser/phone, offline |
| 3 | LLM cross-classifier | GBs | Server/desktop, opt-in |
Australia, Austria, Belgium, Brazil, Canada, China, Denmark, Finland, France, Germany, Ghana, Hong Kong, India, Ireland, Israel, Italy, Japan, Kenya, Mexico, Netherlands, New Zealand, Nigeria, Norway, Pakistan, Philippines, Portugal, Russia, South Africa, South Korea, Spain, Sweden, Switzerland, United Kingdom, United States.
Plus global fallback via findahelpline.com (175+ countries).
SafeChat is under continuous, active development. See CHANGELOG.md for a full record of detection improvements, new patterns, and accuracy gains. Every change is tested, timestamped, and publicly documented.
- False negatives are treated as critical defects
- Crisis resource checks are scheduled twice monthly for structure, phone formatting, and reachable chat URLs; service details still require human verification
- Test suite must pass before every release (currently 647 tests)
- Detection patterns are reviewed against published clinical literature
SafeChat uses Business Source License (BSL 1.1).
Free for:
- Personal, educational, research, nonprofit/community use
- Commercial entities under $100,000 USD annual revenue
Commercial entities over $100K must obtain a commercial license: rob@fineartmedia.tech
Changes to MPL 2.0 on 2029-01-01.
SafeChat is provided as is. It is not emergency medical care, legal advice, professional mental health treatment, or a substitute for emergency services, clinicians, safeguarding teams, or local crisis procedures.
By downloading, installing, deploying, integrating, or using SafeChat, you accept responsibility for your own installation, configuration, compliance, testing, supervision, and use. To the maximum extent permitted by law, Rob Graham, FAMTEC, contributors, maintainers, copyright holders, and licensors are not liable for damages, losses, legal claims, regulatory penalties, system damage, data loss, personal injury, death, failure to obtain help, failure to detect crisis content, unlawful use, or other harm arising from installation or use.
SafeChat is a routing layer, not a diagnostic tool. It identifies textual signals and connects users to professional resources. It does not assess clinical risk or replace professional mental health services.
If you do not accept these terms, do not download, install, deploy, integrate, or use SafeChat. See LICENSE and docs/legal-disclaimer.md for the full disclaimer, integrator responsibilities, and limitation of liability.
Help keep crisis resources accurate and expand to more countries. See CONTRIBUTING.md.
This project follows Samaritans safe messaging guidelines.
- GitHub Discussions — questions, research, standards
- Issues — bugs, wrong numbers, detection gaps
- FAMTEC — the team behind SafeChat
Created by Rob Graham / FAMTEC
If you or someone you know is in crisis: findahelpline.com

