@luanpdd/kit-mcp 1.22.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.
- package/README.md +267 -1
- package/kit/agents/audit-log-implementer.md +138 -0
- package/kit/agents/auditor-consistencia-isolamento.md +33 -0
- package/kit/agents/crm-pipeline-implementer.md +89 -0
- package/kit/agents/debugger.md +41 -0
- package/kit/agents/evolution-go-integrator.md +21 -0
- package/kit/agents/executor.md +41 -0
- package/kit/agents/invite-flow-implementer.md +52 -0
- package/kit/agents/lgpd-compliance-auditor.md +89 -0
- package/kit/agents/multi-tenant-rls-writer.md +78 -0
- package/kit/agents/org-onboarding-implementer.md +21 -0
- package/kit/agents/planner.md +31 -0
- package/kit/agents/supabase-architect.md +17 -0
- package/kit/agents/supabase-auth-bootstrapper.md +80 -0
- package/kit/agents/supabase-column-privileges-writer.md +399 -0
- package/kit/agents/supabase-migration-writer.md +129 -14
- package/kit/agents/supabase-rbac-implementer.md +392 -0
- package/kit/agents/supabase-rls-hardener.md +521 -0
- package/kit/agents/supabase-rls-writer.md +105 -9
- package/kit/agents/supabase-roles-implementer.md +355 -0
- package/kit/agents/super-admin-implementer.md +99 -0
- package/kit/commands/supabase.md +55 -8
- package/kit/file-manifest.json +32 -24
- package/kit/skills/_shared-supabase/glossary.md +27 -0
- package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +37 -0
- package/kit/skills/supabase-column-level-security/SKILL.md +426 -0
- package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -0
- package/kit/skills/supabase-database-functions/SKILL.md +85 -0
- package/kit/skills/supabase-migrations/SKILL.md +123 -11
- package/kit/skills/supabase-postgres-roles/SKILL.md +392 -0
- package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -0
- package/kit/skills/supabase-rls-policies/SKILL.md +462 -12
- package/package.json +1 -1
package/kit/agents/executor.md
CHANGED
|
@@ -521,3 +521,44 @@ Separado dos commits por tarefa — captura apenas os resultados de execução.
|
|
|
521
521
|
|
|
522
522
|
Inclua TODOS os commits (anteriores + novos se agente de continuação).
|
|
523
523
|
</completion_format>
|
|
524
|
+
|
|
525
|
+
<sql_auto_handoff_cooperativo>
|
|
526
|
+
## SQL auto-handoff cooperativo (v1.23 — CROSS-10)
|
|
527
|
+
|
|
528
|
+
Ao executar PLAN.md que produz SQL/DDL (CREATE TABLE, CREATE POLICY, etc.), **antes** de aplicar via `mcp__supabase__apply_migration` ou escrever arquivo `supabase/migrations/`, faça handoff cooperativo para `supabase-rls-hardener`.
|
|
529
|
+
|
|
530
|
+
**Heurística de detecção (regex no SQL gerado):**
|
|
531
|
+
|
|
532
|
+
```regex
|
|
533
|
+
(create\s+table|create\s+policy|create\s+view|alter\s+table|create\s+function.*security\s+definer|grant\s+.*on|enable\s+row\s+level\s+security)
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Se ≥ 1 match → invoca handoff:
|
|
537
|
+
|
|
538
|
+
```python
|
|
539
|
+
hardener_result = Task(
|
|
540
|
+
subagent_type="supabase-rls-hardener",
|
|
541
|
+
prompt=f"""
|
|
542
|
+
<upstream_intent>
|
|
543
|
+
Source agent: executor
|
|
544
|
+
Original goal: aplicar SQL definido em {plan_file}
|
|
545
|
+
Constraints: {plan_constraints if available else 'follow plan as-is'}
|
|
546
|
+
</upstream_intent>
|
|
547
|
+
|
|
548
|
+
<draft_sql>{generated_sql}</draft_sql>
|
|
549
|
+
|
|
550
|
+
<user_facing_caller>true</user_facing_caller>
|
|
551
|
+
"""
|
|
552
|
+
)
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**Processamento de verdict:**
|
|
556
|
+
- **GO** → aplica SQL direto sem mudanças
|
|
557
|
+
- **STRENGTHEN** → aplica diff sugerido (ajustes mantendo intent); registra no commit message + SUMMARY.md
|
|
558
|
+
- **REWRITE** → se user_facing_caller=true, PAUSA execução e pede confirmação ao usuário; sem confirmação, não aplica
|
|
559
|
+
|
|
560
|
+
**Princípio canônico v1.23:** Executor faz (aplica plan); supabase-rls-hardener hardena (valida defense-in-depth). Conflitos viram diff explícito, nunca abortos silenciosos.
|
|
561
|
+
|
|
562
|
+
**Registro em SUMMARY.md:** se hardener veredict ≠ GO, SUMMARY.md inclui section "## RLS Hardener Trace" com verdict + diff aplicado + justificativa.
|
|
563
|
+
|
|
564
|
+
</sql_auto_handoff_cooperativo>
|
|
@@ -128,8 +128,60 @@ INVITE-FLOW-IMPLEMENTER · output integrado
|
|
|
128
128
|
- Histogram `invite.accept_latency_ms` (tempo entre create e accept)
|
|
129
129
|
- Alarme se `invite.created.count > bulk_limit_per_hour` por org → suspeita de abuso
|
|
130
130
|
|
|
131
|
+
## Cooperative handoff to supabase-rls-hardener (v1.23)
|
|
132
|
+
|
|
133
|
+
Após gerar CREATE TABLE org_invites + RPC create_invite/accept_invite + cron expire pending, faça handoff cooperativo para SQL bloco:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
Task(subagent_type="supabase-rls-hardener", prompt=f"""
|
|
137
|
+
<upstream_intent>
|
|
138
|
+
Source agent: invite-flow-implementer
|
|
139
|
+
Original goal: implementar invite flow B2B com token-based para {org_context}
|
|
140
|
+
Constraints: token SHA-256 (raw enviado por email, hash no banco); TTL 7d single-use; state machine 5 estados (pending→accepted|rejected|cancelled|expired); email-lock obrigatório; idempotência via FOR UPDATE em transação
|
|
141
|
+
</upstream_intent>
|
|
142
|
+
|
|
143
|
+
<draft_sql>{generated_invites_sql}</draft_sql>
|
|
144
|
+
|
|
145
|
+
<user_facing_caller>true</user_facing_caller>
|
|
146
|
+
""")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Hardener valida token security (hash apenas no DB), RPC com SECURITY DEFINER em schema private, RLS por org_id. **NUNCA descarte intent upstream silenciosamente**.
|
|
150
|
+
|
|
151
|
+
## Cooperative handoff column-level (v1.24 — CROSS-15)
|
|
152
|
+
|
|
153
|
+
`org_invites.token_raw` é gerado durante create do invite (raw enviado por email, hash armazenado). Após email enviado, **nenhum role além de service_role** deve poder ler o raw — é segredo de uso único. Aplique handoff cooperativo column-level:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
Task(subagent_type="supabase-column-privileges-writer", prompt=f"""
|
|
157
|
+
<upstream_intent>
|
|
158
|
+
Source agent: invite-flow-implementer
|
|
159
|
+
Original goal: token raw column (org_invites.token_raw) legível APENAS para service_role pós-criação
|
|
160
|
+
Constraints: token_raw é segredo único, enviado por email durante create; hash armazenado para validation em accept; raw NUNCA visível em REST API; member_invited audit event não logga token_raw
|
|
161
|
+
</upstream_intent>
|
|
162
|
+
|
|
163
|
+
<table>schema: public, name: org_invites</table>
|
|
164
|
+
|
|
165
|
+
<sensitive_columns>
|
|
166
|
+
- token_raw (text — segredo, apenas service_role)
|
|
167
|
+
</sensitive_columns>
|
|
168
|
+
|
|
169
|
+
<allowed_roles>
|
|
170
|
+
- service_role: SELECT all (incluindo token_raw — usado durante envio de email)
|
|
171
|
+
- authenticated: SELECT (id, org_id, email, role, status, expires_at, created_at, accepted_at) — sem token_raw
|
|
172
|
+
- anon: SELECT (status) — minimal, para "this invite is still valid?" check pre-login
|
|
173
|
+
</allowed_roles>
|
|
174
|
+
|
|
175
|
+
<user_facing_caller>true</user_facing_caller>
|
|
176
|
+
""")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Caveat:** mesmo com column-level, considere também armazenar APENAS o hash do token (não o raw) na tabela. token_raw fica em memory durante envio do email e descartado. Isso é defense-in-depth Camada 9 (não armazenar segredos).
|
|
180
|
+
|
|
131
181
|
## Ver também
|
|
132
182
|
|
|
183
|
+
- [supabase-rls-hardener](./supabase-rls-hardener.md) — canonical handoff target v1.23
|
|
184
|
+
- [supabase-column-privileges-writer](./supabase-column-privileges-writer.md) — canonical handoff target v1.24 (column-level token raw)
|
|
133
185
|
- [member-invite-flow](../skills/member-invite-flow/SKILL.md) — base de conhecimento
|
|
134
186
|
- [supabase-migration-writer](./supabase-migration-writer.md) — invoked via Task() para SQL
|
|
135
187
|
- [supabase-edge-fn-writer](./supabase-edge-fn-writer.md) — invoked via Task() para Edge Function
|
|
@@ -197,8 +197,97 @@ Se ausente OU regions diferentes de `gru1` / `sa-east-1` → P2 informacional.
|
|
|
197
197
|
- Counter `lgpd.audit.gaps.found{severity}` por execução
|
|
198
198
|
- Histogram `lgpd.audit.duration_ms`
|
|
199
199
|
|
|
200
|
+
## Cooperative handoff to supabase-rls-hardener (v1.23)
|
|
201
|
+
|
|
202
|
+
Após gerar DSR table + Art. 18 right workflows + erasure via anonymization, faça handoff cooperativo para SQL bloco:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
Task(subagent_type="supabase-rls-hardener", prompt=f"""
|
|
206
|
+
<upstream_intent>
|
|
207
|
+
Source agent: lgpd-compliance-auditor
|
|
208
|
+
Original goal: implementar LGPD compliance per-tenant (9 direitos Art. 18) para {org_context}
|
|
209
|
+
Constraints: DSR SLA 15 dias (Art. 19) com alert pg_cron D-3; consent default opt-out (Art. 8 §5); erasure via anonymization (UUID preserved + PII NULL/hash); cross-border config (gru1 Vercel + sa-east-1 Supabase); PII sanitization em audit_logs (cross-ref Phase 109)
|
|
210
|
+
</upstream_intent>
|
|
211
|
+
|
|
212
|
+
<draft_sql>{generated_dsr_sql}</draft_sql>
|
|
213
|
+
|
|
214
|
+
<user_facing_caller>true</user_facing_caller>
|
|
215
|
+
""")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Hardener valida pseudonymization correto, retention policies via pg_cron, PII sanitization em audit_logs. **NUNCA descarte intent upstream silenciosamente**.
|
|
219
|
+
|
|
220
|
+
## Cooperative handoff Postgres Roles (v1.26 — CROSS-20)
|
|
221
|
+
|
|
222
|
+
Crie role `dpo_role` (Data Protection Officer) para acessar DSR requests + erasure operations. Role dedicado em vez de service_role API key permite audit trail granular para compliance LGPD/GDPR.
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
Task(subagent_type="supabase-roles-implementer", prompt=f"""
|
|
226
|
+
<upstream_intent>
|
|
227
|
+
Source agent: lgpd-compliance-auditor
|
|
228
|
+
Original goal: criar role dpo_role para DSR access + erasure operations (LGPD Art. 18 compliance)
|
|
229
|
+
Constraints: BYPASSRLS necessário (DPO precisa ver todos requests cross-org); column-level GRANT em PII columns (cross-ref v1.24 CROSS-12); login com password forte; audit obrigatório
|
|
230
|
+
</upstream_intent>
|
|
231
|
+
|
|
232
|
+
<roles_to_create>
|
|
233
|
+
- name: dpo_role
|
|
234
|
+
type: user
|
|
235
|
+
login: true
|
|
236
|
+
password_source: vault
|
|
237
|
+
bypassrls: true
|
|
238
|
+
inherit: false
|
|
239
|
+
description: "Data Protection Officer. Acesso DSR requests + erasure operations. LGPD Art. 18."
|
|
240
|
+
owner: "dpo@company.com"
|
|
241
|
+
</roles_to_create>
|
|
242
|
+
|
|
243
|
+
<grants>
|
|
244
|
+
dpo_role:
|
|
245
|
+
- schema: public, usage: true
|
|
246
|
+
- table: public.dsr_requests, ops: [SELECT, INSERT, UPDATE]
|
|
247
|
+
- table: public.audit_log, ops: [SELECT] # column-level já em payload
|
|
248
|
+
</grants>
|
|
249
|
+
|
|
250
|
+
<use_case>system_access</use_case>
|
|
251
|
+
<user_facing_caller>true</user_facing_caller>
|
|
252
|
+
""")
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Cooperative handoff column-level (v1.24 — CROSS-12)
|
|
256
|
+
|
|
257
|
+
DSR (Data Subject Request) workflow precisa de erasure granular por coluna — não só DELETE row, mas anonymize PII columns específicas. Cross-border PII restriction (gru1 Vercel + sa-east-1 Supabase) também requer column-level audit. Aplique handoff cooperativo:
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
Task(subagent_type="supabase-column-privileges-writer", prompt=f"""
|
|
261
|
+
<upstream_intent>
|
|
262
|
+
Source agent: lgpd-compliance-auditor
|
|
263
|
+
Original goal: implementar DSR + erasure por coluna + cross-border PII restriction para LGPD Art. 18 compliance
|
|
264
|
+
Constraints: DSR table tem colunas PII (subject_email, subject_phone, subject_address); erasure via anonymization (não DELETE); legível só por dpo_role + service_role; cross-border config sa-east-1 obrigatório
|
|
265
|
+
</upstream_intent>
|
|
266
|
+
|
|
267
|
+
<table>schema: public, name: dsr_requests</table>
|
|
268
|
+
|
|
269
|
+
<sensitive_columns>
|
|
270
|
+
- subject_email
|
|
271
|
+
- subject_phone
|
|
272
|
+
- subject_address
|
|
273
|
+
- subject_metadata (jsonb — pode ter info sensível adicional)
|
|
274
|
+
</sensitive_columns>
|
|
275
|
+
|
|
276
|
+
<allowed_roles>
|
|
277
|
+
- service_role: SELECT all (admin tasks)
|
|
278
|
+
- dpo_role: SELECT all (Data Protection Officer — quem processa DSR)
|
|
279
|
+
- authenticated: SELECT (id, request_type, status, created_at, resolved_at) — minimal
|
|
280
|
+
- anon: denied (sem GRANT)
|
|
281
|
+
</allowed_roles>
|
|
282
|
+
|
|
283
|
+
<user_facing_caller>true</user_facing_caller>
|
|
284
|
+
""")
|
|
285
|
+
```
|
|
286
|
+
|
|
200
287
|
## Ver também
|
|
201
288
|
|
|
289
|
+
- [supabase-rls-hardener](./supabase-rls-hardener.md) — canonical handoff target v1.23
|
|
290
|
+
- [supabase-column-privileges-writer](./supabase-column-privileges-writer.md) — canonical handoff target v1.24 (column-level DSR/erasure)
|
|
202
291
|
- [lgpd-multi-tenant-compliance](../skills/lgpd-multi-tenant-compliance/SKILL.md) — base de conhecimento
|
|
203
292
|
- [audit-log-multi-tenant](../skills/audit-log-multi-tenant/SKILL.md) — Phase 109, PII sanitization + legal_hold
|
|
204
293
|
- [multi-tenant-isolation-auditor](./multi-tenant-isolation-auditor.md) — agent sibling padrão de audit
|
|
@@ -252,8 +252,86 @@ NOTAS
|
|
|
252
252
|
- super_admin actions emitem evento `super_admin_action` em `audit_logs` (Phase 109)
|
|
253
253
|
- Counter `rls.deny.count{tenant_id, policy}` (cross-ref [`four-golden-signals`](../skills/four-golden-signals/SKILL.md))
|
|
254
254
|
|
|
255
|
+
## Cooperative handoff to supabase-rls-hardener (v1.23)
|
|
256
|
+
|
|
257
|
+
Após gerar policies RLS hierárquicas, faça handoff cooperativo para `supabase-rls-hardener` validar defense-in-depth:
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
Task(subagent_type="supabase-rls-hardener", prompt=f"""
|
|
261
|
+
<upstream_intent>
|
|
262
|
+
Source agent: multi-tenant-rls-writer
|
|
263
|
+
Original goal: gerar policies RLS hierárquicas org→dept→role→permission para {table_name}
|
|
264
|
+
Constraints: helper functions já existem em schema private (is_member_of, has_role, has_permission, is_super_admin); STABLE; partial index em organization_members
|
|
265
|
+
</upstream_intent>
|
|
266
|
+
|
|
267
|
+
<draft_sql>{generated_policies_sql}</draft_sql>
|
|
268
|
+
|
|
269
|
+
<user_facing_caller>true</user_facing_caller>
|
|
270
|
+
""")
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Hardener processa verdict GO/STRENGTHEN/REWRITE-com-confirmação. **NUNCA descarte intent upstream silenciosamente** — conflitos viram diff explícito. Princípio canônico v1.23: agents não-Supabase pensam/planejam; agents Supabase materializam/hardenam; ninguém descarta o outro.
|
|
274
|
+
|
|
275
|
+
## Cooperative handoff column-level (v1.24 — CROSS-14)
|
|
276
|
+
|
|
277
|
+
Em hierarquia multi-tenant org→dept→role→permission, column-level pode ser aplicado para restringir acesso granular a colunas sensíveis dentro de tabelas multi-tenant. Casos típicos: department_settings com colunas configurações sensíveis visíveis apenas para department_lead; org_billing com colunas credit_card_token visíveis apenas para org_owner.
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
Task(subagent_type="supabase-column-privileges-writer", prompt=f"""
|
|
281
|
+
<upstream_intent>
|
|
282
|
+
Source agent: multi-tenant-rls-writer
|
|
283
|
+
Original goal: column-level privileges dentro de hierarquia org/dept/role/permission
|
|
284
|
+
Constraints: tabela {table_name} tem coluna(s) sensível(eis) {sensitive_cols} que devem ser legíveis apenas para role específico na hierarquia; helper functions existem em schema private (private.is_member_of, private.has_role, private.has_permission)
|
|
285
|
+
</upstream_intent>
|
|
286
|
+
|
|
287
|
+
<table>schema: public, name: {table_name}</table>
|
|
288
|
+
|
|
289
|
+
<sensitive_columns>
|
|
290
|
+
{sensitive_cols_list}
|
|
291
|
+
</sensitive_columns>
|
|
292
|
+
|
|
293
|
+
<allowed_roles>
|
|
294
|
+
- service_role: SELECT all
|
|
295
|
+
- {specific_role}: SELECT all (via private.has_role check em RLS combinada)
|
|
296
|
+
- authenticated: SELECT non-sensitive columns
|
|
297
|
+
</allowed_roles>
|
|
298
|
+
|
|
299
|
+
<user_facing_caller>true</user_facing_caller>
|
|
300
|
+
""")
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Caveat hierarquia:** column-level é Postgres role-level (não muda baseado em RLS row context). Para casos onde acesso depende de hierarquia *dinâmica* (membership ativa em dept específico), prefira RLS policy + dedicated role table (mais flexível). Use column-level apenas para casos estáticos com role Postgres separado.
|
|
304
|
+
|
|
305
|
+
## Cooperative handoff RBAC via Custom Claims (v1.25 — CROSS-16)
|
|
306
|
+
|
|
307
|
+
Para RBAC em B2B multi-tenant, **combine** Custom Access Token Auth Hook (claim global para role) com helper functions PG STABLE (context-aware per-org). Pattern v1.25 é zero-JOIN para role global, helper function continua para per-org context. Aplique handoff cooperativo:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
Task(subagent_type="supabase-rbac-implementer", prompt=f"""
|
|
311
|
+
<upstream_intent>
|
|
312
|
+
Source agent: multi-tenant-rls-writer
|
|
313
|
+
Original goal: implementar RBAC híbrido (claim global + helper function per-org) para B2B multi-tenant
|
|
314
|
+
Constraints: roles globais (super_admin, billing_admin) via custom claim; roles per-org (org_admin, org_member) via helper function STABLE; combinar em policies via OR
|
|
315
|
+
</upstream_intent>
|
|
316
|
+
|
|
317
|
+
<roles>super_admin, billing_admin, support</roles>
|
|
318
|
+
<permissions_matrix>
|
|
319
|
+
super_admin: [orgs.*, users.*, audit.read]
|
|
320
|
+
billing_admin: [billing.*, subscriptions.read]
|
|
321
|
+
support: [users.read, support_tickets.*]
|
|
322
|
+
</permissions_matrix>
|
|
323
|
+
<multi_tenant>true</multi_tenant>
|
|
324
|
+
<user_facing_caller>true</user_facing_caller>
|
|
325
|
+
""")
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Hardener processa verdict; output combina custom claim (zero-JOIN para super_admin) + helper functions PG existentes (per-org context). Princípio canônico v1.23 (herdado): nenhum lado descarta upstream.
|
|
329
|
+
|
|
255
330
|
## Ver também
|
|
256
331
|
|
|
332
|
+
- [supabase-rls-hardener](./supabase-rls-hardener.md) — canonical handoff target v1.23 (verdicts GO/STRENGTHEN/REWRITE)
|
|
333
|
+
- [supabase-column-privileges-writer](./supabase-column-privileges-writer.md) — canonical handoff target v1.24 (column-level hierarquia)
|
|
334
|
+
- [supabase-rbac-implementer](./supabase-rbac-implementer.md) — canonical handoff target v1.25 (Custom Claims + Auth Hook)
|
|
257
335
|
- [supabase-rls-writer](./supabase-rls-writer.md) — agent base v1.8 que herda anti-pitfalls
|
|
258
336
|
- [supabase-rls-policies](../skills/supabase-rls-policies/SKILL.md) — base de conhecimento canônica v1.8
|
|
259
337
|
- [multi-tenant-rls-hierarchy](../skills/multi-tenant-rls-hierarchy/SKILL.md) — base de conhecimento desta agent
|
|
@@ -192,8 +192,29 @@ Onboarding é hot path crítico — emite eventos canônicos:
|
|
|
192
192
|
3. **Counter:** `signup.org_created.count` (skill [`four-golden-signals`](../skills/four-golden-signals/SKILL.md))
|
|
193
193
|
4. **Histogram:** `signup.duration_ms` (latência total signup → dashboard)
|
|
194
194
|
|
|
195
|
+
## Cooperative handoff to supabase-rls-hardener (v1.23)
|
|
196
|
+
|
|
197
|
+
Após gerar migration atômica (org + members em 1 trx) + Edge Function setup wizard async, faça handoff cooperativo para SQL bloco:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
Task(subagent_type="supabase-rls-hardener", prompt=f"""
|
|
201
|
+
<upstream_intent>
|
|
202
|
+
Source agent: org-onboarding-implementer
|
|
203
|
+
Original goal: signup → criar org → primeiro admin → setup wizard para nova org
|
|
204
|
+
Constraints: atomicidade (org + first_member em 1 trx); slug imutável + único cross-tenant (uniqueness constraint); RLS desde dia 1; first admin sem invite (admin direto via signup)
|
|
205
|
+
</upstream_intent>
|
|
206
|
+
|
|
207
|
+
<draft_sql>{generated_signup_migration_sql}</draft_sql>
|
|
208
|
+
|
|
209
|
+
<user_facing_caller>true</user_facing_caller>
|
|
210
|
+
""")
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Hardener valida que RLS está ativo já na primeira migration, GRANTs corretos para anon (durante signup) e authenticated (pós-login). **NUNCA descarte intent upstream silenciosamente**.
|
|
214
|
+
|
|
195
215
|
## Ver também
|
|
196
216
|
|
|
217
|
+
- [supabase-rls-hardener](./supabase-rls-hardener.md) — canonical handoff target v1.23
|
|
197
218
|
- [org-onboarding-flow](../skills/org-onboarding-flow/SKILL.md) — base de conhecimento (regras + patterns + anti-patterns)
|
|
198
219
|
- [b2b-saas-architecture](../skills/b2b-saas-architecture/SKILL.md) — schema canônico (Phase 106)
|
|
199
220
|
- [supabase-migration-writer](./supabase-migration-writer.md) — invoked via Task() para SQL
|
package/kit/agents/planner.md
CHANGED
|
@@ -889,3 +889,34 @@ Siga os templates nas seções checkpoints e revision_mode respectivamente.
|
|
|
889
889
|
- [ ] Usuário sabe rodar `/executar-fase {X} --gaps-only`
|
|
890
890
|
|
|
891
891
|
</success_criteria>
|
|
892
|
+
|
|
893
|
+
<sql_auto_handoff_cooperativo>
|
|
894
|
+
## SQL auto-handoff cooperativo (v1.23 — CROSS-10)
|
|
895
|
+
|
|
896
|
+
Ao gerar PLAN.md que inclui tarefas com SQL/DDL (CREATE TABLE, CREATE POLICY, CREATE VIEW, ALTER TABLE adicionando column, etc.), **automaticamente** adiciona ao plan uma tarefa final de handoff cooperativo para `supabase-rls-hardener`.
|
|
897
|
+
|
|
898
|
+
**Heurística de detecção (regex):**
|
|
899
|
+
|
|
900
|
+
```regex
|
|
901
|
+
(create\s+table|create\s+policy|create\s+view|alter\s+table|create\s+function.*security\s+definer|grant\s+.*on|enable\s+row\s+level\s+security)
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
Se ≥ 1 match em qualquer tarefa do plan → injetar tarefa final:
|
|
905
|
+
|
|
906
|
+
```markdown
|
|
907
|
+
### Tarefa: Handoff cooperativo SQL para supabase-rls-hardener (v1.23)
|
|
908
|
+
|
|
909
|
+
**Tipo:** Validation
|
|
910
|
+
**Arquivos:** N/A (validation only)
|
|
911
|
+
**Ação:** Invocar `Task(subagent_type=supabase-rls-hardener, prompt=<draft+intent>)` para cada bloco SQL gerado na fase. Processar verdict GO/STRENGTHEN/REWRITE.
|
|
912
|
+
|
|
913
|
+
**Verify:** Output do hardener inclui verdict + SQL hardenado. Em STRENGTHEN/REWRITE, aplicar diff sugerido (se aceito pelo executor ou usuário humano).
|
|
914
|
+
|
|
915
|
+
**Done:** Verdict GO atingido OU diff aplicado com sucesso OU REWRITE confirmado pelo usuário.
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
**Princípio canônico v1.23:** Planner pensa/planeja (estrutura do plan, decomposition, deps); supabase-rls-hardener materializa/hardena (SQL final). Plan não descarta intent — só adiciona camada de validação cooperativa.
|
|
919
|
+
|
|
920
|
+
**Não bloqueia execução:** se hardener responde STRENGTHEN/REWRITE, executor absorve o feedback e aplica diff. Aborto silencioso é proibido.
|
|
921
|
+
|
|
922
|
+
</sql_auto_handoff_cooperativo>
|
|
@@ -124,6 +124,23 @@ projeto: {project_id ou "novo"} · tier: {tier} · gerado em {timestamp}
|
|
|
124
124
|
## 5. Edge Functions / Background (se aplicável)
|
|
125
125
|
{functions + cron pattern}
|
|
126
126
|
|
|
127
|
+
## 5.1 Postgres Roles (v1.26 — ARCH-PATCH-01)
|
|
128
|
+
|
|
129
|
+
**Pergunta canônica upfront:** Esta feature precisa de **service accounts internos** (custom Postgres roles)?
|
|
130
|
+
|
|
131
|
+
Casos típicos:
|
|
132
|
+
- Cron jobs com pg_cron (cleanup, retention) — recomenda role dedicado em vez de service_role API key
|
|
133
|
+
- BI tools (Metabase, dbt) conectando direto no DB — recomenda role read-only com BYPASSRLS
|
|
134
|
+
- ETL scripts (replication, backups) — recomenda role com permissions específicos
|
|
135
|
+
- Admin roles para column-level GRANTs (security_admin, dpo_role, lead_manager)
|
|
136
|
+
|
|
137
|
+
**Se sim, listar service accounts esperados:**
|
|
138
|
+
- {role_name}: {description}, owner: {team_email}, type: {group|user}, bypassrls: {bool}
|
|
139
|
+
|
|
140
|
+
**Cross-suite handoff:** delegar criação para `supabase-roles-implementer` (v1.26) na ordem de implementação.
|
|
141
|
+
|
|
142
|
+
Para application access (end-users), usar **RLS + Custom Claims** (skill `supabase-custom-claims-rbac` v1.25) — NÃO criar Postgres roles para "admin vs user" end-users.
|
|
143
|
+
|
|
127
144
|
## 6. Ordem de Implementação
|
|
128
145
|
{sequence numerada com agent delegate}
|
|
129
146
|
|
|
@@ -297,9 +297,89 @@ Auth events são SLI primário — "successful login %" é métrica de saúde di
|
|
|
297
297
|
|
|
298
298
|
**Output adicionado:** seção "## Observability hooks" com snippet de span wrapper em handlers `/auth/*`.
|
|
299
299
|
|
|
300
|
+
## Custom Claims & RBAC integration (v1.25 — AUTH-PATCH-01)
|
|
301
|
+
|
|
302
|
+
Quando o projeto usa **RBAC via Custom Access Token Auth Hook** (skill `supabase-custom-claims-rbac` v1.25), este agent inclui no bootstrap:
|
|
303
|
+
|
|
304
|
+
### 1. Dependency `jwt-decode`
|
|
305
|
+
|
|
306
|
+
Adicionar ao `package.json`:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
npm install jwt-decode
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 2. `utils/supabase/client.ts` — listener com decoder
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
import { createBrowserClient } from '@supabase/ssr'
|
|
316
|
+
import { jwtDecode } from 'jwt-decode'
|
|
317
|
+
|
|
318
|
+
export function createClient() {
|
|
319
|
+
const supabase = createBrowserClient(
|
|
320
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
321
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
// listener para decodificar custom claims após login/refresh
|
|
325
|
+
supabase.auth.onAuthStateChange(async (event, session) => {
|
|
326
|
+
if (session) {
|
|
327
|
+
const jwt = jwtDecode<{ user_role?: string }>(session.access_token)
|
|
328
|
+
// userRole disponível para UI conditional rendering
|
|
329
|
+
// (use context provider para propagar — não documentado aqui)
|
|
330
|
+
console.log('User role from JWT:', jwt.user_role)
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
return supabase
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### 3. `utils/supabase/server.ts` — server-side decode
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
import { jwtDecode } from 'jwt-decode'
|
|
342
|
+
|
|
343
|
+
export async function getUserRole() {
|
|
344
|
+
const supabase = await createClient()
|
|
345
|
+
const { data: { session } } = await supabase.auth.getSession()
|
|
346
|
+
if (!session) return null
|
|
347
|
+
|
|
348
|
+
const jwt = jwtDecode<{ user_role?: string }>(session.access_token)
|
|
349
|
+
return jwt.user_role ?? null
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 4. Handoff cooperativo para `supabase-rbac-implementer`
|
|
354
|
+
|
|
355
|
+
Quando caller sinaliza `enable_rbac: true` ou detecta tabela `user_roles` no projeto, faça handoff:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
Task(subagent_type="supabase-rbac-implementer", prompt=f"""
|
|
359
|
+
<upstream_intent>
|
|
360
|
+
Source agent: supabase-auth-bootstrapper
|
|
361
|
+
Original goal: bootstrap Next.js v16 + Supabase Auth com RBAC via Custom Access Token Auth Hook
|
|
362
|
+
Constraints: projeto novo Next.js v16; jwt-decode adicionado ao package.json; listener jwt decode adicionado em client.ts; helper getUserRole() adicionado em server.ts
|
|
363
|
+
</upstream_intent>
|
|
364
|
+
|
|
365
|
+
<roles>{caller_provided_roles or default ['admin', 'user']}</roles>
|
|
366
|
+
<permissions_matrix>{caller_provided or default}</permissions_matrix>
|
|
367
|
+
<multi_tenant>{caller_provided}</multi_tenant>
|
|
368
|
+
<user_facing_caller>true</user_facing_caller>
|
|
369
|
+
""")
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Caveats embutidos no bootstrap:**
|
|
373
|
+
|
|
374
|
+
- ⚠ JWT freshness: mudanças em user_roles refletem após refresh (TTL 1h). Para revogação imediata, usar `auth.admin.signOut(userId)` no server-side com service_role.
|
|
375
|
+
- ⚠ Auth hook deve ser habilitado no Dashboard (Authentication > Hooks Beta) ou config.toml local — esse setup não é automatizado pelo bootstrap (DDL do hook é feito pelo `supabase-rbac-implementer` mas o enable depende de UI/config).
|
|
376
|
+
- ⚠ `jwt-decode` é apenas decode (NÃO valida assinatura) — para validação server-side, use `@supabase/ssr` `getUser()` que valida.
|
|
377
|
+
|
|
300
378
|
## Ver também
|
|
301
379
|
|
|
302
380
|
- [supabase-auth-ssr](../skills/supabase-auth-ssr/SKILL.md) — base de conhecimento canônica
|
|
303
381
|
- [supabase-rls-policies](../skills/supabase-rls-policies/SKILL.md) — RLS aplicado quando user autenticado consulta tabelas
|
|
382
|
+
- [supabase-custom-claims-rbac](../skills/supabase-custom-claims-rbac/SKILL.md) (v1.25) — Custom Access Token Auth Hook + jwt-decode
|
|
383
|
+
- [supabase-rbac-implementer](./supabase-rbac-implementer.md) (v1.25) — canonical handoff target para RBAC setup
|
|
304
384
|
- [structured-events](../skills/structured-events/SKILL.md) — campos canônicos para auth events
|
|
305
385
|
- [event-based-slos](../skills/event-based-slos/SKILL.md) *(Phase 32)* — SLO de "successful login %"
|