Docs: say plainly that only the bot is open source; the dashboard and API are proprietary

- README, license section and architecture no longer point to the dashboard repositories
- schema.sql is the schema for contributors; database changes only need schema.sql and a note in the PR
- self-hosting: settings without a dashboard (SQL for security thresholds, beta settings, opted-in servers)
- beta features, deployment and troubleshooting reworded for people who don't have the dashboard

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
FernandoJVideiraandClaude Sonnet 5.5 committed 2026-10-23 01:54:08 +01:00
1 parent 1fbc38ca56
commit 02b7db1813
11 files changed
+93 -77

No files matched your search

+24 -18
View File
@@ -4,19 +4,26 @@ The bot uses PostgreSQL through `database/sql` with the pgx driver. All SQL live
## Who owns the schema
The bot and the dashboard API share one database, so two files describe it:
**`schema.sql`** in this repository is the schema. It describes every table the bot reads and writes, and it is enough to run the bot. It is safe to run more than once (`CREATE TABLE IF NOT EXISTS`). The bot never runs migrations itself.
- **`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.
The hosted Velox also has a proprietary dashboard API that keeps its own migrations for the same tables. That is the maintainer's concern: if your change adds or alters a table, update `schema.sql` and say so in your pull request, and the maintainer ports it. You never need the API to develop or run the bot.
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.
Two things are not in `schema.sql`, because only a dashboard needs them: the `NOTIFY` triggers on the three `*_commands` tables (see below), and a migration history. If you write a tool that inserts command rows, add the trigger yourself:
**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.
```sql
CREATE OR REPLACE FUNCTION notify_music_command() RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('music_commands', NEW.id::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Two things only exist in the API's migrations, because the bot never inserts into those tables:
CREATE TRIGGER music_commands_notify
AFTER INSERT ON music_commands
FOR EACH ROW EXECUTE FUNCTION notify_music_command();
```
- the `NOTIFY` triggers on the three `*_commands` tables (see below),
- the migration history.
The same works for `ticket_commands` and `security_commands`. Without the trigger the bot still picks the rows up, but only the next time it starts or is notified for another reason.
## Tables
@@ -30,7 +37,7 @@ Two things only exist in the API's migrations, because the bot never inserts int
| 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 |
| Music | `music_settings`, `music_now_playing`, `music_queue`, `music_commands` | State a dashboard can show, plus requests to the bot |
| 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`, `beta_guilds` | `beta_settings` is one row: the beta server and the Beta Tester role. `beta_guilds` lists servers whose owner opted in to beta features. Both are written by the dashboard and read by the bot. `beta_registered` is the bot's own note of where it registered beta commands |
@@ -39,27 +46,26 @@ Discord IDs are `BIGINT` (snowflakes fit in 64 bits). In Go they are `int64`; Di
## Dashboard to bot: settings and requests
There are two kinds of traffic from the dashboard, and the difference matters.
There are two kinds of traffic from a dashboard to the bot, 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.
**Settings.** A 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).
1. A dashboard inserts a row (`command_type`, a JSON `payload`, `requested_by`, `processed_at` empty).
2. A trigger in the database runs `pg_notify('<table>', id)`.
3. The bot holds one connection that ran `LISTEN <table>`. It wakes, loads **every** unprocessed row, carries each out, and sets `processed_at`.
Because the bot loads all unprocessed rows (also when it starts), nothing is lost if it was down when a request arrived. A command that fails is logged and marked processed anyway, since retrying would fail the same way.
Every listener **validates the request again**. The API checks that the requester is allowed, and the bot checks too, because it should not trust a row just because it exists. The listeners live in `internal/music/listener.go`, `internal/tickets/listener.go` and `internal/security/listener.go`, and each reconnects after 10 seconds if its connection drops.
Every listener **validates the request again**. The dashboard should check that the requester is allowed, and the bot checks too, because it should not trust a row just because it exists. The listeners live in `internal/music/listener.go`, `internal/tickets/listener.go` and `internal/security/listener.go`, and each reconnects after 10 seconds if its connection drops.
## Changing the schema
1. Decide who owns the table (see above). New tables for the dashboard's modules are created by an API migration.
2. Write the API migration, **and** update `schema.sql` to match.
3. Add or change the repo method in `internal/db/repos/<area>repo`. Use `IF NOT EXISTS` and avoid destructive changes: the bot and the dashboard may be deployed a few minutes apart, so the old bot must keep working against the new schema for a moment. Add a column or table first, and remove old ones in a later release.
4. If the dashboard must make the bot do something, add a `command_type` to the `CHECK` in the right `*_commands` table and handle it in the listener.
5. Say in the release notes if the API, bot and dashboard have to deploy together.
1. Update `schema.sql`. Use `CREATE TABLE IF NOT EXISTS`, and avoid destructive changes: a bot that is a version behind (or a tool reading the same tables) must keep working against the new schema for a moment. Add a column or table first, and remove old ones in a later release.
2. Add or change the repo method in `internal/db/repos/<area>repo`.
3. If something outside the bot must make it do something, add a `command_type` to the `CHECK` in the right `*_commands` table and handle it in the listener.
4. In your pull request, say that the schema changed and whether an existing table is affected, so the maintainer can port the change to the dashboard's migrations.
## Connection settings