aios-core 2.1.6 → 2.2.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 (367) hide show
  1. package/.aios-core/core/README.md +229 -229
  2. package/.aios-core/core/data/agent-config-requirements.yaml +368 -368
  3. package/.aios-core/core/data/aios-kb.md +923 -923
  4. package/.aios-core/core/data/workflow-patterns.yaml +267 -267
  5. package/.aios-core/core/docs/SHARD-TRANSLATION-GUIDE.md +335 -335
  6. package/.aios-core/core/docs/component-creation-guide.md +457 -457
  7. package/.aios-core/core/docs/session-update-pattern.md +307 -307
  8. package/.aios-core/core/docs/template-syntax.md +266 -266
  9. package/.aios-core/core/docs/troubleshooting-guide.md +624 -624
  10. package/.aios-core/core/elicitation/elicitation-engine.js +1 -1
  11. package/.aios-core/core/index.esm.js +42 -42
  12. package/.aios-core/core/index.js +1 -1
  13. package/.aios-core/core/migration/migration-config.yaml +83 -83
  14. package/.aios-core/core/migration/module-mapping.yaml +89 -89
  15. package/.aios-core/core/quality-gates/layer2-pr-automation.js +1 -1
  16. package/.aios-core/core/quality-gates/quality-gate-config.yaml +86 -86
  17. package/.aios-core/core/registry/README.md +179 -179
  18. package/.aios-core/core/utils/security-utils.js +1 -1
  19. package/.aios-core/core-config.yaml +391 -382
  20. package/.aios-core/data/agent-config-requirements.yaml +368 -368
  21. package/.aios-core/data/aios-kb.md +923 -923
  22. package/.aios-core/data/technical-preferences.md +3 -3
  23. package/.aios-core/data/workflow-patterns.yaml +267 -267
  24. package/.aios-core/development/README.md +142 -142
  25. package/.aios-core/development/agent-teams/team-all.yaml +15 -15
  26. package/.aios-core/development/agent-teams/team-fullstack.yaml +18 -18
  27. package/.aios-core/development/agent-teams/team-ide-minimal.yaml +10 -10
  28. package/.aios-core/development/agent-teams/team-no-ui.yaml +13 -13
  29. package/.aios-core/development/agent-teams/team-qa-focused.yaml +155 -155
  30. package/.aios-core/development/agents/aios-master.md +339 -339
  31. package/.aios-core/development/agents/analyst.md +195 -195
  32. package/.aios-core/development/agents/architect.md +359 -359
  33. package/.aios-core/development/agents/data-engineer.md +468 -468
  34. package/.aios-core/development/agents/dev.md +390 -390
  35. package/.aios-core/development/agents/devops.md +398 -398
  36. package/.aios-core/development/agents/pm.md +198 -198
  37. package/.aios-core/development/agents/po.md +256 -256
  38. package/.aios-core/development/agents/qa.md +312 -312
  39. package/.aios-core/development/agents/sm.md +220 -220
  40. package/.aios-core/development/agents/ux-design-expert.md +451 -451
  41. package/.aios-core/development/scripts/greeting-config-cli.js +85 -85
  42. package/.aios-core/development/tasks/add-mcp.md +319 -319
  43. package/.aios-core/development/tasks/advanced-elicitation.md +318 -318
  44. package/.aios-core/development/tasks/analyst-facilitate-brainstorming.md +341 -341
  45. package/.aios-core/development/tasks/analyze-framework.md +696 -696
  46. package/.aios-core/development/tasks/analyze-performance.md +637 -637
  47. package/.aios-core/development/tasks/apply-qa-fixes.md +340 -340
  48. package/.aios-core/development/tasks/architect-analyze-impact.md +826 -826
  49. package/.aios-core/development/tasks/audit-codebase.md +429 -429
  50. package/.aios-core/development/tasks/audit-tailwind-config.md +270 -270
  51. package/.aios-core/development/tasks/audit-utilities.md +358 -358
  52. package/.aios-core/development/tasks/bootstrap-shadcn-library.md +286 -286
  53. package/.aios-core/development/tasks/brownfield-create-epic.md +485 -485
  54. package/.aios-core/development/tasks/brownfield-create-story.md +356 -356
  55. package/.aios-core/development/tasks/build-component.md +478 -478
  56. package/.aios-core/development/tasks/calculate-roi.md +455 -455
  57. package/.aios-core/development/tasks/ci-cd-configuration.md +764 -764
  58. package/.aios-core/development/tasks/cleanup-utilities.md +670 -670
  59. package/.aios-core/development/tasks/collaborative-edit.md +1108 -1108
  60. package/.aios-core/development/tasks/compose-molecule.md +284 -284
  61. package/.aios-core/development/tasks/consolidate-patterns.md +414 -414
  62. package/.aios-core/development/tasks/correct-course.md +279 -279
  63. package/.aios-core/development/tasks/create-agent.md +321 -321
  64. package/.aios-core/development/tasks/create-brownfield-story.md +726 -726
  65. package/.aios-core/development/tasks/create-deep-research-prompt.md +498 -498
  66. package/.aios-core/development/tasks/create-doc.md +316 -316
  67. package/.aios-core/development/tasks/create-next-story.md +774 -774
  68. package/.aios-core/development/tasks/create-suite.md +283 -283
  69. package/.aios-core/development/tasks/create-task.md +371 -371
  70. package/.aios-core/development/tasks/create-workflow.md +370 -370
  71. package/.aios-core/development/tasks/db-analyze-hotpaths.md +572 -572
  72. package/.aios-core/development/tasks/db-apply-migration.md +381 -381
  73. package/.aios-core/development/tasks/db-bootstrap.md +642 -642
  74. package/.aios-core/development/tasks/db-domain-modeling.md +693 -693
  75. package/.aios-core/development/tasks/db-dry-run.md +293 -293
  76. package/.aios-core/development/tasks/db-env-check.md +260 -260
  77. package/.aios-core/development/tasks/db-expansion-pack-integration.md +663 -663
  78. package/.aios-core/development/tasks/db-explain.md +631 -631
  79. package/.aios-core/development/tasks/db-impersonate.md +495 -495
  80. package/.aios-core/development/tasks/db-load-csv.md +593 -593
  81. package/.aios-core/development/tasks/db-policy-apply.md +653 -653
  82. package/.aios-core/development/tasks/db-rls-audit.md +411 -411
  83. package/.aios-core/development/tasks/db-rollback.md +739 -739
  84. package/.aios-core/development/tasks/db-run-sql.md +613 -613
  85. package/.aios-core/development/tasks/db-schema-audit.md +1011 -1011
  86. package/.aios-core/development/tasks/db-seed.md +390 -390
  87. package/.aios-core/development/tasks/db-smoke-test.md +351 -351
  88. package/.aios-core/development/tasks/db-snapshot.md +569 -569
  89. package/.aios-core/development/tasks/db-supabase-setup.md +712 -712
  90. package/.aios-core/development/tasks/db-verify-order.md +515 -515
  91. package/.aios-core/development/tasks/deprecate-component.md +956 -956
  92. package/.aios-core/development/tasks/dev-apply-qa-fixes.md +318 -318
  93. package/.aios-core/development/tasks/dev-backlog-debt.md +469 -469
  94. package/.aios-core/development/tasks/dev-develop-story.md +846 -846
  95. package/.aios-core/development/tasks/dev-improve-code-quality.md +872 -872
  96. package/.aios-core/development/tasks/dev-optimize-performance.md +1033 -1033
  97. package/.aios-core/development/tasks/dev-suggest-refactoring.md +870 -870
  98. package/.aios-core/development/tasks/dev-validate-next-story.md +348 -348
  99. package/.aios-core/development/tasks/document-project.md +552 -552
  100. package/.aios-core/development/tasks/environment-bootstrap.md +1311 -1311
  101. package/.aios-core/development/tasks/execute-checklist.md +301 -301
  102. package/.aios-core/development/tasks/export-design-tokens-dtcg.md +274 -274
  103. package/.aios-core/development/tasks/extend-pattern.md +269 -269
  104. package/.aios-core/development/tasks/extract-tokens.md +467 -467
  105. package/.aios-core/development/tasks/facilitate-brainstorming-session.md +518 -518
  106. package/.aios-core/development/tasks/generate-ai-frontend-prompt.md +260 -260
  107. package/.aios-core/development/tasks/generate-documentation.md +284 -284
  108. package/.aios-core/development/tasks/generate-migration-strategy.md +522 -522
  109. package/.aios-core/development/tasks/generate-shock-report.md +501 -501
  110. package/.aios-core/development/tasks/github-devops-github-pr-automation.md +427 -427
  111. package/.aios-core/development/tasks/github-devops-pre-push-quality-gate.md +733 -733
  112. package/.aios-core/development/tasks/github-devops-repository-cleanup.md +374 -374
  113. package/.aios-core/development/tasks/github-devops-version-management.md +483 -483
  114. package/.aios-core/development/tasks/improve-self.md +822 -822
  115. package/.aios-core/development/tasks/index-docs.md +387 -387
  116. package/.aios-core/development/tasks/init-project-status.md +506 -506
  117. package/.aios-core/development/tasks/integrate-expansion-pack.md +314 -314
  118. package/.aios-core/development/tasks/kb-mode-interaction.md +283 -283
  119. package/.aios-core/development/tasks/learn-patterns.md +900 -900
  120. package/.aios-core/development/tasks/mcp-workflow.md +437 -437
  121. package/.aios-core/development/tasks/modify-agent.md +381 -381
  122. package/.aios-core/development/tasks/modify-task.md +424 -424
  123. package/.aios-core/development/tasks/modify-workflow.md +465 -465
  124. package/.aios-core/development/tasks/po-backlog-add.md +370 -370
  125. package/.aios-core/development/tasks/po-manage-story-backlog.md +523 -523
  126. package/.aios-core/development/tasks/po-pull-story-from-clickup.md +540 -540
  127. package/.aios-core/development/tasks/po-pull-story.md +316 -316
  128. package/.aios-core/development/tasks/po-stories-index.md +351 -351
  129. package/.aios-core/development/tasks/po-sync-story-to-clickup.md +457 -457
  130. package/.aios-core/development/tasks/po-sync-story.md +303 -303
  131. package/.aios-core/development/tasks/pr-automation.md +701 -701
  132. package/.aios-core/development/tasks/propose-modification.md +842 -842
  133. package/.aios-core/development/tasks/qa-backlog-add-followup.md +425 -425
  134. package/.aios-core/development/tasks/qa-gate.md +373 -373
  135. package/.aios-core/development/tasks/qa-generate-tests.md +1174 -1174
  136. package/.aios-core/development/tasks/qa-nfr-assess.md +557 -557
  137. package/.aios-core/development/tasks/qa-review-proposal.md +1157 -1157
  138. package/.aios-core/development/tasks/qa-review-story.md +682 -682
  139. package/.aios-core/development/tasks/qa-risk-profile.md +566 -566
  140. package/.aios-core/development/tasks/qa-run-tests.md +277 -277
  141. package/.aios-core/development/tasks/qa-test-design.md +387 -387
  142. package/.aios-core/development/tasks/qa-trace-requirements.md +476 -476
  143. package/.aios-core/development/tasks/release-management.md +723 -723
  144. package/.aios-core/development/tasks/security-audit.md +554 -554
  145. package/.aios-core/development/tasks/security-scan.md +790 -790
  146. package/.aios-core/development/tasks/setup-database.md +741 -741
  147. package/.aios-core/development/tasks/setup-design-system.md +462 -462
  148. package/.aios-core/development/tasks/setup-github.md +874 -874
  149. package/.aios-core/development/tasks/setup-llm-routing.md +1 -1
  150. package/.aios-core/development/tasks/setup-mcp-docker.md +584 -584
  151. package/.aios-core/development/tasks/setup-project-docs.md +1 -1
  152. package/.aios-core/development/tasks/shard-doc.md +537 -537
  153. package/.aios-core/development/tasks/sm-create-next-story.md +480 -480
  154. package/.aios-core/development/tasks/sync-documentation.md +864 -864
  155. package/.aios-core/development/tasks/tailwind-upgrade.md +294 -294
  156. package/.aios-core/development/tasks/test-as-user.md +621 -621
  157. package/.aios-core/development/tasks/test-validation-task.md +171 -171
  158. package/.aios-core/development/tasks/undo-last.md +346 -346
  159. package/.aios-core/development/tasks/update-manifest.md +409 -409
  160. package/.aios-core/development/tasks/ux-create-wireframe.md +617 -617
  161. package/.aios-core/development/tasks/ux-ds-scan-artifact.md +672 -672
  162. package/.aios-core/development/tasks/ux-user-research.md +559 -559
  163. package/.aios-core/development/tasks/validate-next-story.md +422 -422
  164. package/.aios-core/development/workflows/README.md +83 -83
  165. package/.aios-core/development/workflows/brownfield-fullstack.yaml +297 -297
  166. package/.aios-core/development/workflows/brownfield-service.yaml +187 -187
  167. package/.aios-core/development/workflows/brownfield-ui.yaml +197 -197
  168. package/.aios-core/development/workflows/greenfield-fullstack.yaml +333 -333
  169. package/.aios-core/development/workflows/greenfield-service.yaml +206 -206
  170. package/.aios-core/development/workflows/greenfield-ui.yaml +235 -235
  171. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +335 -335
  172. package/.aios-core/docs/component-creation-guide.md +457 -457
  173. package/.aios-core/docs/session-update-pattern.md +307 -307
  174. package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +572 -572
  175. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +185 -185
  176. package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +354 -354
  177. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +1963 -1963
  178. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +821 -821
  179. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +1190 -1190
  180. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +439 -439
  181. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +1339 -1339
  182. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +5398 -5398
  183. package/.aios-core/docs/standards/EXECUTOR-DECISION-TREE.md +697 -697
  184. package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +511 -511
  185. package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +556 -556
  186. package/.aios-core/docs/standards/STANDARDS-INDEX.md +210 -210
  187. package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +550 -550
  188. package/.aios-core/docs/standards/TASK-FORMAT-SPECIFICATION-V1.md +1414 -1414
  189. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +523 -523
  190. package/.aios-core/docs/template-syntax.md +266 -266
  191. package/.aios-core/docs/troubleshooting-guide.md +624 -624
  192. package/.aios-core/index.esm.js +15 -15
  193. package/.aios-core/index.js +1 -1
  194. package/.aios-core/infrastructure/README.md +126 -126
  195. package/.aios-core/infrastructure/integrations/pm-adapters/README.md +59 -59
  196. package/.aios-core/infrastructure/scripts/approval-workflow.js +1 -1
  197. package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
  198. package/.aios-core/infrastructure/scripts/component-generator.js +3 -3
  199. package/.aios-core/infrastructure/scripts/component-metadata.js +1 -1
  200. package/.aios-core/infrastructure/scripts/component-search.js +1 -1
  201. package/.aios-core/infrastructure/scripts/coverage-analyzer.js +1 -1
  202. package/.aios-core/infrastructure/scripts/dependency-analyzer.js +1 -1
  203. package/.aios-core/infrastructure/scripts/dependency-impact-analyzer.js +1 -1
  204. package/.aios-core/infrastructure/scripts/framework-analyzer.js +1 -1
  205. package/.aios-core/infrastructure/scripts/improvement-engine.js +1 -1
  206. package/.aios-core/infrastructure/scripts/llm-routing/install-llm-routing.js +26 -13
  207. package/.aios-core/infrastructure/scripts/llm-routing/templates/claude-free-tracked.cmd +127 -0
  208. package/.aios-core/infrastructure/scripts/llm-routing/templates/claude-free-tracked.sh +108 -0
  209. package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-proxy.cmd +71 -0
  210. package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-proxy.sh +65 -0
  211. package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-usage.cmd +51 -0
  212. package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-usage.sh +16 -0
  213. package/.aios-core/infrastructure/scripts/llm-routing/usage-tracker/index.js +549 -0
  214. package/.aios-core/infrastructure/scripts/modification-risk-assessment.js +1 -1
  215. package/.aios-core/infrastructure/scripts/performance-analyzer.js +1 -1
  216. package/.aios-core/infrastructure/scripts/pm-adapter.js +134 -134
  217. package/.aios-core/infrastructure/scripts/repository-detector.js +3 -3
  218. package/.aios-core/infrastructure/scripts/template-engine.js +1 -1
  219. package/.aios-core/infrastructure/scripts/template-validator.js +1 -1
  220. package/.aios-core/infrastructure/scripts/test-generator.js +1 -1
  221. package/.aios-core/infrastructure/scripts/test-quality-assessment.js +1 -1
  222. package/.aios-core/infrastructure/scripts/transaction-manager.js +1 -1
  223. package/.aios-core/infrastructure/scripts/usage-analytics.js +1 -1
  224. package/.aios-core/infrastructure/scripts/visual-impact-generator.js +2 -2
  225. package/.aios-core/infrastructure/templates/github-workflows/README.md +109 -109
  226. package/.aios-core/infrastructure/tests/regression-suite-v2.md +621 -621
  227. package/.aios-core/infrastructure/tools/README.md +222 -222
  228. package/.aios-core/infrastructure/tools/cli/github-cli.yaml +200 -200
  229. package/.aios-core/infrastructure/tools/cli/railway-cli.yaml +260 -260
  230. package/.aios-core/infrastructure/tools/cli/supabase-cli.yaml +224 -224
  231. package/.aios-core/infrastructure/tools/local/ffmpeg.yaml +261 -261
  232. package/.aios-core/infrastructure/tools/mcp/21st-dev-magic.yaml +127 -127
  233. package/.aios-core/infrastructure/tools/mcp/browser.yaml +103 -103
  234. package/.aios-core/infrastructure/tools/mcp/clickup.yaml +534 -534
  235. package/.aios-core/infrastructure/tools/mcp/context7.yaml +78 -78
  236. package/.aios-core/infrastructure/tools/mcp/desktop-commander.yaml +180 -180
  237. package/.aios-core/infrastructure/tools/mcp/exa.yaml +103 -103
  238. package/.aios-core/infrastructure/tools/mcp/google-workspace.yaml +930 -930
  239. package/.aios-core/infrastructure/tools/mcp/n8n.yaml +551 -551
  240. package/.aios-core/infrastructure/tools/mcp/supabase.yaml +808 -808
  241. package/.aios-core/install-manifest.yaml +347 -347
  242. package/.aios-core/product/README.md +56 -56
  243. package/.aios-core/product/checklists/accessibility-wcag-checklist.md +80 -0
  244. package/.aios-core/product/checklists/architect-checklist.md +443 -443
  245. package/.aios-core/product/checklists/change-checklist.md +182 -182
  246. package/.aios-core/product/checklists/component-quality-checklist.md +74 -0
  247. package/.aios-core/product/checklists/database-design-checklist.md +119 -119
  248. package/.aios-core/product/checklists/dba-predeploy-checklist.md +97 -97
  249. package/.aios-core/product/checklists/dba-rollback-checklist.md +99 -99
  250. package/.aios-core/product/checklists/migration-readiness-checklist.md +75 -0
  251. package/.aios-core/product/checklists/pattern-audit-checklist.md +88 -0
  252. package/.aios-core/product/checklists/pm-checklist.md +375 -375
  253. package/.aios-core/product/checklists/po-master-checklist.md +441 -441
  254. package/.aios-core/product/checklists/pre-push-checklist.md +108 -108
  255. package/.aios-core/product/checklists/release-checklist.md +122 -122
  256. package/.aios-core/product/checklists/story-dod-checklist.md +101 -101
  257. package/.aios-core/product/checklists/story-draft-checklist.md +215 -215
  258. package/.aios-core/product/data/atomic-design-principles.md +108 -0
  259. package/.aios-core/product/data/brainstorming-techniques.md +36 -36
  260. package/.aios-core/product/data/consolidation-algorithms.md +142 -0
  261. package/.aios-core/product/data/database-best-practices.md +182 -0
  262. package/.aios-core/product/data/design-token-best-practices.md +107 -0
  263. package/.aios-core/product/data/elicitation-methods.md +134 -134
  264. package/.aios-core/product/data/integration-patterns.md +207 -0
  265. package/.aios-core/product/data/migration-safety-guide.md +329 -0
  266. package/.aios-core/product/data/mode-selection-best-practices.md +471 -471
  267. package/.aios-core/product/data/postgres-tuning-guide.md +300 -0
  268. package/.aios-core/product/data/rls-security-patterns.md +333 -0
  269. package/.aios-core/product/data/roi-calculation-guide.md +142 -0
  270. package/.aios-core/product/data/supabase-patterns.md +330 -0
  271. package/.aios-core/product/data/test-levels-framework.md +148 -148
  272. package/.aios-core/product/data/test-priorities-matrix.md +174 -174
  273. package/.aios-core/product/data/wcag-compliance-guide.md +267 -0
  274. package/.aios-core/product/templates/1mcp-config.yaml +225 -225
  275. package/.aios-core/product/templates/activation-instructions-inline-greeting.yaml +63 -63
  276. package/.aios-core/product/templates/activation-instructions-template.md +258 -258
  277. package/.aios-core/product/templates/agent-template.yaml +120 -120
  278. package/.aios-core/product/templates/architecture-tmpl.yaml +650 -650
  279. package/.aios-core/product/templates/brainstorming-output-tmpl.yaml +155 -155
  280. package/.aios-core/product/templates/brownfield-architecture-tmpl.yaml +475 -475
  281. package/.aios-core/product/templates/brownfield-prd-tmpl.yaml +279 -279
  282. package/.aios-core/product/templates/changelog-template.md +134 -134
  283. package/.aios-core/product/templates/command-rationalization-matrix.md +152 -152
  284. package/.aios-core/product/templates/competitor-analysis-tmpl.yaml +292 -292
  285. package/.aios-core/product/templates/design-story-tmpl.yaml +587 -587
  286. package/.aios-core/product/templates/ds-artifact-analysis.md +70 -70
  287. package/.aios-core/product/templates/front-end-architecture-tmpl.yaml +205 -205
  288. package/.aios-core/product/templates/front-end-spec-tmpl.yaml +348 -348
  289. package/.aios-core/product/templates/fullstack-architecture-tmpl.yaml +804 -804
  290. package/.aios-core/product/templates/github-pr-template.md +67 -67
  291. package/.aios-core/product/templates/gordon-mcp.yaml +140 -140
  292. package/.aios-core/product/templates/ide-rules/antigravity-rules.md +115 -115
  293. package/.aios-core/product/templates/ide-rules/claude-rules.md +221 -221
  294. package/.aios-core/product/templates/ide-rules/cline-rules.md +84 -84
  295. package/.aios-core/product/templates/ide-rules/copilot-rules.md +92 -92
  296. package/.aios-core/product/templates/ide-rules/cursor-rules.md +115 -115
  297. package/.aios-core/product/templates/ide-rules/gemini-rules.md +85 -85
  298. package/.aios-core/product/templates/ide-rules/roo-rules.md +86 -86
  299. package/.aios-core/product/templates/ide-rules/trae-rules.md +104 -104
  300. package/.aios-core/product/templates/ide-rules/windsurf-rules.md +80 -80
  301. package/.aios-core/product/templates/index-strategy-tmpl.yaml +53 -53
  302. package/.aios-core/product/templates/market-research-tmpl.yaml +251 -251
  303. package/.aios-core/product/templates/mcp-workflow.js +271 -271
  304. package/.aios-core/product/templates/migration-plan-tmpl.yaml +1022 -1022
  305. package/.aios-core/product/templates/migration-strategy-tmpl.md +524 -524
  306. package/.aios-core/product/templates/personalized-agent-template.md +258 -258
  307. package/.aios-core/product/templates/personalized-checklist-template.md +340 -340
  308. package/.aios-core/product/templates/personalized-task-template-v2.md +905 -905
  309. package/.aios-core/product/templates/personalized-task-template.md +344 -344
  310. package/.aios-core/product/templates/personalized-template-file.yaml +322 -322
  311. package/.aios-core/product/templates/personalized-workflow-template.yaml +460 -460
  312. package/.aios-core/product/templates/prd-tmpl.yaml +201 -201
  313. package/.aios-core/product/templates/project-brief-tmpl.yaml +220 -220
  314. package/.aios-core/product/templates/qa-gate-tmpl.yaml +240 -240
  315. package/.aios-core/product/templates/rls-policies-tmpl.yaml +1203 -1203
  316. package/.aios-core/product/templates/schema-design-tmpl.yaml +428 -428
  317. package/.aios-core/product/templates/state-persistence-tmpl.yaml +219 -219
  318. package/.aios-core/product/templates/story-tmpl.yaml +331 -331
  319. package/.aios-core/product/templates/task-execution-report.md +495 -495
  320. package/.aios-core/product/templates/task-template.md +122 -122
  321. package/.aios-core/product/templates/token-exports-tailwind-tmpl.js +395 -395
  322. package/.aios-core/product/templates/tokens-schema-tmpl.yaml +305 -305
  323. package/.aios-core/product/templates/workflow-template.yaml +133 -133
  324. package/.aios-core/scripts/README.md +354 -354
  325. package/.aios-core/scripts/aios-doc-template.md +325 -325
  326. package/.aios-core/scripts/elicitation-engine.js +1 -1
  327. package/.aios-core/scripts/test-template-system.js +1 -1
  328. package/.aios-core/scripts/workflow-management.md +69 -69
  329. package/.aios-core/user-guide.md +1413 -1413
  330. package/.aios-core/working-in-the-brownfield.md +361 -361
  331. package/LICENSE +1 -1
  332. package/README.md +702 -703
  333. package/bin/aios-init-old.js +3 -3
  334. package/bin/aios-init-v4.js +1 -1
  335. package/bin/aios-init.backup-v1.1.4.js +1 -1
  336. package/bin/aios-init.js +3 -3
  337. package/bin/aios.js +279 -279
  338. package/bin/utils/install-errors.js +339 -339
  339. package/bin/utils/install-transaction.js +445 -445
  340. package/index.d.ts +18 -18
  341. package/index.esm.js +20 -20
  342. package/index.js +6 -6
  343. package/package.json +8 -10
  344. package/packages/installer/src/config/templates/env-template.js +27 -4
  345. package/packages/installer/src/detection/detect-project-type.js +81 -81
  346. package/packages/installer/tests/integration/wizard-detection.test.js +8 -6
  347. package/packages/installer/tests/unit/env-template.test.js +8 -8
  348. package/src/config/ide-configs.js +1 -1
  349. package/src/wizard/feedback.js +2 -2
  350. package/src/wizard/index.js +1 -1
  351. package/src/wizard/validation/report-generator.js +1 -1
  352. package/src/wizard/validation/troubleshooting-system.js +13 -13
  353. package/.aios-core/infrastructure/scripts/_archived/final-todo-count.js +0 -122
  354. package/.aios-core/infrastructure/scripts/_archived/fix-yaml-formatting.js +0 -89
  355. package/.aios-core/infrastructure/scripts/_archived/migration-generator.js +0 -780
  356. package/.aios-core/infrastructure/scripts/_archived/migration-path-generator.js +0 -950
  357. package/.aios-core/infrastructure/scripts/_archived/phase2-entrada-saida-errors.js +0 -425
  358. package/.aios-core/infrastructure/scripts/_archived/phase2-spot-check.js +0 -132
  359. package/.aios-core/infrastructure/scripts/_archived/phase3-tools-scripts-validation.js +0 -381
  360. package/.aios-core/infrastructure/scripts/_archived/phase4-metadata-performance.js +0 -203
  361. package/.aios-core/infrastructure/scripts/_archived/test-yaml-parsing.js +0 -24
  362. package/.aios-core/infrastructure/scripts/_archived/verify-yaml-fix.js +0 -51
  363. package/.aios-core/tasks/find-component.md.legacy +0 -391
  364. package/.aios-core/tasks/generate-commit-message.md.legacy +0 -426
  365. package/.aios-core/tasks/generate-migration.md.legacy +0 -382
  366. package/.aios-core/tasks/rollback-modification.md.legacy +0 -307
  367. package/.aios-core/tasks/update-tests.md.legacy +0 -283
