Skip to content

Moderation verdicts, abuse suspension with alerting, realtime canary, and new surfaces#7

Merged
CMGS merged 6 commits into
mainfrom
feat/safety-alerts-surface
Jul 18, 2026
Merged

Moderation verdicts, abuse suspension with alerting, realtime canary, and new surfaces#7
CMGS merged 6 commits into
mainfrom
feat/safety-alerts-surface

Conversation

@CMGS

@CMGS CMGS commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Four feature batches plus an external-review hardening round.

Moderation chain — the moderator seam grows the full verdict set: Allow, Mask (byte spans into the reviewed text, mapped back onto the request's text slots by replaying the shared walk — char-boundary snapped, overlaps merged, works across multi-part messages, typed params, and realtime frames), Degrade (reroutes through the tenant fallback via the same swap the quota gate uses; a request already on the fallback serves rather than denies; denies when none is configured), and Deny. Realtime maps Degrade to a deny — a live session can't switch models — and masks frames in place. Every verdict records a security event.

Abuse auto-suspension + alert webhook — admission rejections (the shared 429 code, REST surface) count against a day-scoped fleet-shared counter; crossing a configured tier suspends the key until a deadline — self-recovering, ordered under ban, preserved across config reloads (only a patch touches it), audited as a system actor, and alerted. : is now banned in key names so the prefixed governance keyspaces can't collide with a real key. Alerts ride a bounded lossy bus drained by a dispatcher that POSTs to an env-named webhook with a (kind, subject) dedup window; sources: abuse suspensions, account cooldown transitions (both engine attempts), and model availability transitions from a 60s sweep.

Realtime session canary + availability — the handshake runs the variant split (sticky by attributed user, pinned for the session, same pick_variant as the REST DAG); entitlement and the per-(AK, model) counter judge the public name while QPM, pricing, and the upstream URL follow the served variant. Realtime feeds availability: a success per billed turn, errors at pool exhaustion, connect failure, or mid-session upstream death — attributed to the public name. A turn denies if a reload removed the model the session pinned (pricing an unknown model silently bills zero); wire-direct sessions are exempt.

New surfaces + presets/v1/moderations (OpenAI shape) and /v1/rerank (Cohere/Jina-compatible, billing from either usage.total_tokens or meta.tokens) as full protocols with typed params covered by the blocklist/DLP walkers; /v1/audio/translations as a request property on the STT engine; provider presets moonshot and siliconflow.

Hardening

An external review contributed seven confirmed findings, all fixed: SiliconFlow rerank usage dialect, the realtime zero-price reload hole, the already-on-fallback degrade denial, a lost cooldown alert on the failover retry account, and security-event surfaces now recording the real protocol on every family handler (a pre-existing placeholder wart, fixed across the board).

Testing

cargo fmt --check, cargo clippy --all-targets -- -D warnings, and cargo test --workspace green on macOS and in a rust:1 Linux container at every commit. Highlights: span masking across slots and UTF-8 boundaries, degrade/deny/mask end-to-end with security-event assertions, a real TCP listener asserting the webhook POST, suspension tripping and self-recovery, a WebSocket session proving the canary serves while the ledger splits requested/served, and reload tests pinning both the suspension-preservation and the removed-variant denial.

CMGS added 6 commits July 18, 2026 11:28
The moderator seam grows the Trust-Layer-shaped verdict set: Mask carries
byte spans into the reviewed text, mapped back onto the request's slots by
replaying the shared text walk (char-boundary snapped, overlaps merged);
Degrade reroutes through the tenant fallback via the same admission swap
the quota gate uses (and denies when none is configured); realtime maps
Degrade to a deny — a live session can't switch models — and masks frames
in place. Every verdict records a security event. Also pins two audit test
gaps: an aborted stream leaves no availability sample, and a quota-degraded
request skips the variant split.
Admission rejections (the shared 429 code, REST surface) count against a
day-scoped fleet-shared abuse counter; crossing a configured tier patches a
suspended_until deadline onto the key — self-recovering, ordered under ban,
preserved across config reloads/upserts (only a patch touches it), audited
as a system actor, and alerted. ':' is now banned in ak names so the
prefixed governance keyspaces cannot collide with a real key (a key named
abuse:X could otherwise force-suspend X). Alerts ride a bounded lossy bus
drained by a dispatcher task that POSTs to an env-named webhook with a
(kind, subject) dedup window; sources: abuse suspensions, account cooldown
trips, and model availability transitions from a 60s sweep. Tier lists
must ascend (validated); abuse counting is REST-only by construction
(documented).
A realtime handshake now runs the variant split: sticky by the attributed
user (per-connection spread when anonymous), pinned for the whole session,
via the same pick_variant the REST DAG uses (hoisted to gw-config). The
session's model identity threads as requested/served — entitlement and the
per-(AK, model) counter judge the public name, QPM/pricing/the upstream URL
follow the served variant, clients only ever see the public name. Realtime
now feeds availability too: a success per billed turn, an error at
handshake pool exhaustion, upstream connect failure, or a mid-session
upstream death (client disconnects stay excluded), all attributed to the
public name; the status endpoint and the alert sweep stop excluding
realtime models. The load-time realtime-variants rejection is withdrawn —
it existed to keep the config surface honest until this landed.
Two new wire protocols with family engines: moderations (OpenAI shape,
string-or-array input shared with the embeddings parser, per-input
verdicts summarized) and rerank (Cohere/Jina-compatible query/documents/
top_n with vendor usage flowing to billing). /v1/audio/translations rides
the STT engine as a request property — the same param-driven path switch
the image edit endpoint already uses — rather than a new protocol. Both
new families' text fields join the one walker the blocklist and DLP
traverse. Presets: moonshot and siliconflow (the latter carrying
embeddings + rerank wires). Demo models and docs included.
Rerank usage parses both wire dialects (Jina usage.total_tokens, else
SiliconFlow/Cohere meta.tokens input+output) so a live SiliconFlow rerank
bills instead of settling zero. A realtime turn now denies when the model
the session pinned at handshake was removed by a reload — pricing an
unknown model silently bills zero and a long-lived connection never
re-picks; wire-direct sessions (never in config) are exempt.
swap_to_fallback is tri-state: a request already on the tenant fallback is
degraded, not 'no fallback configured' — moderation Degrade now serves it.
The failover retry account's cooldown transition alerts like the first
attempt's. Security-event surfaces record the real protocol on every
family handler (embeddings/images/tts/stt and the new
moderations/rerank/translations) instead of the openai-chat placeholder.
The retry attempt's cooldown-alert block and the first attempt's collapse
into one note_failure — copy-pasted call+alert logic around the
fleet-backed record_failure is exactly what drifts on the next edit. The
transcriptions/translations handler pair shares one body behind the
translate flag. Net -58 lines, no behavior change.
@CMGS
CMGS merged commit 37b1155 into main Jul 18, 2026
7 checks passed
@CMGS
CMGS deleted the feat/safety-alerts-surface branch July 18, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant