Skip to main content

jevmod

Moderation for communities and apps: every message gets a probability for spam, scam, harassment, nsfw, off-topic, self-harm, doxxing, sexual content involving minors, and for rules you write in plain English. You set the thresholds and the actions. Every decision is logged with its numbers.

It runs on Jev, TypeSafe's System One model: you ask yes/no questions about a message and get probabilities back, no text generation. About $0.04 per 1,000 messages with all categories on.

jevmod check and the HTTP API in a terminal

$ jevmod check "FREE NITRO for the first 100!! claim at discord-gifts.ru/nitro"
scam 0.99      'FREE NITRO for the first 100!! claim at discord-gifts.ru/nitro'  [scam 0.99, spam 0.98, harassment 0.02]

Flag-only by default: nothing is deleted until you turn that on. Fails open: if Jev is unreachable, messages are left alone and the failure is logged. Self-harm is flag-only by design so a moderator can reach out.

you are you get start
a community owner, not technical a Discord bot you tune with commands (Telegram with fewer commands, Reddit by env vars) Run the bot
a developer a CLI, a Python package, an npm package, or one HTTP call Developer
a coding agent, or someone using one an MCP server and a Claude Code skill that wires jevmod into a codebase Agents

On the benchmark (2,531 messages from OpenAI's moderation eval, Jigsaw and YouTube spam) jevmod had the best AUROC in every category it was compared on in OpenAI's human-labelled set: harassment 0.93 and sexual 0.98 against Llama Guard 3 8B, ShieldGemma 2B and toxic-bert; self-harm 0.99 and minors 0.98 against Llama Guard, the only other system with those labels. Calibration was measured too; caveats are in the same file.

The key, once

Every surface needs a TypeSafe API key (free tier at console.typesafe.ai).

pip install "jevmod[keys]"
jevmod init          # asks for the key without echo, verifies it with one call, stores it in the OS keyring

Resolution order everywhere (CLI, API, bots, MCP, Moderator): TYPESAFE_API_KEY in the environment, then the keyring, then a .env in the current directory. In a container or a headless server jevmod init --env-file writes .env with mode 600. jevmod init --forget removes the keyring entry (pip uninstall does not). Keys never go into git; .env is ignored.

Run the bot

Discord

  1. Developer Portal → New Application → Bot → Reset Token → enable Message Content Intent (the only privileged intent used).
  2. OAuth2 → URL Generator → scopes bot + applications.commands; permissions: Read Messages, Send Messages, Manage Messages, Moderate Members, Manage Channels, Embed Links, Add Reactions. Open the URL, add it to your server.
  3. pip install "jevmod[discord]", set DISCORD_TOKEN, run jevmod discord.

The bot creates a private #jevmod-log channel and starts flagging there.

command (server managers only) what
/mod status settings and this month's usage
/mod set <category> <action> [threshold] any category → off, flag, delete, timeout
/mod rule <name> <text> [action] [threshold] a rule in your words: "No politics. News about the game is fine." (max 5)
/mod trust <role> messages from that role are never judged
/mod topic <text> what the current channel is for (turns on offtopic)
/mod log, /mod recent choose the log channel; last decisions with probabilities
/mod forget, /mod forget_user @member delete everything stored about the server, or one member

React ❌ on a log entry to mark a false positive (that category's threshold goes up a notch), ✅ to confirm a correct call (down a notch, floor 0.5).

Telegram and Reddit

Telegram: @BotFather/newbot, make the bot a group admin, pip install "jevmod[telegram]", set TELEGRAM_TOKEN, run jevmod telegram. Admin commands: /mod_status, /mod_set, /mod_rule, /mod_topic, /mod_log.

Reddit: for your own subreddit with your own "script" app credentials, non-commercial (Reddit's API terms). pip install "jevmod[reddit]", fill the REDDIT_* variables from .env.example, run jevmod reddit. Reports by default; removal and bans are opt-in.

What the bots send where

Only the message text and the channel topic go to TypeSafe. Author names and ids never do. Locally, jevmod keeps a decision log (category, probabilities, action, first 300 characters); rows older than 30 days are purged on every batch. /mod forget deletes everything; leaving the server does the same. Members whose message is removed get a direct message saying an automated system did it and how to appeal.

Developer

CLI

pip install jevmod
jevmod check "some text"                                   # exit 0 clean, 1 something triggered, 2 error
cat comments.txt | jevmod check --json --rule "No politics. Game news is fine." -

One message per line on stdin, one Jev request per 50. --topic turns on the off-topic check, --threshold sets one for every category, --json prints one object per line with every probability. examples/cli/ has a file screener and a pre-commit hook.

Python

from jevmod import Moderator, Policy

d = Moderator().check("FREE NITRO for the first 100!! claim at discord-gifts.ru/nitro", channel_topic="gaming")
d.action, d.category, d.probability  # ('flag', 'scam', 0.99)
d.scores  # {'spam': 0.98, 'scam': 0.99, 'harassment': 0.02, 'nsfw': 0.01, ...}

p = Policy()
p.set_category("scam", "delete", 0.7)
p.set_rule("no_politics", "No political discussion. Game news is fine.", action="flag", threshold=0.8)
Moderator(policy=p).check_many(["...", "..."], channel_topic="support")  # one request for the batch

npm

import { Moderator, Policy } from "jevmod";
const d = await new Moderator().check("FREE NITRO ...", { channelTopic: "gaming" });
d.action, d.category, d.scores            // same shape as Python and the HTTP API

Node 20+. Same questions (jevmod/categories.json is copied byte for byte and CI fails if it drifts), same policy, same decision. JevmodClient talks to a deployed HTTP API instead, so browsers and edge functions never hold the TypeSafe key. Express middleware in packages/jevmod-js/examples/. Details in packages/jevmod-js/README.md.

HTTP API

JEVMOD_ADMIN_TOKEN=... jevmod api                     # or: docker compose up -d
curl -X POST localhost:8080/v1/keys -H "Authorization: Bearer $JEVMOD_ADMIN_TOKEN" \
     -H "Content-Type: application/json" -d '{"tenant":"my-app"}'          # {"api_key":"jm_...", shown once}
curl -X POST localhost:8080/v1/moderate -H "Authorization: Bearer jm_..." -H "Content-Type: application/json" \
     -d '{"messages":[{"id":"a","text":"FREE NITRO for the first 100!! claim at discord-gifts.ru/nitro"}]}'
endpoint what
POST /v1/moderate up to 50 messages → decisions; the X-Request-Id you send comes back as request_id and as a response header
GET/PUT /v1/policy thresholds, actions, rules for this tenant
GET /v1/decisions the audit log
DELETE /v1/tenant forget this tenant
POST /v1/keys (admin) mint a tenant key, stored hashed
GET /v1/health, GET /metrics liveness, Prometheus counters

OpenAPI at /docs, a Postman collection in postman/. Any chatbot, forum or comment system that can make an HTTP call can use it; the bots are adapters over the same service.

Examples, one folder per surface

folder what
examples/sdk/ Moderator basics, a custom policy with a rule, batching 50 per request
examples/cli/ screen_file.sh exits 1 on hits; pre-commit.sh blocks flagged text files
examples/api/ curl, a stdlib Python client, a Node client against a local jevmod api
examples/discord/ run the bot; custom_adapter.py puts any chat platform on ModerationService in 20 lines
examples/agent_harness/ @guarded decorator, Claude Agent SDK PreToolUse/PostToolUse hooks, LangChain callback
examples/input_validation/ FastAPI dependency that answers 422, a pydantic ModeratedText field
packages/jevmod-js/examples/ Express middleware and a plain Node script

Agents

MCP server. pip install "jevmod[mcp]" then jevmod mcp (stdio). Tools: moderate(texts, channel_topic?, rules?) returns one decision per text; categories() describes each category and its default threshold.

claude mcp add jevmod -- jevmod mcp                                   # Claude Code
{"mcpServers": {"jevmod": {"command": "jevmod", "args": ["mcp"]}}}    # Cursor, Codex, others

Claude Code plugin. This repository is its own marketplace:

/plugin marketplace add ohernandezdev/jevmod
/plugin install jevmod@jevmod

jevmod-integrate adds moderation to an existing codebase (detects the stack, picks SDK/npm/HTTP/MCP, wires the key, inserts the call where it belongs, adds a real test). jevmod-moderate screens text or datasets from the terminal while working. AGENTS.md has every signature; docs/llms.txt indexes the docs.

Categories

category true when default
spam unsolicited promotion, invite farming, bare link drops, mass mentions flag ≥ 0.85
scam fake giveaways, phishing domains, impersonated support, "DM me for a deal" flag ≥ 0.75
harassment insults, slurs, threats, targeted abuse, in any language flag ≥ 0.75
nsfw sexual or gore content for a general audience (below the threshold means SFW) flag ≥ 0.80
offtopic unrelated to channel_topic; needs a topic to mean anything off, 0.90
selfharm the author is in crisis or considering self-harm; alert moderators, never punish flag ≥ 0.80
doxxing reveals or hunts private data about a real person flag ≥ 0.80
minors sexualises a minor or shows grooming behaviour flag ≥ 0.70
rule:<name> your rule in plain language, exceptions included, up to 5 flag ≥ 0.80

Every check returns all enabled categories at once, in one request. The questions are in jevmod/categories.json: one yes/no question per category with explicit true/false criteria, the pattern of TypeSafe's guardrails cookbook. Jev's probabilities move about ±0.03 between runs, so anything within that band of a threshold will flip; the ❌/✅ feedback and PUT /v1/policy exist to move the line.

How it works

architecture

Message → pre-filter (trusted authors, under eight letters without a link, repeats of judged text never reach Jev) → batch for 2 s per community → one Jev request for the batch → probabilities → policy → action → audit log. Batched messages are sent as a dict keyed by position; as a list, probabilities leaked between neighbours, which the 98-message adversarial red team caught and which now runs as a regression suite in CI. More in docs/diagrams/ and PLAN.md.

Self-host

One image, one variable picks the role: api, discord, telegram, reddit. SQLite on a volume.

cp .env.example .env && docker compose up -d              # API on :8080
docker compose --profile discord up -d                    # add the Discord bot

A $4/month VM, Fly.io or Railway with a volume is enough. Failure policy: Jev unreachable → decisions come back reason="error_open" and nothing is acted on. There is no quota by default; JEVMOD_MONTHLY_QUOTA=5000 pauses judging for a tenant after 5,000 judged messages in a month as a cost guard, tells the owner once, deletes nothing.

Public demo

The landing page's live check talks to jevmod demo, a separate role that keeps the key on the server and stops at a monthly budget (JEVMOD_DEMO_BUDGET_USD, default $0.50), with per-visitor limits and a CORS allow-list. It logs what visitors try (text, scores, hashed IP) for the operator. deploy/demo/ has a Caddy + Docker compose for a small VPS with HTTPS in two commands.

Development

git clone https://github.com/ohernandezdev/jevmod && cd jevmod
python -m venv .venv && .venv/bin/pip install -e ".[all,dev,examples]"      # Windows: .venv\Scripts\pip
ruff check . && mypy jevmod && pytest          # offline tests run without a key; the rest hit the real API
cd packages/jevmod-js && npm ci && npm test    # same for the npm package

Tests never mock Jev. tests/test_redteam.py is the adversarial regression set; benchmark/ reproduces BENCHMARK.md.

License

MIT, © Omar Hernandez. See DISCLAIMER.md: decisions are probabilistic, the operator owns the thresholds, the actions and legal compliance; not affiliated with TypeSafe, Discord, Telegram or Reddit.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jevmod-0.2.0.tar.gz (53.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jevmod-0.2.0-py3-none-any.whl (44.2 kB view details)

Uploaded Python 3

File details

Details for the file jevmod-0.2.0.tar.gz.

File metadata

  • Download URL: jevmod-0.2.0.tar.gz
  • Upload date:
  • Size: 53.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for jevmod-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6b1d92263f2e38de7b67ce745a176c70601e44d84f5a4e63e337a61e220b58b3
MD5 e49ae7ae96eff6b337bd03e232a0653b
BLAKE2b-256 b87a9417c1917470bb07eb2649ddb24b5a78b14d691947ae0a200b42986ddd92

See more details on using hashes here.

Provenance

The following attestation bundles were made for jevmod-0.2.0.tar.gz:

Publisher: release.yml on ohernandezdev/jevmod

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jevmod-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: jevmod-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 44.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for jevmod-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 68f72201dbac739789f3380039c0a0bac396065ee9d20e07e90071570d1860e0
MD5 794d1ba331eb9f9f4c32efdd8e7e7c3c
BLAKE2b-256 68c790c1db2fdd36fdc40e2cd0c3ebfc9487e5d495515721767cf1759703e356

See more details on using hashes here.

Provenance

The following attestation bundles were made for jevmod-0.2.0-py3-none-any.whl:

Publisher: release.yml on ohernandezdev/jevmod

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page