Moderation verdicts, abuse suspension with alerting, realtime canary, and new surfaces#7
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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), andDeny. Realtime mapsDegradeto 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_variantas 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 eitherusage.total_tokensormeta.tokens) as full protocols with typed params covered by the blocklist/DLP walkers;/v1/audio/translationsas a request property on the STT engine; provider presetsmoonshotandsiliconflow.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, andcargo test --workspacegreen on macOS and in arust:1Linux 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.