v1.53.0 · Unity 2022.3 LTS and Unity 6

Unity Supabase Toolkit documentation.

Find setup steps, API examples, game backend patterns, security guidance and troubleshooting.

Back to the Unity Supabase Toolkit overview · View on the Unity Asset Store

Before you start

ShipIt is designed for teams that want Supabase as their game backend while keeping the Unity workflow practical. The runtime talks to your Supabase project directly; editor-only tools use privileged keys only when you explicitly configure them.

Runtime configuration

Project URL and anon/publishable key live in the runtime settings asset and are safe for client builds when RLS is configured correctly.

Editor-only administration

Secret or legacy service-role keys are stored locally for schema browsing, SQL provisioning, migrations and AI context generation.

Backend responsibility

Scores, economy, entitlements and moderation actions should be validated by Postgres functions, Edge Functions or service-role workflows.

If you are new to Supabase, create a development project first. Apply backend packs and verify RLS in dev before connecting a production game.

Install & configure

  1. Import the package (Package Manager → My Assets), then open Tools → ShipIt → Supabase Toolkit.
  2. Open Setup Wizard and enter your Project URL plus anon/publishable key (Supabase dashboard → Project Settings → API Keys).
  3. Add a Secret key or legacy service-role key for schema and admin tools. It stays in machine-local editor preferences and is never written to runtime assets or builds.
  4. Click Test & save. It validates the connection and writes the settings asset to a Resources folder so it loads at runtime. Test only checks the connection without saving.
Use the Environments tab to keep separate Dev / Staging / Prod profiles and switch between them with one click.

Initialize

using ShipIt.Supabase;

void Awake()
{
    SupabaseClient.InitializeFromResources();
}

Everything hangs off SupabaseClient.Instance: .Auth, .Database, .Realtime, .Storage, .Functions, .Rpc, plus the .Http layer the feature services use.

Common workflows

GoalUse these tabsResult
Connect a new projectSetup Wizard, Environments, DiagnosticsRuntime settings asset plus editor-local admin credentials.
Generate safe C# modelsSchema Browser, Code Generator, AI AssistantSerializable model classes and typed query snippets based on your schema.
Add a game backend systemSQL Packs, Provision, RLS SimulatorTables, RLS policies, helper functions and usage notes for the selected feature.
Debug live network behaviorRequest Inspector, Realtime Monitor, Error ExplainerReadable requests, responses, channel state and actionable troubleshooting hints.
Prepare launch operationsLive-Ops Dashboard, Remote Config, Feature FlagsConfig, announcements, rewards and moderation flows without a new client build.

Authentication

var auth = SupabaseClient.Instance.Auth;

await auth.SignUpWithEmailAsync(email, password);
var session = await auth.SignInWithEmailAsync(email, password);

var oauthUrl = auth.BuildOAuthSignInUrl("google", "mygame://auth");
Application.OpenURL(oauthUrl);
var oauthSession = await auth.HandleOAuthCallbackAsync(callbackUrl);

var nativeSession = await auth.SignInWithIdTokenAsync("apple", identityToken, nonce: rawNonce);

await auth.SendMagicLinkAsync(email);
await auth.SendPhoneOtpAsync("+14155551234");
await auth.VerifyPhoneOtpAsync("+14155551234", "123456");

var user = auth.CurrentSession?.User;
await auth.SendPasswordResetAsync(email);
await auth.SignOutAsync();

string export = await auth.ExportMyDataAsync();
await auth.DeleteAccountAsync();

Browser OAuth uses PKCE and Unity deep links. The drop-in Auth screen completes callbacks automatically. Auth also supports CAPTCHA-protected flows, MFA (TOTP), native identity linking and unlinking, reauthentication, resilient token refresh, and session persistence.

Database

A typed query builder with lambda filters, ordering, pagination, full-text search and resource embedding.

var db = SupabaseClient.Instance.Database;

var top = await db.From<Player>("players")
    .Where(p => p.score > 1000)
    .OrderByDescending(p => p.score)
    .Limit(50)
    .ExecuteAsync();

