委託先(external_partner)オンボーディング runbook¶
KYO-106 Phase3。委託先(external_partner 衛星テナント)が自社ログインで、共栄(collector)が保持する
「自社帰属の委託料精算(純額 + 品目別内訳・finalized のみ)」を 読み取り専用で越境閲覧できるように
するための、作成〜紐付け〜解約の手順書。
関連 migration: mig97(種別 3 値化 + external_tenant_id 列 + INVOKER guard + link/unlink/get RPC)/
mig98(越境読取 DEFINER RPC get_external_partner_settlement_summary / _items)。
前提・不変条件(必ず守る)¶
- 公開 signup は external_partner を作れない(要確認1=A)。
SIGNUP_TENANT_TYPES(api/web)とsignup_invite_codesCHECK(mig48)は 2 値のまま。衛星テナント + founding admin は 運営スクリプト(service-role INSERT)でのみ作成する。 external_tenant_idを書く正規経路は link/unlink RPC 一本。既存 API / PostgREST 直書きは INVOKER guard(external_partners_guard_external_tenant_link)で遮断される。- 委託先ユーザーは
role='admin'固定・profiles は作らない(discharger と同型)。越境読取 RPC は role/profiles に依存せずcurrent_tenant_type()='external_partner'+current_tenant_id()で解決する。 - read RPC は fail-closed(collector/discharger/種別 NULL/tenant_id NULL は空)・
finalizedのみ・deleted_at is null再明示・他委託先非漏洩・生sign非返却(signed_amount = amount×sign)・vehicle_lease明細は非表示(総額のみ)。
1. オンボーディング(順序固定・G11 TOCTOU 回避)¶
必ず (1) → (2) の順。逆にすると link RPC が「該当する委託先アカウントが見つかりません」で恒久 fail する
(衛星テナントの auth.users がまだ存在せず count=0 になるため)。
(1) 衛星テナント + founding admin を作成(運営スクリプト・service-role)¶
bootstrap-org.mjs が external_partner の正規作成経路(service-role で tenants を直接 INSERT する。
tenants_type_check は mig97 で 3 値化済みなので通る)。invite-org.mjs は使えない — invite-org は
signup_invite_codes へ INSERT する公開 signup 系で、その CHECK(mig48)は collector/discharger の 2 値に
固定されているため --tenant-type external_partner は CHECK 違反で失敗する(要確認1=A・意図的に非 3値化)。
bootstrap-org の実引数(--email は無い。email は --admin-login-id から loginIdToEmail で合成される):
node scripts/bootstrap-org.mjs \
--org-name "◯◯運輸(委託先)" \
--tenant-type external_partner \
--admin-login-id ep-maruunyu-keiri \
--admin-name "◯◯運輸 経理" \
--password "<8文字以上の強いパスワード>"
- 作成される admin の login email は
ep-maruunyu-keiri@members.waste-link.local(合成メール)。 app_metadataは全種別共通で{ tenant_id, tenant_type: 'external_partner', role: 'admin' }が焼き込まれる(G18)。 external_partner は profiles を作らない(discharger と同型・app_metadata だけでテナント解決)。- login_id 命名規約(要確認8): 衛星ユーザーの login_id は collector 側の driver / staff と別にする。
同一 login_id は合成メール(
<login_id>@members.waste-link.local)のauth.users.email一意制約に 抵触して 409 になる。実在の人物が「委託先の経理」と「共栄の driver」を兼ねる場合でも、 衛星ログイン用に別 login_id(例ep-<会社>-keiri)を発行する。
(2) 共栄 admin/normal が委託先マスタ行を衛星テナントに link¶
共栄(collector)側の担当者(admin または normal)が、自社の委託先マスタ(external_partners)行を
衛星テナントに紐付ける。web UI(後続タスクで用意)または RPC を直接呼ぶ:
select public.link_external_partner_to_tenant(
'<external_partners.id(自社所有)>',
'ep-maruunyu-keiri' -- 衛星 admin の login_id(@ 無しは合成メール ...@members.waste-link.local に変換)。
-- 合成メールをそのまま渡してもよい。
);
-- 返り値: { external_tenant_id, name }
- link RPC は DEFINER で、
current_tenant_id()で所有検証する。他社のexternal_partners行や削除済み行は 「external_partner not found」で拒否(存在漏洩なし)。 - email → 衛星テナント解決は
tenant_type='external_partner'厳密 +count(distinct)=1。不在/不一意は 「該当する委託先アカウントが見つかりません」(内部 warning にコードを残す)。 - link 後、委託先が自社ログインで
/external-partner/settlements(web)にアクセスすると、共栄が finalize 済みの自社帰属精算を閲覧できる。
現在の紐付け先の確認:
select public.get_external_partner_tenant_link('<external_partners.id>'); -- { external_tenant_id, name } | null
2. オフボーディング(解約・批評 critical#1)¶
委託先との取引を終了するときは、必ず以下の両方を実施する(二重防御)。read RPC は
external_tenant_id(紐付け)と deleted_at(ソフトデリート)の両方で 0 件化するが、運用としては両方揃える。
(a) 紐付け解除(unlink)¶
external_tenant_idを null に戻す。以降、衛星ログインでの越境読取は 0 件になる(join 述語で外れる)。- 他社行に対しては 0 行更新(存在漏洩なし)。
(b) 委託先マスタのソフトデリート(必要に応じて)¶
委託先マスタ自体を廃止する場合は external_partners.deleted_at をセット(法定保存ソフトデリート)。
read RPC は deleted_at is null を再明示しているため、削除済み委託先の精算は衛星に返らない。
衛星テナントの
auth.users/ tenants 行そのものの扱い(無効化 / 削除)は運営ポリシーに従う。 read RPC は紐付けと削除状態で閉じるため、アカウント残置でも精算は漏れない。
3. dev シード(rls-proof 用)¶
scripts/verify-external-partner-settlement-rls.mjs(rls-proofs CI)は、dev に以下が揃うと positive path
(衛星 admin が自社 finalized 精算を取得)まで検証する。未整備の間は skip→exit 0(プロジェクトを
恒久的に赤くしない)。seed 手順:
- 上記 (1) で dev に衛星テナント + admin(既定 login_id
demo-external-partner・password は demo 共通Demo!2026)を作成。別 login_id を使う場合は CI envDEV_EP_SATELLITE_LOGINを合わせる。 - 上記 (2) で collector demo の
external_partners行を衛星に link。 - collector demo 側で対象月(既定
2027-03-01・CI envDEV_EP_FINALIZED_MONTH)の委託精算をrecompute→finalize(Phase2 の RPC)して finalized 精算を 1 件用意する。
fail-closed(N1 collector / N2 discharger が空)は衛星 seed が無くても検証される。
4. 参照¶
- 種別 3 値化ドリフト防止:
scripts/tenant-types-drift.test.mjs(8 箇所に external_partner が揃うこと + 公開 signup が external_partner を含まないことを機械固定)。 - docker proof:
scripts/proofs/mig97-external-partner-phase3.sql/mig98-external-partner-phase3.sql(allow/deny を素の postgres:15 で実証・#1–16)。 - mobile は委託先未対応: 衛星ログインは起動時に封鎖(signOut)され driver UI には着地しない(§7.5)。