@neyugn/agent-kits 0.1.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/LICENSE +21 -0
- package/README.md +514 -0
- package/README.vi.md +410 -0
- package/README.zh.md +410 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +422 -0
- package/kits/coder/ARCHITECTURE.md +289 -0
- package/kits/coder/agents/ai-engineer.md +344 -0
- package/kits/coder/agents/backend-specialist.md +270 -0
- package/kits/coder/agents/cloud-architect.md +363 -0
- package/kits/coder/agents/code-reviewer.md +284 -0
- package/kits/coder/agents/data-engineer.md +401 -0
- package/kits/coder/agents/database-specialist.md +251 -0
- package/kits/coder/agents/debugger.md +209 -0
- package/kits/coder/agents/devops-engineer.md +281 -0
- package/kits/coder/agents/documentation-writer.md +296 -0
- package/kits/coder/agents/frontend-specialist.md +298 -0
- package/kits/coder/agents/i18n-specialist.md +348 -0
- package/kits/coder/agents/integration-specialist.md +314 -0
- package/kits/coder/agents/mobile-developer.md +271 -0
- package/kits/coder/agents/multi-tenant-architect.md +281 -0
- package/kits/coder/agents/orchestrator.md +263 -0
- package/kits/coder/agents/performance-analyst.md +327 -0
- package/kits/coder/agents/project-planner.md +277 -0
- package/kits/coder/agents/queue-specialist.md +282 -0
- package/kits/coder/agents/realtime-specialist.md +267 -0
- package/kits/coder/agents/security-auditor.md +253 -0
- package/kits/coder/agents/test-engineer.md +315 -0
- package/kits/coder/agents/ux-researcher.md +388 -0
- package/kits/coder/rules/.cursorrules +287 -0
- package/kits/coder/rules/CLAUDE.md +287 -0
- package/kits/coder/rules/CODEX.md +287 -0
- package/kits/coder/rules/GEMINI.md +287 -0
- package/kits/coder/scripts/checklist.py +318 -0
- package/kits/coder/scripts/kit_status.py +292 -0
- package/kits/coder/scripts/skills_manager.py +243 -0
- package/kits/coder/scripts/verify_all.py +391 -0
- package/kits/coder/skills/accessibility-patterns/SKILL.md +372 -0
- package/kits/coder/skills/accessibility-patterns/scripts/a11y_checker.py +211 -0
- package/kits/coder/skills/ai-rag-patterns/SKILL.md +444 -0
- package/kits/coder/skills/api-patterns/SKILL.md +316 -0
- package/kits/coder/skills/api-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/api-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/api-patterns/scripts/api_validator.py +253 -0
- package/kits/coder/skills/api-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/auth-patterns/SKILL.md +267 -0
- package/kits/coder/skills/aws-patterns/SKILL.md +576 -0
- package/kits/coder/skills/brainstorming/SKILL.md +370 -0
- package/kits/coder/skills/brainstorming/assets/.gitkeep +1 -0
- package/kits/coder/skills/brainstorming/references/deep-dive.md +21 -0
- package/kits/coder/skills/brainstorming/scripts/validate.py +56 -0
- package/kits/coder/skills/clean-code/SKILL.md +240 -0
- package/kits/coder/skills/clean-code/assets/.gitkeep +1 -0
- package/kits/coder/skills/clean-code/references/deep-dive.md +21 -0
- package/kits/coder/skills/clean-code/scripts/lint_runner.py +186 -0
- package/kits/coder/skills/clean-code/scripts/validate.py +56 -0
- package/kits/coder/skills/database-design/SKILL.md +255 -0
- package/kits/coder/skills/database-design/assets/.gitkeep +1 -0
- package/kits/coder/skills/database-design/references/deep-dive.md +21 -0
- package/kits/coder/skills/database-design/scripts/schema_validator.py +272 -0
- package/kits/coder/skills/database-design/scripts/validate.py +56 -0
- package/kits/coder/skills/docker-patterns/SKILL.md +240 -0
- package/kits/coder/skills/documentation-templates/SKILL.md +441 -0
- package/kits/coder/skills/e2e-testing/SKILL.md +457 -0
- package/kits/coder/skills/flutter-patterns/SKILL.md +330 -0
- package/kits/coder/skills/frontend-design/SKILL.md +127 -0
- package/kits/coder/skills/github-actions/SKILL.md +349 -0
- package/kits/coder/skills/gitlab-ci-patterns/SKILL.md +466 -0
- package/kits/coder/skills/graphql-patterns/SKILL.md +558 -0
- package/kits/coder/skills/i18n-localization/SKILL.md +345 -0
- package/kits/coder/skills/i18n-localization/scripts/i18n_checker.py +267 -0
- package/kits/coder/skills/kubernetes-patterns/SKILL.md +357 -0
- package/kits/coder/skills/mermaid-diagrams/SKILL.md +351 -0
- package/kits/coder/skills/mobile-design/SKILL.md +305 -0
- package/kits/coder/skills/monitoring-observability/SKILL.md +458 -0
- package/kits/coder/skills/multi-tenancy/SKILL.md +317 -0
- package/kits/coder/skills/multi-tenancy/assets/.gitkeep +1 -0
- package/kits/coder/skills/multi-tenancy/references/deep-dive.md +21 -0
- package/kits/coder/skills/multi-tenancy/scripts/validate.py +56 -0
- package/kits/coder/skills/nodejs-best-practices/SKILL.md +220 -0
- package/kits/coder/skills/performance-profiling/SKILL.md +333 -0
- package/kits/coder/skills/performance-profiling/assets/.gitkeep +1 -0
- package/kits/coder/skills/performance-profiling/references/deep-dive.md +21 -0
- package/kits/coder/skills/performance-profiling/scripts/validate.py +56 -0
- package/kits/coder/skills/plan-writing/SKILL.md +360 -0
- package/kits/coder/skills/plan-writing/assets/.gitkeep +1 -0
- package/kits/coder/skills/plan-writing/references/deep-dive.md +21 -0
- package/kits/coder/skills/plan-writing/scripts/validate.py +56 -0
- package/kits/coder/skills/postgres-patterns/SKILL.md +361 -0
- package/kits/coder/skills/prompt-engineering/SKILL.md +277 -0
- package/kits/coder/skills/queue-patterns/SKILL.md +359 -0
- package/kits/coder/skills/queue-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/queue-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/queue-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/react-native-patterns/SKILL.md +393 -0
- package/kits/coder/skills/react-patterns/SKILL.md +319 -0
- package/kits/coder/skills/realtime-patterns/SKILL.md +506 -0
- package/kits/coder/skills/realtime-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/realtime-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/realtime-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/redis-patterns/SKILL.md +484 -0
- package/kits/coder/skills/security-fundamentals/SKILL.md +363 -0
- package/kits/coder/skills/security-fundamentals/assets/.gitkeep +1 -0
- package/kits/coder/skills/security-fundamentals/references/deep-dive.md +21 -0
- package/kits/coder/skills/security-fundamentals/scripts/security_scan.py +326 -0
- package/kits/coder/skills/security-fundamentals/scripts/validate.py +56 -0
- package/kits/coder/skills/seo-patterns/SKILL.md +262 -0
- package/kits/coder/skills/seo-patterns/scripts/seo_checker.py +211 -0
- package/kits/coder/skills/systematic-debugging/SKILL.md +478 -0
- package/kits/coder/skills/systematic-debugging/assets/.gitkeep +1 -0
- package/kits/coder/skills/systematic-debugging/references/deep-dive.md +21 -0
- package/kits/coder/skills/systematic-debugging/scripts/validate.py +56 -0
- package/kits/coder/skills/tailwind-patterns/SKILL.md +395 -0
- package/kits/coder/skills/terraform-patterns/SKILL.md +470 -0
- package/kits/coder/skills/testing-patterns/SKILL.md +285 -0
- package/kits/coder/skills/testing-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/testing-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/kits/coder/skills/testing-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/typescript-patterns/SKILL.md +417 -0
- package/kits/coder/skills/ui-ux-pro-max/SKILL.md +364 -0
- package/kits/coder/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/kits/coder/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/kits/coder/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/kits/coder/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/kits/coder/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/kits/coder/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/kits/coder/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/kits/coder/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/kits/coder/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/kits/coder/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/kits/coder/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/kits/coder/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/core.py +257 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/design_system.py +488 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/search.py +76 -0
- package/kits/coder/workflows/.gitkeep +20 -0
- package/kits/coder/workflows/create.md +152 -0
- package/kits/coder/workflows/debug.md +223 -0
- package/kits/coder/workflows/deploy.md +283 -0
- package/kits/coder/workflows/orchestrate.md +243 -0
- package/kits/coder/workflows/plan.md +134 -0
- package/kits/coder/workflows/test.md +237 -0
- package/kits/coder/workflows/ui-ux-pro-max.md +109 -0
- package/package.json +49 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: multi-tenancy
|
|
3
|
+
description: Multi-tenant architecture principles and decision-making. Use when designing tenant isolation, data partitioning, context propagation, or building SaaS applications. Covers database strategies, resource isolation, and compliance patterns.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags: [architecture, saas, isolation, tenancy, compliance]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Multi-Tenancy - SaaS Architecture Skill
|
|
9
|
+
|
|
10
|
+
> **Purpose:** Enable AI agents to design and implement robust multi-tenant systems with proper isolation, context propagation, and scalability.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 📑 Navigation
|
|
15
|
+
|
|
16
|
+
- [Philosophy](#-philosophy)
|
|
17
|
+
- [Isolation Strategies](#-isolation-strategies)
|
|
18
|
+
- [Context Propagation](#-context-propagation)
|
|
19
|
+
- [Decision Frameworks](#-decision-frameworks)
|
|
20
|
+
- [Anti-Patterns](#-anti-patterns)
|
|
21
|
+
- [Checklist](#-implementation-checklist)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 💡 Philosophy
|
|
26
|
+
|
|
27
|
+
> **"Multi-tenancy is trust architecture—design for distrust, verify always."**
|
|
28
|
+
|
|
29
|
+
| Principle | Implementation |
|
|
30
|
+
| -------------------------- | -------------------------------------------- |
|
|
31
|
+
| **Defense in Depth** | Multiple isolation layers (app + DB + infra) |
|
|
32
|
+
| **Context Everywhere** | Tenant ID flows through every layer |
|
|
33
|
+
| **Fail Closed** | Missing tenant context = deny access |
|
|
34
|
+
| **Explicit Over Implicit** | Never infer tenant, always verify |
|
|
35
|
+
| **Audit Everything** | Log all cross-boundary access |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 🔄 ISOLATION STRATEGIES
|
|
40
|
+
|
|
41
|
+
### Database Isolation Spectrum
|
|
42
|
+
|
|
43
|
+
| Strategy | Isolation | Cost | Complexity | When to Use |
|
|
44
|
+
| --------------------- | --------- | ------ | ---------- | ------------------------------------------- |
|
|
45
|
+
| **Shared DB + RLS** | Medium | Low | Low | Startups, <100 tenants, cost-sensitive |
|
|
46
|
+
| **Schema-per-Tenant** | High | Medium | Medium | 100-1000 tenants, moderate compliance |
|
|
47
|
+
| **DB-per-Tenant** | Highest | High | High | Enterprise, HIPAA/Financial, data residency |
|
|
48
|
+
|
|
49
|
+
### Row-Level Security (RLS) Pattern
|
|
50
|
+
|
|
51
|
+
```sql
|
|
52
|
+
-- 1. Enable RLS on tenant tables
|
|
53
|
+
ALTER TABLE conversations ENABLE ROW LEVEL SECURITY;
|
|
54
|
+
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
|
|
55
|
+
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
|
56
|
+
|
|
57
|
+
-- 2. Create isolation policy
|
|
58
|
+
CREATE POLICY tenant_isolation ON conversations
|
|
59
|
+
FOR ALL
|
|
60
|
+
USING (tenant_id = current_setting('app.tenant_id')::uuid);
|
|
61
|
+
|
|
62
|
+
-- 3. Set context at connection start
|
|
63
|
+
SET app.tenant_id = 'tenant-uuid-here';
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Schema-per-Tenant Pattern
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Database: app_db
|
|
70
|
+
├── tenant_acme/ # Schema for ACME Corp
|
|
71
|
+
│ ├── users
|
|
72
|
+
│ ├── conversations
|
|
73
|
+
│ └── ...
|
|
74
|
+
├── tenant_globex/ # Schema for Globex Inc
|
|
75
|
+
│ ├── users
|
|
76
|
+
│ ├── conversations
|
|
77
|
+
│ └── ...
|
|
78
|
+
└── public/ # Shared lookup tables
|
|
79
|
+
├── plans
|
|
80
|
+
└── features
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### DB-per-Tenant Pattern
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
Master Database (shared)
|
|
87
|
+
├── tenants # Tenant registry
|
|
88
|
+
│ ├── id, name, slug
|
|
89
|
+
│ ├── db_connection_string
|
|
90
|
+
│ └── redis_db_index
|
|
91
|
+
└── plans
|
|
92
|
+
|
|
93
|
+
Tenant Databases (isolated)
|
|
94
|
+
├── acme_db # Full isolation
|
|
95
|
+
├── globex_db # Full isolation
|
|
96
|
+
└── ...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 🔀 CONTEXT PROPAGATION
|
|
102
|
+
|
|
103
|
+
### Request Lifecycle
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
┌─────────────────────────────────────────────────────────┐
|
|
107
|
+
│ 1. TENANT RESOLUTION (Middleware) │
|
|
108
|
+
├─────────────────────────────────────────────────────────┤
|
|
109
|
+
│ Extract from: subdomain | header | JWT claim | path │
|
|
110
|
+
│ Validate: tenant exists & active │
|
|
111
|
+
│ Cache: Redis TTL 5-15 mins │
|
|
112
|
+
│ Attach: to request-scoped context │
|
|
113
|
+
└─────────────────────────────────────────────────────────┘
|
|
114
|
+
│
|
|
115
|
+
▼
|
|
116
|
+
┌─────────────────────────────────────────────────────────┐
|
|
117
|
+
│ 2. REQUEST-SCOPED CONTEXT │
|
|
118
|
+
├─────────────────────────────────────────────────────────┤
|
|
119
|
+
│ context = { │
|
|
120
|
+
│ tenantId: "acme", │
|
|
121
|
+
│ tenantConfig: { features, limits, dbPool }, │
|
|
122
|
+
│ userId: "user123", │
|
|
123
|
+
│ requestId: "req-uuid" │
|
|
124
|
+
│ } │
|
|
125
|
+
└─────────────────────────────────────────────────────────┘
|
|
126
|
+
│
|
|
127
|
+
▼
|
|
128
|
+
┌─────────────────────────────────────────────────────────┐
|
|
129
|
+
│ 3. SERVICE LAYER (Tenant-Aware) │
|
|
130
|
+
├─────────────────────────────────────────────────────────┤
|
|
131
|
+
│ All queries automatically filtered by tenant_id │
|
|
132
|
+
│ Repository base class includes tenant filter │
|
|
133
|
+
└─────────────────────────────────────────────────────────┘
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Context Technologies by Platform
|
|
137
|
+
|
|
138
|
+
| Platform | Mechanism | Pattern |
|
|
139
|
+
| --------------- | ----------------------------- | ------------------------- |
|
|
140
|
+
| **Node.js** | `AsyncLocalStorage` | Thread-safe async context |
|
|
141
|
+
| **Python** | `contextvars` | Async context manager |
|
|
142
|
+
| **Java/Spring** | `ThreadLocal` + Request scope | Bean scoping |
|
|
143
|
+
| **.NET** | `AsyncLocal<T>` | Async flow context |
|
|
144
|
+
| **Go** | `context.Context` | Explicit propagation |
|
|
145
|
+
|
|
146
|
+
### Background Job Context
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// ❌ WRONG: Context lost in background job
|
|
150
|
+
queue.add("sendEmail", { userId, templateId });
|
|
151
|
+
|
|
152
|
+
// ✅ CORRECT: Tenant context preserved
|
|
153
|
+
queue.add("sendEmail", {
|
|
154
|
+
tenantId, // ALWAYS include
|
|
155
|
+
userId,
|
|
156
|
+
templateId,
|
|
157
|
+
correlationId, // For tracing
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Job processor
|
|
161
|
+
async function processJob(job) {
|
|
162
|
+
const { tenantId, ...data } = job.data;
|
|
163
|
+
await setTenantContext(tenantId); // Restore context
|
|
164
|
+
// Process with tenant context...
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🧭 DECISION FRAMEWORKS
|
|
171
|
+
|
|
172
|
+
### Isolation Level Decision Tree
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
START
|
|
176
|
+
│
|
|
177
|
+
▼
|
|
178
|
+
┌──────────────────────────┐
|
|
179
|
+
│ Compliance Requirements? │
|
|
180
|
+
│ (HIPAA, PCIDSS, SOC2) │
|
|
181
|
+
└──────────────────────────┘
|
|
182
|
+
│
|
|
183
|
+
├── Yes → DB-per-Tenant + Dedicated Compute
|
|
184
|
+
│
|
|
185
|
+
▼
|
|
186
|
+
┌──────────────────────────┐
|
|
187
|
+
│ Enterprise Customers? │
|
|
188
|
+
│ (Paying for isolation) │
|
|
189
|
+
└──────────────────────────┘
|
|
190
|
+
│
|
|
191
|
+
├── Yes → Hybrid (Shared for SMB, Dedicated for Enterprise)
|
|
192
|
+
│
|
|
193
|
+
▼
|
|
194
|
+
┌──────────────────────────┐
|
|
195
|
+
│ Expected Tenant Count? │
|
|
196
|
+
└──────────────────────────┘
|
|
197
|
+
│
|
|
198
|
+
├── <100 → Shared DB + RLS
|
|
199
|
+
├── 100-1000 → Schema-per-Tenant
|
|
200
|
+
└── >1000 → Shared DB + RLS + Good Sharding
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Resource Isolation Matrix
|
|
204
|
+
|
|
205
|
+
| Resource | Shared Strategy | Isolated Strategy |
|
|
206
|
+
| -------------- | ------------------------- | -------------------- |
|
|
207
|
+
| **Database** | RLS + tenant_id column | DB-per-tenant |
|
|
208
|
+
| **Redis** | Key prefix `{tenant}:` | DB index per tenant |
|
|
209
|
+
| **S3/Storage** | Prefix `tenants/{id}/` | Bucket per tenant |
|
|
210
|
+
| **Queue** | tenant_id in job data | Queue per tenant |
|
|
211
|
+
| **WebSocket** | Room prefix `tenant:{id}` | Namespace per tenant |
|
|
212
|
+
|
|
213
|
+
### Tenant Identification Priority
|
|
214
|
+
|
|
215
|
+
| Method | Security | When to Use |
|
|
216
|
+
| ----------------- | -------- | -------------------------------- |
|
|
217
|
+
| **JWT Claim** | High | API calls with auth |
|
|
218
|
+
| **Subdomain** | High | `acme.app.com` |
|
|
219
|
+
| **Custom Header** | Medium | Internal services, microservices |
|
|
220
|
+
| **Path Segment** | Low | `/api/tenants/{id}/...` (avoid) |
|
|
221
|
+
| **Query Param** | Very Low | Never use for tenant ID |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## ❌ ANTI-PATTERNS
|
|
226
|
+
|
|
227
|
+
### Critical Mistakes
|
|
228
|
+
|
|
229
|
+
| Anti-Pattern | Risk | Correct Approach |
|
|
230
|
+
| --------------------------------- | ---------------------------------- | --------------------------------------------- |
|
|
231
|
+
| **Trust client tenant ID** | Data breach | Validate from auth token/subdomain |
|
|
232
|
+
| **No RLS on shared tables** | SQL injection → full DB exposure | Enable RLS as defense in depth |
|
|
233
|
+
| **Global cache without prefix** | Cross-tenant data leakage | Always prefix: `{tenant}:{key}` |
|
|
234
|
+
| **Background job without tenant** | Orphaned operations, wrong context | Include tenant_id in EVERY job |
|
|
235
|
+
| **Single connection pool** | Noisy neighbor, unclear isolation | Pool per tenant or connection tagging |
|
|
236
|
+
| **Tenant ID in URL path** | Tampering risk | Subdomain or header (cleaner, safer) |
|
|
237
|
+
| **No audit logging** | Cannot detect breaches | Log all cross-boundary access |
|
|
238
|
+
| **Skipping context in async** | Context lost in callbacks | Use AsyncLocalStorage or explicit propagation |
|
|
239
|
+
|
|
240
|
+
### Code Smells
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// ❌ SMELL: Direct query without tenant filter
|
|
244
|
+
const users = await db.query("SELECT * FROM users");
|
|
245
|
+
|
|
246
|
+
// ✅ CORRECT: Always include tenant filter
|
|
247
|
+
const users = await db.query("SELECT * FROM users WHERE tenant_id = $1", [
|
|
248
|
+
tenantId,
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
// ❌ SMELL: Trusting user input for tenant
|
|
252
|
+
const tenantId = req.query.tenant; // NEVER
|
|
253
|
+
|
|
254
|
+
// ✅ CORRECT: Extract from verified source
|
|
255
|
+
const tenantId = req.headers["x-tenant-id"]; // From gateway
|
|
256
|
+
// OR
|
|
257
|
+
const tenantId = extractFromJWT(req.auth.token);
|
|
258
|
+
// OR
|
|
259
|
+
const tenantId = extractFromSubdomain(req.hostname);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## ✅ IMPLEMENTATION CHECKLIST
|
|
265
|
+
|
|
266
|
+
### Tenant Resolution
|
|
267
|
+
|
|
268
|
+
- [ ] Tenant extracted from trusted source (subdomain/header/JWT)
|
|
269
|
+
- [ ] Tenant existence validated against master DB
|
|
270
|
+
- [ ] Tenant config cached with appropriate TTL (5-15 mins)
|
|
271
|
+
- [ ] Invalid/missing tenant returns 404/401 (not 500)
|
|
272
|
+
- [ ] Tenant context attached to request early in middleware
|
|
273
|
+
|
|
274
|
+
### Data Isolation
|
|
275
|
+
|
|
276
|
+
- [ ] RLS enabled on ALL tenant tables (defense in depth)
|
|
277
|
+
- [ ] All repositories include tenant filter by default
|
|
278
|
+
- [ ] Database connection tagged with tenant context
|
|
279
|
+
- [ ] Cross-tenant queries explicitly blocked at service layer
|
|
280
|
+
- [ ] tenant_id column is NOT NULL + indexed
|
|
281
|
+
|
|
282
|
+
### Context Propagation
|
|
283
|
+
|
|
284
|
+
- [ ] Request-scoped context mechanism in place
|
|
285
|
+
- [ ] Context flows through async boundaries
|
|
286
|
+
- [ ] Background jobs include tenant_id + correlation_id
|
|
287
|
+
- [ ] WebSocket connections have tenant context
|
|
288
|
+
- [ ] Logs include tenant_id for every entry
|
|
289
|
+
|
|
290
|
+
### Resource Isolation
|
|
291
|
+
|
|
292
|
+
- [ ] Cache keys prefixed: `{tenant}:{key}`
|
|
293
|
+
- [ ] Storage paths include tenant: `tenants/{id}/...`
|
|
294
|
+
- [ ] Rate limiting applied per tenant
|
|
295
|
+
- [ ] Queue jobs tagged with tenant context
|
|
296
|
+
- [ ] Metrics labeled by tenant for monitoring
|
|
297
|
+
|
|
298
|
+
### Compliance & Security
|
|
299
|
+
|
|
300
|
+
- [ ] Audit logs for all data access
|
|
301
|
+
- [ ] No cross-tenant data exposure in errors
|
|
302
|
+
- [ ] Data residency requirements met
|
|
303
|
+
- [ ] Tenant offboarding procedure documented
|
|
304
|
+
- [ ] Regular penetration testing includes tenant isolation
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## 📚 References
|
|
309
|
+
|
|
310
|
+
- [Azure Multi-Tenant Architecture Patterns](https://docs.microsoft.com/en-us/azure/architecture/guide/multitenant/overview)
|
|
311
|
+
- [PostgreSQL Row Level Security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
|
|
312
|
+
- [SaaS Tenant Isolation Strategies](https://aws.amazon.com/blogs/apn/multi-tenant-saas-database-tenancy-patterns/)
|
|
313
|
+
- [AsyncLocalStorage (Node.js)](https://nodejs.org/api/async_context.html)
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
> **Remember:** In multi-tenant systems, ONE missed tenant filter = ALL customer data exposed. Verify tenant context at every boundary, filter everywhere, and audit continuously.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Assets directory - add templates, images, etc.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Reference Documentation for Multi Tenancy
|
|
2
|
+
|
|
3
|
+
[TODO: Add detailed reference content here]
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
[Detailed explanation of concepts]
|
|
8
|
+
|
|
9
|
+
## Deep Dive Topics
|
|
10
|
+
|
|
11
|
+
### Topic 1
|
|
12
|
+
|
|
13
|
+
[Content]
|
|
14
|
+
|
|
15
|
+
### Topic 2
|
|
16
|
+
|
|
17
|
+
[Content]
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
[Real-world examples]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example validator for multi-tenancy
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python validate.py <project_path>
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate(project_path: str) -> dict:
|
|
14
|
+
"""Main validation logic"""
|
|
15
|
+
results = {
|
|
16
|
+
'errors': [],
|
|
17
|
+
'warnings': [],
|
|
18
|
+
'passed': []
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# TODO: Add validation logic
|
|
22
|
+
results['passed'].append('Placeholder validation passed')
|
|
23
|
+
|
|
24
|
+
return results
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def print_results(results: dict):
|
|
28
|
+
"""Pretty print results"""
|
|
29
|
+
print("\n🔍 Validation Results\n")
|
|
30
|
+
|
|
31
|
+
if results['errors']:
|
|
32
|
+
print(f"❌ Errors ({len(results['errors'])})")
|
|
33
|
+
for error in results['errors']:
|
|
34
|
+
print(f" - {error}")
|
|
35
|
+
|
|
36
|
+
if results['warnings']:
|
|
37
|
+
print(f"\n⚠️ Warnings ({len(results['warnings'])})")
|
|
38
|
+
for warning in results['warnings']:
|
|
39
|
+
print(f" - {warning}")
|
|
40
|
+
|
|
41
|
+
if results['passed']:
|
|
42
|
+
print(f"\n✅ Passed ({len(results['passed'])})")
|
|
43
|
+
for passed in results['passed']:
|
|
44
|
+
print(f" - {passed}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
if len(sys.argv) < 2:
|
|
49
|
+
print("Usage: python validate.py <project_path>")
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
|
|
52
|
+
project_path = sys.argv[1]
|
|
53
|
+
results = validate(project_path)
|
|
54
|
+
print_results(results)
|
|
55
|
+
|
|
56
|
+
sys.exit(1 if results['errors'] else 0)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nodejs-best-practices
|
|
3
|
+
description: Node.js development principles. Express/Fastify patterns, async handling, error management, security.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Node.js Best Practices
|
|
8
|
+
|
|
9
|
+
> JavaScript on the server, done right.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Core Principles
|
|
14
|
+
|
|
15
|
+
1. **Async by default** - Never block the event loop
|
|
16
|
+
2. **Error handling is mandatory** - Catch everything, crash gracefully
|
|
17
|
+
3. **Security first** - Validate input, sanitize output
|
|
18
|
+
4. **Observability built-in** - Log structured, trace distributed
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🔧 Framework Selection
|
|
23
|
+
|
|
24
|
+
| Framework | Best For |
|
|
25
|
+
| ----------- | --------------------------- |
|
|
26
|
+
| **Express** | Simple APIs, custom needs |
|
|
27
|
+
| **Fastify** | Performance, schema-first |
|
|
28
|
+
| **NestJS** | Enterprise, DDD, TypeScript |
|
|
29
|
+
| **Hono** | Edge runtime, lightweight |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 📁 Project Structure
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
src/
|
|
37
|
+
├── modules/
|
|
38
|
+
│ ├── user/
|
|
39
|
+
│ │ ├── user.controller.ts
|
|
40
|
+
│ │ ├── user.service.ts
|
|
41
|
+
│ │ ├── user.repository.ts
|
|
42
|
+
│ │ └── user.dto.ts
|
|
43
|
+
│ └── auth/
|
|
44
|
+
├── common/
|
|
45
|
+
│ ├── middleware/
|
|
46
|
+
│ ├── guards/
|
|
47
|
+
│ └── decorators/
|
|
48
|
+
├── config/
|
|
49
|
+
├── lib/
|
|
50
|
+
└── app.ts
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## ⚡ Async Patterns
|
|
56
|
+
|
|
57
|
+
### Error Handling Wrapper
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const asyncHandler =
|
|
61
|
+
(fn: RequestHandler) => (req: Request, res: Response, next: NextFunction) =>
|
|
62
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
63
|
+
|
|
64
|
+
// Usage
|
|
65
|
+
app.get(
|
|
66
|
+
"/users",
|
|
67
|
+
asyncHandler(async (req, res) => {
|
|
68
|
+
const users = await userService.findAll();
|
|
69
|
+
res.json(users);
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Promise Concurrency
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// ✅ Parallel when independent
|
|
78
|
+
const [users, posts] = await Promise.all([getUsers(), getPosts()]);
|
|
79
|
+
|
|
80
|
+
// ✅ Sequential when dependent
|
|
81
|
+
const user = await getUser(id);
|
|
82
|
+
const posts = await getPostsByUser(user.id);
|
|
83
|
+
|
|
84
|
+
// ✅ Limit concurrency for bulk
|
|
85
|
+
import pLimit from "p-limit";
|
|
86
|
+
const limit = pLimit(5);
|
|
87
|
+
await Promise.all(items.map((i) => limit(() => process(i))));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 🔒 Security
|
|
93
|
+
|
|
94
|
+
### Input Validation (Zod)
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { z } from "zod";
|
|
98
|
+
|
|
99
|
+
const createUserSchema = z.object({
|
|
100
|
+
email: z.string().email(),
|
|
101
|
+
name: z.string().min(2).max(100),
|
|
102
|
+
password: z.string().min(8).max(100),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Middleware
|
|
106
|
+
app.post(
|
|
107
|
+
"/users",
|
|
108
|
+
asyncHandler(async (req, res) => {
|
|
109
|
+
const data = createUserSchema.parse(req.body);
|
|
110
|
+
// data is now typed and validated
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Rate Limiting
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import rateLimit from "express-rate-limit";
|
|
119
|
+
|
|
120
|
+
const limiter = rateLimit({
|
|
121
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
122
|
+
max: 100,
|
|
123
|
+
standardHeaders: true,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
app.use("/api/", limiter);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 🛠️ Error Handling
|
|
132
|
+
|
|
133
|
+
### Error Class
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
class AppError extends Error {
|
|
137
|
+
constructor(
|
|
138
|
+
public statusCode: number,
|
|
139
|
+
public code: string,
|
|
140
|
+
message: string,
|
|
141
|
+
) {
|
|
142
|
+
super(message);
|
|
143
|
+
Error.captureStackTrace(this, this.constructor);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Usage
|
|
148
|
+
throw new AppError(404, "USER_NOT_FOUND", "User not found");
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Global Handler
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
155
|
+
if (err instanceof AppError) {
|
|
156
|
+
return res.status(err.statusCode).json({
|
|
157
|
+
error: { code: err.code, message: err.message },
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.error(err);
|
|
162
|
+
res.status(500).json({
|
|
163
|
+
error: { code: "INTERNAL_ERROR", message: "Something went wrong" },
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 📊 Observability
|
|
171
|
+
|
|
172
|
+
### Structured Logging
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import pino from "pino";
|
|
176
|
+
|
|
177
|
+
const logger = pino({
|
|
178
|
+
level: process.env.LOG_LEVEL || "info",
|
|
179
|
+
transport:
|
|
180
|
+
process.env.NODE_ENV === "development"
|
|
181
|
+
? { target: "pino-pretty" }
|
|
182
|
+
: undefined,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Usage
|
|
186
|
+
logger.info({ userId: user.id }, "User created");
|
|
187
|
+
logger.error({ err, requestId }, "Request failed");
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## ✅ Checklist
|
|
193
|
+
|
|
194
|
+
- [ ] Never `throw` in async without catching
|
|
195
|
+
- [ ] All inputs validated and sanitized
|
|
196
|
+
- [ ] Rate limiting on public endpoints
|
|
197
|
+
- [ ] Structured logging configured
|
|
198
|
+
- [ ] Health check endpoint
|
|
199
|
+
- [ ] Graceful shutdown handling
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## ❌ Anti-Patterns
|
|
204
|
+
|
|
205
|
+
| Don't | Do |
|
|
206
|
+
| ------------------------------ | --------------------------- |
|
|
207
|
+
| `async` without error handling | Wrap with asyncHandler |
|
|
208
|
+
| Callback APIs | Promisify or use async libs |
|
|
209
|
+
| `console.log` in production | Structured logging |
|
|
210
|
+
| Sync file operations | Use async fs methods |
|
|
211
|
+
| Throwing strings | Custom Error classes |
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 🔗 Related Skills
|
|
216
|
+
|
|
217
|
+
- `api-patterns` - API design
|
|
218
|
+
- `auth-patterns` - Authentication
|
|
219
|
+
- `testing-patterns` - Testing
|
|
220
|
+
- `docker-patterns` - Containerization
|