@@ -1,1203 +1,1203 @@
1
- ---
2
- template_name: "Supabase RLS Policies"
3
- template_version: "1.0.0"
4
- output_format: "markdown"
5
- destination: "rls-policies.md"
6
- description: "Row Level Security policies for Supabase tables"
7
- ---
8
-
9
- sections:
10
- - id: overview
11
- title: "RLS Overview"
12
- instruction: |
13
- Document the Row Level Security strategy:
14
-
15
- ## Purpose
16
- Explain the overall security model and why RLS is being used.
17
-
18
- ## Authentication Context
19
- - How users are authenticated (Supabase Auth, JWT, etc)
20
- - Available auth context variables:
21
- - `auth.uid()` - Current user ID
22
- - `auth.jwt()` - JWT claims
23
- - `auth.email()` - User email
24
- - Custom claims in JWT
25
-
26
- ## Security Model
27
- - Role-based access control (RBAC)
28
- - Multi-tenancy approach (if applicable)
29
- - Public vs authenticated vs specific role access
30
-
31
- ## Performance Considerations
32
- - RLS policy performance impact
33
- - Indexing strategy to support policies
34
- - Caching considerations
35
-
36
- ## Testing Strategy
37
- - How policies will be tested
38
- - Test users and scenarios
39
- elicit: true
40
-
41
- - id: policy-patterns
42
- title: "Common Policy Patterns"
43
- instruction: |
44
- Document reusable policy patterns used across tables:
45
-
46
- ## Pattern 1: Owner-Only Access
47
- ```sql
48
- -- Users can only access their own records
49
- (auth.uid() = user_id)
50
- ```
51
-
52
- ## Pattern 2: Tenant-Based Access
53
- ```sql
54
- -- Users can access records in their organization
55
- (auth.uid() IN (
56
- SELECT user_id FROM org_members
57
- WHERE org_id = table.org_id
58
- ))
59
- ```
60
-
61
- ## Pattern 3: Role-Based Access
62
- ```sql
63
- -- Only admins can access
64
- ((auth.jwt() ->> 'role')::text = 'admin')
65
- ```
66
-
67
- ## Pattern 4: Public Read, Authenticated Write
68
- ```sql
69
- -- SELECT: true (public read)
70
- -- INSERT/UPDATE/DELETE: auth.uid() IS NOT NULL
71
- ```
72
-
73
- ## Pattern 5: Hierarchical Permissions
74
- ```sql
75
- -- Access based on organizational hierarchy
76
- ```
77
-
78
- Document any other patterns specific to your application.
79
- elicit: true
80
-
81
- - id: table-policies
82
- title: "Table-by-Table Policies"
83
- instruction: |
84
- For each table requiring RLS, document comprehensive policies:
85
-
86
- # Table: `table_name`
87
-
88
- ## Enable RLS
89
- ```sql
90
- ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
91
- ```
92
-
93
- ## SELECT Policies
94
-
95
- ### Policy: `policy_name_select`
96
- **Purpose**: Describe who can read what
97
-
98
- **Policy Expression**:
99
- ```sql
100
- CREATE POLICY "policy_name_select"
101
- ON table_name
102
- FOR SELECT
103
- TO authenticated -- or public, anon, etc
104
- USING (
105
- -- Policy expression
106
- auth.uid() = user_id
107
- );
108
- ```
109
-
110
- **Rationale**: Explain the business rule
111
-
112
- **Performance**: Any indexes needed to support this policy
113
-
114
- ## INSERT Policies
115
-
116
- ### Policy: `policy_name_insert`
117
- **Purpose**: Describe who can create records
118
-
119
- **Policy Expression**:
120
- ```sql
121
- CREATE POLICY "policy_name_insert"
122
- ON table_name
123
- FOR INSERT
124
- TO authenticated
125
- WITH CHECK (
126
- -- Policy expression
127
- auth.uid() = user_id
128
- );
129
- ```
130
-
131
- **Validation**: What validations this ensures
132
-
133
- ## UPDATE Policies
134
-
135
- ### Policy: `policy_name_update`
136
- **Purpose**: Describe who can modify records
137
-
138
- **Policy Expression**:
139
- ```sql
140
- CREATE POLICY "policy_name_update"
141
- ON table_name
142
- FOR UPDATE
143
- TO authenticated
144
- USING (
145
- -- Who can see the record to update it
146
- auth.uid() = user_id
147
- )
148
- WITH CHECK (
149
- -- What values they can set
150
- auth.uid() = user_id
151
- );
152
- ```
153
-
154
- **Notes**: USING checks old values, WITH CHECK validates new values
155
-
156
- ## DELETE Policies
157
-
158
- ### Policy: `policy_name_delete`
159
- **Purpose**: Describe who can delete records
160
-
161
- **Policy Expression**:
162
- ```sql
163
- CREATE POLICY "policy_name_delete"
164
- ON table_name
165
- FOR DELETE
166
- TO authenticated
167
- USING (
168
- -- Who can delete
169
- auth.uid() = user_id OR
170
- (auth.jwt() ->> 'role')::text = 'admin'
171
- );
172
- ```
173
-
174
- ## ALL Policies (if using combined policy)
175
-
176
- Sometimes a single policy for all operations is clearer:
177
-
178
- ```sql
179
- CREATE POLICY "policy_name_all"
180
- ON table_name
181
- FOR ALL
182
- TO authenticated
183
- USING (auth.uid() = user_id)
184
- WITH CHECK (auth.uid() = user_id);
185
- ```
186
-
187
- ---
188
-
189
- Repeat this section for each table.
190
- elicit: true
191
-
192
- - id: public-tables
193
- title: "Public Tables"
194
- instruction: |
195
- Document tables with public read access:
196
-
197
- # Table: `public_table_name`
198
-
199
- ## Public Read Policy
200
- ```sql
201
- ALTER TABLE public_table_name ENABLE ROW LEVEL SECURITY;
202
-
203
- CREATE POLICY "public_read_policy"
204
- ON public_table_name
205
- FOR SELECT
206
- TO anon, authenticated
207
- USING (true);
208
- ```
209
-
210
- ## Restricted Write Policy
211
- ```sql
212
- CREATE POLICY "authenticated_write_policy"
213
- ON public_table_name
214
- FOR INSERT
215
- TO authenticated
216
- WITH CHECK (auth.uid() IS NOT NULL);
217
- ```
218
-
219
- **Rationale**: Why this table is public
220
-
221
- **Security Considerations**: What data is safe to expose
222
- elicit: false
223
-
224
- - id: service-role
225
- title: "Service Role Bypass"
226
- instruction: |
227
- Document scenarios where service role (bypass RLS) is used:
228
-
229
- ## Service Role Usage
230
-
231
- ### Backend Operations
232
- Operations that need to bypass RLS:
233
- - Scheduled jobs (cron, edge functions with service key)
234
- - Admin operations
235
- - Data migration
236
- - Analytics aggregation
237
-
238
- ### Safety Measures
239
- - How service key is secured
240
- - Where service role operations are logged
241
- - Who has access to service key
242
-
243
- ### Alternatives
244
- When possible, prefer:
245
- - Security definer functions with RLS
246
- - Elevated permission policies
247
- - Temporary privilege escalation
248
- elicit: false
249
-
250
- - id: helper-functions
251
- title: "Security Helper Functions"
252
- instruction: |
253
- Document PostgreSQL functions that support RLS:
254
-
255
- ## Function: `check_user_permission`
256
- ```sql
257
- CREATE OR REPLACE FUNCTION check_user_permission(
258
- user_id uuid,
259
- resource_id uuid,
260
- permission_type text
261
- )
262
- RETURNS boolean
263
- LANGUAGE plpgsql
264
- SECURITY DEFINER
265
- AS $$
266
- BEGIN
267
- -- Permission checking logic
268
- RETURN EXISTS (
269
- SELECT 1 FROM permissions
270
- WHERE user_id = $1
271
- AND resource_id = $2
272
- AND permission = $3
273
- );
274
- END;
275
- $$;
276
- ```
277
-
278
- **Usage in Policies**:
279
- ```sql
280
- USING (check_user_permission(auth.uid(), id, 'read'))
281
- ```
282
-
283
- ## Function: `get_user_org_id`
284
- ```sql
285
- CREATE OR REPLACE FUNCTION get_user_org_id()
286
- RETURNS uuid
287
- LANGUAGE sql
288
- STABLE
289
- AS $$
290
- SELECT org_id FROM user_profiles
291
- WHERE user_id = auth.uid();
292
- $$;
293
- ```
294
-
295
- **Usage in Policies**:
296
- ```sql
297
- USING (org_id = get_user_org_id())
298
- ```
299
-
300
- Document all helper functions used in RLS policies.
301
- elicit: false
302
-
303
- - id: multi-tenancy
304
- title: "Multi-Tenancy Implementation"
305
- condition: "multi_tenant"
306
- instruction: |
307
- If implementing multi-tenancy with RLS:
308
-
309
- ## Tenant Isolation Strategy
310
-
311
- ### Tenant Identification
312
- - How tenants are identified (org_id, team_id, etc)
313
- - Where tenant ID is stored (JWT claim, database lookup)
314
-
315
- ### Tenant-Scoped Tables
316
-
317
- For each tenant-scoped table:
318
-
319
- ```sql
320
- -- Example: projects table
321
- CREATE POLICY "tenant_isolation_policy"
322
- ON projects
323
- FOR ALL
324
- TO authenticated
325
- USING (
326
- org_id = (auth.jwt() ->> 'org_id')::uuid
327
- )
328
- WITH CHECK (
329
- org_id = (auth.jwt() ->> 'org_id')::uuid
330
- );
331
- ```
332
-
333
- ### Cross-Tenant Scenarios
334
- - Shared resources across tenants
335
- - Super admin access
336
- - Tenant-to-tenant relationships
337
-
338
- ### Performance
339
- - Indexes on tenant_id columns
340
- - Query patterns that leverage tenant isolation
341
- elicit: true
342
-
343
- - id: storage-policies
344
- title: "Supabase Storage Policies"
345
- instruction: |
346
- RLS policies for Supabase Storage buckets (storage.objects table).
347
-
348
- ## Overview
349
-
350
- Supabase Storage uses RLS on the `storage.objects` system table to control file access.
351
- By default, Storage requires explicit RLS policies - no uploads allowed without policies.
352
-
353
- ## Pattern 1: User-Specific Uploads
354
-
355
- Users can only upload files to their own folder:
356
-
357
- ```sql
358
- CREATE POLICY "Users upload own avatars"
359
- ON storage.objects
360
- FOR INSERT
361
- TO authenticated
362
- WITH CHECK (
363
- bucket_id = 'avatars' AND
364
- (select auth.uid())::text = (storage.foldername(name))[1]
365
- );
366
- ```
367
-
368
- **Folder Structure**: `avatars/{user_id}/filename.jpg`
369
-
370
- **How it works**:
371
- - `storage.foldername(name)` splits path by `/` returning array
372
- - `[1]` gets first folder (user_id)
373
- - Compares with authenticated user's ID
374
-
375
- ## Pattern 2: Public Read, Authenticated Write
376
-
377
- Anyone can view files, only authenticated users can upload:
378
-
379
- ```sql
380
- -- Public read
381
- CREATE POLICY "Public avatars readable"
382
- ON storage.objects
383
- FOR SELECT
384
- TO public
385
- USING (bucket_id = 'avatars');
386
-
387
- -- Authenticated write
388
- CREATE POLICY "Authenticated upload avatars"
389
- ON storage.objects
390
- FOR INSERT
391
- TO authenticated
392
- WITH CHECK (bucket_id = 'avatars');
393
- ```
394
-
395
- **Use case**: Public profile pictures, logos, marketing assets.
396
-
397
- ## Pattern 3: Tenant-Scoped Files
398
-
399
- Users can only access files from their organization:
400
-
401
- ```sql
402
- CREATE POLICY "Tenant file isolation"
403
- ON storage.objects
404
- FOR SELECT
405
- TO authenticated
406
- USING (
407
- bucket_id = 'documents' AND
408
- (storage.foldername(name))[1] = ((select auth.jwt()) ->> 'org_id')
409
- );
410
-
411
- CREATE POLICY "Tenant file uploads"
412
- ON storage.objects
413
- FOR INSERT
414
- TO authenticated
415
- WITH CHECK (
416
- bucket_id = 'documents' AND
417
- (storage.foldername(name))[1] = ((select auth.jwt()) ->> 'org_id')
418
- );
419
- ```
420
-
421
- **Folder Structure**: `documents/{org_id}/{file_id}.pdf`
422
-
423
- ## Pattern 4: Delete Own Files
424
-
425
- Users can delete their own files:
426
-
427
- ```sql
428
- CREATE POLICY "Users delete own files"
429
- ON storage.objects
430
- FOR DELETE
431
- TO authenticated
432
- USING (
433
- bucket_id = 'avatars' AND
434
- (select auth.uid())::text = (storage.foldername(name))[1]
435
- );
436
- ```
437
-
438
- ## Pattern 5: File Overwriting (upsert)
439
-
440
- For file overwriting via `upsert` option, grant SELECT + UPDATE:
441
-
442
- ```sql
443
- -- Allow reading (required for upsert check)
444
- CREATE POLICY "Users read own files"
445
- ON storage.objects
446
- FOR SELECT
447
- TO authenticated
448
- USING (
449
- bucket_id = 'avatars' AND
450
- (select auth.uid())::text = (storage.foldername(name))[1]
451
- );
452
-
453
- -- Allow updating (for upsert)
454
- CREATE POLICY "Users update own files"
455
- ON storage.objects
456
- FOR UPDATE
457
- TO authenticated
458
- USING (
459
- bucket_id = 'avatars' AND
460
- (select auth.uid())::text = (storage.foldername(name))[1]
461
- )
462
- WITH CHECK (
463
- bucket_id = 'avatars' AND
464
- (select auth.uid())::text = (storage.foldername(name))[1]
465
- );
466
- ```
467
-
468
- ## Bucket Configuration
469
-
470
- Create buckets with appropriate public/private settings:
471
-
472
- ```sql
473
- -- Create private bucket (requires policies)
474
- INSERT INTO storage.buckets (id, name, public)
475
- VALUES ('avatars', 'avatars', false);
476
-
477
- -- Create public bucket (files publicly accessible by URL)
478
- INSERT INTO storage.buckets (id, name, public)
479
- VALUES ('public-images', 'public-images', true);
480
- ```
481
-
482
- **Note**: Even public buckets respect RLS for uploads/deletes.
483
-
484
- ## Helper Functions
485
-
486
- ```sql
487
- -- Extract user folder from path
488
- CREATE OR REPLACE FUNCTION storage.user_owns_file(file_path text)
489
- RETURNS boolean
490
- LANGUAGE sql
491
- STABLE
492
- AS $$
493
- SELECT (select auth.uid())::text = (storage.foldername(file_path))[1];
494
- $$;
495
-
496
- -- Usage in policy
497
- CREATE POLICY "Users access own files"
498
- ON storage.objects
499
- FOR ALL
500
- TO authenticated
501
- USING (
502
- bucket_id = 'private' AND
503
- storage.user_owns_file(name)
504
- );
505
- ```
506
-
507
- ## Security Considerations
508
-
509
- 1. **Always validate bucket_id** in policies (prevent cross-bucket access)
510
- 2. **Use folder structure** for user/tenant isolation
511
- 3. **Set appropriate bucket public/private** settings
512
- 4. **Monitor storage size** per user/tenant (implement quotas)
513
- 5. **Validate file types** server-side (policies can't check content)
514
-
515
- ## Testing
516
-
517
- Test storage policies with Supabase client:
518
-
519
- ```typescript
520
- // Test upload as user A
521
- const { data, error } = await supabase.storage
522
- .from('avatars')
523
- .upload(`${user.id}/avatar.jpg`, file);
524
-
525
- // Should succeed for own folder
526
- expect(error).toBeNull();
527
-
528
- // Test read as user B (should fail for user A's folder)
529
- const { data: files, error: listError } = await supabase.storage
530
- .from('avatars')
531
- .list(`${otherUserId}/`);
532
-
533
- // Should return empty or error
534
- expect(files).toHaveLength(0);
535
- ```
536
-
537
- ## Performance
538
-
539
- Storage policies are evaluated on every file operation.
540
-
541
- **Optimize**:
542
- - Use simple folder path checks (fast)
543
- - Avoid complex JOINs in storage policies
544
- - Cache JWT claims in helper functions
545
- - Use indexes on bucket_id (already indexed by Supabase)
546
-
547
- ## Common Pitfalls
548
-
549
- - **Forgetting SELECT policy** for upsert operations
550
- - **Not validating bucket_id** (allows cross-bucket access)
551
- - **Complex policies** (slow file operations)
552
- - **Mixing public/private** bucket settings incorrectly
553
- elicit: false
554
-
555
- - id: performance-optimization
556
- title: "RLS Performance Optimization"
557
- instruction: |
558
- Critical performance optimizations for RLS policies validated by Supabase documentation.
559
-
560
- ## 🚀 Optimization 1: Wrap Auth Functions with SELECT (94.97% faster)
561
-
562
- **Critical Discovery** from Supabase docs: Wrapping auth functions enables query caching.
563
-
564
- ### ❌ SLOW (no caching):
565
- ```sql
566
- CREATE POLICY "users_select"
567
- ON users
568
- FOR SELECT
569
- TO authenticated
570
- USING (auth.uid() = user_id);
571
- ```
572
-
573
- ### ✅ FAST (cached, 94.97% improvement):
574
- ```sql
575
- CREATE POLICY "users_select"
576
- ON users
577
- FOR SELECT
578
- TO authenticated
579
- USING ((select auth.uid()) = user_id);
580
- ```
581
-
582
- **Why**: PostgreSQL caches the result of `(select auth.uid())` for the duration of the transaction,
583
- avoiding repeated function calls.
584
-
585
- **Impact**: **19x faster queries** in high-traffic scenarios.
586
-
587
- **Apply to all auth functions**:
588
- - `(select auth.uid())`
589
- - `(select auth.jwt())`
590
- - `(select auth.email())`
591
-
592
- ## 🚀 Optimization 2: Index Policy Columns (99.94% improvement)
593
-
594
- **Always index columns used in policy expressions.**
595
-
596
- ```sql
597
- -- Policy uses user_id
598
- CREATE POLICY "user_policy" ON posts
599
- USING ((select auth.uid()) = user_id);
600
-
601
- -- Index user_id (99.94% faster)
602
- CREATE INDEX idx_posts_user_id ON posts(user_id);
603
- ```
604
-
605
- **Critical indexes**:
606
- ```sql
607
- -- Tenant isolation
608
- CREATE INDEX idx_table_org_id ON table_name(org_id);
609
-
610
- -- Owner-based policies
611
- CREATE INDEX idx_table_user_id ON table_name(user_id);
612
-
613
- -- Time-based policies
614
- CREATE INDEX idx_table_scheduling
615
- ON table_name(publish_at, expire_at)
616
- WHERE publish_at IS NOT NULL OR expire_at IS NOT NULL;
617
- ```
618
-
619
- ## 🚀 Optimization 3: Filter Client-Side Explicitly
620
-
621
- **Even with RLS policies, explicitly filter in client queries.**
622
-
623
- ```typescript
624
- // ❌ Relies only on RLS
625
- const { data } = await supabase
626
- .from('users')
627
- .select('*'); // Returns only own data due to RLS, but query planner doesn't know
628
-
629
- // ✅ Explicit filter (helps query planner)
630
- const { data } = await supabase
631
- .from('users')
632
- .select('*')
633
- .eq('user_id', userId); // Query planner can use index
634
- ```
635
-
636
- **Why**: Explicit filters help PostgreSQL query planner choose optimal index scan.
637
-
638
- ## 🚀 Optimization 4: Specify Roles Explicitly
639
-
640
- ```sql
641
- -- ❌ Applies to all roles (unnecessary checks)
642
- CREATE POLICY "policy" ON table USING (...);
643
-
644
- -- ✅ Specific role (fewer checks)
645
- CREATE POLICY "policy" ON table
646
- TO authenticated -- Only authenticated users
647
- USING (...);
648
- ```
649
-
650
- **Common roles**:
651
- - `TO authenticated` - Logged-in users
652
- - `TO anon` - Anonymous users
653
- - `TO public` - Both authenticated and anon
654
-
655
- ## 🚀 Optimization 5: Use Security Definer Functions
656
-
657
- **Bypass RLS on join tables** with security definer functions.
658
-
659
- ```sql
660
- -- ❌ Policy with JOIN (slow, 99.99% slower)
661
- CREATE POLICY "team_access" ON documents
662
- USING (
663
- team_id IN (
664
- SELECT team_id FROM user_teams
665
- WHERE user_id = (select auth.uid())
666
- )
667
- );
668
-
669
- -- ✅ Security definer function (99.99% improvement)
670
- CREATE OR REPLACE FUNCTION user_team_ids()
671
- RETURNS TABLE(team_id UUID)
672
- LANGUAGE sql
673
- STABLE
674
- SECURITY DEFINER
675
- AS $$
676
- SELECT team_id FROM user_teams
677
- WHERE user_id = auth.uid();
678
- $$;
679
-
680
- CREATE POLICY "team_access" ON documents
681
- USING (team_id IN (SELECT user_team_ids()));
682
- ```
683
-
684
- **Why**: Security definer functions run with elevated privileges, bypassing RLS on join tables.
685
-
686
- ## 🚀 Optimization 6: Minimize Joins
687
-
688
- **Avoid joining source and target tables in policies.**
689
-
690
- ```sql
691
- -- ❌ JOIN in policy (slow)
692
- CREATE POLICY "org_access" ON documents
693
- USING (
694
- EXISTS (
695
- SELECT 1 FROM users
696
- WHERE users.id = (select auth.uid())
697
- AND users.org_id = documents.org_id
698
- )
699
- );
700
-
701
- -- ✅ Use JWT claims (no JOIN)
702
- CREATE POLICY "org_access" ON documents
703
- USING (
704
- org_id = ((select auth.jwt()) ->> 'org_id')::uuid
705
- );
706
- ```
707
-
708
- **Strategy**: Store necessary claims in JWT (org_id, role, tenant_id).
709
-
710
- ## Performance Checklist
711
-
712
- Apply to every RLS policy:
713
-
714
- - [ ] Wrap `auth.uid()` with `(select auth.uid())`
715
- - [ ] Index all columns used in USING/WITH CHECK
716
- - [ ] Specify role explicitly (TO authenticated vs TO public)
717
- - [ ] Use JWT claims instead of JOINs where possible
718
- - [ ] Create security definer functions for complex permission checks
719
- - [ ] Test query plans with EXPLAIN ANALYZE
720
- - [ ] Filter client-side explicitly
721
-
722
- ## Measuring Performance
723
-
724
- ```sql
725
- -- Check query plan (look for Sequential Scan vs Index Scan)
726
- EXPLAIN ANALYZE
727
- SELECT * FROM users WHERE user_id = 'xxx';
728
-
729
- -- Monitor slow queries
730
- SELECT
731
- query,
732
- calls,
733
- mean_exec_time,
734
- max_exec_time
735
- FROM pg_stat_statements
736
- WHERE query LIKE '%users%'
737
- ORDER BY mean_exec_time DESC
738
- LIMIT 10;
739
- ```
740
-
741
- ## Real-World Impact
742
-
743
- **Before optimization**:
744
- - Query time: 250ms
745
- - Database CPU: 80%
746
- - Queries/sec: 40
747
-
748
- **After optimization** (wrapped functions + indexes):
749
- - Query time: 12ms (95% improvement)
750
- - Database CPU: 15%
751
- - Queries/sec: 800 (20x increase)
752
- elicit: false
753
-
754
- - id: advanced-patterns
755
- title: "Advanced RLS Patterns"
756
- instruction: |
757
- Advanced RLS patterns beyond basic owner-only and tenant isolation.
758
-
759
- ## Pattern 6: Time-Based Access (Scheduled Content)
760
-
761
- **Use case**: Blog posts with scheduled publishing, promotions with expiration, time-limited content.
762
-
763
- ```sql
764
- CREATE POLICY "scheduled_content"
765
- ON posts
766
- FOR SELECT
767
- TO authenticated
768
- USING (
769
- (publish_at IS NULL OR publish_at <= NOW()) AND
770
- (expire_at IS NULL OR expire_at > NOW())
771
- );
772
- ```
773
-
774
- **How it works**:
775
- - `publish_at IS NULL` - No schedule, always visible
776
- - `publish_at <= NOW()` - Past publish date, visible
777
- - `expire_at IS NULL` - No expiration, always visible
778
- - `expire_at > NOW()` - Not expired, visible
779
-
780
- **Performance**:
781
- ```sql
782
- -- Index for time-based queries
783
- CREATE INDEX idx_posts_scheduling
784
- ON posts(publish_at, expire_at)
785
- WHERE publish_at IS NOT NULL OR expire_at IS NOT NULL;
786
- ```
787
-
788
- **Client-side filtering** (helps query planner):
789
- ```typescript
790
- const { data } = await supabase
791
- .from('posts')
792
- .select('*')
793
- .lte('publish_at', new Date().toISOString())
794
- .or('publish_at.is.null')
795
- .gte('expire_at', new Date().toISOString())
796
- .or('expire_at.is.null');
797
- ```
798
-
799
- ## Pattern 7: Hierarchical Organizations (Detailed)
800
-
801
- **Use case**: Org > Team > User hierarchy with different access levels.
802
-
803
- ### Simple Hierarchy (Org-Level):
804
- ```sql
805
- CREATE POLICY "org_hierarchy"
806
- ON resources
807
- FOR SELECT
808
- TO authenticated
809
- USING (
810
- org_id IN (
811
- SELECT org_id
812
- FROM user_org_memberships
813
- WHERE user_id = (select auth.uid())
814
- )
815
- );
816
- ```
817
-
818
- **User sees resources from ALL orgs they belong to.**
819
-
820
- ### Complex Hierarchy (Org + Team):
821
- ```sql
822
- -- Option 1: User sees resources from their teams
823
- CREATE POLICY "team_hierarchy"
824
- ON resources
825
- FOR SELECT
826
- TO authenticated
827
- USING (
828
- team_id IN (
829
- SELECT team_id
830
- FROM user_team_memberships
831
- WHERE user_id = (select auth.uid())
832
- )
833
- );
834
-
835
- -- Option 2: Combined (org admin sees all, team member sees team only)
836
- CREATE POLICY "combined_hierarchy"
837
- ON resources
838
- FOR SELECT
839
- TO authenticated
840
- USING (
841
- -- Org admin sees all resources in org
842
- (
843
- org_id IN (
844
- SELECT org_id
845
- FROM user_org_memberships
846
- WHERE user_id = (select auth.uid()) AND role = 'admin'
847
- )
848
- )
849
- OR
850
- -- Team member sees only team resources
851
- (
852
- team_id IN (
853
- SELECT team_id
854
- FROM user_team_memberships
855
- WHERE user_id = (select auth.uid())
856
- )
857
- )
858
- );
859
- ```
860
-
861
- **Performance**:
862
- ```sql
863
- -- Indexes for hierarchy lookups
864
- CREATE INDEX idx_user_org_memberships_user
865
- ON user_org_memberships(user_id, org_id);
866
-
867
- CREATE INDEX idx_user_team_memberships_user
868
- ON user_team_memberships(user_id, team_id);
869
-
870
- CREATE INDEX idx_resources_org_id ON resources(org_id);
871
- CREATE INDEX idx_resources_team_id ON resources(team_id);
872
- ```
873
-
874
- **Optimization** (security definer function):
875
- ```sql
876
- CREATE OR REPLACE FUNCTION user_accessible_orgs()
877
- RETURNS TABLE(org_id UUID)
878
- LANGUAGE sql
879
- STABLE
880
- SECURITY DEFINER
881
- AS $$
882
- SELECT org_id FROM user_org_memberships
883
- WHERE user_id = auth.uid();
884
- $$;
885
-
886
- CREATE POLICY "org_hierarchy_optimized"
887
- ON resources
888
- FOR SELECT
889
- TO authenticated
890
- USING (org_id IN (SELECT user_accessible_orgs()));
891
- ```
892
-
893
- ## Pattern 8: Role-Based with Custom Claims (Advanced)
894
-
895
- **Use case**: Different permissions per role (admin, manager, analyst, user).
896
-
897
- ### Setup: Add role to JWT
898
- ```sql
899
- CREATE OR REPLACE FUNCTION custom_access_token_hook(event jsonb)
900
- RETURNS jsonb AS $$
901
- DECLARE
902
- claims jsonb;
903
- user_role text;
904
- user_org_id uuid;
905
- BEGIN
906
- -- Get user role and org from profiles table
907
- SELECT role, org_id INTO user_role, user_org_id
908
- FROM public.user_profiles
909
- WHERE user_id = (event->>'user_id')::uuid;
910
-
911
- -- Add to JWT claims
912
- claims := event->'claims';
913
- claims := jsonb_set(claims, '{role}', to_jsonb(user_role));
914
- claims := jsonb_set(claims, '{org_id}', to_jsonb(user_org_id));
915
-
916
- RETURN jsonb_set(event, '{claims}', claims);
917
- END;
918
- $$ LANGUAGE plpgsql SECURITY DEFINER;
919
- ```
920
-
921
- **Configure in Supabase Dashboard**: Authentication > Hooks > Custom Access Token
922
-
923
- ### Policy: Role-Based Access
924
- ```sql
925
- -- Admin sees all
926
- CREATE POLICY "admin_full_access"
927
- ON sensitive_data
928
- FOR ALL
929
- TO authenticated
930
- USING (
931
- ((select auth.jwt()) ->> 'role') = 'admin'
932
- );
933
-
934
- -- Manager sees org data
935
- CREATE POLICY "manager_org_access"
936
- ON sensitive_data
937
- FOR SELECT
938
- TO authenticated
939
- USING (
940
- ((select auth.jwt()) ->> 'role') = 'manager' AND
941
- org_id = ((select auth.jwt()) ->> 'org_id')::uuid
942
- );
943
-
944
- -- User sees own data only
945
- CREATE POLICY "user_own_access"
946
- ON sensitive_data
947
- FOR SELECT
948
- TO authenticated
949
- USING (
950
- ((select auth.jwt()) ->> 'role') = 'user' AND
951
- user_id = (select auth.uid())
952
- );
953
- ```
954
-
955
- ### Policy: Role Hierarchy (Admin > Manager > User)
956
- ```sql
957
- CREATE POLICY "role_hierarchy"
958
- ON resources
959
- FOR ALL
960
- TO authenticated
961
- USING (
962
- CASE ((select auth.jwt()) ->> 'role')
963
- WHEN 'admin' THEN true -- Admin sees everything
964
- WHEN 'manager' THEN org_id = ((select auth.jwt()) ->> 'org_id')::uuid
965
- ELSE user_id = (select auth.uid()) -- User sees own only
966
- END
967
- );
968
- ```
969
-
970
- ## Pattern 9: Multi-Factor Authentication (AAL2)
971
-
972
- **Use case**: Sensitive operations require MFA.
973
-
974
- ```sql
975
- CREATE POLICY "mfa_required_for_sensitive_ops"
976
- ON sensitive_operations
977
- FOR INSERT
978
- TO authenticated
979
- USING (
980
- ((select auth.jwt()) ->> 'aal') = 'aal2' -- Assurance Level 2 (MFA)
981
- );
982
- ```
983
-
984
- **AAL levels**:
985
- - `aal1` - Single factor (password only)
986
- - `aal2` - Multi-factor (password + OTP/biometric)
987
-
988
- ## Pattern 10: IP-Based Restrictions
989
-
990
- **Use case**: Restrict admin operations to office IP.
991
-
992
- ```sql
993
- CREATE POLICY "admin_office_only"
994
- ON admin_operations
995
- FOR ALL
996
- TO authenticated
997
- USING (
998
- ((select auth.jwt()) ->> 'role') = 'admin' AND
999
- inet_client_addr() << '192.168.1.0/24'::inet -- Office network
1000
- );
1001
- ```
1002
-
1003
- **Note**: `inet_client_addr()` returns client IP.
1004
-
1005
- ## Advanced Patterns Summary
1006
-
1007
- | Pattern | Use Case | Complexity | Performance Impact |
1008
- |---------|----------|------------|-------------------|
1009
- | Time-based | Scheduled content | Low | Low (with index) |
1010
- | Hierarchical | Org > Team > User | Medium | Medium (needs indexes) |
1011
- | Role-based claims | RBAC | Low | Low (JWT cached) |
1012
- | AAL2 MFA | Sensitive ops | Low | None |
1013
- | IP restrictions | Office-only | Low | None |
1014
-
1015
- **Best practices**:
1016
- - Prefer JWT claims over database lookups (faster)
1017
- - Always index columns used in policies
1018
- - Use security definer functions for complex checks
1019
- - Test with EXPLAIN ANALYZE
1020
- - Wrap auth functions with SELECT for caching
1021
- elicit: false
1022
-
1023
- - id: testing
1024
- title: "RLS Testing Strategy"
1025
- instruction: |
1026
- How to test and validate RLS policies:
1027
-
1028
- ## Unit Tests
1029
-
1030
- Test individual policies with different auth contexts:
1031
-
1032
- ```sql
1033
- -- Test as user A
1034
- SET request.jwt.claims = '{"sub": "user-a-uuid"}';
1035
- SELECT * FROM table_name; -- Should only see user A's records
1036
-
1037
- -- Test as user B
1038
- SET request.jwt.claims = '{"sub": "user-b-uuid"}';
1039
- SELECT * FROM table_name; -- Should only see user B's records
1040
- ```
1041
-
1042
- ## Integration Tests
1043
-
1044
- Test with actual Supabase client:
1045
-
1046
- ```typescript
1047
- // Test authenticated access
1048
- const { data, error } = await supabase
1049
- .from('table_name')
1050
- .select('*');
1051
-
1052
- // Verify only authorized records returned
1053
- ```
1054
-
1055
- ## Security Audit
1056
-
1057
- Checklist for RLS validation:
1058
- - [ ] All tables with sensitive data have RLS enabled
1059
- - [ ] No accidental policy holes (test with unauthorized users)
1060
- - [ ] Service role usage is documented and justified
1061
- - [ ] Policies perform well (no slow queries)
1062
- - [ ] Cross-tenant data leakage tested
1063
- - [ ] Anonymous vs authenticated access verified
1064
- - [ ] Edge cases tested (null values, missing context)
1065
-
1066
- ## Automated Testing
1067
-
1068
- Script or framework for continuous validation.
1069
- elicit: false
1070
-
1071
- - id: migration
1072
- title: "RLS Migration Scripts"
1073
- instruction: |
1074
- Concrete SQL migration for implementing these policies:
1075
-
1076
- ## Migration: `YYYYMMDDHHMMSS_add_rls_policies.sql`
1077
-
1078
- ```sql
1079
- -- Enable RLS on tables
1080
- ALTER TABLE table1 ENABLE ROW LEVEL SECURITY;
1081
- ALTER TABLE table2 ENABLE ROW LEVEL SECURITY;
1082
-
1083
- -- Drop existing policies if re-running
1084
- DROP POLICY IF EXISTS "policy_name" ON table_name;
1085
-
1086
- -- Create policies
1087
- CREATE POLICY "policy_name_select"
1088
- ON table_name
1089
- FOR SELECT
1090
- TO authenticated
1091
- USING (auth.uid() = user_id);
1092
-
1093
- CREATE POLICY "policy_name_insert"
1094
- ON table_name
1095
- FOR INSERT
1096
- TO authenticated
1097
- WITH CHECK (auth.uid() = user_id);
1098
-
1099
- -- ... additional policies ...
1100
-
1101
- -- Create indexes to support policies
1102
- CREATE INDEX IF NOT EXISTS idx_table_user_id
1103
- ON table_name(user_id);
1104
-
1105
- -- Grant permissions
1106
- GRANT SELECT, INSERT, UPDATE, DELETE ON table_name TO authenticated;
1107
- GRANT SELECT ON table_name TO anon;
1108
- ```
1109
-
1110
- ## Rollback Migration
1111
-
1112
- ```sql
1113
- -- Remove policies
1114
- DROP POLICY IF EXISTS "policy_name_select" ON table_name;
1115
- DROP POLICY IF EXISTS "policy_name_insert" ON table_name;
1116
-
1117
- -- Disable RLS
1118
- ALTER TABLE table_name DISABLE ROW LEVEL SECURITY;
1119
- ```
1120
- elicit: false
1121
-
1122
- - id: monitoring
1123
- title: "RLS Monitoring & Debugging"
1124
- instruction: |
1125
- How to monitor and debug RLS policies:
1126
-
1127
- ## Query Performance
1128
-
1129
- Identify slow queries caused by RLS:
1130
-
1131
- ```sql
1132
- -- Check query plans
1133
- EXPLAIN ANALYZE
1134
- SELECT * FROM table_name;
1135
- ```
1136
-
1137
- ## Policy Effectiveness
1138
-
1139
- Verify policies are being applied:
1140
-
1141
- ```sql
1142
- -- Check active policies
1143
- SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual
1144
- FROM pg_policies
1145
- WHERE tablename = 'your_table';
1146
- ```
1147
-
1148
- ## Common Issues
1149
-
1150
- ### Issue: Policy not applying
1151
- - Check RLS is enabled on table
1152
- - Verify user role matches policy target
1153
- - Check auth context is set correctly
1154
-
1155
- ### Issue: Performance degradation
1156
- - Add indexes for policy columns
1157
- - Simplify policy expressions
1158
- - Consider denormalization
1159
-
1160
- ### Issue: Unexpected access
1161
- - Audit all policies on table
1162
- - Check for permissive vs restrictive policies
1163
- - Verify no service role leakage
1164
-
1165
- ## Logging
1166
-
1167
- Log policy violations or unexpected access patterns.
1168
- elicit: false
1169
-
1170
- - id: best-practices
1171
- title: "RLS Best Practices"
1172
- instruction: |
1173
- ## Design Principles
1174
-
1175
- 1. **Start Restrictive**: Default deny, explicitly allow
1176
- 2. **Minimize Policy Complexity**: Simpler policies are easier to audit
1177
- 3. **Use Helper Functions**: Encapsulate complex logic
1178
- 4. **Index Policy Columns**: Performance is critical
1179
- 5. **Test Extensively**: Security bugs are costly
1180
- 6. **Document Everything**: Future you will thank you
1181
- 7. **Audit Regularly**: Policies drift over time
1182
- 8. **Avoid Service Role**: Use it only when absolutely necessary
1183
-
1184
- ## Common Pitfalls
1185
-
1186
- - Forgetting to enable RLS on new tables
1187
- - Policy expressions with poor performance
1188
- - Not testing with actual user contexts
1189
- - Overly permissive catch-all policies
1190
- - Mixing USING and WITH CHECK incorrectly
1191
- - Not considering null values in policies
1192
-
1193
- ## Security Checklist
1194
-
1195
- - [ ] All sensitive tables have RLS enabled
1196
- - [ ] Policies tested with unauthorized users
1197
- - [ ] Anonymous access limited to public data only
1198
- - [ ] Service role usage is minimal and documented
1199
- - [ ] Policies use indexes for performance
1200
- - [ ] Multi-tenant isolation is verified
1201
- - [ ] Edge cases tested (nulls, empty results)
1202
- - [ ] Policies are documented and reviewed
1203
- elicit: false
1
+ ---
2
+ template_name: "Supabase RLS Policies"
3
+ template_version: "1.0.0"
4
+ output_format: "markdown"
5
+ destination: "rls-policies.md"
6
+ description: "Row Level Security policies for Supabase tables"
7
+ ---
8
+
9
+ sections:
10
+ - id: overview
11
+ title: "RLS Overview"
12
+ instruction: |
13
+ Document the Row Level Security strategy:
14
+
15
+ ## Purpose
16
+ Explain the overall security model and why RLS is being used.
17
+
18
+ ## Authentication Context
19
+ - How users are authenticated (Supabase Auth, JWT, etc)
20
+ - Available auth context variables:
21
+ - `auth.uid()` - Current user ID
22
+ - `auth.jwt()` - JWT claims
23
+ - `auth.email()` - User email
24
+ - Custom claims in JWT
25
+
26
+ ## Security Model
27
+ - Role-based access control (RBAC)
28
+ - Multi-tenancy approach (if applicable)
29
+ - Public vs authenticated vs specific role access
30
+
31
+ ## Performance Considerations
32
+ - RLS policy performance impact
33
+ - Indexing strategy to support policies
34
+ - Caching considerations
35
+
36
+ ## Testing Strategy
37
+ - How policies will be tested
38
+ - Test users and scenarios
39
+ elicit: true
40
+
41
+ - id: policy-patterns
42
+ title: "Common Policy Patterns"
43
+ instruction: |
44
+ Document reusable policy patterns used across tables:
45
+
46
+ ## Pattern 1: Owner-Only Access
47
+ ```sql
48
+ -- Users can only access their own records
49
+ (auth.uid() = user_id)
50
+ ```
51
+
52
+ ## Pattern 2: Tenant-Based Access
53
+ ```sql
54
+ -- Users can access records in their organization
55
+ (auth.uid() IN (
56
+ SELECT user_id FROM org_members
57
+ WHERE org_id = table.org_id
58
+ ))
59
+ ```
60
+
61
+ ## Pattern 3: Role-Based Access
62
+ ```sql
63
+ -- Only admins can access
64
+ ((auth.jwt() ->> 'role')::text = 'admin')
65
+ ```
66
+
67
+ ## Pattern 4: Public Read, Authenticated Write
68
+ ```sql
69
+ -- SELECT: true (public read)
70
+ -- INSERT/UPDATE/DELETE: auth.uid() IS NOT NULL
71
+ ```
72
+
73
+ ## Pattern 5: Hierarchical Permissions
74
+ ```sql
75
+ -- Access based on organizational hierarchy
76
+ ```
77
+
78
+ Document any other patterns specific to your application.
79
+ elicit: true
80
+
81
+ - id: table-policies
82
+ title: "Table-by-Table Policies"
83
+ instruction: |
84
+ For each table requiring RLS, document comprehensive policies:
85
+
86
+ # Table: `table_name`
87
+
88
+ ## Enable RLS
89
+ ```sql
90
+ ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
91
+ ```
92
+
93
+ ## SELECT Policies
94
+
95
+ ### Policy: `policy_name_select`
96
+ **Purpose**: Describe who can read what
97
+
98
+ **Policy Expression**:
99
+ ```sql
100
+ CREATE POLICY "policy_name_select"
101
+ ON table_name
102
+ FOR SELECT
103
+ TO authenticated -- or public, anon, etc
104
+ USING (
105
+ -- Policy expression
106
+ auth.uid() = user_id
107
+ );
108
+ ```
109
+
110
+ **Rationale**: Explain the business rule
111
+
112
+ **Performance**: Any indexes needed to support this policy
113
+
114
+ ## INSERT Policies
115
+
116
+ ### Policy: `policy_name_insert`
117
+ **Purpose**: Describe who can create records
118
+
119
+ **Policy Expression**:
120
+ ```sql
121
+ CREATE POLICY "policy_name_insert"
122
+ ON table_name
123
+ FOR INSERT
124
+ TO authenticated
125
+ WITH CHECK (
126
+ -- Policy expression
127
+ auth.uid() = user_id
128
+ );
129
+ ```
130
+
131
+ **Validation**: What validations this ensures
132
+
133
+ ## UPDATE Policies
134
+
135
+ ### Policy: `policy_name_update`
136
+ **Purpose**: Describe who can modify records
137
+
138
+ **Policy Expression**:
139
+ ```sql
140
+ CREATE POLICY "policy_name_update"
141
+ ON table_name
142
+ FOR UPDATE
143
+ TO authenticated
144
+ USING (
145
+ -- Who can see the record to update it
146
+ auth.uid() = user_id
147
+ )
148
+ WITH CHECK (
149
+ -- What values they can set
150
+ auth.uid() = user_id
151
+ );
152
+ ```
153
+
154
+ **Notes**: USING checks old values, WITH CHECK validates new values
155
+
156
+ ## DELETE Policies
157
+
158
+ ### Policy: `policy_name_delete`
159
+ **Purpose**: Describe who can delete records
160
+
161
+ **Policy Expression**:
162
+ ```sql
163
+ CREATE POLICY "policy_name_delete"
164
+ ON table_name
165
+ FOR DELETE
166
+ TO authenticated
167
+ USING (
168
+ -- Who can delete
169
+ auth.uid() = user_id OR
170
+ (auth.jwt() ->> 'role')::text = 'admin'
171
+ );
172
+ ```
173
+
174
+ ## ALL Policies (if using combined policy)
175
+
176
+ Sometimes a single policy for all operations is clearer:
177
+
178
+ ```sql
179
+ CREATE POLICY "policy_name_all"
180
+ ON table_name
181
+ FOR ALL
182
+ TO authenticated
183
+ USING (auth.uid() = user_id)
184
+ WITH CHECK (auth.uid() = user_id);
185
+ ```
186
+
187
+ ---
188
+
189
+ Repeat this section for each table.
190
+ elicit: true
191
+
192
+ - id: public-tables
193
+ title: "Public Tables"
194
+ instruction: |
195
+ Document tables with public read access:
196
+
197
+ # Table: `public_table_name`
198
+
199
+ ## Public Read Policy
200
+ ```sql
201
+ ALTER TABLE public_table_name ENABLE ROW LEVEL SECURITY;
202
+
203
+ CREATE POLICY "public_read_policy"
204
+ ON public_table_name
205
+ FOR SELECT
206
+ TO anon, authenticated
207
+ USING (true);
208
+ ```
209
+
210
+ ## Restricted Write Policy
211
+ ```sql
212
+ CREATE POLICY "authenticated_write_policy"
213
+ ON public_table_name
214
+ FOR INSERT
215
+ TO authenticated
216
+ WITH CHECK (auth.uid() IS NOT NULL);
217
+ ```
218
+
219
+ **Rationale**: Why this table is public
220
+
221
+ **Security Considerations**: What data is safe to expose
222
+ elicit: false
223
+
224
+ - id: service-role
225
+ title: "Service Role Bypass"
226
+ instruction: |
227
+ Document scenarios where service role (bypass RLS) is used:
228
+
229
+ ## Service Role Usage
230
+
231
+ ### Backend Operations
232
+ Operations that need to bypass RLS:
233
+ - Scheduled jobs (cron, edge functions with service key)
234
+ - Admin operations
235
+ - Data migration
236
+ - Analytics aggregation
237
+
238
+ ### Safety Measures
239
+ - How service key is secured
240
+ - Where service role operations are logged
241
+ - Who has access to service key
242
+
243
+ ### Alternatives
244
+ When possible, prefer:
245
+ - Security definer functions with RLS
246
+ - Elevated permission policies
247
+ - Temporary privilege escalation
248
+ elicit: false
249
+
250
+ - id: helper-functions
251
+ title: "Security Helper Functions"
252
+ instruction: |
253
+ Document PostgreSQL functions that support RLS:
254
+
255
+ ## Function: `check_user_permission`
256
+ ```sql
257
+ CREATE OR REPLACE FUNCTION check_user_permission(
258
+ user_id uuid,
259
+ resource_id uuid,
260
+ permission_type text
261
+ )
262
+ RETURNS boolean
263
+ LANGUAGE plpgsql
264
+ SECURITY DEFINER
265
+ AS $$
266
+ BEGIN
267
+ -- Permission checking logic
268
+ RETURN EXISTS (
269
+ SELECT 1 FROM permissions
270
+ WHERE user_id = $1
271
+ AND resource_id = $2
272
+ AND permission = $3
273
+ );
274
+ END;
275
+ $$;
276
+ ```
277
+
278
+ **Usage in Policies**:
279
+ ```sql
280
+ USING (check_user_permission(auth.uid(), id, 'read'))
281
+ ```
282
+
283
+ ## Function: `get_user_org_id`
284
+ ```sql
285
+ CREATE OR REPLACE FUNCTION get_user_org_id()
286
+ RETURNS uuid
287
+ LANGUAGE sql
288
+ STABLE
289
+ AS $$
290
+ SELECT org_id FROM user_profiles
291
+ WHERE user_id = auth.uid();
292
+ $$;
293
+ ```
294
+
295
+ **Usage in Policies**:
296
+ ```sql
297
+ USING (org_id = get_user_org_id())
298
+ ```
299
+
300
+ Document all helper functions used in RLS policies.
301
+ elicit: false
302
+
303
+ - id: multi-tenancy
304
+ title: "Multi-Tenancy Implementation"
305
+ condition: "multi_tenant"
306
+ instruction: |
307
+ If implementing multi-tenancy with RLS:
308
+
309
+ ## Tenant Isolation Strategy
310
+
311
+ ### Tenant Identification
312
+ - How tenants are identified (org_id, team_id, etc)
313
+ - Where tenant ID is stored (JWT claim, database lookup)
314
+
315
+ ### Tenant-Scoped Tables
316
+
317
+ For each tenant-scoped table:
318
+
319
+ ```sql
320
+ -- Example: projects table
321
+ CREATE POLICY "tenant_isolation_policy"
322
+ ON projects
323
+ FOR ALL
324
+ TO authenticated
325
+ USING (
326
+ org_id = (auth.jwt() ->> 'org_id')::uuid
327
+ )
328
+ WITH CHECK (
329
+ org_id = (auth.jwt() ->> 'org_id')::uuid
330
+ );
331
+ ```
332
+
333
+ ### Cross-Tenant Scenarios
334
+ - Shared resources across tenants
335
+ - Super admin access
336
+ - Tenant-to-tenant relationships
337
+
338
+ ### Performance
339
+ - Indexes on tenant_id columns
340
+ - Query patterns that leverage tenant isolation
341
+ elicit: true
342
+
343
+ - id: storage-policies
344
+ title: "Supabase Storage Policies"
345
+ instruction: |
346
+ RLS policies for Supabase Storage buckets (storage.objects table).
347
+
348
+ ## Overview
349
+
350
+ Supabase Storage uses RLS on the `storage.objects` system table to control file access.
351
+ By default, Storage requires explicit RLS policies - no uploads allowed without policies.
352
+
353
+ ## Pattern 1: User-Specific Uploads
354
+
355
+ Users can only upload files to their own folder:
356
+
357
+ ```sql
358
+ CREATE POLICY "Users upload own avatars"
359
+ ON storage.objects
360
+ FOR INSERT
361
+ TO authenticated
362
+ WITH CHECK (
363
+ bucket_id = 'avatars' AND
364
+ (select auth.uid())::text = (storage.foldername(name))[1]
365
+ );
366
+ ```
367
+
368
+ **Folder Structure**: `avatars/{user_id}/filename.jpg`
369
+
370
+ **How it works**:
371
+ - `storage.foldername(name)` splits path by `/` returning array
372
+ - `[1]` gets first folder (user_id)
373
+ - Compares with authenticated user's ID
374
+
375
+ ## Pattern 2: Public Read, Authenticated Write
376
+
377
+ Anyone can view files, only authenticated users can upload:
378
+
379
+ ```sql
380
+ -- Public read
381
+ CREATE POLICY "Public avatars readable"
382
+ ON storage.objects
383
+ FOR SELECT
384
+ TO public
385
+ USING (bucket_id = 'avatars');
386
+
387
+ -- Authenticated write
388
+ CREATE POLICY "Authenticated upload avatars"
389
+ ON storage.objects
390
+ FOR INSERT
391
+ TO authenticated
392
+ WITH CHECK (bucket_id = 'avatars');
393
+ ```
394
+
395
+ **Use case**: Public profile pictures, logos, marketing assets.
396
+
397
+ ## Pattern 3: Tenant-Scoped Files
398
+
399
+ Users can only access files from their organization:
400
+
401
+ ```sql
402
+ CREATE POLICY "Tenant file isolation"
403
+ ON storage.objects
404
+ FOR SELECT
405
+ TO authenticated
406
+ USING (
407
+ bucket_id = 'documents' AND
408
+ (storage.foldername(name))[1] = ((select auth.jwt()) ->> 'org_id')
409
+ );
410
+
411
+ CREATE POLICY "Tenant file uploads"
412
+ ON storage.objects
413
+ FOR INSERT
414
+ TO authenticated
415
+ WITH CHECK (
416
+ bucket_id = 'documents' AND
417
+ (storage.foldername(name))[1] = ((select auth.jwt()) ->> 'org_id')
418
+ );
419
+ ```
420
+
421
+ **Folder Structure**: `documents/{org_id}/{file_id}.pdf`
422
+
423
+ ## Pattern 4: Delete Own Files
424
+
425
+ Users can delete their own files:
426
+
427
+ ```sql
428
+ CREATE POLICY "Users delete own files"
429
+ ON storage.objects
430
+ FOR DELETE
431
+ TO authenticated
432
+ USING (
433
+ bucket_id = 'avatars' AND
434
+ (select auth.uid())::text = (storage.foldername(name))[1]
435
+ );
436
+ ```
437
+
438
+ ## Pattern 5: File Overwriting (upsert)
439
+
440
+ For file overwriting via `upsert` option, grant SELECT + UPDATE:
441
+
442
+ ```sql
443
+ -- Allow reading (required for upsert check)
444
+ CREATE POLICY "Users read own files"
445
+ ON storage.objects
446
+ FOR SELECT
447
+ TO authenticated
448
+ USING (
449
+ bucket_id = 'avatars' AND
450
+ (select auth.uid())::text = (storage.foldername(name))[1]
451
+ );
452
+
453
+ -- Allow updating (for upsert)
454
+ CREATE POLICY "Users update own files"
455
+ ON storage.objects
456
+ FOR UPDATE
457
+ TO authenticated
458
+ USING (
459
+ bucket_id = 'avatars' AND
460
+ (select auth.uid())::text = (storage.foldername(name))[1]
461
+ )
462
+ WITH CHECK (
463
+ bucket_id = 'avatars' AND
464
+ (select auth.uid())::text = (storage.foldername(name))[1]
465
+ );
466
+ ```
467
+
468
+ ## Bucket Configuration
469
+
470
+ Create buckets with appropriate public/private settings:
471
+
472
+ ```sql
473
+ -- Create private bucket (requires policies)
474
+ INSERT INTO storage.buckets (id, name, public)
475
+ VALUES ('avatars', 'avatars', false);
476
+
477
+ -- Create public bucket (files publicly accessible by URL)
478
+ INSERT INTO storage.buckets (id, name, public)
479
+ VALUES ('public-images', 'public-images', true);
480
+ ```
481
+
482
+ **Note**: Even public buckets respect RLS for uploads/deletes.
483
+
484
+ ## Helper Functions
485
+
486
+ ```sql
487
+ -- Extract user folder from path
488
+ CREATE OR REPLACE FUNCTION storage.user_owns_file(file_path text)
489
+ RETURNS boolean
490
+ LANGUAGE sql
491
+ STABLE
492
+ AS $$
493
+ SELECT (select auth.uid())::text = (storage.foldername(file_path))[1];
494
+ $$;
495
+
496
+ -- Usage in policy
497
+ CREATE POLICY "Users access own files"
498
+ ON storage.objects
499
+ FOR ALL
500
+ TO authenticated
501
+ USING (
502
+ bucket_id = 'private' AND
503
+ storage.user_owns_file(name)
504
+ );
505
+ ```
506
+
507
+ ## Security Considerations
508
+
509
+ 1. **Always validate bucket_id** in policies (prevent cross-bucket access)
510
+ 2. **Use folder structure** for user/tenant isolation
511
+ 3. **Set appropriate bucket public/private** settings
512
+ 4. **Monitor storage size** per user/tenant (implement quotas)
513
+ 5. **Validate file types** server-side (policies can't check content)
514
+
515
+ ## Testing
516
+
517
+ Test storage policies with Supabase client:
518
+
519
+ ```typescript
520
+ // Test upload as user A
521
+ const { data, error } = await supabase.storage
522
+ .from('avatars')
523
+ .upload(`${user.id}/avatar.jpg`, file);
524
+
525
+ // Should succeed for own folder
526
+ expect(error).toBeNull();
527
+
528
+ // Test read as user B (should fail for user A's folder)
529
+ const { data: files, error: listError } = await supabase.storage
530
+ .from('avatars')
531
+ .list(`${otherUserId}/`);
532
+
533
+ // Should return empty or error
534
+ expect(files).toHaveLength(0);
535
+ ```
536
+
537
+ ## Performance
538
+
539
+ Storage policies are evaluated on every file operation.
540
+
541
+ **Optimize**:
542
+ - Use simple folder path checks (fast)
543
+ - Avoid complex JOINs in storage policies
544
+ - Cache JWT claims in helper functions
545
+ - Use indexes on bucket_id (already indexed by Supabase)
546
+
547
+ ## Common Pitfalls
548
+
549
+ - **Forgetting SELECT policy** for upsert operations
550
+ - **Not validating bucket_id** (allows cross-bucket access)
551
+ - **Complex policies** (slow file operations)
552
+ - **Mixing public/private** bucket settings incorrectly
553
+ elicit: false
554
+
555
+ - id: performance-optimization
556
+ title: "RLS Performance Optimization"
557
+ instruction: |
558
+ Critical performance optimizations for RLS policies validated by Supabase documentation.
559
+
560
+ ## 🚀 Optimization 1: Wrap Auth Functions with SELECT (94.97% faster)
561
+
562
+ **Critical Discovery** from Supabase docs: Wrapping auth functions enables query caching.
563
+
564
+ ### ❌ SLOW (no caching):
565
+ ```sql
566
+ CREATE POLICY "users_select"
567
+ ON users
568
+ FOR SELECT
569
+ TO authenticated
570
+ USING (auth.uid() = user_id);
571
+ ```
572
+
573
+ ### ✅ FAST (cached, 94.97% improvement):
574
+ ```sql
575
+ CREATE POLICY "users_select"
576
+ ON users
577
+ FOR SELECT
578
+ TO authenticated
579
+ USING ((select auth.uid()) = user_id);
580
+ ```
581
+
582
+ **Why**: PostgreSQL caches the result of `(select auth.uid())` for the duration of the transaction,
583
+ avoiding repeated function calls.
584
+
585
+ **Impact**: **19x faster queries** in high-traffic scenarios.
586
+
587
+ **Apply to all auth functions**:
588
+ - `(select auth.uid())`
589
+ - `(select auth.jwt())`
590
+ - `(select auth.email())`
591
+
592
+ ## 🚀 Optimization 2: Index Policy Columns (99.94% improvement)
593
+
594
+ **Always index columns used in policy expressions.**
595
+
596
+ ```sql
597
+ -- Policy uses user_id
598
+ CREATE POLICY "user_policy" ON posts
599
+ USING ((select auth.uid()) = user_id);
600
+
601
+ -- Index user_id (99.94% faster)
602
+ CREATE INDEX idx_posts_user_id ON posts(user_id);
603
+ ```
604
+
605
+ **Critical indexes**:
606
+ ```sql
607
+ -- Tenant isolation
608
+ CREATE INDEX idx_table_org_id ON table_name(org_id);
609
+
610
+ -- Owner-based policies
611
+ CREATE INDEX idx_table_user_id ON table_name(user_id);
612
+
613
+ -- Time-based policies
614
+ CREATE INDEX idx_table_scheduling
615
+ ON table_name(publish_at, expire_at)
616
+ WHERE publish_at IS NOT NULL OR expire_at IS NOT NULL;
617
+ ```
618
+
619
+ ## 🚀 Optimization 3: Filter Client-Side Explicitly
620
+
621
+ **Even with RLS policies, explicitly filter in client queries.**
622
+
623
+ ```typescript
624
+ // ❌ Relies only on RLS
625
+ const { data } = await supabase
626
+ .from('users')
627
+ .select('*'); // Returns only own data due to RLS, but query planner doesn't know
628
+
629
+ // ✅ Explicit filter (helps query planner)
630
+ const { data } = await supabase
631
+ .from('users')
632
+ .select('*')
633
+ .eq('user_id', userId); // Query planner can use index
634
+ ```
635
+
636
+ **Why**: Explicit filters help PostgreSQL query planner choose optimal index scan.
637
+
638
+ ## 🚀 Optimization 4: Specify Roles Explicitly
639
+
640
+ ```sql
641
+ -- ❌ Applies to all roles (unnecessary checks)
642
+ CREATE POLICY "policy" ON table USING (...);
643
+
644
+ -- ✅ Specific role (fewer checks)
645
+ CREATE POLICY "policy" ON table
646
+ TO authenticated -- Only authenticated users
647
+ USING (...);
648
+ ```
649
+
650
+ **Common roles**:
651
+ - `TO authenticated` - Logged-in users
652
+ - `TO anon` - Anonymous users
653
+ - `TO public` - Both authenticated and anon
654
+
655
+ ## 🚀 Optimization 5: Use Security Definer Functions
656
+
657
+ **Bypass RLS on join tables** with security definer functions.
658
+
659
+ ```sql
660
+ -- ❌ Policy with JOIN (slow, 99.99% slower)
661
+ CREATE POLICY "team_access" ON documents
662
+ USING (
663
+ team_id IN (
664
+ SELECT team_id FROM user_teams
665
+ WHERE user_id = (select auth.uid())
666
+ )
667
+ );
668
+
669
+ -- ✅ Security definer function (99.99% improvement)
670
+ CREATE OR REPLACE FUNCTION user_team_ids()
671
+ RETURNS TABLE(team_id UUID)
672
+ LANGUAGE sql
673
+ STABLE
674
+ SECURITY DEFINER
675
+ AS $$
676
+ SELECT team_id FROM user_teams
677
+ WHERE user_id = auth.uid();
678
+ $$;
679
+
680
+ CREATE POLICY "team_access" ON documents
681
+ USING (team_id IN (SELECT user_team_ids()));
682
+ ```
683
+
684
+ **Why**: Security definer functions run with elevated privileges, bypassing RLS on join tables.
685
+
686
+ ## 🚀 Optimization 6: Minimize Joins
687
+
688
+ **Avoid joining source and target tables in policies.**
689
+
690
+ ```sql
691
+ -- ❌ JOIN in policy (slow)
692
+ CREATE POLICY "org_access" ON documents
693
+ USING (
694
+ EXISTS (
695
+ SELECT 1 FROM users
696
+ WHERE users.id = (select auth.uid())
697
+ AND users.org_id = documents.org_id
698
+ )
699
+ );
700
+
701
+ -- ✅ Use JWT claims (no JOIN)
702
+ CREATE POLICY "org_access" ON documents
703
+ USING (
704
+ org_id = ((select auth.jwt()) ->> 'org_id')::uuid
705
+ );
706
+ ```
707
+
708
+ **Strategy**: Store necessary claims in JWT (org_id, role, tenant_id).
709
+
710
+ ## Performance Checklist
711
+
712
+ Apply to every RLS policy:
713
+
714
+ - [ ] Wrap `auth.uid()` with `(select auth.uid())`
715
+ - [ ] Index all columns used in USING/WITH CHECK
716
+ - [ ] Specify role explicitly (TO authenticated vs TO public)
717
+ - [ ] Use JWT claims instead of JOINs where possible
718
+ - [ ] Create security definer functions for complex permission checks
719
+ - [ ] Test query plans with EXPLAIN ANALYZE
720
+ - [ ] Filter client-side explicitly
721
+
722
+ ## Measuring Performance
723
+
724
+ ```sql
725
+ -- Check query plan (look for Sequential Scan vs Index Scan)
726
+ EXPLAIN ANALYZE
727
+ SELECT * FROM users WHERE user_id = 'xxx';
728
+
729
+ -- Monitor slow queries
730
+ SELECT
731
+ query,
732
+ calls,
733
+ mean_exec_time,
734
+ max_exec_time
735
+ FROM pg_stat_statements
736
+ WHERE query LIKE '%users%'
737
+ ORDER BY mean_exec_time DESC
738
+ LIMIT 10;
739
+ ```
740
+
741
+ ## Real-World Impact
742
+
743
+ **Before optimization**:
744
+ - Query time: 250ms
745
+ - Database CPU: 80%
746
+ - Queries/sec: 40
747
+
748
+ **After optimization** (wrapped functions + indexes):
749
+ - Query time: 12ms (95% improvement)
750
+ - Database CPU: 15%
751
+ - Queries/sec: 800 (20x increase)
752
+ elicit: false
753
+
754
+ - id: advanced-patterns
755
+ title: "Advanced RLS Patterns"
756
+ instruction: |
757
+ Advanced RLS patterns beyond basic owner-only and tenant isolation.
758
+
759
+ ## Pattern 6: Time-Based Access (Scheduled Content)
760
+
761
+ **Use case**: Blog posts with scheduled publishing, promotions with expiration, time-limited content.
762
+
763
+ ```sql
764
+ CREATE POLICY "scheduled_content"
765
+ ON posts
766
+ FOR SELECT
767
+ TO authenticated
768
+ USING (
769
+ (publish_at IS NULL OR publish_at <= NOW()) AND
770
+ (expire_at IS NULL OR expire_at > NOW())
771
+ );
772
+ ```
773
+
774
+ **How it works**:
775
+ - `publish_at IS NULL` - No schedule, always visible
776
+ - `publish_at <= NOW()` - Past publish date, visible
777
+ - `expire_at IS NULL` - No expiration, always visible
778
+ - `expire_at > NOW()` - Not expired, visible
779
+
780
+ **Performance**:
781
+ ```sql
782
+ -- Index for time-based queries
783
+ CREATE INDEX idx_posts_scheduling
784
+ ON posts(publish_at, expire_at)
785
+ WHERE publish_at IS NOT NULL OR expire_at IS NOT NULL;
786
+ ```
787
+
788
+ **Client-side filtering** (helps query planner):
789
+ ```typescript
790
+ const { data } = await supabase
791
+ .from('posts')
792
+ .select('*')
793
+ .lte('publish_at', new Date().toISOString())
794
+ .or('publish_at.is.null')
795
+ .gte('expire_at', new Date().toISOString())
796
+ .or('expire_at.is.null');
797
+ ```
798
+
799
+ ## Pattern 7: Hierarchical Organizations (Detailed)
800
+
801
+ **Use case**: Org > Team > User hierarchy with different access levels.
802
+
803
+ ### Simple Hierarchy (Org-Level):
804
+ ```sql
805
+ CREATE POLICY "org_hierarchy"
806
+ ON resources
807
+ FOR SELECT
808
+ TO authenticated
809
+ USING (
810
+ org_id IN (
811
+ SELECT org_id
812
+ FROM user_org_memberships
813
+ WHERE user_id = (select auth.uid())
814
+ )
815
+ );
816
+ ```
817
+
818
+ **User sees resources from ALL orgs they belong to.**
819
+
820
+ ### Complex Hierarchy (Org + Team):
821
+ ```sql
822
+ -- Option 1: User sees resources from their teams
823
+ CREATE POLICY "team_hierarchy"
824
+ ON resources
825
+ FOR SELECT
826
+ TO authenticated
827
+ USING (
828
+ team_id IN (
829
+ SELECT team_id
830
+ FROM user_team_memberships
831
+ WHERE user_id = (select auth.uid())
832
+ )
833
+ );
834
+
835
+ -- Option 2: Combined (org admin sees all, team member sees team only)
836
+ CREATE POLICY "combined_hierarchy"
837
+ ON resources
838
+ FOR SELECT
839
+ TO authenticated
840
+ USING (
841
+ -- Org admin sees all resources in org
842
+ (
843
+ org_id IN (
844
+ SELECT org_id
845
+ FROM user_org_memberships
846
+ WHERE user_id = (select auth.uid()) AND role = 'admin'
847
+ )
848
+ )
849
+ OR
850
+ -- Team member sees only team resources
851
+ (
852
+ team_id IN (
853
+ SELECT team_id
854
+ FROM user_team_memberships
855
+ WHERE user_id = (select auth.uid())
856
+ )
857
+ )
858
+ );
859
+ ```
860
+
861
+ **Performance**:
862
+ ```sql
863
+ -- Indexes for hierarchy lookups
864
+ CREATE INDEX idx_user_org_memberships_user
865
+ ON user_org_memberships(user_id, org_id);
866
+
867
+ CREATE INDEX idx_user_team_memberships_user
868
+ ON user_team_memberships(user_id, team_id);
869
+
870
+ CREATE INDEX idx_resources_org_id ON resources(org_id);
871
+ CREATE INDEX idx_resources_team_id ON resources(team_id);
872
+ ```
873
+
874
+ **Optimization** (security definer function):
875
+ ```sql
876
+ CREATE OR REPLACE FUNCTION user_accessible_orgs()
877
+ RETURNS TABLE(org_id UUID)
878
+ LANGUAGE sql
879
+ STABLE
880
+ SECURITY DEFINER
881
+ AS $$
882
+ SELECT org_id FROM user_org_memberships
883
+ WHERE user_id = auth.uid();
884
+ $$;
885
+
886
+ CREATE POLICY "org_hierarchy_optimized"
887
+ ON resources
888
+ FOR SELECT
889
+ TO authenticated
890
+ USING (org_id IN (SELECT user_accessible_orgs()));
891
+ ```
892
+
893
+ ## Pattern 8: Role-Based with Custom Claims (Advanced)
894
+
895
+ **Use case**: Different permissions per role (admin, manager, analyst, user).
896
+
897
+ ### Setup: Add role to JWT
898
+ ```sql
899
+ CREATE OR REPLACE FUNCTION custom_access_token_hook(event jsonb)
900
+ RETURNS jsonb AS $$
901
+ DECLARE
902
+ claims jsonb;
903
+ user_role text;
904
+ user_org_id uuid;
905
+ BEGIN
906
+ -- Get user role and org from profiles table
907
+ SELECT role, org_id INTO user_role, user_org_id
908
+ FROM public.user_profiles
909
+ WHERE user_id = (event->>'user_id')::uuid;
910
+
911
+ -- Add to JWT claims
912
+ claims := event->'claims';
913
+ claims := jsonb_set(claims, '{role}', to_jsonb(user_role));
914
+ claims := jsonb_set(claims, '{org_id}', to_jsonb(user_org_id));
915
+
916
+ RETURN jsonb_set(event, '{claims}', claims);
917
+ END;
918
+ $$ LANGUAGE plpgsql SECURITY DEFINER;
919
+ ```
920
+
921
+ **Configure in Supabase Dashboard**: Authentication > Hooks > Custom Access Token
922
+
923
+ ### Policy: Role-Based Access
924
+ ```sql
925
+ -- Admin sees all
926
+ CREATE POLICY "admin_full_access"
927
+ ON sensitive_data
928
+ FOR ALL
929
+ TO authenticated
930
+ USING (
931
+ ((select auth.jwt()) ->> 'role') = 'admin'
932
+ );
933
+
934
+ -- Manager sees org data
935
+ CREATE POLICY "manager_org_access"
936
+ ON sensitive_data
937
+ FOR SELECT
938
+ TO authenticated
939
+ USING (
940
+ ((select auth.jwt()) ->> 'role') = 'manager' AND
941
+ org_id = ((select auth.jwt()) ->> 'org_id')::uuid
942
+ );
943
+
944
+ -- User sees own data only
945
+ CREATE POLICY "user_own_access"
946
+ ON sensitive_data
947
+ FOR SELECT
948
+ TO authenticated
949
+ USING (
950
+ ((select auth.jwt()) ->> 'role') = 'user' AND
951
+ user_id = (select auth.uid())
952
+ );
953
+ ```
954
+
955
+ ### Policy: Role Hierarchy (Admin > Manager > User)
956
+ ```sql
957
+ CREATE POLICY "role_hierarchy"
958
+ ON resources
959
+ FOR ALL
960
+ TO authenticated
961
+ USING (
962
+ CASE ((select auth.jwt()) ->> 'role')
963
+ WHEN 'admin' THEN true -- Admin sees everything
964
+ WHEN 'manager' THEN org_id = ((select auth.jwt()) ->> 'org_id')::uuid
965
+ ELSE user_id = (select auth.uid()) -- User sees own only
966
+ END
967
+ );
968
+ ```
969
+
970
+ ## Pattern 9: Multi-Factor Authentication (AAL2)
971
+
972
+ **Use case**: Sensitive operations require MFA.
973
+
974
+ ```sql
975
+ CREATE POLICY "mfa_required_for_sensitive_ops"
976
+ ON sensitive_operations
977
+ FOR INSERT
978
+ TO authenticated
979
+ USING (
980
+ ((select auth.jwt()) ->> 'aal') = 'aal2' -- Assurance Level 2 (MFA)
981
+ );
982
+ ```
983
+
984
+ **AAL levels**:
985
+ - `aal1` - Single factor (password only)
986
+ - `aal2` - Multi-factor (password + OTP/biometric)
987
+
988
+ ## Pattern 10: IP-Based Restrictions
989
+
990
+ **Use case**: Restrict admin operations to office IP.
991
+
992
+ ```sql
993
+ CREATE POLICY "admin_office_only"
994
+ ON admin_operations
995
+ FOR ALL
996
+ TO authenticated
997
+ USING (
998
+ ((select auth.jwt()) ->> 'role') = 'admin' AND
999
+ inet_client_addr() << '192.168.1.0/24'::inet -- Office network
1000
+ );
1001
+ ```
1002
+
1003
+ **Note**: `inet_client_addr()` returns client IP.
1004
+
1005
+ ## Advanced Patterns Summary
1006
+
1007
+ | Pattern | Use Case | Complexity | Performance Impact |
1008
+ |---------|----------|------------|-------------------|
1009
+ | Time-based | Scheduled content | Low | Low (with index) |
1010
+ | Hierarchical | Org > Team > User | Medium | Medium (needs indexes) |
1011
+ | Role-based claims | RBAC | Low | Low (JWT cached) |
1012
+ | AAL2 MFA | Sensitive ops | Low | None |
1013
+ | IP restrictions | Office-only | Low | None |
1014
+
1015
+ **Best practices**:
1016
+ - Prefer JWT claims over database lookups (faster)
1017
+ - Always index columns used in policies
1018
+ - Use security definer functions for complex checks
1019
+ - Test with EXPLAIN ANALYZE
1020
+ - Wrap auth functions with SELECT for caching
1021
+ elicit: false
1022
+
1023
+ - id: testing
1024
+ title: "RLS Testing Strategy"
1025
+ instruction: |
1026
+ How to test and validate RLS policies:
1027
+
1028
+ ## Unit Tests
1029
+
1030
+ Test individual policies with different auth contexts:
1031
+
1032
+ ```sql
1033
+ -- Test as user A
1034
+ SET request.jwt.claims = '{"sub": "user-a-uuid"}';
1035
+ SELECT * FROM table_name; -- Should only see user A's records
1036
+
1037
+ -- Test as user B
1038
+ SET request.jwt.claims = '{"sub": "user-b-uuid"}';
1039
+ SELECT * FROM table_name; -- Should only see user B's records
1040
+ ```
1041
+
1042
+ ## Integration Tests
1043
+
1044
+ Test with actual Supabase client:
1045
+
1046
+ ```typescript
1047
+ // Test authenticated access
1048
+ const { data, error } = await supabase
1049
+ .from('table_name')
1050
+ .select('*');
1051
+
1052
+ // Verify only authorized records returned
1053
+ ```
1054
+
1055
+ ## Security Audit
1056
+
1057
+ Checklist for RLS validation:
1058
+ - [ ] All tables with sensitive data have RLS enabled
1059
+ - [ ] No accidental policy holes (test with unauthorized users)
1060
+ - [ ] Service role usage is documented and justified
1061
+ - [ ] Policies perform well (no slow queries)
1062
+ - [ ] Cross-tenant data leakage tested
1063
+ - [ ] Anonymous vs authenticated access verified
1064
+ - [ ] Edge cases tested (null values, missing context)
1065
+
1066
+ ## Automated Testing
1067
+
1068
+ Script or framework for continuous validation.
1069
+ elicit: false
1070
+
1071
+ - id: migration
1072
+ title: "RLS Migration Scripts"
1073
+ instruction: |
1074
+ Concrete SQL migration for implementing these policies:
1075
+
1076
+ ## Migration: `YYYYMMDDHHMMSS_add_rls_policies.sql`
1077
+
1078
+ ```sql
1079
+ -- Enable RLS on tables
1080
+ ALTER TABLE table1 ENABLE ROW LEVEL SECURITY;
1081
+ ALTER TABLE table2 ENABLE ROW LEVEL SECURITY;
1082
+
1083
+ -- Drop existing policies if re-running
1084
+ DROP POLICY IF EXISTS "policy_name" ON table_name;
1085
+
1086
+ -- Create policies
1087
+ CREATE POLICY "policy_name_select"
1088
+ ON table_name
1089
+ FOR SELECT
1090
+ TO authenticated
1091
+ USING (auth.uid() = user_id);
1092
+
1093
+ CREATE POLICY "policy_name_insert"
1094
+ ON table_name
1095
+ FOR INSERT
1096
+ TO authenticated
1097
+ WITH CHECK (auth.uid() = user_id);
1098
+
1099
+ -- ... additional policies ...
1100
+
1101
+ -- Create indexes to support policies
1102
+ CREATE INDEX IF NOT EXISTS idx_table_user_id
1103
+ ON table_name(user_id);
1104
+
1105
+ -- Grant permissions
1106
+ GRANT SELECT, INSERT, UPDATE, DELETE ON table_name TO authenticated;
1107
+ GRANT SELECT ON table_name TO anon;
1108
+ ```
1109
+
1110
+ ## Rollback Migration
1111
+
1112
+ ```sql
1113
+ -- Remove policies
1114
+ DROP POLICY IF EXISTS "policy_name_select" ON table_name;
1115
+ DROP POLICY IF EXISTS "policy_name_insert" ON table_name;
1116
+
1117
+ -- Disable RLS
1118
+ ALTER TABLE table_name DISABLE ROW LEVEL SECURITY;
1119
+ ```
1120
+ elicit: false
1121
+
1122
+ - id: monitoring
1123
+ title: "RLS Monitoring & Debugging"
1124
+ instruction: |
1125
+ How to monitor and debug RLS policies:
1126
+
1127
+ ## Query Performance
1128
+
1129
+ Identify slow queries caused by RLS:
1130
+
1131
+ ```sql
1132
+ -- Check query plans
1133
+ EXPLAIN ANALYZE
1134
+ SELECT * FROM table_name;
1135
+ ```
1136
+
1137
+ ## Policy Effectiveness
1138
+
1139
+ Verify policies are being applied:
1140
+
1141
+ ```sql
1142
+ -- Check active policies
1143
+ SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual
1144
+ FROM pg_policies
1145
+ WHERE tablename = 'your_table';
1146
+ ```
1147
+
1148
+ ## Common Issues
1149
+
1150
+ ### Issue: Policy not applying
1151
+ - Check RLS is enabled on table
1152
+ - Verify user role matches policy target
1153
+ - Check auth context is set correctly
1154
+
1155
+ ### Issue: Performance degradation
1156
+ - Add indexes for policy columns
1157
+ - Simplify policy expressions
1158
+ - Consider denormalization
1159
+
1160
+ ### Issue: Unexpected access
1161
+ - Audit all policies on table
1162
+ - Check for permissive vs restrictive policies
1163
+ - Verify no service role leakage
1164
+
1165
+ ## Logging
1166
+
1167
+ Log policy violations or unexpected access patterns.
1168
+ elicit: false
1169
+
1170
+ - id: best-practices
1171
+ title: "RLS Best Practices"
1172
+ instruction: |
1173
+ ## Design Principles
1174
+
1175
+ 1. **Start Restrictive**: Default deny, explicitly allow
1176
+ 2. **Minimize Policy Complexity**: Simpler policies are easier to audit
1177
+ 3. **Use Helper Functions**: Encapsulate complex logic
1178
+ 4. **Index Policy Columns**: Performance is critical
1179
+ 5. **Test Extensively**: Security bugs are costly
1180
+ 6. **Document Everything**: Future you will thank you
1181
+ 7. **Audit Regularly**: Policies drift over time
1182
+ 8. **Avoid Service Role**: Use it only when absolutely necessary
1183
+
1184
+ ## Common Pitfalls
1185
+
1186
+ - Forgetting to enable RLS on new tables
1187
+ - Policy expressions with poor performance
1188
+ - Not testing with actual user contexts
1189
+ - Overly permissive catch-all policies
1190
+ - Mixing USING and WITH CHECK incorrectly
1191
+ - Not considering null values in policies
1192
+
1193
+ ## Security Checklist
1194
+
1195
+ - [ ] All sensitive tables have RLS enabled
1196
+ - [ ] Policies tested with unauthorized users
1197
+ - [ ] Anonymous access limited to public data only
1198
+ - [ ] Service role usage is minimal and documented
1199
+ - [ ] Policies use indexes for performance
1200
+ - [ ] Multi-tenant isolation is verified
1201
+ - [ ] Edge cases tested (nulls, empty results)
1202
+ - [ ] Policies are documented and reviewed
1203
+ elicit: false