Files
discord_bot/specs/SPEC-003-safety.md
T

77 lines
3.3 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-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.