Skip to content

Supabase

Supabase is optional. BetterPunish always writes its local YAML files first and uses the Supabase REST API as an additional store for selected data.

What is synchronized

Table Current use
punishments Insert punishments, warnings, kicks, unban/unmute events; patch proofs and edited reason/staff/duration; pull punishment history.
pending_warnings Insert offline warnings, fetch them on join, then delete delivered rows.
player_ips Upsert player/address associations and query linked accounts.
reports Insert reports and patch claim/resolve state.

Staff notes, chat-review flags, command logs, offense log lines, freeze state, chat-control state, and Vulcan suspect state remain local or in memory.

Configure BetterPunish

supabase:
  url: "YOUR_SUPABASE_URL"
  anon-key: "YOUR_SUPABASE_KEY"

The integration is disabled while the shipped URL/key placeholders remain. Do not use a service-role key in config.yml; the current implementation is explicitly configured around the anon-key field.

Required columns

The REST calls expect these public-schema shapes:

create table if not exists punishments (
  id text primary key,
  type text not null,
  player_uuid uuid not null,
  player_name text not null,
  actor_id text not null,
  actor_name text not null,
  reason text not null,
  duration text not null,
  created_at timestamptz not null,
  expires_at timestamptz,
  proofs text default '',
  silent boolean default false
);

create table if not exists pending_warnings (
  id bigint generated by default as identity primary key,
  player_uuid uuid not null,
  staff_name text not null,
  reason text not null,
  created_at timestamptz not null
);

create table if not exists player_ips (
  player_uuid uuid not null,
  player_name text not null,
  ip_address text not null,
  last_seen timestamptz not null,
  primary key (player_uuid, ip_address)
);

create table if not exists reports (
  id bigint primary key,
  reporter_uuid uuid not null,
  reporter_name text not null,
  target_uuid uuid not null,
  target_name text not null,
  reason text not null,
  timestamp timestamptz not null,
  status text not null,
  resolved_by text default ''
);

The configured anon role needs the operations used above: select, insert, update, and delete on the relevant tables and identity sequence. With Row Level Security enabled, create policies that allow exactly these operations for your deployment.

Current authentication constraint

BetterPunish sends only the configured anon key as apikey and bearer authorization. Broad anonymous write/delete policies make possession of that key sufficient to change moderation data. Use a dedicated project, restrict exposure, monitor access, and rotate the key if it leaks.

Migration

/punishmigrate

This iterates local punishment history and upserts core fields into punishments. It does not delete local data.

The current migration payload does not export proof values, edit history, silent flags, or local revocation metadata. Test against a staging project before treating it as a complete historical migration.

Pull

/punishpull

The command fetches all punishments rows, sorts them by creation time, rebuilds local history, applies cloud UNBAN/UNMUTE events to matching active records, recalculates the next numeric ID, saves, and reloads services.

Danger

Pull is replacement, not merge. It clears the in-memory local punishment history before saving the fetched history. Back up data/punishments.yml first.

Local fallback and errors

  • New local actions continue to be stored when Supabase is unconfigured.
  • Failed player-IP queries return locally known associations.
  • Asynchronous write failures are logged to the server console; they do not roll back the local record.
  • A pull failure before replacement leaves the current local history unchanged.
  • Schema, RLS, grant, URL, and key errors normally appear as non-success HTTP status warnings.

Use /punishreload after changing credentials.