var posts = await db.From<Post>("posts")
    .TextSearch("body", "dragon OR wizard")
    .ExecuteAsync();

var active = await db.From<Player>("players")
    .WhereNot("status", FilterOperator.Equals, "banned")
    .Or("tier.eq.gold,tier.eq.platinum")
    .ExecuteAsync();

var members = await db.From<GuildMemberRow>("guild_members")
    .Select("role", "guilds(name,tag)")
    .ExecuteAsync();
Update and Delete require a .Where(...) filter — the toolkit refuses to mutate the whole table by accident.

Realtime

await SupabaseClient.Instance.Realtime.ConnectAsync();

var channel = SupabaseClient.Instance.Realtime.FromTable("players");
channel.OnInsert<Player>(p => Debug.Log($"New: {p.name}"));
await channel.SubscribeAsync();

var room = SupabaseClient.Instance.Realtime.Channel("room:42");
room.OnBroadcast("move", json => HandleMove(json));
await room.SubscribeAsync();
await room.SendBroadcastAsync("move", new { x = 1, y = 2 });

var priv = SupabaseClient.Instance.Realtime.Channel("room:42", isPrivate: true);
await priv.SubscribeAsync();

The client reconnects with bounded exponential backoff, rejoins known channels and pushes refreshed session tokens to private channels automatically. WebGL uses the browser's native WebSocket.

Storage

var bucket = SupabaseClient.Instance.Storage.FromBucket("avatars");

await bucket.UploadFileAsync("user/avatar.png", localPath, upsert: true);

var files = await bucket.ListAsync("user", new StorageListOptions {
    Search = "avatar",
    Limit = 50
});

Texture2D tex = await bucket.DownloadTextureAsync("user/avatar.png");
Sprite icon   = await bucket.DownloadSpriteAsync("user/avatar.png");
AudioClip sfx = await bucket.DownloadAudioClipAsync("sfx/hit.mp3", AudioType.MPEG);

string thumb = bucket.GetPublicTransformUrl("user/avatar.png",
    new ImageTransform(128, 128, ImageResize.Cover, 80));

await bucket.UploadResumableAsync("clips/replay.bin", bytes,
    onProgress: (sent, total) => Debug.Log($"{sent}/{total}"));

TUS upload locations are persisted locally. Repeating the same upload after an interruption probes the server offset and continues from the confirmed byte.

Edge Functions & RPC

var result = await SupabaseClient.Instance.Functions
    .InvokeAsync<MyResponse>("hello-world", new { name = "Unity" });

var rows = await SupabaseClient.Instance.Rpc
    .CallListAsync<LeaderboardRow>("get_leaderboard", new { limit_n = 10 });

The Edge Function gallery ships ready-to-deploy templates: GDPR account (export/delete), FCM push, IAP receipt validation, anti-cheat score, AI NPC streaming proxy, and AI content moderation.

Backend packs

38 drop-in, RLS-secured SQL recipes. Each creates tables, policies and helper functions for a common game system. Browse them in the Backend Packs tab; every pack lists exactly what it creates and how to use it.

AreaPacks
PlayersProfiles, Cloud Saves, Cloud Save Versioning & History
ProgressionLeaderboard, Anti-Cheat Scores, Achievements, Daily Rewards, Quests, Seasons & Battle Pass
EconomyInventory & Economy, Economy: Ledger & Trading, Auction House / Marketplace, In-App Purchases
SocialFriends, Guilds/Clans, Mailbox/Gifts, Moderation (reports & blocks), Referrals / Invite Codes
MultiplayerLobbies & Matchmaking, Ranked Matchmaking (ELO), Tournaments & Brackets, Realtime Authorization (private channels)
Live-opsRemote Config, A/B Experiments, Localization, Announcements, Push Notifications, Seasons & scheduled jobs, Webhooks / Event Outbox
AISemantic Search (pgvector)
SecurityRealtime Authorization (private channels), Rate Limiting / Abuse Guard, Idempotency Keys, Audit Log / Admin Action Trail

Provisioning

