vibes-plug 1.0.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +20 -0
- package/AGENTS.md +66 -0
- package/BLUEPRINT.md +152 -60
- package/CHANGELOG.md +50 -0
- package/README.md +346 -194
- package/package.json +25 -25
- package/plugin.json +1 -1
- package/skills/ai-cost-token-optimizer/SKILL.md +52 -0
- package/skills/ai-llm-integration-expert/SKILL.md +180 -162
- package/skills/api-design-expert/SKILL.md +314 -310
- package/skills/app-analyzer-optimizer/SKILL.md +193 -189
- package/skills/apple-ecosystem-expert/SKILL.md +142 -0
- package/skills/async-queue-temporal-expert/SKILL.md +54 -0
- package/skills/authentication-identity-expert/SKILL.md +251 -20
- package/skills/auto-doc-updater/SKILL.md +214 -204
- package/skills/autonomous-chaos-monkey/SKILL.md +63 -0
- package/skills/autonomous-red-teamer/SKILL.md +59 -0
- package/skills/autonomous-swarm-director/SKILL.md +69 -0
- package/skills/autonomous-tdd-debugger/SKILL.md +65 -0
- package/skills/bootstrap-to-modern/SKILL.md +90 -86
- package/skills/brainstorming/SKILL.md +373 -353
- package/skills/browser-automation-expert/SKILL.md +46 -0
- package/skills/ci-cd-devops-architect/SKILL.md +72 -45
- package/skills/cloud-hosting-expert/SKILL.md +244 -244
- package/skills/coderabbit/SKILL.md +192 -192
- package/skills/cron-scheduler-expert/SKILL.md +298 -0
- package/skills/data-telemetry-expert/SKILL.md +213 -213
- package/skills/database-orm-expert/SKILL.md +294 -294
- package/skills/dependency-upgrade-migrator/SKILL.md +295 -0
- package/skills/design-system-architect/SKILL.md +27 -10
- package/skills/doku-mcp-server/SKILL.md +251 -0
- package/skills/doku-payment-gateway/SKILL.md +227 -0
- package/skills/e2e-testing-expert/SKILL.md +315 -315
- package/skills/edge-serverless-db-expert/SKILL.md +43 -0
- package/skills/email-notification-expert/SKILL.md +362 -0
- package/skills/error-resilience-expert/SKILL.md +480 -0
- package/skills/event-driven-architect/SKILL.md +81 -81
- package/skills/feature-flag-analytics-expert/SKILL.md +46 -0
- package/skills/file-upload-media-expert/SKILL.md +431 -0
- package/skills/form-validation-expert/SKILL.md +401 -0
- package/skills/fullstack-expert/SKILL.md +202 -202
- package/skills/fullstack-expert/references/api_design_guide.md +466 -466
- package/skills/fullstack-expert/references/multi_language_backend.md +528 -528
- package/skills/fullstack-expert/scripts/api_contract_validator.py +253 -253
- package/skills/fullstack-expert/scripts/architecture_analyzer.py +326 -326
- package/skills/gemini-agent-booster/SKILL.md +135 -135
- package/skills/global-a11y-i18n-expert/SKILL.md +81 -81
- package/skills/glsl-shader-expert/SKILL.md +101 -0
- package/skills/go-programming-expert/SKILL.md +295 -295
- package/skills/graphql-apollo-expert/SKILL.md +108 -0
- package/skills/hig/SKILL.md +188 -188
- package/skills/hyper-context-synthesizer/SKILL.md +55 -0
- package/skills/js-backend-expert/SKILL.md +34 -9
- package/skills/legacy-code-translator/SKILL.md +65 -0
- package/skills/llm-cost-arbitrage-router/SKILL.md +59 -0
- package/skills/logging-error-tracking-expert/SKILL.md +338 -0
- package/skills/mcp-client-orchestrator/SKILL.md +70 -0
- package/skills/mcp-server-architect/SKILL.md +194 -194
- package/skills/micro-frontend-architect/SKILL.md +106 -0
- package/skills/mobile-expo-expert/SKILL.md +186 -186
- package/skills/mobile-push-notification-expert/SKILL.md +51 -0
- package/skills/monday-design-aesthetic/SKILL.md +67 -67
- package/skills/monorepo-architect/SKILL.md +227 -227
- package/skills/mpa-orchestrator/SKILL.md +101 -101
- package/skills/multi-agent-orchestration/SKILL.md +234 -234
- package/skills/multiple-entry-points/SKILL.md +55 -55
- package/skills/mvc-expert/SKILL.md +231 -231
- package/skills/payment-gateway-expert/SKILL.md +45 -45
- package/skills/performance-web-vitals/SKILL.md +332 -332
- package/skills/post-quantum-crypto-migrator/SKILL.md +57 -0
- package/skills/prd-architect/SKILL.md +201 -191
- package/skills/proactive-background-watcher/SKILL.md +62 -0
- package/skills/production-ready-hardener/PRODUCTION_READINESS_REPORT.md +67 -0
- package/skills/production-ready-hardener/SKILL.md +173 -186
- package/skills/production-ready-hardener/references/production_checklist.md +161 -161
- package/skills/production-ready-hardener/scripts/production_readiness_scanner.py +881 -875
- package/skills/project-context-mapper/SKILL.md +79 -0
- package/skills/python-programming-expert/SKILL.md +263 -132
- package/skills/rate-limit-abuse-prevention/SKILL.md +371 -0
- package/skills/realtime-collaboration-expert/SKILL.md +45 -45
- package/skills/rust-programming-expert/SKILL.md +235 -235
- package/skills/saas-billing/SKILL.md +377 -377
- package/skills/saas-multi-tenant/SKILL.md +251 -237
- package/skills/saas-mvp-launcher/SKILL.md +10 -0
- package/skills/saas-transformer/SKILL.md +187 -144
- package/skills/saas-transformer/references/billing_integration_guide.md +401 -401
- package/skills/saas-transformer/references/feature_gating_patterns.md +137 -137
- package/skills/saas-transformer/references/saas_transformation_checklist.md +121 -121
- package/skills/saas-transformer/scripts/saas_transformation_scanner.py +39 -29
- package/skills/scalability-clean-code/SKILL.md +229 -229
- package/skills/self-evolving-memory-graph/SKILL.md +75 -0
- package/skills/self-healing-cloud-orchestrator/SKILL.md +57 -0
- package/skills/senior-frontend/SKILL.md +161 -161
- package/skills/senior-fullstack/SKILL.md +167 -167
- package/skills/seo/SKILL.md +235 -225
- package/skills/seo-geo/SKILL.md +188 -188
- package/skills/session-context-loader/SKILL.md +77 -0
- package/skills/session-handoff-resume/SKILL.md +158 -158
- package/skills/skill_baru/SKILL.md +172 -147
- package/skills/spa-orchestrator/SKILL.md +288 -288
- package/skills/state-management-expert/SKILL.md +272 -272
- package/skills/supabase-security-expert/SKILL.md +243 -243
- package/skills/tailwind-expert/SKILL.md +188 -188
- package/skills/tanstack-query-expert/SKILL.md +199 -199
- package/skills/token-saver/SKILL.md +119 -111
- package/skills/typescript-expert/SKILL.md +324 -279
- package/skills/ui-components-expert/SKILL.md +263 -46
- package/skills/ui-ux-pro-max/SKILL.md +202 -201
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-310.pyc +0 -0
- package/skills/ui_ux_expert/SKILL.md +17 -6
- package/skills/vector-db-rag-expert/SKILL.md +52 -0
- package/skills/vibe-code-gardener/SKILL.md +181 -173
- package/skills/visual-qa-vision-agent/SKILL.md +65 -0
- package/skills/vue-frontend-expert/SKILL.md +126 -0
- package/skills/web-3d-graphics-expert/SKILL.md +131 -0
- package/skills/web-game-engine-expert/SKILL.md +96 -0
- package/skills/web-scraper/SKILL.md +207 -205
- package/skills/website-design-cloner/SKILL.md +174 -0
- package/skills/webxr-ar-vr-expert/SKILL.md +117 -0
- package/skills/zero-to-prod-orchestrator/SKILL.md +206 -180
- package/skills/zero-trust-secret-vault/SKILL.md +40 -0
- package/vibes-swarm-demo.gif +0 -0
|
@@ -1,237 +1,251 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: saas-multi-tenant
|
|
3
|
-
description: "Design and implement multi-tenant SaaS architectures with RLS, tenant isolation, and PostgreSQL / Desain dan implementasikan arsitektur SaaS multi-tenant dengan RLS, isolasi tenant, dan PostgreSQL."
|
|
4
|
-
author: "Roedy Rustam"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# SaaS Multi-Tenant Expert (2026 Edition)
|
|
8
|
-
|
|
9
|
-
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
<a name="english"></a>
|
|
14
|
-
## English
|
|
15
|
-
|
|
16
|
-
### Description
|
|
17
|
-
Expert guide for designing and implementing multi-tenant SaaS architectures with full tenant isolation, Supabase/PostgreSQL Row Level Security (RLS), schema-per-org patterns, RBAC, and Super Admin access controls.
|
|
18
|
-
|
|
19
|
-
### Trigger Conditions
|
|
20
|
-
- Building a SaaS application that serves multiple organizations (workspaces/tenants).
|
|
21
|
-
- Implementing Row Level Security (RLS) policies in Supabase or PostgreSQL.
|
|
22
|
-
- Designing a data model that isolates tenant data securely.
|
|
23
|
-
- Implementing role-based access control (RBAC) within a tenant.
|
|
24
|
-
- Building the Super Admin management panel for cross-tenant operations.
|
|
25
|
-
- Choosing between shared schema vs. schema-per-org isolation strategies.
|
|
26
|
-
|
|
27
|
-
### Tenant Isolation Strategies
|
|
28
|
-
|
|
29
|
-
| Strategy | Isolation Level | Cost | Complexity | Best For |
|
|
30
|
-
|---|---|---|---|---|
|
|
31
|
-
| **Shared Schema + RLS** | Row-level | Low | Medium | Standard SaaS (< 1M tenants) |
|
|
32
|
-
| **Schema per Org** | Table-level | Medium | High | Compliance-heavy (HIPAA, finance) |
|
|
33
|
-
| **DB per Org** | Database-level | High | Very High | Enterprise, regulated industries |
|
|
34
|
-
|
|
35
|
-
### Strategy 1: Shared Schema + RLS (Recommended for Most SaaS)
|
|
36
|
-
|
|
37
|
-
#### Core Schema Design
|
|
38
|
-
```sql
|
|
39
|
-
-- Central workspaces (tenants) table
|
|
40
|
-
CREATE TABLE workspaces (
|
|
41
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
42
|
-
name TEXT NOT NULL,
|
|
43
|
-
slug TEXT UNIQUE NOT NULL,
|
|
44
|
-
plan TEXT NOT NULL DEFAULT 'free' CHECK (plan IN ('free', 'pro', 'enterprise')),
|
|
45
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
-- Users belong to multiple workspaces via memberships
|
|
49
|
-
CREATE TABLE workspace_members (
|
|
50
|
-
workspace_id UUID REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
51
|
-
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
52
|
-
role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('owner', 'admin', 'member', 'viewer')),
|
|
53
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
54
|
-
PRIMARY KEY (workspace_id, user_id)
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
-- All tenant data has workspace_id
|
|
58
|
-
CREATE TABLE projects (
|
|
59
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
60
|
-
workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
61
|
-
name TEXT NOT NULL,
|
|
62
|
-
created_by UUID REFERENCES auth.users(id),
|
|
63
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
64
|
-
);
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
#### Row Level Security Policies
|
|
68
|
-
```sql
|
|
69
|
-
-- Enable RLS on all tenant tables
|
|
70
|
-
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
|
|
71
|
-
|
|
72
|
-
-- Policy: Users can only see projects in their workspaces
|
|
73
|
-
CREATE POLICY "workspace members can view projects"
|
|
74
|
-
ON projects FOR SELECT
|
|
75
|
-
USING (
|
|
76
|
-
workspace_id IN (
|
|
77
|
-
SELECT workspace_id FROM workspace_members
|
|
78
|
-
WHERE user_id = auth.uid()
|
|
79
|
-
)
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
-- Policy: Only admins and owners can create projects
|
|
83
|
-
CREATE POLICY "admins can create projects"
|
|
84
|
-
ON projects FOR INSERT
|
|
85
|
-
WITH CHECK (
|
|
86
|
-
workspace_id IN (
|
|
87
|
-
SELECT workspace_id FROM workspace_members
|
|
88
|
-
WHERE user_id = auth.uid()
|
|
89
|
-
AND role IN ('owner', 'admin')
|
|
90
|
-
)
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
-- Policy: Super Admin can bypass RLS (service role only)
|
|
94
|
-
-- ⚠️ NEVER expose service role key to frontend
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
#### Session Management & RLS Optimization
|
|
98
|
-
To avoid performance bottlenecks, embed `workspace_id` and `role` directly into the user's Session JWT (Custom Claims). This allows RLS policies to check the session token directly (`auth.jwt()->>'workspace_id'`) instead of joining the `workspace_members` table on every query.
|
|
99
|
-
|
|
100
|
-
#### RLS Helper Functions
|
|
101
|
-
```sql
|
|
102
|
-
-- Helper: Check if current user has a minimum role in a workspace
|
|
103
|
-
CREATE OR REPLACE FUNCTION user_has_role(
|
|
104
|
-
p_workspace_id UUID,
|
|
105
|
-
p_min_role TEXT
|
|
106
|
-
) RETURNS BOOLEAN AS $$
|
|
107
|
-
DECLARE
|
|
108
|
-
role_hierarchy TEXT[] := ARRAY['viewer', 'member', 'admin', 'owner'];
|
|
109
|
-
user_role TEXT;
|
|
110
|
-
BEGIN
|
|
111
|
-
SELECT role INTO user_role
|
|
112
|
-
FROM workspace_members
|
|
113
|
-
WHERE workspace_id = p_workspace_id AND user_id = auth.uid();
|
|
114
|
-
|
|
115
|
-
RETURN (
|
|
116
|
-
array_position(role_hierarchy, user_role) >=
|
|
117
|
-
array_position(role_hierarchy, p_min_role)
|
|
118
|
-
);
|
|
119
|
-
END;
|
|
120
|
-
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Strategy 2: Schema per Org (Compliance-Heavy)
|
|
124
|
-
For regulated industries requiring strict data separation:
|
|
125
|
-
```sql
|
|
126
|
-
-- Dynamically create a schema for each new tenant
|
|
127
|
-
CREATE OR REPLACE FUNCTION create_tenant_schema(p_slug TEXT) RETURNS VOID AS $$
|
|
128
|
-
BEGIN
|
|
129
|
-
EXECUTE format('CREATE SCHEMA IF NOT EXISTS tenant_%s', p_slug);
|
|
130
|
-
|
|
131
|
-
-- Create all tenant tables in the new schema
|
|
132
|
-
EXECUTE format('
|
|
133
|
-
CREATE TABLE tenant_%s.projects (
|
|
134
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
135
|
-
name TEXT NOT NULL,
|
|
136
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
137
|
-
)', p_slug);
|
|
138
|
-
END;
|
|
139
|
-
$$ LANGUAGE plpgsql;
|
|
140
|
-
|
|
141
|
-
-- Bypass pattern: set search_path per connection
|
|
142
|
-
SET search_path TO tenant_acme, public;
|
|
143
|
-
SELECT * FROM projects; -- Reads from tenant_acme.projects only
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### Super Admin Architecture
|
|
147
|
-
|
|
148
|
-
Super Admin is a separate system that operates **across all tenants** with elevated privileges:
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// Super Admin routes are ONLY accessible at admin.yourdomain.com
|
|
152
|
-
// Enforced at DNS + middleware level
|
|
153
|
-
|
|
154
|
-
// middleware.ts — verify super admin domain
|
|
155
|
-
if (hostname === 'admin.yourdomain.com') {
|
|
156
|
-
const session = await verifyAdminSession(req);
|
|
157
|
-
if (!session?.user.isSuperAdmin) {
|
|
158
|
-
return NextResponse.redirect('https://yourdomain.com');
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
```sql
|
|
164
|
-
-- Super Admin uses service role to bypass RLS
|
|
165
|
-
-- admin.sql — queries bypass all RLS policies when using service role key
|
|
166
|
-
|
|
167
|
-
-- Cross-tenant query (only accessible with service role)
|
|
168
|
-
SELECT w.name, COUNT(wm.user_id) as member_count, w.plan
|
|
169
|
-
FROM workspaces w
|
|
170
|
-
LEFT JOIN workspace_members wm ON w.id = wm.workspace_id
|
|
171
|
-
GROUP BY w.id
|
|
172
|
-
ORDER BY member_count DESC;
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### RBAC Implementation (TypeScript)
|
|
176
|
-
```typescript
|
|
177
|
-
type Role = 'owner' | 'admin' | 'member' | 'viewer';
|
|
178
|
-
|
|
179
|
-
const ROLE_PERMISSIONS: Record<Role, string[]> = {
|
|
180
|
-
owner: ['*'], // All permissions
|
|
181
|
-
admin: ['project:create', 'project:delete', 'member:invite', 'member:remove'],
|
|
182
|
-
member: ['project:create', 'project:read', 'project:update'],
|
|
183
|
-
viewer: ['project:read'],
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
function can(userRole: Role, permission: string): boolean {
|
|
187
|
-
const perms = ROLE_PERMISSIONS[userRole];
|
|
188
|
-
return perms.includes('*') || perms.includes(permission);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Usage in API handler
|
|
192
|
-
if (!can(currentMember.role, 'project:delete')) {
|
|
193
|
-
throw new ForbiddenError('Insufficient permissions');
|
|
194
|
-
}
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
1
|
+
---
|
|
2
|
+
name: saas-multi-tenant
|
|
3
|
+
description: "Design and implement multi-tenant SaaS architectures with RLS, tenant isolation, and PostgreSQL / Desain dan implementasikan arsitektur SaaS multi-tenant dengan RLS, isolasi tenant, dan PostgreSQL."
|
|
4
|
+
author: "Roedy Rustam"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# SaaS Multi-Tenant Expert (2026 Edition)
|
|
8
|
+
|
|
9
|
+
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<a name="english"></a>
|
|
14
|
+
## English
|
|
15
|
+
|
|
16
|
+
### Description
|
|
17
|
+
Expert guide for designing and implementing multi-tenant SaaS architectures with full tenant isolation, Supabase/PostgreSQL Row Level Security (RLS), schema-per-org patterns, RBAC, and Super Admin access controls.
|
|
18
|
+
|
|
19
|
+
### Trigger Conditions
|
|
20
|
+
- Building a SaaS application that serves multiple organizations (workspaces/tenants).
|
|
21
|
+
- Implementing Row Level Security (RLS) policies in Supabase or PostgreSQL.
|
|
22
|
+
- Designing a data model that isolates tenant data securely.
|
|
23
|
+
- Implementing role-based access control (RBAC) within a tenant.
|
|
24
|
+
- Building the Super Admin management panel for cross-tenant operations.
|
|
25
|
+
- Choosing between shared schema vs. schema-per-org isolation strategies.
|
|
26
|
+
|
|
27
|
+
### Tenant Isolation Strategies
|
|
28
|
+
|
|
29
|
+
| Strategy | Isolation Level | Cost | Complexity | Best For |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| **Shared Schema + RLS** | Row-level | Low | Medium | Standard SaaS (< 1M tenants) |
|
|
32
|
+
| **Schema per Org** | Table-level | Medium | High | Compliance-heavy (HIPAA, finance) |
|
|
33
|
+
| **DB per Org** | Database-level | High | Very High | Enterprise, regulated industries |
|
|
34
|
+
|
|
35
|
+
### Strategy 1: Shared Schema + RLS (Recommended for Most SaaS)
|
|
36
|
+
|
|
37
|
+
#### Core Schema Design
|
|
38
|
+
```sql
|
|
39
|
+
-- Central workspaces (tenants) table
|
|
40
|
+
CREATE TABLE workspaces (
|
|
41
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
42
|
+
name TEXT NOT NULL,
|
|
43
|
+
slug TEXT UNIQUE NOT NULL,
|
|
44
|
+
plan TEXT NOT NULL DEFAULT 'free' CHECK (plan IN ('free', 'pro', 'enterprise')),
|
|
45
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
-- Users belong to multiple workspaces via memberships
|
|
49
|
+
CREATE TABLE workspace_members (
|
|
50
|
+
workspace_id UUID REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
51
|
+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
52
|
+
role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('owner', 'admin', 'member', 'viewer')),
|
|
53
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
54
|
+
PRIMARY KEY (workspace_id, user_id)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
-- All tenant data has workspace_id
|
|
58
|
+
CREATE TABLE projects (
|
|
59
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
60
|
+
workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
61
|
+
name TEXT NOT NULL,
|
|
62
|
+
created_by UUID REFERENCES auth.users(id),
|
|
63
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
64
|
+
);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Row Level Security Policies
|
|
68
|
+
```sql
|
|
69
|
+
-- Enable RLS on all tenant tables
|
|
70
|
+
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
|
|
71
|
+
|
|
72
|
+
-- Policy: Users can only see projects in their workspaces
|
|
73
|
+
CREATE POLICY "workspace members can view projects"
|
|
74
|
+
ON projects FOR SELECT
|
|
75
|
+
USING (
|
|
76
|
+
workspace_id IN (
|
|
77
|
+
SELECT workspace_id FROM workspace_members
|
|
78
|
+
WHERE user_id = auth.uid()
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
-- Policy: Only admins and owners can create projects
|
|
83
|
+
CREATE POLICY "admins can create projects"
|
|
84
|
+
ON projects FOR INSERT
|
|
85
|
+
WITH CHECK (
|
|
86
|
+
workspace_id IN (
|
|
87
|
+
SELECT workspace_id FROM workspace_members
|
|
88
|
+
WHERE user_id = auth.uid()
|
|
89
|
+
AND role IN ('owner', 'admin')
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
-- Policy: Super Admin can bypass RLS (service role only)
|
|
94
|
+
-- ⚠️ NEVER expose service role key to frontend
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Session Management & RLS Optimization
|
|
98
|
+
To avoid performance bottlenecks, embed `workspace_id` and `role` directly into the user's Session JWT (Custom Claims). This allows RLS policies to check the session token directly (`auth.jwt()->>'workspace_id'`) instead of joining the `workspace_members` table on every query.
|
|
99
|
+
|
|
100
|
+
#### RLS Helper Functions
|
|
101
|
+
```sql
|
|
102
|
+
-- Helper: Check if current user has a minimum role in a workspace
|
|
103
|
+
CREATE OR REPLACE FUNCTION user_has_role(
|
|
104
|
+
p_workspace_id UUID,
|
|
105
|
+
p_min_role TEXT
|
|
106
|
+
) RETURNS BOOLEAN AS $$
|
|
107
|
+
DECLARE
|
|
108
|
+
role_hierarchy TEXT[] := ARRAY['viewer', 'member', 'admin', 'owner'];
|
|
109
|
+
user_role TEXT;
|
|
110
|
+
BEGIN
|
|
111
|
+
SELECT role INTO user_role
|
|
112
|
+
FROM workspace_members
|
|
113
|
+
WHERE workspace_id = p_workspace_id AND user_id = auth.uid();
|
|
114
|
+
|
|
115
|
+
RETURN (
|
|
116
|
+
array_position(role_hierarchy, user_role) >=
|
|
117
|
+
array_position(role_hierarchy, p_min_role)
|
|
118
|
+
);
|
|
119
|
+
END;
|
|
120
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Strategy 2: Schema per Org (Compliance-Heavy)
|
|
124
|
+
For regulated industries requiring strict data separation:
|
|
125
|
+
```sql
|
|
126
|
+
-- Dynamically create a schema for each new tenant
|
|
127
|
+
CREATE OR REPLACE FUNCTION create_tenant_schema(p_slug TEXT) RETURNS VOID AS $$
|
|
128
|
+
BEGIN
|
|
129
|
+
EXECUTE format('CREATE SCHEMA IF NOT EXISTS tenant_%s', p_slug);
|
|
130
|
+
|
|
131
|
+
-- Create all tenant tables in the new schema
|
|
132
|
+
EXECUTE format('
|
|
133
|
+
CREATE TABLE tenant_%s.projects (
|
|
134
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
135
|
+
name TEXT NOT NULL,
|
|
136
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
137
|
+
)', p_slug);
|
|
138
|
+
END;
|
|
139
|
+
$$ LANGUAGE plpgsql;
|
|
140
|
+
|
|
141
|
+
-- Bypass pattern: set search_path per connection
|
|
142
|
+
SET search_path TO tenant_acme, public;
|
|
143
|
+
SELECT * FROM projects; -- Reads from tenant_acme.projects only
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Super Admin Architecture
|
|
147
|
+
|
|
148
|
+
Super Admin is a separate system that operates **across all tenants** with elevated privileges:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Super Admin routes are ONLY accessible at admin.yourdomain.com
|
|
152
|
+
// Enforced at DNS + middleware level
|
|
153
|
+
|
|
154
|
+
// middleware.ts — verify super admin domain
|
|
155
|
+
if (hostname === 'admin.yourdomain.com') {
|
|
156
|
+
const session = await verifyAdminSession(req);
|
|
157
|
+
if (!session?.user.isSuperAdmin) {
|
|
158
|
+
return NextResponse.redirect('https://yourdomain.com');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```sql
|
|
164
|
+
-- Super Admin uses service role to bypass RLS
|
|
165
|
+
-- admin.sql — queries bypass all RLS policies when using service role key
|
|
166
|
+
|
|
167
|
+
-- Cross-tenant query (only accessible with service role)
|
|
168
|
+
SELECT w.name, COUNT(wm.user_id) as member_count, w.plan
|
|
169
|
+
FROM workspaces w
|
|
170
|
+
LEFT JOIN workspace_members wm ON w.id = wm.workspace_id
|
|
171
|
+
GROUP BY w.id
|
|
172
|
+
ORDER BY member_count DESC;
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### RBAC Implementation (TypeScript)
|
|
176
|
+
```typescript
|
|
177
|
+
type Role = 'owner' | 'admin' | 'member' | 'viewer';
|
|
178
|
+
|
|
179
|
+
const ROLE_PERMISSIONS: Record<Role, string[]> = {
|
|
180
|
+
owner: ['*'], // All permissions
|
|
181
|
+
admin: ['project:create', 'project:delete', 'member:invite', 'member:remove'],
|
|
182
|
+
member: ['project:create', 'project:read', 'project:update'],
|
|
183
|
+
viewer: ['project:read'],
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
function can(userRole: Role, permission: string): boolean {
|
|
187
|
+
const perms = ROLE_PERMISSIONS[userRole];
|
|
188
|
+
return perms.includes('*') || perms.includes(permission);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Usage in API handler
|
|
192
|
+
if (!can(currentMember.role, 'project:delete')) {
|
|
193
|
+
throw new ForbiddenError('Insufficient permissions');
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Skill Orchestration & Handoff
|
|
198
|
+
- **Upstream Orchestrator**: Executes during **Phase 3** of `zero-to-prod-orchestrator` or SaaS design lock in `brainstorming`.
|
|
199
|
+
- **Database & Security**: Delegate database schema migrations to `supabase-migration` and security audit / App Check rules to `supabase-security-expert`.
|
|
200
|
+
- **SaaS Billing & Monitization**: Delegate subscription state machines and Stripe/Polar integration to `saas-billing` and `payment-gateway-expert`.
|
|
201
|
+
- **Multi-Entry Points**: Delegate Super Admin domain isolation (`admin.yourdomain.com`) to `multiple-entry-points`.
|
|
202
|
+
- **Transformation Roadmap**: Coordinate with `saas-transformer` and `saas-mvp-launcher` when upgrading single-tenant apps to multi-tenant.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
<a name="bahasa-indonesia"></a>
|
|
207
|
+
## Bahasa Indonesia
|
|
208
|
+
|
|
209
|
+
### Deskripsi
|
|
210
|
+
Panduan ahli untuk merancang dan mengimplementasikan arsitektur SaaS multi-tenant dengan isolasi tenant penuh, Row Level Security (RLS) Supabase/PostgreSQL, pola schema-per-org, RBAC, dan kontrol akses Super Admin.
|
|
211
|
+
|
|
212
|
+
### Kondisi Pemicu
|
|
213
|
+
- Membangun aplikasi SaaS yang melayani beberapa organisasi (workspace/tenant).
|
|
214
|
+
- Mengimplementasikan kebijakan Row Level Security (RLS) di Supabase atau PostgreSQL.
|
|
215
|
+
- Merancang model data yang mengisolasi data tenant dengan aman.
|
|
216
|
+
- Mengimplementasikan role-based access control (RBAC) dalam tenant.
|
|
217
|
+
- Membangun panel manajemen Super Admin untuk operasi lintas tenant.
|
|
218
|
+
- Memilih antara shared schema vs. schema-per-org.
|
|
219
|
+
|
|
220
|
+
### Strategi Isolasi Tenant
|
|
221
|
+
|
|
222
|
+
| Strategi | Level Isolasi | Biaya | Kompleksitas | Terbaik Untuk |
|
|
223
|
+
|---|---|---|---|---|
|
|
224
|
+
| **Shared Schema + RLS** | Row-level | Rendah | Sedang | SaaS standar (< 1M tenant) |
|
|
225
|
+
| **Schema per Org** | Table-level | Sedang | Tinggi | Kepatuhan ketat (HIPAA, keuangan) |
|
|
226
|
+
| **DB per Org** | Database-level | Tinggi | Sangat Tinggi | Enterprise, industri teratur |
|
|
227
|
+
|
|
228
|
+
### Strategi 1: Shared Schema + RLS (Direkomendasikan)
|
|
229
|
+
Rancang tabel `workspaces` (tenant), `workspace_members` (keanggotaan + role), dan semua tabel data dengan kolom `workspace_id`. Terapkan RLS agar pengguna hanya dapat melihat data workspace mereka sendiri.
|
|
230
|
+
|
|
231
|
+
#### Optimasi Session Management & RLS
|
|
232
|
+
Untuk menghindari bottleneck performa, sematkan `workspace_id` dan `role` langsung ke dalam Session JWT pengguna (Custom Claims). Ini memungkinkan kebijakan RLS untuk memeriksa token sesi secara langsung (`auth.jwt()->>'workspace_id'`) daripada melakukan join ke tabel `workspace_members` pada setiap query.
|
|
233
|
+
|
|
234
|
+
#### Fungsi Helper RLS
|
|
235
|
+
Buat fungsi `user_has_role()` yang dapat digunakan kembali di seluruh kebijakan RLS untuk memeriksa apakah pengguna saat ini memiliki role minimum yang diperlukan dalam workspace tertentu.
|
|
236
|
+
|
|
237
|
+
### Strategi 2: Schema per Org
|
|
238
|
+
Untuk industri teratur yang membutuhkan pemisahan data ketat. Buat schema PostgreSQL terpisah untuk setiap tenant secara dinamis. Atur `search_path` per koneksi untuk mengarahkan query ke schema tenant yang benar.
|
|
239
|
+
|
|
240
|
+
### Arsitektur Super Admin
|
|
241
|
+
Super Admin adalah sistem terpisah yang beroperasi **di semua tenant** dengan hak istimewa yang ditingkatkan. Hanya dapat diakses di `admin.yourdomain.com` — diberlakukan di level DNS dan middleware. Menggunakan service role key Supabase untuk mem-bypass RLS dan melakukan query lintas tenant.
|
|
242
|
+
|
|
243
|
+
### Implementasi RBAC
|
|
244
|
+
Definisikan peta izin per role (`owner`, `admin`, `member`, `viewer`) dan fungsi `can()` helper untuk memeriksa izin dalam API handler.
|
|
245
|
+
|
|
246
|
+
### Orkestrasi Skill & Serah Terima
|
|
247
|
+
- **Orkestrator Utama**: Dieksekusi pada **Fase 3** dari `zero-to-prod-orchestrator` atau saat finalisasi SaaS di `brainstorming`.
|
|
248
|
+
- **Database & Keamanan**: Delegasikan migrasi skema ke `supabase-migration` dan audit keamanan / RLS ke `supabase-security-expert`.
|
|
249
|
+
- **SaaS Billing & Monitisasi**: Delegasikan state machine langganan dan integrasi Stripe/Polar ke `saas-billing` dan `payment-gateway-expert`.
|
|
250
|
+
- **Multi-Entry Points**: Delegasikan isolasi domain Super Admin (`admin.domain.com`) ke `multiple-entry-points`.
|
|
251
|
+
- **Roadmap Transformasi**: Koordinasikan dengan `saas-transformer` dan `saas-mvp-launcher` saat mentransformasi aplikasi single-tenant ke multi-tenant.
|
|
@@ -229,3 +229,13 @@ export const subscriptions = pgTable('subscriptions', {
|
|
|
229
229
|
currentPeriodEnd: timestamp('current_period_end').notNull(),
|
|
230
230
|
});
|
|
231
231
|
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
### 🎨 Automatic Visual Assets Generation Mandate (CRITICAL)
|
|
235
|
+
**MANDATORY**: Whenever you are building a new application, scaffolding a project, or finalizing the initial UI/UX, you MUST automatically use the `generate_image` tool to create a custom logo that perfectly matches the application's core concept and aesthetic.
|
|
236
|
+
This generated image MUST be explicitly used as:
|
|
237
|
+
1. The primary application logo (e.g., in the header/navbar).
|
|
238
|
+
2. The website favicon (`favicon.ico` or equivalent).
|
|
239
|
+
3. The Open Graph (OG) image for SEO metadata (`og:image`).
|
|
240
|
+
|
|
241
|
+
Do not use placeholders for these assets. Generate and integrate them automatically.
|