83 lines
3.5 KiB
Markdown
83 lines
3.5 KiB
Markdown
# SPEC-003 — Safety, privacy + abuse hardening
|
|
|
|
FDB-005 lands the injection-defense subset (the old `hack`
|
|
self-report was the only defense — S4). Consequential actions are
|
|
gated *outside* the model: the model proposes, deterministic code
|
|
disposes. Quotas, budget caps, memory policies and GDPR controls
|
|
follow with FDB-014 (SAF-10+, reserved).
|
|
|
|
### SAF-01 — Model-proposed channel routing is allowlisted (coverage: test)
|
|
|
|
A model-proposed answer channel is honored only when it is in the
|
|
allowed set: config `allowed-channels` when set, otherwise the
|
|
channels already named in config (`chat-channel`, `staff-channel`,
|
|
`welcome-channel`, `additional-responders`). Anything else falls back
|
|
to the origin channel and is logged. Prompt injection must not be
|
|
able to redirect the bot into arbitrary channels.
|
|
|
|
### SAF-02 — Outbound messages cannot ping (coverage: test)
|
|
|
|
The bot is constructed with `allowed_mentions = none`: no user, role
|
|
or @everyone/@here pings in any outbound message, regardless of what
|
|
the model emits.
|
|
|
|
### SAF-03 — External text is sanitized before prompting (coverage: test)
|
|
|
|
Text from external sources injected into prompts or tool results —
|
|
news-feed content and IGDB results — passes `sanitize_external_text`:
|
|
control characters stripped, `@everyone`/`@here` neutralized with a
|
|
zero-width space, length capped (default 4000 chars). RSS headlines
|
|
and game descriptions are attacker-influenced input.
|
|
|
|
The `hack` envelope field remains as an advisory signal (logged,
|
|
staff-notified) but is no longer the defense.
|
|
|
|
### SAF-10 — Model calls carry a hashed user identifier (coverage: test)
|
|
|
|
Chat calls pass `safety_identifier` = a short SHA-256 digest of the
|
|
message author's name — OpenAI-side abuse tracing without shipping
|
|
raw Discord identities (Codex review recommendation).
|
|
|
|
### SAF-04 — Hard daily budget, fail-closed (coverage: test)
|
|
|
|
When `daily-budget-usd` is configured and today's estimated spend
|
|
reaches it, no further model or image calls happen: the responder
|
|
refuses before calling the API, and the bot answers nothing until
|
|
midnight. Staff get exactly one alert per day about the silence.
|
|
Cost estimation uses `price-input-per-m` (default 1.0),
|
|
`price-output-per-m` (default 6.0) and `price-per-image` (default
|
|
0.05). A budget of 0 means fully silent — fail-closed by
|
|
construction.
|
|
|
|
### SAF-05 — Usage is metered and survives restarts (coverage: test)
|
|
|
|
Token counts (prompt/completion) from every model call and every
|
|
generated image are recorded per calendar day; with a store
|
|
configured the counters persist across restarts (usage table,
|
|
schema v2).
|
|
|
|
### SAF-06 — Per-user daily message quota (coverage: test)
|
|
|
|
When `user-daily-messages` is configured, messages beyond the cap
|
|
from one user on one day are ignored (logged, no model call). The
|
|
`system` user (bot-initiated flows) is exempt.
|
|
|
|
### SAF-07 — Per-user daily image quota (coverage: test)
|
|
|
|
When `user-daily-images` is configured, picture requests beyond the
|
|
user's daily cap are stripped from the response (the text answer
|
|
still goes out).
|
|
|
|
### SAF-08 — !forgetme purges a user's history (coverage: test)
|
|
|
|
`!forgetme` removes the requesting user's messages from all live
|
|
responder histories and from the store, then confirms in-channel.
|
|
Since FDB-007 the purge extends to memory itself — facts,
|
|
observations and episode traces (MEM-09).
|
|
|
|
### SAF-09 — !privacy states the data practice (coverage: test)
|
|
|
|
`!privacy` answers with the configured `privacy-notice` (a default
|
|
notice ships in code): what is stored, that `!forgetme` exists.
|
|
Works even while the bot is paused.
|