Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9353942
feat: setup supabase_realtime_admin
leandrocp May 7, 2026
3560c43
Merge branch 'main' into lp-revoke-supabase_realtime_admin
leandrocp May 15, 2026
2ce3d1b
use supabase_realtime_admin in compose.tests too
leandrocp May 15, 2026
5c2c82f
fixes and cleanup
leandrocp May 18, 2026
82eab19
Merge remote-tracking branch 'origin/main' into lp-revoke-supabase_re…
leandrocp May 19, 2026
96d353d
feature flag: use_supabase_realtime_admin
leandrocp May 19, 2026
0a3e0be
Merge remote-tracking branch 'origin/main' into lp-revoke-supabase_re…
leandrocp May 20, 2026
6ed268c
fix tests
leandrocp May 20, 2026
e171dee
chore: update tenant_db_baseline.json
github-actions[bot] May 20, 2026
e88123a
expose pg_delta filter
leandrocp May 21, 2026
e51f4e6
Merge branch 'main' into lp-revoke-supabase_realtime_admin
leandrocp May 22, 2026
d4d5a1f
pg 14-17
leandrocp May 25, 2026
0a728d9
Merge remote-tracking branch 'origin/main' into lp-revoke-supabase_re…
leandrocp May 25, 2026
5074bd2
chore: update tenant_db_baseline.json
github-actions[bot] May 25, 2026
8c9177b
remove unnecessary dev setup
leandrocp May 25, 2026
c71f996
chore: update tenant_db_baseline.json
github-actions[bot] May 25, 2026
c1a7c35
revert grant removal
leandrocp May 25, 2026
e347204
fix min pg15 version
leandrocp May 25, 2026
c5693d9
Merge remote-tracking branch 'origin/main' into lp-revoke-supabase_re…
leandrocp Jun 5, 2026
64b8525
chore: update tenant_db_baseline.json
github-actions[bot] Jun 5, 2026
794d033
chore: update tenant_db_baseline.json
github-actions[bot] Jun 5, 2026
218192b
fix realtime.subscription_check_filters()
leandrocp Jun 8, 2026
b4a9bfa
Merge remote-tracking branch 'origin/main' into lp-revoke-supabase_re…
leandrocp Jun 8, 2026
061aa47
chore: update tenant_db_baseline.json
github-actions[bot] Jun 8, 2026
5dec004
cleanup
leandrocp Jun 8, 2026
34d92e7
dev grant
leandrocp Jun 8, 2026
15c2a15
chore: update tenant_db_baseline.json
github-actions[bot] Jun 8, 2026
a70e28d
fix partitions owner
leandrocp Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,25 @@ jobs:
runs-on: blacksmith-8vcpu-ubuntu-2404
env:
POSTGRES_IMAGE: ${{ matrix.postgres_image }}
DB_USER: ${{ matrix.db_user }}
strategy:
fail-fast: false
matrix:
postgres: [pg14, pg15, pg17]
postgres: [pg14, pg15, pg15_latest, pg17]
include:
- postgres: pg14
postgres_image: supabase/postgres:14.1.0.85
postgres_image: supabase/postgres:14.1.0.82
db_user: supabase_admin
# test before supautils.policy_grants added all necessary grants
- postgres: pg15
postgres_image: supabase/postgres:15.14.1.127
postgres_image: supabase/postgres:15.1.0.1
db_user: supabase_admin
- postgres: pg15_latest
postgres_image: supabase/postgres:15.14.1.129
db_user: supabase_realtime_admin
- postgres: pg17
postgres_image: supabase/postgres:17.6.1.127
db_user: supabase_realtime_admin

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down
16 changes: 13 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ on:
- "assets/**"
- "rel/**"
- "native/**"
- "dev/**"
- "mix.exs"
- "compose.dbs.yml"
- "mix.lock"
- "Dockerfile"
- "run.sh"
Expand All @@ -35,18 +37,26 @@ jobs:
runs-on: blacksmith-8vcpu-ubuntu-2404
env:
POSTGRES_IMAGE: ${{ matrix.postgres_image }}
DB_USER: ${{ matrix.db_user }}
strategy:
fail-fast: false
matrix:
partition: [1, 2, 3, 4]
postgres: [pg14, pg15, pg17]
postgres: [pg14, pg15, pg15_latest, pg17]
include:
- postgres: pg14
postgres_image: supabase/postgres:14.1.0.85
postgres_image: supabase/postgres:14.1.0.82
db_user: supabase_admin
# test before supautils.policy_grants added all necessary grants
- postgres: pg15
postgres_image: supabase/postgres:15.14.1.127
postgres_image: supabase/postgres:15.1.0.1
db_user: supabase_admin
- postgres: pg15_latest
postgres_image: supabase/postgres:15.14.1.129
db_user: supabase_realtime_admin
- postgres: pg17
postgres_image: supabase/postgres:17.6.1.127
db_user: supabase_realtime_admin

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ Once your environment is up and running, check out the following docs to customi
- [ERROR_CODES.md](ERROR_CODES.md) - list of operational codes
- [OBSERVABILITY_METRICS.md](OBSERVABILITY_METRICS.md) - monitoring information

