Skip to content

fix(db): provision the runtime login without requiring SUPERUSER - #69

Open
yufoxda wants to merge 2 commits into
developfrom
fix/provision-runtime-role-on-managed-postgres
Open

fix(db): provision the runtime login without requiring SUPERUSER#69
yufoxda wants to merge 2 commits into
developfrom
fix/provision-runtime-role-on-managed-postgres

Conversation

@yufoxda

@yufoxda yufoxda commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

本番DBへ適用する作業中に見つかった、runbook のバグ修正です。

何が起きたか

provision_app_runtime_login.sql はロールを作成した後、ALTER ROLE ... nosuperuser nocreatedb nocreaterole noreplication nobypassrls password ...全属性を再指定していました。

SUPERUSER 属性の変更には SUPERUSER 権限が必要です。Supabase の postgres は superuser ではありません(実測: rolsuper=false)。そのため runbook 全体が

permission denied to alter role

で失敗し、専用ログインを作成できませんでした。結果として Workers は postgres(rolbypassrls=true)で接続し続けるしかなく、今回の一連の作業が防ごうとしていた「RLSを素通りできる資格情報でアプリが動く」状態が解消できません。

修正

ALTER ROLEパスワード設定のみに変更しました。

  • 属性は直前の CREATE ROLE で設定済み
  • 既存ロールの場合は、その手前の分岐が「最小権限であること」を検証してから到達する

したがって安全性は何も緩んでいません

ロール名の変更

例示ロール名を app_runtime_loginapp_runtime に変更しました。

  • PostgreSQL では LOGIN 属性を持つロールを「ログインロール」と呼ぶため、_login は重複
  • DBに既にある app_rls / app_api / app_auth の命名と揃う
  • Workers が不正なロールを拒否する際のメッセージ(Unsafe database runtime role)とも一致

runbook のファイル名は据え置きです(「app runtime login を用意する手順書」という意味では正しいため)。

検証

本番DBで実際に適用し、動作を確認しています。

項目 結果
app_runtime 作成
Worker の起動判定 ✅ 通過(superuser=false / bypassRLS=false / app_rls 継承)
識別子ありで grades ✅ 11件
識別子なしで grades ✅ 0件(RLSが機能)
member テスト ✅ 18 pass(runbook の再生を含む)

🤖 Generated with Claude Code

yufoxda and others added 2 commits July 27, 2026 22:34
The provisioning runbook restated every role attribute in an ALTER ROLE after
creating the role. Changing the SUPERUSER attribute requires SUPERUSER, which
the operator role is not on a managed platform: on Supabase the whole runbook
failed with "permission denied to alter role", so the dedicated login could
never be created and the Workers had to keep connecting as postgres — the
BYPASSRLS role the rest of this work exists to stop using.

The ALTER now only sets the password. The attributes are already set by the
CREATE above, and the branch that handles a pre-existing role verifies them
before reaching this point, so nothing is weakened.

Also renames the example role from app_runtime_login to app_runtime. In
PostgreSQL a role with LOGIN is a login role, so the suffix said nothing, and
app_runtime matches both the app_rls/app_api/app_auth naming already in the
database and the "runtime role" wording the Workers use when they reject an
unsafe login.

Found while applying this to the production database, which now runs as
app_runtime: not a superuser, no BYPASSRLS, inherits app_rls.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…secrets

Promoting the first admin still joined public.account, which the schema split
moved to app_auth, so the runbook failed before it could grant the role. That
was an oversight in the split, not a separate change.

The deployment order also never said which secrets each Worker needs, and three
separate outages during the production rollout came from that: member had no
DISCORD_TOKEN or DISCORD_GUILD_ID and answered 500 on every application, and
frontend had no JWT_SECRET, so its middleware threw on each request and put the
site in a redirect loop between / and /login. Both are new requirements of this
release that an existing deployment cannot have.

Adds the per-Worker table, notes that a deploy can drop values that were only
set through the dashboard, and records that DATABASE_URL should stay unset in
production rather than keeping a credential that would bypass the dedicated
runtime role.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant