@luanpdd/kit-mcp 1.21.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +914 -648
  3. package/kit/COMANDOS.md +138 -138
  4. package/kit/README.md +76 -52
  5. package/kit/agents/advisor-researcher.md +106 -106
  6. package/kit/agents/assumptions-analyzer.md +107 -107
  7. package/kit/agents/audit-log-implementer.md +138 -0
  8. package/kit/agents/auditor-consistencia-isolamento.md +413 -0
  9. package/kit/agents/codebase-mapper.md +768 -768
  10. package/kit/agents/crm-pipeline-implementer.md +106 -0
  11. package/kit/agents/debugger.md +813 -772
  12. package/kit/agents/detector-tenant-quente.md +337 -0
  13. package/kit/agents/evolution-go-integrator.md +21 -0
  14. package/kit/agents/example-reviewer.md +21 -21
  15. package/kit/agents/executor.md +564 -523
  16. package/kit/agents/integration-checker.md +200 -200
  17. package/kit/agents/invite-flow-implementer.md +52 -0
  18. package/kit/agents/lgpd-compliance-auditor.md +89 -0
  19. package/kit/agents/multi-tenant-isolation-auditor.md +10 -0
  20. package/kit/agents/multi-tenant-rls-writer.md +78 -0
  21. package/kit/agents/nyquist-auditor.md +178 -178
  22. package/kit/agents/org-onboarding-implementer.md +21 -0
  23. package/kit/agents/phase-researcher.md +696 -696
  24. package/kit/agents/plan-checker.md +272 -272
  25. package/kit/agents/planner.md +922 -891
  26. package/kit/agents/project-researcher.md +652 -652
  27. package/kit/agents/research-synthesizer.md +245 -245
  28. package/kit/agents/roadmapper.md +677 -677
  29. package/kit/agents/supabase-architect.md +27 -0
  30. package/kit/agents/supabase-auth-bootstrapper.md +80 -0
  31. package/kit/agents/supabase-column-privileges-writer.md +399 -0
  32. package/kit/agents/supabase-migration-writer.md +141 -14
  33. package/kit/agents/supabase-rbac-implementer.md +392 -0
  34. package/kit/agents/supabase-rls-hardener.md +521 -0
  35. package/kit/agents/supabase-rls-writer.md +105 -9
  36. package/kit/agents/supabase-roles-implementer.md +355 -0
  37. package/kit/agents/super-admin-implementer.md +99 -0
  38. package/kit/agents/ui-auditor.md +437 -437
  39. package/kit/agents/ui-checker.md +302 -302
  40. package/kit/agents/ui-researcher.md +355 -355
  41. package/kit/agents/user-profiler.md +175 -175
  42. package/kit/agents/validador-evolucao-schema.md +335 -0
  43. package/kit/agents/verifier.md +728 -728
  44. package/kit/commands/adicionar-backlog.md +75 -75
  45. package/kit/commands/adicionar-fase.md +42 -42
  46. package/kit/commands/adicionar-tarefa.md +45 -45
  47. package/kit/commands/adicionar-testes.md +41 -41
  48. package/kit/commands/ajuda.md +21 -21
  49. package/kit/commands/atualizar.md +37 -37
  50. package/kit/commands/auditar-marco.md +179 -179
  51. package/kit/commands/auditar-uat.md +23 -23
  52. package/kit/commands/autonomo.md +40 -40
  53. package/kit/commands/branch-pr.md +24 -24
  54. package/kit/commands/concluir-marco.md +247 -247
  55. package/kit/commands/configuracoes.md +36 -36
  56. package/kit/commands/dados-distribuidos.md +188 -0
  57. package/kit/commands/definir-perfil.md +10 -10
  58. package/kit/commands/depurar.md +190 -190
  59. package/kit/commands/discutir-fase.md +131 -131
  60. package/kit/commands/entrar-discord.md +17 -17
  61. package/kit/commands/estatisticas.md +18 -18
  62. package/kit/commands/example-greeting.md +33 -33
  63. package/kit/commands/executar-fase.md +58 -58
  64. package/kit/commands/expresso.md +56 -56
  65. package/kit/commands/fase-ui.md +34 -34
  66. package/kit/commands/fazer.md +57 -57
  67. package/kit/commands/fio.md +125 -125
  68. package/kit/commands/fluxos-trabalho.md +64 -64
  69. package/kit/commands/forense.md +176 -176
  70. package/kit/commands/gerenciador.md +38 -38
  71. package/kit/commands/inserir-fase.md +31 -31
  72. package/kit/commands/limpeza.md +17 -17
  73. package/kit/commands/listar-hipoteses-fase.md +45 -45
  74. package/kit/commands/listar-workspaces.md +18 -18
  75. package/kit/commands/mapear-codebase.md +70 -70
  76. package/kit/commands/nota.md +33 -33
  77. package/kit/commands/novo-marco.md +43 -43
  78. package/kit/commands/novo-projeto.md +41 -41
  79. package/kit/commands/novo-workspace.md +43 -43
  80. package/kit/commands/pausar-trabalho.md +37 -37
  81. package/kit/commands/perfil-usuario.md +45 -45
  82. package/kit/commands/pesquisar-fase.md +195 -195
  83. package/kit/commands/planejar-fase.md +67 -67
  84. package/kit/commands/planejar-lacunas.md +33 -33
  85. package/kit/commands/plantar-ideia.md +25 -25
  86. package/kit/commands/progresso.md +24 -24
  87. package/kit/commands/proximo.md +30 -30
  88. package/kit/commands/publicar.md +490 -490
  89. package/kit/commands/rapido.md +35 -35
  90. package/kit/commands/reaplicar-patches.md +124 -124
  91. package/kit/commands/relatorio-sessao.md +19 -19
  92. package/kit/commands/remover-fase.md +31 -31
  93. package/kit/commands/remover-workspace.md +26 -26
  94. package/kit/commands/resumo-marco.md +50 -50
  95. package/kit/commands/retomar-trabalho.md +40 -40
  96. package/kit/commands/revisar-backlog.md +60 -60
  97. package/kit/commands/revisar-ui.md +32 -32
  98. package/kit/commands/revisar.md +37 -37
  99. package/kit/commands/saude.md +21 -21
  100. package/kit/commands/setup-notion.md +93 -93
  101. package/kit/commands/supabase.md +55 -8
  102. package/kit/commands/sync-main.md +68 -68
  103. package/kit/commands/validar-fase.md +35 -35
  104. package/kit/commands/verificar-tarefas.md +44 -44
  105. package/kit/commands/verificar-trabalho.md +64 -64
  106. package/kit/file-manifest.json +52 -32
  107. package/kit/framework/bin/lib/commands.cjs +959 -959
  108. package/kit/framework/bin/lib/config.cjs +442 -442
  109. package/kit/framework/bin/lib/core.cjs +1230 -1230
  110. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  111. package/kit/framework/bin/lib/init.cjs +1442 -1442
  112. package/kit/framework/bin/lib/milestone.cjs +252 -252
  113. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  114. package/kit/framework/bin/lib/phase.cjs +888 -888
  115. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  116. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  117. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  118. package/kit/framework/bin/lib/security.cjs +382 -382
  119. package/kit/framework/bin/lib/state.cjs +1031 -1031
  120. package/kit/framework/bin/lib/template.cjs +222 -222
  121. package/kit/framework/bin/lib/uat.cjs +282 -282
  122. package/kit/framework/bin/lib/verify.cjs +888 -888
  123. package/kit/framework/bin/lib/workstream.cjs +491 -491
  124. package/kit/framework/bin/tools.cjs +918 -918
  125. package/kit/framework/commands/workstreams.md +63 -63
  126. package/kit/framework/references/checkpoints.md +778 -778
  127. package/kit/framework/references/continuation-format.md +249 -249
  128. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  129. package/kit/framework/references/git-integration.md +295 -295
  130. package/kit/framework/references/git-planning-commit.md +38 -38
  131. package/kit/framework/references/model-profile-resolution.md +36 -36
  132. package/kit/framework/references/model-profiles.md +139 -139
  133. package/kit/framework/references/phase-argument-parsing.md +61 -61
  134. package/kit/framework/references/planning-config.md +202 -202
  135. package/kit/framework/references/questioning.md +162 -162
  136. package/kit/framework/references/tdd.md +263 -263
  137. package/kit/framework/references/ui-brand.md +160 -160
  138. package/kit/framework/references/user-profiling.md +657 -657
  139. package/kit/framework/references/verification-patterns.md +612 -612
  140. package/kit/framework/references/workstream-flag.md +58 -58
  141. package/kit/framework/templates/DEBUG.md +164 -164
  142. package/kit/framework/templates/UAT.md +265 -265
  143. package/kit/framework/templates/UI-SPEC.md +100 -100
  144. package/kit/framework/templates/VALIDATION.md +76 -76
  145. package/kit/framework/templates/claude-md.md +122 -122
  146. package/kit/framework/templates/codebase/architecture.md +185 -185
  147. package/kit/framework/templates/codebase/concerns.md +205 -205
  148. package/kit/framework/templates/codebase/conventions.md +204 -204
  149. package/kit/framework/templates/codebase/integrations.md +192 -192
  150. package/kit/framework/templates/codebase/stack.md +158 -158
  151. package/kit/framework/templates/codebase/structure.md +199 -199
  152. package/kit/framework/templates/codebase/testing.md +301 -301
  153. package/kit/framework/templates/config.json +44 -44
  154. package/kit/framework/templates/context.md +352 -352
  155. package/kit/framework/templates/continue-here.md +78 -78
  156. package/kit/framework/templates/copilot-instructions.md +7 -7
  157. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  158. package/kit/framework/templates/dev-preferences.md +20 -20
  159. package/kit/framework/templates/discovery.md +146 -146
  160. package/kit/framework/templates/discussion-log.md +63 -63
  161. package/kit/framework/templates/milestone-archive.md +123 -123
  162. package/kit/framework/templates/milestone.md +115 -115
  163. package/kit/framework/templates/phase-prompt.md +610 -610
  164. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  165. package/kit/framework/templates/project.md +186 -186
  166. package/kit/framework/templates/requirements.md +231 -231
  167. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  168. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  169. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  170. package/kit/framework/templates/research-project/STACK.md +120 -120
  171. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  172. package/kit/framework/templates/research.md +419 -419
  173. package/kit/framework/templates/retrospective.md +54 -54
  174. package/kit/framework/templates/roadmap.md +202 -202
  175. package/kit/framework/templates/state.md +176 -176
  176. package/kit/framework/templates/summary-complex.md +59 -59
  177. package/kit/framework/templates/summary-minimal.md +41 -41
  178. package/kit/framework/templates/summary-standard.md +48 -48
  179. package/kit/framework/templates/summary.md +209 -209
  180. package/kit/framework/templates/user-profile.md +146 -146
  181. package/kit/framework/templates/user-setup.md +256 -256
  182. package/kit/framework/templates/verification-report.md +258 -258
  183. package/kit/framework/workflows/add-phase.md +112 -112
  184. package/kit/framework/workflows/add-tests.md +351 -351
  185. package/kit/framework/workflows/add-todo.md +158 -158
  186. package/kit/framework/workflows/audit-milestone.md +340 -340
  187. package/kit/framework/workflows/audit-uat.md +109 -109
  188. package/kit/framework/workflows/autonomous.md +891 -891
  189. package/kit/framework/workflows/check-todos.md +177 -177
  190. package/kit/framework/workflows/cleanup.md +152 -152
  191. package/kit/framework/workflows/complete-milestone.md +696 -696
  192. package/kit/framework/workflows/diagnose-issues.md +231 -231
  193. package/kit/framework/workflows/discovery-phase.md +289 -289
  194. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  195. package/kit/framework/workflows/discuss-phase.md +784 -784
  196. package/kit/framework/workflows/do.md +104 -104
  197. package/kit/framework/workflows/execute-phase.md +838 -838
  198. package/kit/framework/workflows/execute-plan.md +510 -510
  199. package/kit/framework/workflows/fast.md +102 -102
  200. package/kit/framework/workflows/forensics.md +265 -265
  201. package/kit/framework/workflows/health.md +181 -181
  202. package/kit/framework/workflows/help.md +619 -619
  203. package/kit/framework/workflows/insert-phase.md +130 -130
  204. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  205. package/kit/framework/workflows/list-workspaces.md +56 -56
  206. package/kit/framework/workflows/manager.md +362 -362
  207. package/kit/framework/workflows/map-codebase.md +377 -377
  208. package/kit/framework/workflows/milestone-summary.md +223 -223
  209. package/kit/framework/workflows/new-milestone.md +486 -486
  210. package/kit/framework/workflows/new-project.md +1159 -1159
  211. package/kit/framework/workflows/new-workspace.md +237 -237
  212. package/kit/framework/workflows/next.md +97 -97
  213. package/kit/framework/workflows/node-repair.md +92 -92
  214. package/kit/framework/workflows/note.md +156 -156
  215. package/kit/framework/workflows/pause-work.md +176 -176
  216. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  217. package/kit/framework/workflows/plan-phase.md +765 -765
  218. package/kit/framework/workflows/plant-seed.md +169 -169
  219. package/kit/framework/workflows/pr-branch.md +129 -129
  220. package/kit/framework/workflows/profile-user.md +450 -450
  221. package/kit/framework/workflows/progress.md +507 -507
  222. package/kit/framework/workflows/quick.md +757 -757
  223. package/kit/framework/workflows/remove-phase.md +155 -155
  224. package/kit/framework/workflows/remove-workspace.md +90 -90
  225. package/kit/framework/workflows/research-phase.md +82 -82
  226. package/kit/framework/workflows/resume-project.md +326 -326
  227. package/kit/framework/workflows/review.md +228 -228
  228. package/kit/framework/workflows/session-report.md +146 -146
  229. package/kit/framework/workflows/settings.md +283 -283
  230. package/kit/framework/workflows/ship.md +228 -228
  231. package/kit/framework/workflows/stats.md +60 -60
  232. package/kit/framework/workflows/transition.md +671 -671
  233. package/kit/framework/workflows/ui-phase.md +302 -302
  234. package/kit/framework/workflows/ui-review.md +165 -165
  235. package/kit/framework/workflows/update.md +323 -323
  236. package/kit/framework/workflows/validate-phase.md +174 -174
  237. package/kit/framework/workflows/verify-phase.md +252 -252
  238. package/kit/framework/workflows/verify-work.md +637 -637
  239. package/kit/hooks/check-update.js +118 -118
  240. package/kit/hooks/context-monitor.js +163 -163
  241. package/kit/hooks/prompt-guard.js +103 -103
  242. package/kit/hooks/statusline.js +125 -125
  243. package/kit/hooks/workflow-guard.js +101 -101
  244. package/kit/settings.json +45 -45
  245. package/kit/skills/_shared-dados-distribuidos/glossary.md +224 -0
  246. package/kit/skills/_shared-supabase/glossary.md +27 -0
  247. package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -0
  248. package/kit/skills/audit-log-multi-tenant/SKILL.md +6 -0
  249. package/kit/skills/cascading-failures/SKILL.md +4 -0
  250. package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -0
  251. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +17 -0
  252. package/kit/skills/escolha-modelo-consistencia/SKILL.md +495 -0
  253. package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -0
  254. package/kit/skills/example-skill/SKILL.md +42 -42
  255. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +4 -0
  256. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +4 -0
  257. package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -0
  258. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +37 -0
  259. package/kit/skills/streams-eventos-cdc/SKILL.md +712 -0
  260. package/kit/skills/supabase-column-level-security/SKILL.md +426 -0
  261. package/kit/skills/supabase-cron-queues/SKILL.md +9 -0
  262. package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -0
  263. package/kit/skills/supabase-database-functions/SKILL.md +85 -0
  264. package/kit/skills/supabase-migrations/SKILL.md +133 -11
  265. package/kit/skills/supabase-postgres-roles/SKILL.md +392 -0
  266. package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -0
  267. package/kit/skills/supabase-rls-policies/SKILL.md +462 -12
  268. package/kit/skills/super-admin-platform-pattern/SKILL.md +4 -0
  269. package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -0
  270. package/package.json +63 -63
  271. package/src/core/kit.js +216 -216
  272. package/src/core/reflect.js +247 -247
  273. package/src/core/reverse-sync.js +372 -372
  274. package/src/core/sync.js +418 -418
  275. package/src/core/watch.js +121 -121
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: supabase-rls-policies
3
- description: Use ao criar/auditar RLS — sempre (select auth.uid()), policies separadas por operação, índices nas colunas, NUNCA user_metadata em autorização.
3
+ description: Use ao criar/auditar RLS — sempre (select auth.uid()), policies separadas por operação, GRANTs antes de ENABLE RLS, IS NOT NULL para anti silent-fail, índices nas colunas, NUNCA user_metadata em autorização. v1.23 incorpora 100% da doc oficial RLS Supabase + defense in depth.
4
4
  ---
5
5
 
6
6
  # Supabase — RLS Policies
@@ -14,35 +14,161 @@ LLM carrega esta skill quando criar, auditar ou debugar Row Level Security em Su
14
14
  - "auth.uid()", "auth.jwt()"
15
15
  - "MFA enforcement", "AAL2"
16
16
  - "auditar segurança de tabela Supabase"
17
+ - "GRANT antes de ENABLE RLS", "defense in depth", "security_invoker"
18
+ - "anon role vs anonymous user", "raw_app_meta_data vs raw_user_meta_data"
19
+
20
+ ## Defense in depth — RLS como camada (v1.23)
21
+
22
+ RLS é um **Postgres primitive** que oferece **defense in depth**: protege dados mesmo quando acessados por third-party tooling (Metabase, dbt, ferramentas BI conectadas via JDBC). Mesmo se um vazamento de chave API ou bypass na camada de aplicação acontece, RLS impede acesso indevido **no banco**.
23
+
24
+ A regra mestre:
25
+
26
+ > **RLS *must* always be enabled on any tables stored in an exposed schema. By default, this is the `public` schema.**
27
+
28
+ Se você não tem RLS na camada do banco, você está confiando que **todo cliente** (front-end, backend, third-party, scripts, MCP tools) faça filtering corretamente. Isso é frágil.
29
+
30
+ **Princípio em v1.23 (handoff cooperativo):** todo SQL gerado pelo kit passa pelo `supabase-rls-hardener` antes do output final — drafts upstream são preservados, mas hardening RLS é obrigatório.
17
31
 
18
32
  ## Regras absolutas
19
33
 
20
- **WARNING — REGRA #1 (segurança crítica):** **NUNCA** referencie `user_metadata` em policy de autorização. `user_metadata` é editável pelo cliente via `auth.updateUser({data: {...}})` — usuário pode auto-elevar `role: 'admin'` ou `plan: 'premium'`. Use **`app_metadata`** (set apenas via service_role) para roles/permissions.
34
+ **WARNING — REGRA #1 (segurança crítica):** **NUNCA** referencie `user_metadata` em policy de autorização. `user_metadata` é editável pelo cliente via `auth.updateUser({data: {...}})` — usuário pode auto-elevar `role: 'admin'` ou `plan: 'premium'`. Use **`app_metadata`** (set apenas via service_role) para roles/permissions. Splinter linter 0015 detecta automaticamente.
21
35
 
22
- **REGRA #2 (performance crítica):** **SEMPRE** envolva `auth.uid()` em `(select auth.uid())`. Sem o wrapper, Postgres reavalia a função **uma vez por linha** — degrada queries com filtro RLS em **até 1000×**.
36
+ **REGRA #2 (performance crítica):** **SEMPRE** envolva `auth.uid()` em `(select auth.uid())`. Sem o wrapper, Postgres reavalia a função **uma vez por linha** — degrada queries com filtro RLS em **até 1000×**. Documentado nos benchmarks oficiais (`test2a-wrappedSQL-uid`: 179ms → 9ms, 94.97% improvement).
37
+
38
+ **REGRA #3 (anti silent-fail anônimo — v1.23):** Para policies que dependem de identidade autenticada, use **`auth.uid() IS NOT NULL AND auth.uid() = user_id`** ao invés de apenas `(select auth.uid()) = user_id`. Quando o usuário não está logado, `auth.uid()` retorna `null`, e `null = user_id` é **sempre false** silenciosamente — a policy "funciona" mas confunde debugging. O check explícito de `IS NOT NULL` deixa intent claro.
23
39
 
24
40
  **Outras regras:**
25
41
 
42
+ - **`GRANT` antes de `ENABLE RLS`** (v1.23) — sempre conceda privilégios necessários aos roles `anon`/`authenticated`/`service_role` ANTES de habilitar RLS. Sem GRANT, mesmo policies "permissive" falham porque o role não tem permissão de tabela.
26
43
  - **`policies separadas por operação`** — uma `for select`, uma `for insert`, uma `for update`, uma `for delete`. **Nunca** `for all` cobrindo CRUD inteiro.
27
- - **`TO authenticated`** ou **`to anon`** sempre explícito — nunca deixar implícito (default `to public` é insecure).
44
+ - **`TO authenticated`** ou **`to anon`** sempre explícito — nunca deixar implícito (default `to public` é insecure; impede otimização do executor que skipa execução de policy para roles fora do TO clause).
28
45
  - `for select` e `for delete` usam **apenas `using`** (sem `with check`).
29
46
  - `for insert` usa **apenas `with check`** (sem `using`).
30
47
  - `for update` usa **`using` + `with check`** (using para qual linha pode ser atualizada, with check para qual estado a linha pode assumir).
31
48
  - Índice obrigatório nas colunas referenciadas pela policy: `create index on public.tasks (user_id);`. Sem index, scan full em cada query.
32
49
  - `permissive` é default e preferido. `restrictive` é raro e exige justificativa explícita.
33
50
  - Para MFA enforcement: `(auth.jwt()->>'aal')::text = 'aal2'` em policies que exigem 2FA ativo.
51
+ - **Views** com `security_invoker=true` (Postgres 15+) — por padrão views bypassam RLS (criadas como `security_definer` rodando como `postgres`). Ver seção "Views" abaixo.
52
+
53
+ ## Setup canônico — GRANTs + ENABLE RLS (v1.23)
54
+
55
+ O setup completo para uma tabela em schema exposto é:
56
+
57
+ ```sql
58
+ -- 1. GRANTs por role (faça ANTES de ENABLE RLS)
59
+ grant select on public.tasks to anon;
60
+ grant select, insert, update, delete on public.tasks to authenticated;
61
+ grant select, insert, update, delete on public.tasks to service_role;
62
+
63
+ -- 2. Enable RLS
64
+ alter table public.tasks enable row level security;
65
+
66
+ -- 3. Policies granulares (sem isso, nada é acessível com publishable key)
67
+ create policy "users_select_own_tasks"
68
+ on public.tasks for select to authenticated
69
+ using ((select auth.uid()) is not null and (select auth.uid()) = user_id);
70
+
71
+ -- ... INSERT/UPDATE/DELETE policies análogos
72
+
73
+ -- 4. Índice obrigatório
74
+ create index tasks_user_id_idx on public.tasks (user_id);
75
+ ```
76
+
77
+ **Por que GRANTs antes de ENABLE RLS:** RLS é uma camada de filtragem de linhas **adicionada** sobre as permissões de tabela. Sem `GRANT SELECT TO authenticated`, mesmo se a policy retorna true, a query falha com "permission denied". Os GRANTs estabelecem o que o role *pode tentar* fazer; RLS estabelece *quais linhas* ele vê.
78
+
79
+ **Roles canônicos Supabase:**
80
+
81
+ - `anon` — requisição sem autenticação (usuário deslogado)
82
+ - `authenticated` — requisição com JWT válido de Supabase Auth
83
+ - `service_role` — bypassa RLS (ver "Bypassing RLS" abaixo); use APENAS em backend/admin
84
+
85
+ ## Auto-enable RLS para tabelas novas (v1.23)
86
+
87
+ Para evitar esquecimento humano, instale event trigger que ativa RLS automaticamente em `CREATE TABLE`:
88
+
89
+ ```sql
90
+ create or replace function rls_auto_enable()
91
+ returns event_trigger
92
+ language plpgsql
93
+ security definer
94
+ set search_path = pg_catalog
95
+ as $$
96
+ declare
97
+ cmd record;
98
+ begin
99
+ for cmd in
100
+ select *
101
+ from pg_event_trigger_ddl_commands()
102
+ where command_tag in ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO')
103
+ and object_type in ('table','partitioned table')
104
+ loop
105
+ if cmd.schema_name is not null and cmd.schema_name in ('public') and cmd.schema_name not in ('pg_catalog','information_schema') and cmd.schema_name not like 'pg_toast%' and cmd.schema_name not like 'pg_temp%' then
106
+ begin
107
+ execute format('alter table if exists %s enable row level security', cmd.object_identity);
108
+ raise log 'rls_auto_enable: enabled RLS on %', cmd.object_identity;
109
+ exception
110
+ when others then
111
+ raise log 'rls_auto_enable: failed to enable RLS on %', cmd.object_identity;
112
+ end;
113
+ else
114
+ raise log 'rls_auto_enable: skip % (system schema or not in enforced list)', cmd.object_identity;
115
+ end if;
116
+ end loop;
117
+ end;
118
+ $$;
119
+
120
+ drop event trigger if exists ensure_rls;
121
+ create event trigger ensure_rls
122
+ on ddl_command_end
123
+ when tag in ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO')
124
+ execute function rls_auto_enable();
125
+ ```
126
+
127
+ **Caveats:**
128
+ - Aplica-se a tabelas criadas **após** o trigger ser instalado — existentes precisam de `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` manual.
129
+ - Tabelas em `pg_catalog`, `information_schema`, `pg_toast*`, `pg_temp*` são skipped (sistema).
130
+ - Padrão completo + variações em [`supabase-rls-defense-in-depth`](../supabase-rls-defense-in-depth/SKILL.md) (v1.23).
131
+
132
+ ## `anon` Postgres role vs anonymous Auth user (v1.23)
133
+
134
+ Confusão comum: **`anon` Postgres role** ≠ **anonymous user de Supabase Auth**.
135
+
136
+ - **`anon` Postgres role** — requisição sem JWT (usuário deslogado). É um Postgres role como qualquer outro; aparece em `TO anon` clauses.
137
+ - **anonymous user de Supabase Auth** — usuário criado via `supabase.auth.signInAnonymously()` (Auth feature). Tem JWT válido, assume o role `authenticated`, e pode ser diferenciado por checar a claim `is_anonymous` no JWT.
138
+
139
+ **Implicação em policies:**
140
+
141
+ ```sql
142
+ -- visivel a qualquer cliente (anon Postgres ou authenticated com is_anonymous=true ou regular)
143
+ create policy "public_profiles_view"
144
+ on public.profiles for select
145
+ to authenticated, anon
146
+ using (true);
147
+
148
+ -- bloqueia anonymous Auth users mesmo que estejam autenticados
149
+ create policy "premium_features_no_anonymous"
150
+ on public.premium_data for select
151
+ to authenticated
152
+ using (
153
+ (select auth.jwt()->>'is_anonymous')::boolean is not true
154
+ and (select auth.uid()) = user_id
155
+ );
156
+ ```
34
157
 
35
158
  ## Patterns canônicos
36
159
 
37
160
  ### SELECT — usuário lê apenas suas próprias linhas
38
161
 
39
162
  ```sql
40
- -- política de SELECT com wrapper (select auth.uid()) obrigatório
163
+ -- política de SELECT com wrapper (select auth.uid()) + IS NOT NULL anti silent-fail
41
164
  create policy "users_select_own_tasks"
42
165
  on public.tasks
43
166
  for select
44
167
  to authenticated
45
- using ((select auth.uid()) = user_id);
168
+ using (
169
+ (select auth.uid()) is not null
170
+ and (select auth.uid()) = user_id
171
+ );
46
172
 
47
173
  -- index obrigatório (sem isso, scan full)
48
174
  create index tasks_user_id_idx on public.tasks (user_id);
@@ -56,22 +182,35 @@ create policy "users_insert_own_tasks"
56
182
  on public.tasks
57
183
  for insert
58
184
  to authenticated
59
- with check ((select auth.uid()) = user_id);
185
+ with check (
186
+ (select auth.uid()) is not null
187
+ and (select auth.uid()) = user_id
188
+ );
60
189
 
61
190
  -- UPDATE — restringe quais linhas (using) E qual estado novo (with check)
191
+ -- IMPORTANTE: UPDATE também exige policy SELECT correspondente (sem ela, UPDATE não funciona)
62
192
  create policy "users_update_own_tasks"
63
193
  on public.tasks
64
194
  for update
65
195
  to authenticated
66
- using ((select auth.uid()) = user_id)
67
- with check ((select auth.uid()) = user_id);
196
+ using (
197
+ (select auth.uid()) is not null
198
+ and (select auth.uid()) = user_id
199
+ )
200
+ with check (
201
+ (select auth.uid()) is not null
202
+ and (select auth.uid()) = user_id
203
+ );
68
204
 
69
205
  -- DELETE — apenas a coluna using (sem with check)
70
206
  create policy "users_delete_own_tasks"
71
207
  on public.tasks
72
208
  for delete
73
209
  to authenticated
74
- using ((select auth.uid()) = user_id);
210
+ using (
211
+ (select auth.uid()) is not null
212
+ and (select auth.uid()) = user_id
213
+ );
75
214
  ```
76
215
 
77
216
  ### Role admin via `app_metadata`
@@ -91,20 +230,180 @@ create policy "admins_manage_all_tasks"
91
230
  );
92
231
  ```
93
232
 
233
+ ### Team membership via `app_metadata` (array)
234
+
235
+ ```sql
236
+ -- exemplo: app_metadata.teams = ["team_a", "team_b"]
237
+ create policy "team_members_view"
238
+ on public.team_resources
239
+ for select
240
+ to authenticated
241
+ using (
242
+ team_id::text in (select jsonb_array_elements_text((select auth.jwt()->'app_metadata'->'teams')))
243
+ );
244
+ ```
245
+
94
246
  ### MFA enforcement (AAL2)
95
247
 
96
248
  ```sql
97
249
  -- exigir 2FA ativo para acessar dados sensíveis
250
+ -- restrictive force AND com policy SELECT base
98
251
  create policy "mfa_required_for_billing"
99
252
  on public.billing_records
253
+ as restrictive
100
254
  for select
101
255
  to authenticated
102
256
  using (
103
257
  (select (auth.jwt()->>'aal')::text) = 'aal2'
104
- and (select auth.uid()) = user_id
105
258
  );
106
259
  ```
107
260
 
261
+ ### Views com `security_invoker=true` (Postgres 15+) — v1.23
262
+
263
+ Por padrão, views são criadas com `security_definer` (rodam com permissões do criador, geralmente `postgres`) — **bypassam RLS** das tabelas subjacentes. Em Postgres 15+, use `security_invoker=true` para fazer a view respeitar as policies RLS do role chamador:
264
+
265
+ ```sql
266
+ create view public.user_active_tasks
267
+ with (security_invoker = true)
268
+ as
269
+ select id, title, status, created_at
270
+ from public.tasks
271
+ where status = 'active';
272
+ ```
273
+
274
+ **Em versões < Postgres 15:** revoke acesso de `anon`/`authenticated` à view, ou crie em schema não-exposto:
275
+
276
+ ```sql
277
+ -- alternativa pré-Postgres 15: revoke acesso
278
+ revoke select on public.legacy_view from anon, authenticated;
279
+
280
+ -- ou crie em schema privado:
281
+ create view private.internal_view as ...;
282
+ ```
283
+
284
+ ## `app_metadata` vs `user_metadata` — caveats canônicos (v1.23)
285
+
286
+ A função `auth.jwt()` retorna o JWT do usuário fazendo a requisição. Existem duas claims relacionadas a metadata:
287
+
288
+ - **`raw_user_meta_data`** — pode ser atualizado pelo authenticated end-user via `supabase.auth.updateUser({ data: { ... } })`. **NÃO é seguro para authorization.** Use apenas para preferences (tema, idioma, avatar URL).
289
+ - **`raw_app_meta_data`** — **NÃO pode ser atualizado pelo cliente** — só via service_role + admin API. **É o lugar correto** para roles, permissions, team memberships, plan tier.
290
+
291
+ **Caveat #1 (JWT freshness):** JWT nem sempre está "fresh". Se você remove um user de um team e atualiza `app_metadata`, isso não reflete em `auth.jwt()` até o JWT ser refreshed (geralmente 1h TTL). Para invalidação imediata, force logout do user via `auth.admin.signOut()`.
292
+
293
+ **Caveat #2 (cookie 4096 bytes):** Se você usa Cookies para Auth, esteja atento ao JWT size. Browsers limitam cada cookie a 4096 bytes. Se você embarca arrays grandes em `app_metadata.teams` ou similar, o JWT pode passar do limite. Mitigação: store apenas IDs, busque membership via SQL com policy/RPC.
294
+
295
+ **Caveat #3 (NULL handling):** Para requests sem auth, `auth.uid()` retorna `null`. `null = user_id` é sempre **false silenciosamente** em SQL — a policy não erra, só não match. Sempre use `IS NOT NULL AND ...` (REGRA #3) para deixar intent claro.
296
+
297
+ ## Performance — recomendações canônicas (v1.23)
298
+
299
+ Toda authorization tem custo. RLS é poderoso mas pode ser caro em queries que scaneam muitas linhas. Recomendações baseadas em benchmarks oficiais Supabase:
300
+
301
+ ### 1. Index nas colunas usadas em policies (99.94% improvement)
302
+
303
+ ```sql
304
+ -- antes: scan full
305
+ -- depois: index scan
306
+ create index tasks_user_id_idx on public.tasks (user_id);
307
+ ```
308
+
309
+ ### 2. Envolva funções em `(select ...)` para caching de plano (até 99.99% improvement)
310
+
311
+ ```sql
312
+ -- antes: re-executa auth.uid() por linha (179ms para 100k rows)
313
+ using (auth.uid() = user_id)
314
+
315
+ -- depois: initPlan, executa 1 vez e reusa (9ms)
316
+ using ((select auth.uid()) = user_id)
317
+ ```
318
+
319
+ Aplicável a qualquer função que não muda baseado em row data: `auth.uid()`, `auth.jwt()`, `security definer` functions. **Não funciona** se o resultado depende da linha (ex: `is_owner(row_id)` precisa rodar por linha).
320
+
321
+ ### 3. Adicione filtros redundantes nas queries client-side (94.74% improvement)
322
+
323
+ Mesmo com policy aplicada, adicione `.eq()` ou `where` explícito no client:
324
+
325
+ ```js
326
+ // errado — confia 100% na policy
327
+ const { data } = supabase.from('tasks').select()
328
+
329
+ // certo — Postgres usa o filtro para construir query plan melhor
330
+ const { data } = supabase.from('tasks').select().eq('user_id', userId)
331
+ ```
332
+
333
+ Policy é "implicit where clause"; filtro explícito ajuda o planner Postgres a escolher index scan ao invés de seq scan.
334
+
335
+ ### 4. Specify role com `TO` clause (99.78% improvement)
336
+
337
+ ```sql
338
+ -- antes (sem TO): policy roda para todos roles, inclusive anon
339
+ create policy "rls_test_select" on rls_test
340
+ using ((select auth.uid()) = user_id);
341
+
342
+ -- depois: anon skipa execução
343
+ create policy "rls_test_select" on rls_test
344
+ to authenticated
345
+ using ((select auth.uid()) = user_id);
346
+ ```
347
+
348
+ ### 5. Use `security definer` functions para policies caras
349
+
350
+ Se a policy precisa fazer JOIN ou query custosa, encapsule em função `security definer` que bypassa RLS interno:
351
+
352
+ ```sql
353
+ -- função pode acessar roles_table sem aplicar RLS recursivamente
354
+ create function private.has_good_role()
355
+ returns boolean
356
+ language plpgsql
357
+ security definer
358
+ set search_path = ''
359
+ as $$
360
+ begin
361
+ return exists (
362
+ select 1 from public.roles_table
363
+ where (select auth.uid()) = user_id and role = 'good_role'
364
+ );
365
+ end;
366
+ $$;
367
+
368
+ -- policy fica simples e cacheável
369
+ create policy "rls_test_select"
370
+ on public.test_table
371
+ to authenticated
372
+ using ((select private.has_good_role()));
373
+ ```
374
+
375
+ **IMPORTANTE:** funções `security definer` NUNCA em schema exposto (`public`). Sempre em `private` ou similar (não exposto via API settings).
376
+
377
+ ### 6. Minimize joins (99.78% improvement)
378
+
379
+ Reescreva policies que fazem join na source table para usar `IN` ao invés:
380
+
381
+ ```sql
382
+ -- antes: join entre test_table (source) e team_user (target)
383
+ create policy "rls_test_select" on public.test_table
384
+ to authenticated
385
+ using (
386
+ (select auth.uid()) in (
387
+ select user_id
388
+ from public.team_user
389
+ where team_user.team_id = team_id -- join com source!
390
+ )
391
+ );
392
+
393
+ -- depois: filtra primeiro, depois IN
394
+ create policy "rls_test_select" on public.test_table
395
+ to authenticated
396
+ using (
397
+ team_id in (
398
+ select team_id
399
+ from public.team_user
400
+ where user_id = (select auth.uid()) -- sem join
401
+ )
402
+ );
403
+ ```
404
+
405
+ Se a lista interna pode passar de 1000 items, considere abordagem com `security definer` function ao invés.
406
+
108
407
  ## Anti-patterns
109
408
 
110
409
  ### Anti-pattern 1: `auth.uid()` sem `(select)` wrapper
@@ -176,10 +475,161 @@ create policy "users_select_own_tasks" on public.tasks
176
475
  create index tasks_user_id_idx on public.tasks (user_id);
177
476
  ```