## Postgres compatibility

| `supabase/postgres` version | Role | Status |
| --------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| < 14 | - | Not officially supported. |
| 14.x | `supabase_admin` | Requires superuser: `log_min_messages` can only be set by a superuser; supautils doesn't expose per-parameter delegation on this version. On <= 14.5, `realtime.broadcast_changes(...)` called from a trigger via `PERFORM` is unsupported. |
| 15.x < 15.14.1.018 | `supabase_admin` | Requires superuser: `supautils.policy_grants` on `realtime.subscription` is missing until [supabase/postgres@1b916920](https://github.com/supabase/postgres/commit/1b916920). |
| 15.x >= 15.14.1.018, 16.x, 17.x | `supabase_realtime_admin` | No superuser needed. Role must have `REPLICATION` and policies are managed by supautils. |

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md)
Expand Down
6 changes: 4 additions & 2 deletions compose.dbs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ services:
ports:
- "5432:5432"
volumes:
- ./dev/postgres/zz-supabase-schema.sql:/docker-entrypoint-initdb.d/zz-supabase-schema.sql
- ./dev/postgres/za-permit-supabase-admin.sh:/docker-entrypoint-initdb.d/za-permit-supabase-admin.sh
- ./dev/postgres/zb-supabase-schema.sql:/docker-entrypoint-initdb.d/zb-supabase-schema.sql
command: postgres -c config_file=/etc/postgresql/postgresql.conf
environment:
POSTGRES_HOST: /var/run/postgresql
Expand All @@ -20,7 +21,8 @@ services:
ports:
- "5433:5432"
volumes:
- ./dev/postgres/zz-supabase-schema.sql:/docker-entrypoint-initdb.d/zz-supabase-schema.sql
- ./dev/postgres/za-permit-supabase-admin.sh:/docker-entrypoint-initdb.d/za-permit-supabase-admin.sh
- ./dev/postgres/zb-supabase-schema.sql:/docker-entrypoint-initdb.d/zb-supabase-schema.sql
command: postgres -c config_file=/etc/postgresql/postgresql.conf
environment:
POSTGRES_HOST: /var/run/postgresql
Expand Down
5 changes: 3 additions & 2 deletions compose.tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ services:
ports:
- "5532:5432"
volumes:
- ./dev/postgres:/docker-entrypoint-initdb.d/
- ./dev/postgres/za-permit-supabase-admin.sh:/docker-entrypoint-initdb.d/za-permit-supabase-admin.sh
- ./dev/postgres/zb-supabase-schema.sql:/docker-entrypoint-initdb.d/zb-supabase-schema.sql
command: postgres -c config_file=/etc/postgresql/postgresql.conf
environment:
POSTGRES_HOST: /var/run/postgresql
Expand All @@ -29,7 +30,7 @@ services:
PORT: 4100
DB_HOST: host.docker.internal
DB_PORT: 5532
DB_USER: supabase_admin
DB_USER: ${DB_USER:-supabase_realtime_admin}
DB_PASSWORD: postgres
DB_NAME: postgres
DB_ENC_KEY: 1234567890123456
Expand Down
2 changes: 1 addition & 1 deletion config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ db_replica_host = System.get_env("DB_REPLICA_HOST")
db_replica_pool_size = Env.get_integer("DB_REPLICA_POOL_SIZE", 5)
db_ssl = Env.get_boolean("DB_SSL", false)
db_ssl_ca_cert = System.get_env("DB_SSL_CA_CERT")
db_user = System.get_env("DB_USER", "supabase_admin")
db_user = System.get_env("DB_USER", "supabase_realtime_admin")
disable_healthcheck_logging = Env.get_boolean("DISABLE_HEALTHCHECK_LOGGING", false)
dns_nodes = System.get_env("DNS_NODES")
gen_rpc_compress = Env.get_integer("GEN_RPC_COMPRESS", 0)
Expand Down
2 changes: 1 addition & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ for repo <- [
Realtime.Repo.Replica.SanJose
] do
config :realtime, repo,
username: "supabase_admin",
username: "supabase_realtime_admin",
password: "postgres",
database: "realtime_test#{partition}",
hostname: "127.0.0.1",
Expand Down
8 changes: 8 additions & 0 deletions dev/postgres/za-permit-supabase-admin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
# Allow peer auth as supabase_admin so the next init .sql can `\connect -` to it without a password
set -euo pipefail

echo "supabase_map postgres supabase_admin" >> "${PGDATA}/pg_ident.conf"
printf 'local all supabase_admin peer map=supabase_map\n%s' "$(cat "${PGDATA}/pg_hba.conf")" > "${PGDATA}/pg_hba.conf.new"
mv "${PGDATA}/pg_hba.conf.new" "${PGDATA}/pg_hba.conf"
pg_ctl reload -D "${PGDATA}" >/dev/null
27 changes: 27 additions & 0 deletions dev/postgres/zb-supabase-schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
\connect - supabase_admin

do $$
begin
if not exists (select from pg_roles where rolname = 'supabase_realtime_admin') then
create user supabase_realtime_admin noinherit createrole login replication password 'postgres';
end if;
end$$;

create schema if not exists _realtime;

alter user supabase_realtime_admin set search_path = public, extensions, realtime;
grant create on database postgres to supabase_realtime_admin;
do $$
begin
if current_setting('server_version_num')::int >= 150000 then
execute 'grant set on parameter log_min_messages to supabase_realtime_admin';
end if;
end$$;
grant anon, authenticated, service_role to supabase_realtime_admin;
grant create, usage on schema public to supabase_realtime_admin;
grant usage on schema extensions to supabase_realtime_admin;
grant usage on schema auth to supabase_realtime_admin;
grant execute on all functions in schema auth to supabase_realtime_admin;
grant usage on schema realtime to postgres, anon, authenticated, service_role;
grant all on schema realtime to supabase_realtime_admin with grant option;
grant create, usage on schema _realtime to supabase_realtime_admin;
69 changes: 0 additions & 69 deletions dev/postgres/zz-supabase-schema.sql

This file was deleted.

19 changes: 15 additions & 4 deletions lib/realtime/tenants.ex
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,18 @@ defmodule Realtime.Tenants do
end_timestamp = Date.to_string(Date.add(date, 1))

Database.transaction(db_conn_pid, fn conn ->
query = """
create = """
CREATE TABLE IF NOT EXISTS realtime.#{partition_name}
PARTITION OF realtime.messages
FOR VALUES FROM ('#{start_timestamp}') TO ('#{end_timestamp}');
"""

case Postgrex.query(conn, query, []) do
{:ok, _} -> Logger.debug("Partition #{partition_name} created")
alter_owner = "ALTER TABLE realtime.#{partition_name} OWNER TO supabase_realtime_admin"

with {:ok, _} <- Postgrex.query(conn, create, []),
{:ok, _} <- Postgrex.query(conn, alter_owner, []) do
Logger.debug("Partition #{partition_name} created")
else
{:error, %Postgrex.Error{postgres: %{code: :duplicate_table}}} -> :ok
{:error, error} -> log_error("PartitionCreationFailed", error)
end
Expand Down Expand Up @@ -511,7 +515,14 @@ defmodule Realtime.Tenants do
Checks if migrations for a given tenant need to run.
"""
@spec run_migrations?(Tenant.t() | integer()) :: boolean()
def run_migrations?(%Tenant{} = tenant), do: run_migrations?(tenant.migrations_ran)
def run_migrations?(%Tenant{} = tenant) do
available_migrations =
tenant.external_id
|> Migrations.migrations()
|> Enum.count()

tenant.migrations_ran < available_migrations
end

def run_migrations?(migrations_ran) when is_integer(migrations_ran),
do: migrations_ran < Enum.count(Migrations.migrations())
Expand Down
33 changes: 27 additions & 6 deletions lib/realtime/tenants/migrations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule Realtime.Tenants.Migrations do

alias Realtime.Tenants
alias Realtime.Database
alias Realtime.FeatureFlags
alias Realtime.Registry.Unique
alias Realtime.Repo
alias Realtime.Api.Tenant
Expand Down Expand Up @@ -89,7 +90,9 @@ defmodule Realtime.Tenants.Migrations do
AddSelectColumnsToSubscriptions,
Wal2jsonEscapeSpecialChars,
AddSendBinaryFunction,
RenameBroadcastSendWarning
RenameBroadcastSendWarning,
SubscriptionCheckFiltersUsePgAttribute,
SetupSupabaseRealtimeAdmin
}

