Verifies that Sentinel's PII/secret redaction guardrail survives routing through CUSTOM_PROVIDERS to a self-hosted OpenAI-compatible backend (vLLM, TGI, Ollama, or anything speaking /v1/chat/completions) — not just Sentinel's first-party Anthropic/OpenAI/Google routes. Starts the local Sentinel gate
设计与多媒体
Sentinel Modality-Router Redaction Check
试用Confirms Sentinel's PII redaction guardrail survives routing through a modality-aware LiteLLM router to two different self-hosted model types -- a text model (Ollama) and a vision model (SmolVLM-Instruct) -- picked automatically by whether the request contains an image. Checks both redaction and rou
它能做什么
Confirms Sentinel's PII redaction guardrail survives routing through a modality-aware LiteLLM router to two different self-hosted model types -- a text model (Ollama) and a vision model (SmolVLM-Instruct) -- picked automatically by whether the request contains an image. Checks both redaction and routing correctness per modality, and links the resulting Jaeger trace if tracing is available.
技能文档
Sentinel Modality-Router Redaction Check
This skill re-verifies, on a schedule or on demand, something that was
already confirmed by hand once: that Sentinel's PII redaction fires on
every request going through router.py's CUSTOM_PROVIDERS route,
regardless of which backend model router.py ultimately picks. It's the
companion to the earlier sentinel-custom-provider-check skill (built for
the vLLM-in-Colab project), extended for a setup with more than one backend
behind the same gateway.
What this skill actually does (permissions/behavior disclosure)
This reads as a passive check, but running it grants two things that aren't obvious from the name alone:
- It stops and restarts your local Sentinel gateway container with a new
CUSTOM_PROVIDERSconfig (_start_gateway()inskill.py). That's a real reconfiguration of a security control, not a read-only probe -- anything else relying on that gateway staying up will see a brief interruption every time this skill runs. - It can send diagnostic content to an external Telegram bot if
TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_IDare set. On a failed probe, that message includes up to 300 characters of the raw backend response (_format_result_message()). In this skill's own probes that content is always synthetic PII the skill generated itself -- never anything a real user typed -- but if you fork this to probe with real prompts instead of the fixed synthetic values, that failure-path snippet would carry real content off the machine. LeaveTELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_IDblank if you don't want any network egress beyond the gateway/router/ Jaeger calls -- the skill still runs and prints its result to stdout.
Two things can silently break between checks, and this skill catches both:
- Redaction regresses -- someone changes the gateway config, upgrades
Sentinel, or points
CUSTOM_PROVIDERSsomewhere new, and PII starts leaking through unredacted. - Routing regresses --
router.py's modality detection breaks (e.g. an image payload silently falls through to the text model), which would otherwise go unnoticed since both legs return a normal-looking chat response either way.
Why this check needs a specific method (read before running)
Do not "verify" this by pasting real PII into a chat with an AI assistant and reading its reply. If that assistant's own session is itself routed through a Sentinel gateway (as is the case in the environment this skill was built in), inbound PII-shaped text gets redacted on the way in to that session -- independent of whatever actually happened at the router/backend level. That makes a broken pipeline look identical to a working one.
The only trustworthy check is reading the raw JSON the backend itself
returned, over a channel nothing upstream can quietly re-redact. That's
what _probe_text() and _probe_image() in skill.py do: they hit the
gateway directly with requests.post(...) and inspect the literal response
body for the exact synthetic phone number/email used in the probe.
The synthetic PII values (PROBE_PHONE, PROBE_EMAIL in skill.py) are
built by joining string parts ("-".join([...]), "@".join([...])) rather
than written as single contiguous literals. This isn't stylistic --
sentinel-custom-provider-check's original skill.py shipped with
PROBE_EMAIL accidentally set to the literal string {{REDACTED}} instead
of a real fake address, because a plain PII-shaped literal typed directly
into source got mangled by a local dev-environment redaction layer before
it ever reached disk. Building the value from parts avoids that failure
mode entirely.
What You Need From the User
- The parent project (
sentinel-litellm-local-router) already running locally:router.py, the LiteLLM proxy, Ollama, andvlm_server.py. This skill does not start any of those -- it only points the Sentinel gateway'sCUSTOM_PROVIDERSatrouter.pyand drives two probes through it. ROUTER_BASE_URL-- the address the gateway should forward to (http://host.docker.internal:8200if the gateway runs in Docker androuter.pyruns on the host; see.env.example).TEXT_MODEL_ALIAS/VISION_MODEL_ALIAS-- the model namesrouter.pyis expected to pick for each modality (defaults:qwen-local,smolvlm-local).TEST_IMAGE_PATH-- any small PNG/JPG, just needs to exist so the second probe has something to attach.- Optional:
JAEGER_BASE_URL/JAEGER_SERVICE_NAMEif Jaeger tracing is running (docker compose up -din the parent project) -- the report will include the resulting trace ID and a direct link. Leave blank to skip; redaction/routing pass-fail never depends on this. - Telegram bot token + chat ID already configured for OpenClaw (same values reused, no separate bot needed).
Step 1 -- Install dependencies
cd sentinel-litellm-local-router/sentinel-modality-router-skill
pip install -r requirements.txt
cp .env.example .env
# edit .env with your ROUTER_BASE_URL, model aliases, image path, etc.
Step 2 -- Run the check
python skill.py
This will:
- Stop and restart the local Sentinel gateway with
CUSTOM_PROVIDERSpointed atROUTER_BASE_URL. - Wait for the gateway to respond.
- Send a text-only PII probe (expects
TEXT_MODEL_ALIASin the response). - Send an image+text PII probe (expects
VISION_MODEL_ALIAS). - Best-effort look up the most recent matching Jaeger trace.
- Print the result and send it via Telegram if configured.
Step 3 -- Register as an OpenClaw skill
Point OpenClaw's skill loader at this directory so /sentinel_modality_check
triggers run() in skill.py. See OpenClaw's own skill-registration docs
for the exact mechanism in your setup -- this skill follows the same
SKILL_META shape (trigger, webhook_url, method, mcp_servers) as
sentinel-custom-provider-check, so anything already wired for that skill
should work the same way here.
Interpreting results
- ✅ Both probes pass: redaction fired on both legs, and each request reached the model it should have. This is the state you want on a schedule.
- ⚠️
redacted_seen=Falseor*_leaked=True: redaction did not fire on that leg -- treat as a real regression, not noise. Escalate before trusting the gateway with anything sensitive again. - ⚠️
routed_correctly=False: redaction may be fine, butrouter.py's modality detection sent the request to the wrong backend (e.g. an image payload landed on the text model). This is a routing bug, not a privacy one, but it means the demo/pipeline isn't doing what it claims to. - Trace link present: open it in Jaeger to see the exact spans this run produced -- useful for pairing this skill's automated check with a visual walkthrough (e.g. for a screenshot or a stakeholder demo).
- Trace link absent: Jaeger wasn't reachable at
JAEGER_BASE_URL, or it wasn't configured. Redaction/routing results above are unaffected either way -- the trace lookup is purely informational.
Trust boundaries (read before running unattended)
.env values (ROUTER_BASE_URL, TEST_IMAGE_PATH, JAEGER_BASE_URL) are
treated as trusted, admin-supplied config, not untrusted input -- they go
directly into subprocess args and outbound URLs with no sanitization,
allowlisting, or path-traversal guard. That's an intentional, low-risk
choice for a skill meant to be configured by the same person who runs it
locally, not a skill designed to accept those values from an external
caller. If this skill is ever adapted to take any of these from a webhook
body, a Telegram command argument, or any other source outside the
operator's own .env file, add validation before that value reaches
subprocess.run(...) or a requests call.
Relatedly: there's no retry/backoff on a transient 429/503 from the
gateway or a backend -- a single failure fails the whole check. This is
deliberate, not an oversight: vlm_server.py in the parent project returns
503 on purpose when it's mid-request (see that project's README), and
retrying would mask exactly the concurrency signal that design is meant to
surface. For a check that runs on a schedule against a system expected to
be idle between runs, failing loudly on an unexpected 503 is more useful
than silently retrying past it.
Background / provenance
This skill extends the reference implementation at
sentinel-litellm-local-router (modality-based local model routing behind
Sentinel: router.py inspects incoming messages for an image_url block
and picks a text or vision model alias before handing off to a LiteLLM
proxy). It generalizes sentinel-custom-provider-check's single-backend
redaction probe to a router with more than one model type behind it, and
adds a routing-correctness check that the earlier skill had no reason to
include (it only ever had one possible destination).
Per Superwise's PII redaction docs, the guardrail matches structured identifiers (email, phone with area code, SSN, credit card, IP, VIN, etc.) and explicitly does not match free-text names -- this skill's probes only assert on the phone/email fields for that reason.
Troubleshooting
sentinel gateway startfails or hangs: check that no other process already holds the configured port;sentinel gateway stopfirst if unsure.- Gateway never becomes reachable (
_wait_for_gatewaytimes out): confirmROUTER_BASE_URLis reachable from inside the gateway's container --localhostwill not resolve to the host machine; usehost.docker.internal(or the container's actual bridge IP) instead. - Image probe fails with a 503 or times out: if
vlm_server.pyuses a non-blocking lock (as it does in this project) and something else is mid-request against it, the probe will get a 503 rather than queue. Re-run once nothing else is calling the vision backend. - Both probes report
passed=Falsewithredacted_seen=Falsebut no PII leaked either: check the model's raw response text directly (printed to stdout) -- some backends paraphrase instead of repeating values verbatim, which can make the marker check look like a false failure. This is a probe-prompt tuning issue, not a redaction issue. - Trace lookup returns nothing even with Jaeger running: confirm
JAEGER_SERVICE_NAMEmatches the service namerouter.pyactually registers as (check/api/serviceson the Jaeger base URL) -- it may differ from the default if the parent project's OTel setup was renamed.
相关技能
AI Firewall for Open Claw agents. Scrubs inbound messages and tool results for prompt injection, jailbreaks, and data exfiltration attempts using Sentinel's multi-layer detection pipeline.
Smart LLM Router for OpenClaw. Save up to 70% by routing every request to the right model. No coding required.
扫描并脱敏 AI 输出中的 8 类个人身份信息,可选用链上可验证的实时正则方案。
在文本送入大模型前调用一次 HTTPS POST 即可抹除 PII,Solana 按次结算或 50 次免费试用。
Independent fail-closed second opinion before acting: allow/review/block a risky action, fact-check a claim, screen text for prompt injection, or flag PII/se...