178
477
 
478
+ ### Anti-pattern 5: ENABLE RLS sem GRANT — query falha silenciosa (v1.23)
479
+
480
+ **Errado:**
481
+ ```sql
482
+ alter table public.tasks enable row level security;
483
+ -- esqueceu de grant select on public.tasks to authenticated;
484
+ create policy "users_select" ... using (...);
485
+ ```
486
+
487
+ **Por quê:** RLS é camada *sobre* permissões de tabela. Sem GRANT, role não pode tentar acessar a tabela — query retorna "permission denied" mesmo se policy permitiria.
488
+
489
+ **Certo:** sempre GRANT primeiro, depois ENABLE RLS, depois policies (ver "Setup canônico" acima).
490
+
491
+ ### Anti-pattern 6: View sem `security_invoker=true` em Postgres 15+ — bypass de RLS (v1.23)
492
+
493
+ **Errado:**
494
+ ```sql
495
+ -- view criada como postgres user — bypassa RLS de public.tasks
496
+ create view public.user_tasks_view as
497
+ select id, title from public.tasks where user_id = auth.uid();
498
+ ```
499
+
500
+ **Por quê:** views por default são `security_definer` (rodam com permissões do criador). Cliente acessando a view bypass RLS das tabelas underlying. Atacante consegue ler dados de outros users via SELECT na view.
501
+
502
+ **Certo:**
503
+ ```sql
504
+ create view public.user_tasks_view
505
+ with (security_invoker = true)
506
+ as
507
+ select id, title from public.tasks
508
+ where user_id = (select auth.uid());
509
+ ```
510
+
511
+ ### Anti-pattern 7: `null = user_id` silent-fail (v1.23)
512
+
513
+ **Errado:**
514
+ ```sql
515
+ using ((select auth.uid()) = user_id)
516
+ -- se user não logado, auth.uid() é null
517
+ -- null = user_id é always false → policy "funciona" mas confunde
518
+ ```
519
+
520
+ **Por quê:** intent ambíguo — você queria bloquear não-logados ou tratar como caso especial? `null = X` retornar false silenciosamente esconde o intent e dificulta debug.
521
+
522
+ **Certo:**
523
+ ```sql
524
+ using (
525
+ (select auth.uid()) is not null
526
+ and (select auth.uid()) = user_id
527
+ )
528
+ ```
529
+
530
+ ## Postgres Roles vs RLS — quando usar qual (v1.26)
531
+
532
+ Postgres roles e RLS são **conceitos complementares**:
533
+
534
+ | | Postgres Roles | RLS |
535
+ |---|---|---|
536
+ | Escopo | System access (service accounts, cron, BI) | Application access (end-users) |
537
+ | Identidade | Login Postgres (`SET ROLE`) | JWT (`auth.uid()`) |
538
+ | Granularidade | Per schema/table/function | Per linha + coluna |
539
+ | Audit | pg_stat_statements por role | RLS denial logs |
540
+ | Use case | "Cron job pode SELECT em todas tabs" | "User vê apenas próprias rows" |
541
+
542
+ **Princípio canônico v1.26:**
543
+
544
+ - **Para end-users:** RLS + Custom Claims (v1.25) — NÃO criar role Postgres por user
545
+ - **Para service accounts:** Postgres roles dedicados (NÃO usar service_role API key sempre)
546
+ - **Para column-level access:** Postgres roles + column-level GRANTs (skill `supabase-column-level-security` v1.24)
547
+
548
+ Padrão completo de Postgres roles em [`supabase-postgres-roles`](../supabase-postgres-roles/SKILL.md) (v1.26).
549
+
550
+ ## RBAC via Custom Claims + authorize() function (v1.25)
551
+
552
+ A partir de v1.25, o pattern **canônico** de RBAC em Supabase é via **Custom Access Token Auth Hook** que injeta `user_role` no JWT durante geração do token. Em vez de policies fazendo JOIN custoso em `user_roles` table, a policy lê o claim direto via `auth.jwt() ->> 'user_role'` (ou via `authorize()` function que abstrai role → permission lookup).
553
+
554
+ ```sql
555
+ -- Pattern v1.25 — RLS policy usando authorize() (claim do JWT consultado)
556
+ create policy "Allow authorized delete access" on public.channels for delete
557
+ to authenticated
558
+ using ((SELECT authorize('channels.delete')));
559
+
560
+ -- vs. pattern v1.21 — RLS policy usando helper function STABLE (JOIN em DB)
561
+ create policy "Allow admin delete" on public.channels for delete
562
+ to authenticated
563
+ using ((SELECT private.has_role('admin')));
564
+ ```
565
+
566
+ **Vantagens canônicas do pattern v1.25:**
567
+
568
+ - **Performance:** claim no JWT é zero-JOIN; helper function STABLE faz query em user_roles table
569
+ - **Composability:** `authorize(permission)` abstrai role → permission; trocar quem tem permission = UPDATE em role_permissions (sem alterar policies)
570
+ - **Type safety:** `app_permission` enum garante consistência cross-policy
571
+
572
+ **Caveat JWT freshness:** mudanças em `user_roles` só refletem no JWT após refresh (TTL 1h). Para revogação imediata, force logout via `auth.admin.signOut(userId)`. Em multi-tenant complexo (role por org), **combine** custom claim (role global) + helper function PG (role context-aware) — claim sozinho não cobre per-org context.
573
+
574
+ Padrão completo (7 passos + anti-patterns + caveats) em [`supabase-custom-claims-rbac`](../supabase-custom-claims-rbac/SKILL.md) (v1.25).
575
+
576
+ ## Combining RLS with Column-Level Privileges (v1.24)
577
+
578
+ RLS row-level e column-level privileges são **camadas complementares**:
579
+
580
+ - **RLS** filtra **quais linhas** o role vê/modifica
581
+ - **Column privileges** filtra **quais colunas** o role pode acessar dentro da linha
582
+
583
+ Combinação canônica: RLS + column-level (Camada 8 de defense-in-depth, skill `supabase-rls-defense-in-depth` v1.24).
584
+
585
+ ```sql
586
+ -- 1. RLS row-level — user só vê próprias posts
587
+ create policy "users_select_own_posts"
588
+ on public.posts for select to authenticated
589
+ using ((select auth.uid()) is not null and (select auth.uid()) = user_id);
590
+
591
+ -- 2. Column-level — mesmo nas próprias posts, user não vê coluna sensível (ex: admin_notes)
592
+ revoke select on table public.posts from authenticated;
593
+ grant select (id, user_id, title, content, created_at) on table public.posts to authenticated;
594
+
595
+ -- 3. service_role / admin_role vê tudo (incluindo admin_notes)
596
+ grant select on table public.posts to service_role;
597
+
598
+ -- Cliente DEVE listar colunas explicitamente:
599
+ -- ❌ supabase.from('posts').select() — FALHA (wildcard expansion → admin_notes)
600
+ -- ✅ supabase.from('posts').select('id, user_id, title, content, created_at')
601
+ ```
602
+
603
+ **Quando combinar:**
604
+
605
+ - Compliance LGPD/GDPR onde algumas colunas (PII) precisam restrição extra além do RLS
606
+ - Audit log com payload sanitizado — RLS filtra por org, column priv filtra payload
607
+ - Billing data — RLS filtra por owner, column priv filtra credit_card_token
608
+
609
+ **Quando NÃO combinar:**
610
+
611
+ - Caso comum (admin/user roles) — use dedicated role table (skill [`supabase-column-level-security`](../supabase-column-level-security/SKILL.md)) ao invés
612
+ - Tabelas sem PII real — overhead sem benefício
613
+
614
+ **Caveat crítico:** com column privileges, **todo SELECT deve listar colunas explicitamente** — `SELECT *` falha. Atualize SDK calls + queries SQL ad-hoc + ferramentas BI conectadas.
615
+
616
+ Padrão completo + 4 patterns canônicos em [`supabase-column-level-security`](../supabase-column-level-security/SKILL.md) (v1.24).
617
+
618
+ ## Bypassing RLS — quando e como
619
+
620
+ 3 mecanismos canônicos para bypass de RLS:
621
+
622
+ 1. **`service_role`** — chave Supabase com bypass automático. **NUNCA** exponha ao cliente. Use APENAS em backend (Edge Functions com env var `SUPABASE_SERVICE_ROLE_KEY`, scripts admin, migrations). Caveat: ao chamar SDK Supabase com service_role mas com `Authorization: Bearer <user_jwt>` ainda set, RLS do user é aplicado (override).
623
+ 2. **`alter role <name> with bypassrls`** — privilégio Postgres que permite role bypass RLS sempre. Use para roles internos (`postgres`, custom admin role). NUNCA conceda a um role que recebe requisições de cliente.
624
+ 3. **`security definer` functions** — função roda com permissões do criador (geralmente `postgres` = bypassrls). Encapsule lógica admin/cross-tenant em função `security definer` no schema `private`.
625
+
626
+ Padrões avançados em [`supabase-rls-defense-in-depth`](../supabase-rls-defense-in-depth/SKILL.md) (v1.23).
627
+
179
628
  ## Ver também