@migrations [
Expand Down Expand Up @@ -166,7 +169,9 @@ defmodule Realtime.Tenants.Migrations do
{20_260_527_120_000, AddSelectColumnsToSubscriptions},
{20_260_528_120_000, Wal2jsonEscapeSpecialChars},
{20_260_603_120_000, AddSendBinaryFunction},
{20_260_605_120_000, RenameBroadcastSendWarning}
{20_260_605_120_000, RenameBroadcastSendWarning},
{20_260_606_110_000, SubscriptionCheckFiltersUsePgAttribute},
{20_260_606_120_000, SetupSupabaseRealtimeAdmin}
]

defstruct [:tenant_external_id, :settings, migrations_ran: 0]
Expand All @@ -176,8 +181,6 @@ defmodule Realtime.Tenants.Migrations do
settings: map()
}

def migrations(), do: @migrations

@doc """
Run migrations for the given tenant.
"""
Expand Down Expand Up @@ -228,7 +231,7 @@ defmodule Realtime.Tenants.Migrations do
:ok ->
Task.Supervisor.async_nolink(__MODULE__.TaskSupervisor, Api, :update_migrations_ran, [
tenant_external_id,
Enum.count(@migrations)
Enum.count(migrations(tenant_external_id))
])

:ignore
Expand Down Expand Up @@ -261,7 +264,7 @@ defmodule Realtime.Tenants.Migrations do

try do
opts = [all: true, prefix: "realtime", dynamic_repo: repo]
result = Ecto.Migrator.run(Repo, @migrations, :up, opts)
result = Ecto.Migrator.run(Repo, migrations(tenant_external_id), :up, opts)
Telemetry.stop(event, start_time, Map.put(metadata, :migrations_executed, length(result)))
rescue
error ->
Expand All @@ -285,4 +288,22 @@ defmodule Realtime.Tenants.Migrations do
defp error_code(%Postgrex.Error{postgres: %{code: code}}), do: code
defp error_code(%DBConnection.ConnectionError{}), do: :connection_error
defp error_code(_), do: :other

@doc """
Returns the migrations to run.
"""
@spec migrations(String.t() | nil) :: [{pos_integer(), module()}]
def migrations(tenant_external_id \\ nil) do
Enum.filter(@migrations, fn {_version, module} -> migration_enabled?(module, tenant_external_id) end)
end

defp migration_enabled?(SetupSupabaseRealtimeAdmin, nil = _tenant_external_id) do
FeatureFlags.enabled?("use_supabase_realtime_admin")
end

defp migration_enabled?(SetupSupabaseRealtimeAdmin, tenant_external_id) when is_binary(tenant_external_id) do
FeatureFlags.enabled?("use_supabase_realtime_admin", tenant_external_id)
end

defp migration_enabled?(_migration, _tenant_external_id), do: true
end
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ defmodule Realtime.Tenants.Migrations.CreateRealtimeAdminAndMoveOwnership do
execute("ALTER table realtime.presences OWNER TO supabase_realtime_admin")
execute("ALTER function realtime.channel_name() owner to supabase_realtime_admin")

execute("GRANT supabase_realtime_admin TO postgres")
execute("""
DO $$
BEGIN
IF (SELECT rolsuper FROM pg_roles WHERE rolname = current_user) THEN
GRANT supabase_realtime_admin TO postgres;
END IF;
END $$;
""")
end
end
Loading
Loading