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

3.3 KiB

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. v1 limitation, stated in the confirmation: the single-string channel memory may still contain summarized traces — full fact-level erasure arrives with the structured memory (FDB-007).

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.