180
629
 
630
+ - [supabase-rls-defense-in-depth](../supabase-rls-defense-in-depth/SKILL.md) — event trigger, BYPASSRLS, service_role caveat, security definer, views security_invoker (v1.23)
181
631
  - [supabase-database-functions](../supabase-database-functions/SKILL.md) — funções com `set search_path = ''` que respeitam RLS
182
632
  - [supabase-storage](../supabase-storage/SKILL.md) — RLS sobre `storage.objects` (multi-tenant path isolation)
183
633
  - [supabase-auth-ssr](../supabase-auth-ssr/SKILL.md) — autenticação que popula `auth.uid()`
184
- - [supabase-migrations](../supabase-migrations/SKILL.md) — migrations sempre com RLS habilitado em novas tabelas
634
+ - [supabase-migrations](../supabase-migrations/SKILL.md) — migrations sempre com GRANT + RLS habilitado em novas tabelas
185
635
  - [glossário](../_shared-supabase/glossary.md) — termos PT-BR↔EN + roles + comandos CLI
@@ -312,6 +312,10 @@ using ((auth.jwt()->'user_metadata'->>'super_admin')::boolean = true)
312
312
 
313
313
  **Certo:** REGRA #6 — modal exige typed slug + reason + checkbox + RPC valida tudo server-side. Soft delete preferred.
314
314
 
315
+ ## Fencing Token para TTL de Impersonação (v1.22+)
316
+
317
+ > A TTL de 30min de impersonação é vulnerável a split-brain durante GC pause: super-admin A inicia impersonação, sofre GC pause de 35min, TTL expira, super-admin B inicia outra impersonação no mesmo target, A volta ainda achando que tem sessão. Mitigação canônica: fencing token monotônico em `super_admin_impersonations` table — storage rejeita writes com token < último visto. Padrão completo em [`armadilhas-sistemas-distribuidos`](../armadilhas-sistemas-distribuidos/SKILL.md) (v1.22 — DDIA Ch 8).
318
+
315
319
  ## Ver também
316
320
 
317
321
  - [audit-log-multi-tenant](../audit-log-multi-tenant/SKILL.md) — Phase 109, audit_logs + event `super_admin_action` (REGRA #1)