Three ways to apply a pack to your project:

  • Copy SQL from the Backend Packs tab into the Supabase SQL editor.
  • One-click Provision tab — tick the packs you need and apply them (uses your editor-only service_role key; idempotent).
  • MCP — let an AI agent apply packs for you (see AI & agents).

Leaderboards

var lb = new LeaderboardService(settings, SupabaseClient.Instance.Http);
await lb.SubmitAsync("Aria", 12345);
var top10 = await lb.GetTopAsync(10);
int rank   = await lb.GetRankAsync(12345);
long total = await lb.GetCountAsync();

For tamper-proof boards use SecureLeaderboardService + the Anti-Cheat Scores pack (server-validated range, ceiling and rate limit).

Economy & trading

Server-authoritative and auditable — built on a double-entry currency ledger. Apply the Inventory & Economy and Economy: Ledger & Trading packs.

var econ = new EconomyService(settings, SupabaseClient.Instance.Http);

long bal = await econ.AdjustCurrencyAsync("gold", 500, "quest_reward");
await econ.TransferCurrencyAsync(toUserId, "gold", 100);

var offer   = new TradeBundle().Currency("gold", 50).Item("sword", 1);
var request = new TradeBundle().Item("shield", 2);
var tradeId = await econ.CreateTradeAsync(toUserId, offer, request);
await econ.AcceptTradeAsync(tradeId);

Seasons / live-ops automation

var seasons = new SeasonService(settings, SupabaseClient.Instance.Http);
var current = await seasons.GetCurrentSeasonAsync("leaderboard");
var mine    = await seasons.GetMyResultsAsync();

Rollover and reward payout run automatically via pg_cron (or your service_role key) — they pay into the currency ledger. Admin-only by design.

Social & progression services

First-class C# services wrap each pack so you never hand-write REST/RPC. All are RLS-scoped to the caller.

ServiceHighlights
FriendServicesend/accept/decline requests, block, list friends
GuildServicecreate/join/leave, members & roles
MailboxServiceinbox, claim reward payloads
QuestServicecatalog + per-player progress (increment_quest)
AchievementServiceunlock + list achievements
DailyRewardServiceclaim + streak (server-validated)
InventoryServiceatomic grant/consume, wallet
MatchmakingServicelobbies / matchmaking
TournamentServicesingle-elim / round-robin brackets, auto-advance
RankedMatchmakingServiceELO queue, closest-rating pairing, ladder
AuctionHouseServicemarketplace listings, escrow + atomic buyout
RateLimitServiceserver-side throttle for abusable/expensive actions
CloudSaveHistoryServicecloud-save version history + non-destructive restore
WebhookServiceoutbound webhooks (outbox, retries, HMAC) — service-role
ReferralServiceinvite codes + referral attribution
IdempotencyServiceretry-safe actions (run-once keys, cached result)
AuditServiceappend-only admin action trail (service-role)
BattlePassServiceXP, tiers, claim free/premium
PresenceServiceonline heartbeat + "who's online"
ModerationServicereport, block, ban check, AI AnalyzeAsync

Remote config, flags, experiments, localization

var config = new RemoteConfigService(settings, SupabaseClient.Instance.Http);
await config.RefreshAsync();
float coin = config.GetFloat("coin_multiplier", 1f);

var flags = new FeatureFlagService(settings, http);
await flags.RefreshAsync();
bool newUi = flags.IsEnabled("new_inventory_ui");
var variant = await new ExperimentService(settings, http).GetVariantAsync("new_ui");
var loc = new LocalizationService(settings, http);
await loc.LoadAsync("es");
title.text = loc.Get("welcome");

Retune balance, toggle features, run A/B tests, and translate UI without shipping a build.

Live-Ops Dashboard (web console)

A self-hostable, zero-dependency web console for running your live game straight against Supabase — no backend to deploy.

It is a single static HTML file shipped at Documentation~/LiveOpsDashboard/index.html (a hosted copy is linked from this site as Live-Ops). Open it, paste your Project URL and an API key, and operate the systems the toolkit already creates:

TabBackend packWhat you can do
Overviewanalytics, players, AuthKPIs: auth users, player rows, active users today, events today, bans
AnnouncementsannouncementsPublish / delete server-driven news, events, maintenance notices
Feature Flagsfeature_flagsToggle flags, set percentage rollouts, add / delete
Remote Configapp_configEdit live tunables (balance, toggles, copy)
Rewards & MailmailboxSend mail and gift reward payloads (claimed in-game)
ModerationmoderationBan / unban players, review and resolve reports
LeaderboardplayersRead the default name/score board
PlayersAuth admin APILook up users, copy ids, jump to reward / ban
Security: admin actions need the service_role key, which bypasses Row-Level Security — use it only on a machine you trust. The key is entered at runtime and kept in the browser tab's sessionStorage (cleared on close); nothing is baked into the file. Connect with the anon key for read-only views. Install the matching backend pack from the SQL Packs tab before using a tab.

AI & agents (bring your own key)

Chat & streaming NPCs

npc.Say("Got any swords for sale?");

var ai = new AiChatClient(config);
await ai.CompleteStreamAsync(system, user, token => appendToUi(token));

Semantic search, RAG & moderation

var search = new SemanticSearchService(settings, http);
var rag    = new RagService(search, embedder, chat);
var mod    = new ModerationService(settings, http);
var verdict = await mod.AnalyzeAsync(playerChatText);

MCP server (agent-operable backend)

A bundled Model Context Protocol server (Documentation~/McpServer) lets Claude, Cursor, Kiro or Copilot list/apply backend packs, run SQL, inspect your schema, and generate C# models — against your project, with your service_role key. See its README for setup.

AI helpers include opt-in guardrails: PII redaction, prompt-injection hardening, input clamping, and a reply cache to control cost.

UI kit

Two ways to get gameplay UI fast:

  • Themeable UI Toolkit screens — drop-in SupabaseAuthScreen, SupabaseLeaderboardScreen, SupabaseProfileScreen, restyled from a single SupabaseTheme.uss (light/dark).
  • uGUI prefab builders — one-click menu items for Auth, Leaderboard, Profile, Chat, Inventory, Cloud Save, and an achievement toast.

Offline & caching

Enable the offline cache in settings for stale-while-revalidate reads plus a mutation queue that replays writes when the network returns. Useful for flaky mobile connections.

Platforms

Windows, macOS, Linux, Android, iOS, and WebGL. Mono and IL2CPP (AOT-safe; a bundled link.xml protects data types from code stripping). See Documentation~/PlatformSupport.md for the full matrix and WebGL specifics. Run your own device QA per platform, especially consoles.

Security best practices

  • Ship only the anon/publishable key in clients. The toolkit stores the Secret or legacy service-role key in machine-local editor preferences, never in the runtime settings asset.
  • Enable RLS on every table (the packs do this) and verify with the RLS Simulator + coverage checker.
  • Use private channels for sensitive realtime. Currency grants, quest progress and battle-pass XP require a trusted service-role context; client-reported scores still need game-specific validation.
  • The SQL Console / Provisioner executor runs arbitrary SQL — it's dev-only. Remove it before production: drop function if exists public.shipit_exec_sql_json(text);

Troubleshooting

SymptomFix
401 on runtime dataCheck the anon/publishable key and your RLS policies. The error message includes the server response + a hint.
Schema access is forbiddenAdd a Secret key or legacy service-role key in Setup Wizard. Supabase requires privileged authentication for OpenAPI schema introspection.
Connection failedUse the API URL (ends in .supabase.co), not the dashboard URL.
Update/Delete throwsAdd a .Where(...) filter — required for mutations.
Realtime joins do nothingSubscribe after connecting; for private channels apply the Realtime Authorization pack and sign in.
Provision says executor missingRun the SQL Console one-time setup once, then provision.
IL2CPP/WebGL build issuesThe bundled link.xml covers DTOs; see PlatformSupport.md.
Still stuck? Use the in-editor Error Explainer and Diagnostics tabs, or email support (see the Support page).