# Database The bot uses PostgreSQL through `database/sql` with the pgx driver. All SQL lives in repos under `internal/db/repos`. ## Who owns the schema The bot and the dashboard API share one database, so two files describe it: - **`schema.sql`** in this repository: the tables the bot reads and writes. It is safe to run more than once (`CREATE TABLE IF NOT EXISTS`). It is enough to run the bot **on its own**. - **The API's migrations** (`velox-dashboard-api/cmd/db/migrations`): when the API is deployed, it creates and changes the tables, and runs its migrations on start. The bot never runs migrations. Some tables are older and were created by the bot (levels, welcome, and so on). The API's migration `000005_bot_owned_tables` creates them for a fresh combined database. Newer modules (music, tickets, security, meeting lobbies, beta settings) were created by API migrations, and `schema.sql` carries a copy so the bot can run alone. **The rule:** a table must have the same shape in `schema.sql` and in the API migrations. When you change one, change the other in the same release. Two things only exist in the API's migrations, because the bot never inserts into those tables: - the `NOTIFY` triggers on the three `*_commands` tables (see below), - the migration history. ## Tables | Area | Tables | Notes | | --- | --- | --- | | Leveling | `levels`, `levelsettings`, `levelrewards`, `levelup` | XP and level per member, settings, role rewards, the level-up channel | | Welcome | `welcome`, `defaultrole` | Welcome channel, templates, GIF, default role | | Logging | `logsettings` | Log channel and on/off | | Twitch | `twitch`, `twitch_config` | Streamers and the notification channel | | Fun | `rps` | Rock-paper-scissors stats | | Meeting rooms | `meeting_lobbies`, `meeting_temp_channels`, `meeting_settings` | Lobbies (10 per server, enforced by a trigger), the temporary rooms and who created them. `meeting_settings.lobby_channel_id` is the old single lobby column and is no longer read | | Projects | `projects`, `project_members` | | | Scheduling | `schedules`, `schedule_messages`, `user_settings` | Sessions, the DM messages to update, and each member's timezone | | Music | `music_settings`, `music_now_playing`, `music_queue`, `music_commands` | State for the dashboard's player, plus requests from the dashboard | | Tickets | `ticket_settings`, `ticket_categories`, `tickets`, `ticket_commands` | | | Security | `security_settings`, `security_trusted`, `security_quarantines`, `security_lockdowns`, `security_raids`, `security_raid_caught`, `security_snapshots`, `security_events`, `security_commands` | Settings, who is trusted, quarantines (with the roles to give back), locked channels (with the permission to restore), raids in progress, snapshots, an event history, and dashboard requests | | Beta | `beta_settings` | One row: the beta server and the Beta Tester role. Written by the dashboard, read by the bot | Discord IDs are `BIGINT` (snowflakes fit in 64 bits). In Go they are `int64`; Discord's own API gives them to you as strings, so convert at the edge. ## Dashboard to bot: settings and requests There are two kinds of traffic from the dashboard, and the difference matters. **Settings.** The dashboard writes a row, and the bot reads it when it next needs it. Nothing wakes the bot up. Hot paths cache settings for a short time. The bot's beta settings are polled every 30 seconds. **Requests that need the bot to act.** `music_commands`, `ticket_commands` and `security_commands` work as a small queue: 1. The dashboard API inserts a row (`command_type`, a JSON `payload`, `requested_by`, `processed_at` empty). 2. A trigger in the database runs `pg_notify('