@malamute/ai-rules 1.0.0 → 1.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.
- package/README.md +270 -121
- package/bin/cli.js +5 -2
- package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
- package/configs/_shared/.claude/rules/conventions/git.md +265 -0
- package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
- package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
- package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
- package/configs/_shared/.claude/rules/devops/docker.md +275 -0
- package/configs/_shared/.claude/rules/devops/nx.md +194 -0
- package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
- package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
- package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
- package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
- package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
- package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
- package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
- package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
- package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
- package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
- package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
- package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
- package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
- package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
- package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
- package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
- package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
- package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
- package/configs/_shared/.claude/rules/quality/logging.md +45 -0
- package/configs/_shared/.claude/rules/quality/observability.md +240 -0
- package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
- package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
- package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
- package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
- package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
- package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
- package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
- package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
- package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
- package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
- package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
- package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
- package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
- package/configs/_shared/CLAUDE.md +52 -149
- package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
- package/configs/angular/.claude/rules/core/resource.md +285 -0
- package/configs/angular/.claude/rules/core/signals.md +323 -0
- package/configs/angular/.claude/rules/http.md +338 -0
- package/configs/angular/.claude/rules/routing.md +291 -0
- package/configs/angular/.claude/rules/ssr.md +312 -0
- package/configs/angular/.claude/rules/state/signal-store.md +408 -0
- package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
- package/configs/angular/.claude/rules/testing.md +7 -7
- package/configs/angular/.claude/rules/ui/aria.md +422 -0
- package/configs/angular/.claude/rules/ui/forms.md +424 -0
- package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
- package/configs/angular/.claude/settings.json +1 -0
- package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
- package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
- package/configs/angular/CLAUDE.md +24 -216
- package/configs/dotnet/.claude/rules/background-services.md +552 -0
- package/configs/dotnet/.claude/rules/configuration.md +426 -0
- package/configs/dotnet/.claude/rules/ddd.md +447 -0
- package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
- package/configs/dotnet/.claude/rules/mediatr.md +320 -0
- package/configs/dotnet/.claude/rules/middleware.md +489 -0
- package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
- package/configs/dotnet/.claude/rules/validation.md +388 -0
- package/configs/dotnet/.claude/settings.json +21 -3
- package/configs/dotnet/CLAUDE.md +53 -286
- package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
- package/configs/fastapi/.claude/rules/dependencies.md +170 -0
- package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
- package/configs/fastapi/.claude/rules/lifespan.md +274 -0
- package/configs/fastapi/.claude/rules/middleware.md +229 -0
- package/configs/fastapi/.claude/rules/pydantic.md +433 -0
- package/configs/fastapi/.claude/rules/responses.md +251 -0
- package/configs/fastapi/.claude/rules/routers.md +202 -0
- package/configs/fastapi/.claude/rules/security.md +222 -0
- package/configs/fastapi/.claude/rules/testing.md +251 -0
- package/configs/fastapi/.claude/rules/websockets.md +298 -0
- package/configs/fastapi/.claude/settings.json +33 -0
- package/configs/fastapi/CLAUDE.md +144 -0
- package/configs/flask/.claude/rules/blueprints.md +208 -0
- package/configs/flask/.claude/rules/cli.md +285 -0
- package/configs/flask/.claude/rules/configuration.md +281 -0
- package/configs/flask/.claude/rules/context.md +238 -0
- package/configs/flask/.claude/rules/error-handlers.md +278 -0
- package/configs/flask/.claude/rules/extensions.md +278 -0
- package/configs/flask/.claude/rules/flask.md +171 -0
- package/configs/flask/.claude/rules/marshmallow.md +206 -0
- package/configs/flask/.claude/rules/security.md +267 -0
- package/configs/flask/.claude/rules/testing.md +284 -0
- package/configs/flask/.claude/settings.json +33 -0
- package/configs/flask/CLAUDE.md +166 -0
- package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
- package/configs/nestjs/.claude/rules/filters.md +376 -0
- package/configs/nestjs/.claude/rules/interceptors.md +317 -0
- package/configs/nestjs/.claude/rules/middleware.md +321 -0
- package/configs/nestjs/.claude/rules/modules.md +26 -0
- package/configs/nestjs/.claude/rules/pipes.md +351 -0
- package/configs/nestjs/.claude/rules/websockets.md +451 -0
- package/configs/nestjs/.claude/settings.json +16 -2
- package/configs/nestjs/CLAUDE.md +57 -215
- package/configs/nextjs/.claude/rules/api-routes.md +358 -0
- package/configs/nextjs/.claude/rules/authentication.md +355 -0
- package/configs/nextjs/.claude/rules/components.md +52 -0
- package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
- package/configs/nextjs/.claude/rules/database.md +400 -0
- package/configs/nextjs/.claude/rules/middleware.md +303 -0
- package/configs/nextjs/.claude/rules/routing.md +324 -0
- package/configs/nextjs/.claude/rules/seo.md +350 -0
- package/configs/nextjs/.claude/rules/server-actions.md +353 -0
- package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
- package/configs/nextjs/.claude/settings.json +5 -0
- package/configs/nextjs/CLAUDE.md +69 -331
- package/package.json +23 -9
- package/src/cli.js +220 -0
- package/src/config.js +29 -0
- package/src/index.js +13 -0
- package/src/installer.js +361 -0
- package/src/merge.js +116 -0
- package/src/tech-config.json +29 -0
- package/src/utils.js +96 -0
- package/configs/python/.claude/rules/flask.md +0 -332
- package/configs/python/.claude/settings.json +0 -18
- package/configs/python/CLAUDE.md +0 -273
- package/src/install.js +0 -315
- /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
- /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
- /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
- /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
- /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
- /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Observability (Logs, Metrics, Traces)
|
|
7
|
+
|
|
8
|
+
## Three Pillars
|
|
9
|
+
|
|
10
|
+
| Pillar | Purpose | Tools |
|
|
11
|
+
|--------|---------|-------|
|
|
12
|
+
| **Logs** | Event records, debugging | ELK, Loki, CloudWatch |
|
|
13
|
+
| **Metrics** | Numeric measurements | Prometheus, Datadog, CloudWatch |
|
|
14
|
+
| **Traces** | Request flow across services | Jaeger, Zipkin, X-Ray |
|
|
15
|
+
|
|
16
|
+
## Structured Logging
|
|
17
|
+
|
|
18
|
+
### Format (JSON)
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
23
|
+
"level": "info",
|
|
24
|
+
"message": "User logged in",
|
|
25
|
+
"service": "auth-service",
|
|
26
|
+
"version": "1.2.3",
|
|
27
|
+
"traceId": "abc-123-def",
|
|
28
|
+
"spanId": "span-456",
|
|
29
|
+
"userId": "user-789",
|
|
30
|
+
"duration": 45,
|
|
31
|
+
"metadata": {
|
|
32
|
+
"ip": "192.168.1.1",
|
|
33
|
+
"userAgent": "Mozilla/5.0..."
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Log Levels
|
|
39
|
+
|
|
40
|
+
| Level | When to Use |
|
|
41
|
+
|-------|-------------|
|
|
42
|
+
| `error` | Failures requiring attention |
|
|
43
|
+
| `warn` | Recoverable issues, deprecations |
|
|
44
|
+
| `info` | Business events, state changes |
|
|
45
|
+
| `debug` | Development details (disabled in prod) |
|
|
46
|
+
|
|
47
|
+
### What to Log
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
✓ Request received (info)
|
|
51
|
+
✓ Request completed with duration (info)
|
|
52
|
+
✓ Business events (user created, order placed)
|
|
53
|
+
✓ External service calls with duration
|
|
54
|
+
✓ Errors with stack trace and context
|
|
55
|
+
✓ Authentication events
|
|
56
|
+
|
|
57
|
+
✗ Passwords, tokens, secrets
|
|
58
|
+
✗ Credit card numbers, PII
|
|
59
|
+
✗ Full request/response bodies
|
|
60
|
+
✗ Health check spam
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Correlation IDs
|
|
64
|
+
|
|
65
|
+
Pass trace ID through entire request chain:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// Middleware to extract/generate trace ID
|
|
69
|
+
app.use((req, res, next) => {
|
|
70
|
+
req.traceId = req.headers['x-trace-id'] || uuid();
|
|
71
|
+
res.setHeader('x-trace-id', req.traceId);
|
|
72
|
+
next();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Include in all logs
|
|
76
|
+
logger.info('Processing order', {
|
|
77
|
+
traceId: req.traceId,
|
|
78
|
+
orderId: order.id,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Pass to downstream services
|
|
82
|
+
await fetch(url, {
|
|
83
|
+
headers: { 'x-trace-id': req.traceId }
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Metrics
|
|
88
|
+
|
|
89
|
+
### Types
|
|
90
|
+
|
|
91
|
+
| Type | Use Case | Example |
|
|
92
|
+
|------|----------|---------|
|
|
93
|
+
| Counter | Cumulative totals | `http_requests_total` |
|
|
94
|
+
| Gauge | Current value | `active_connections` |
|
|
95
|
+
| Histogram | Value distribution | `request_duration_seconds` |
|
|
96
|
+
| Summary | Percentiles | `request_latency_p99` |
|
|
97
|
+
|
|
98
|
+
### Key Metrics (RED Method)
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
# Rate - requests per second
|
|
102
|
+
http_requests_total{method="GET", path="/api/users"}
|
|
103
|
+
|
|
104
|
+
# Errors - error rate
|
|
105
|
+
http_requests_total{status="5xx"} / http_requests_total
|
|
106
|
+
|
|
107
|
+
# Duration - latency percentiles
|
|
108
|
+
http_request_duration_seconds{quantile="0.99"}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Business Metrics
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
# Orders
|
|
115
|
+
orders_created_total
|
|
116
|
+
orders_value_total
|
|
117
|
+
orders_by_status{status="completed"}
|
|
118
|
+
|
|
119
|
+
# Users
|
|
120
|
+
users_registered_total
|
|
121
|
+
users_active_daily
|
|
122
|
+
|
|
123
|
+
# Performance
|
|
124
|
+
cache_hit_ratio
|
|
125
|
+
queue_depth
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Health Checks
|
|
129
|
+
|
|
130
|
+
### Endpoints
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Liveness - is the app running?
|
|
134
|
+
app.get('/health/live', (req, res) => {
|
|
135
|
+
res.status(200).json({ status: 'ok' });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Readiness - can the app serve traffic?
|
|
139
|
+
app.get('/health/ready', async (req, res) => {
|
|
140
|
+
const checks = {
|
|
141
|
+
database: await checkDatabase(),
|
|
142
|
+
redis: await checkRedis(),
|
|
143
|
+
externalApi: await checkExternalApi(),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const healthy = Object.values(checks).every(c => c.status === 'ok');
|
|
147
|
+
res.status(healthy ? 200 : 503).json({ checks });
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Response Format
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"status": "healthy",
|
|
156
|
+
"version": "1.2.3",
|
|
157
|
+
"uptime": 3600,
|
|
158
|
+
"checks": {
|
|
159
|
+
"database": { "status": "ok", "latency": 5 },
|
|
160
|
+
"redis": { "status": "ok", "latency": 2 },
|
|
161
|
+
"externalApi": { "status": "degraded", "latency": 500 }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Distributed Tracing
|
|
167
|
+
|
|
168
|
+
### Span Structure
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
Trace: abc-123
|
|
172
|
+
├── Span: API Gateway (50ms)
|
|
173
|
+
│ ├── Span: Auth Service (10ms)
|
|
174
|
+
│ └── Span: Order Service (35ms)
|
|
175
|
+
│ ├── Span: Database Query (15ms)
|
|
176
|
+
│ └── Span: Payment Service (18ms)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### OpenTelemetry Setup
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
183
|
+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
184
|
+
|
|
185
|
+
const sdk = new NodeSDK({
|
|
186
|
+
serviceName: 'order-service',
|
|
187
|
+
traceExporter: new OTLPTraceExporter({
|
|
188
|
+
url: 'http://jaeger:4318/v1/traces',
|
|
189
|
+
}),
|
|
190
|
+
instrumentations: [getNodeAutoInstrumentations()],
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
sdk.start();
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Alerting Rules
|
|
197
|
+
|
|
198
|
+
### SLO-based Alerts
|
|
199
|
+
|
|
200
|
+
```yaml
|
|
201
|
+
# Alert when error rate > 1% for 5 minutes
|
|
202
|
+
- alert: HighErrorRate
|
|
203
|
+
expr: |
|
|
204
|
+
sum(rate(http_requests_total{status=~"5.."}[5m]))
|
|
205
|
+
/ sum(rate(http_requests_total[5m])) > 0.01
|
|
206
|
+
for: 5m
|
|
207
|
+
labels:
|
|
208
|
+
severity: critical
|
|
209
|
+
|
|
210
|
+
# Alert when p99 latency > 1s
|
|
211
|
+
- alert: HighLatency
|
|
212
|
+
expr: |
|
|
213
|
+
histogram_quantile(0.99,
|
|
214
|
+
rate(http_request_duration_seconds_bucket[5m])
|
|
215
|
+
) > 1
|
|
216
|
+
for: 5m
|
|
217
|
+
labels:
|
|
218
|
+
severity: warning
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Dashboards
|
|
222
|
+
|
|
223
|
+
### Essential Panels
|
|
224
|
+
|
|
225
|
+
1. **Request rate** (per endpoint)
|
|
226
|
+
2. **Error rate** (4xx, 5xx breakdown)
|
|
227
|
+
3. **Latency** (p50, p95, p99)
|
|
228
|
+
4. **Active connections**
|
|
229
|
+
5. **Resource usage** (CPU, memory)
|
|
230
|
+
6. **Business metrics** (orders, users)
|
|
231
|
+
|
|
232
|
+
## Anti-patterns
|
|
233
|
+
|
|
234
|
+
- Logging sensitive data
|
|
235
|
+
- No correlation IDs
|
|
236
|
+
- Alerting on symptoms not causes
|
|
237
|
+
- Too many alerts (alert fatigue)
|
|
238
|
+
- Metrics without labels
|
|
239
|
+
- No retention policy
|
|
240
|
+
- Health checks that don't check dependencies
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing Principles
|
|
7
|
+
|
|
8
|
+
## Test Structure (AAA Pattern)
|
|
9
|
+
|
|
10
|
+
1. **Arrange** - Set up test data and dependencies
|
|
11
|
+
2. **Act** - Execute the code under test
|
|
12
|
+
3. **Assert** - Verify the expected outcome
|
|
13
|
+
|
|
14
|
+
## Naming Conventions
|
|
15
|
+
|
|
16
|
+
| Language | Pattern |
|
|
17
|
+
|----------|---------|
|
|
18
|
+
| TypeScript | `should_ExpectedBehavior_When_Condition` |
|
|
19
|
+
| Python | `test_action_condition_expected` |
|
|
20
|
+
| C# | `MethodName_Scenario_ExpectedBehavior` |
|
|
21
|
+
|
|
22
|
+
## Test Organization
|
|
23
|
+
|
|
24
|
+
- Group tests by class/module
|
|
25
|
+
- Sub-group by method or behavior
|
|
26
|
+
- Use descriptive describe/context blocks
|
|
27
|
+
|
|
28
|
+
## Mocking Best Practices
|
|
29
|
+
|
|
30
|
+
- Mock only external dependencies
|
|
31
|
+
- Don't mock internal implementation details
|
|
32
|
+
- Use factories for test data
|
|
33
|
+
- Verify interactions when behavior matters
|
|
34
|
+
|
|
35
|
+
## Test Isolation
|
|
36
|
+
|
|
37
|
+
- Each test must be independent
|
|
38
|
+
- No shared mutable state
|
|
39
|
+
- Tests should run in any order
|
|
40
|
+
- Clean up after each test
|
|
41
|
+
|
|
42
|
+
## What to Test
|
|
43
|
+
|
|
44
|
+
| Priority | What | Why |
|
|
45
|
+
|----------|------|-----|
|
|
46
|
+
| High | Business logic | Core value |
|
|
47
|
+
| High | Edge cases | Common bugs |
|
|
48
|
+
| Medium | Error paths | Graceful degradation |
|
|
49
|
+
| Medium | Integration points | Contract verification |
|
|
50
|
+
| Low | Trivial getters/setters | Low value |
|
|
51
|
+
|
|
52
|
+
## Coverage Guidelines
|
|
53
|
+
|
|
54
|
+
- **80%+** on business logic
|
|
55
|
+
- **100%** on critical paths (payments, auth)
|
|
56
|
+
- Don't chase coverage numbers blindly
|
|
57
|
+
- One meaningful test > many trivial tests
|
|
58
|
+
|
|
59
|
+
## Anti-Patterns
|
|
60
|
+
|
|
61
|
+
- Tests that depend on execution order
|
|
62
|
+
- Testing implementation, not behavior
|
|
63
|
+
- Excessive mocking (testing mocks, not code)
|
|
64
|
+
- Flaky tests (non-deterministic)
|
|
65
|
+
- Slow tests in the unit test suite
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/.env*"
|
|
4
|
+
- "**/secrets/**"
|
|
5
|
+
- "**/config/**"
|
|
6
|
+
- "**/appsettings*.json"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Secrets Management
|
|
10
|
+
|
|
11
|
+
## Core Principles
|
|
12
|
+
|
|
13
|
+
- **Never commit secrets** to version control
|
|
14
|
+
- **Rotate regularly** (90 days max for critical secrets)
|
|
15
|
+
- **Least privilege** - only grant necessary access
|
|
16
|
+
- **Audit access** - log who accessed what
|
|
17
|
+
- **Encrypt at rest** and in transit
|
|
18
|
+
|
|
19
|
+
## Environment Files
|
|
20
|
+
|
|
21
|
+
### Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
.env # Shared defaults (commit)
|
|
25
|
+
.env.local # Local overrides (gitignore)
|
|
26
|
+
.env.development # Dev environment (commit, no secrets)
|
|
27
|
+
.env.production # Prod template (commit, no secrets)
|
|
28
|
+
.env.production.local # Prod secrets (gitignore)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### .gitignore
|
|
32
|
+
|
|
33
|
+
```gitignore
|
|
34
|
+
# Always ignore
|
|
35
|
+
.env.local
|
|
36
|
+
.env.*.local
|
|
37
|
+
*.pem
|
|
38
|
+
*.key
|
|
39
|
+
**/secrets/
|
|
40
|
+
credentials.json
|
|
41
|
+
service-account.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Format
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# .env.example (commit this as template)
|
|
48
|
+
DATABASE_URL=postgresql://user:password@localhost:5432/db
|
|
49
|
+
REDIS_URL=redis://localhost:6379
|
|
50
|
+
JWT_SECRET=your-secret-here
|
|
51
|
+
API_KEY=your-api-key-here
|
|
52
|
+
|
|
53
|
+
# Required for production
|
|
54
|
+
# AWS_ACCESS_KEY_ID=
|
|
55
|
+
# AWS_SECRET_ACCESS_KEY=
|
|
56
|
+
# STRIPE_SECRET_KEY=
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Secret Types & Storage
|
|
60
|
+
|
|
61
|
+
| Type | Example | Storage |
|
|
62
|
+
|------|---------|---------|
|
|
63
|
+
| API Keys | Stripe, Twilio | Vault / Cloud Secrets |
|
|
64
|
+
| DB Credentials | Connection strings | Vault / Cloud Secrets |
|
|
65
|
+
| JWT Secrets | Signing keys | Vault / Env vars |
|
|
66
|
+
| OAuth Secrets | Client secrets | Cloud Secrets |
|
|
67
|
+
| Encryption Keys | AES keys | KMS / HSM |
|
|
68
|
+
| Certificates | TLS certs | Cert Manager |
|
|
69
|
+
|
|
70
|
+
## Cloud Provider Solutions
|
|
71
|
+
|
|
72
|
+
### AWS Secrets Manager
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
|
|
76
|
+
|
|
77
|
+
async function getSecret(secretName: string): Promise<string> {
|
|
78
|
+
const client = new SecretsManagerClient({ region: "us-east-1" });
|
|
79
|
+
const response = await client.send(
|
|
80
|
+
new GetSecretValueCommand({ SecretId: secretName })
|
|
81
|
+
);
|
|
82
|
+
return response.SecretString!;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Azure Key Vault
|
|
87
|
+
|
|
88
|
+
```csharp
|
|
89
|
+
var client = new SecretClient(
|
|
90
|
+
new Uri("https://myvault.vault.azure.net/"),
|
|
91
|
+
new DefaultAzureCredential()
|
|
92
|
+
);
|
|
93
|
+
KeyVaultSecret secret = await client.GetSecretAsync("my-secret");
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Google Secret Manager
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from google.cloud import secretmanager
|
|
100
|
+
|
|
101
|
+
def get_secret(secret_id: str) -> str:
|
|
102
|
+
client = secretmanager.SecretManagerServiceClient()
|
|
103
|
+
name = f"projects/my-project/secrets/{secret_id}/versions/latest"
|
|
104
|
+
response = client.access_secret_version(request={"name": name})
|
|
105
|
+
return response.payload.data.decode("UTF-8")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## CI/CD Secrets
|
|
109
|
+
|
|
110
|
+
### GitHub Actions
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
# Set in: Settings > Secrets and variables > Actions
|
|
114
|
+
|
|
115
|
+
jobs:
|
|
116
|
+
deploy:
|
|
117
|
+
environment: production # Environment-specific secrets
|
|
118
|
+
steps:
|
|
119
|
+
- name: Deploy
|
|
120
|
+
env:
|
|
121
|
+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
|
122
|
+
API_KEY: ${{ secrets.API_KEY }}
|
|
123
|
+
run: ./deploy.sh
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### GitLab CI
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
# Set in: Settings > CI/CD > Variables
|
|
130
|
+
# Mark as "Protected" and "Masked"
|
|
131
|
+
|
|
132
|
+
deploy:
|
|
133
|
+
script:
|
|
134
|
+
- echo "$DATABASE_URL" # Available as env var
|
|
135
|
+
environment:
|
|
136
|
+
name: production
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Docker & Kubernetes
|
|
140
|
+
|
|
141
|
+
### Docker Secrets
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# docker-compose.yml
|
|
145
|
+
services:
|
|
146
|
+
app:
|
|
147
|
+
secrets:
|
|
148
|
+
- db_password
|
|
149
|
+
environment:
|
|
150
|
+
- DB_PASSWORD_FILE=/run/secrets/db_password
|
|
151
|
+
|
|
152
|
+
secrets:
|
|
153
|
+
db_password:
|
|
154
|
+
file: ./secrets/db_password.txt
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Kubernetes Secrets
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
# sealed-secret.yaml (encrypted, safe to commit)
|
|
161
|
+
apiVersion: bitnami.com/v1alpha1
|
|
162
|
+
kind: SealedSecret
|
|
163
|
+
metadata:
|
|
164
|
+
name: my-secret
|
|
165
|
+
spec:
|
|
166
|
+
encryptedData:
|
|
167
|
+
password: AgBy3i4OJSWK+...
|
|
168
|
+
|
|
169
|
+
# Usage in pod
|
|
170
|
+
env:
|
|
171
|
+
- name: DB_PASSWORD
|
|
172
|
+
valueFrom:
|
|
173
|
+
secretKeyRef:
|
|
174
|
+
name: my-secret
|
|
175
|
+
key: password
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Validation at Startup
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// Validate required secrets exist at startup
|
|
182
|
+
const requiredSecrets = [
|
|
183
|
+
'DATABASE_URL',
|
|
184
|
+
'JWT_SECRET',
|
|
185
|
+
'REDIS_URL',
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
for (const secret of requiredSecrets) {
|
|
189
|
+
if (!process.env[secret]) {
|
|
190
|
+
throw new Error(`Missing required secret: ${secret}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Secret Rotation
|
|
196
|
+
|
|
197
|
+
1. **Generate new secret**
|
|
198
|
+
2. **Deploy with both old and new** (accept both)
|
|
199
|
+
3. **Update all consumers** to use new
|
|
200
|
+
4. **Remove old secret** from config
|
|
201
|
+
5. **Revoke old secret** in provider
|
|
202
|
+
|
|
203
|
+
## Audit Checklist
|
|
204
|
+
|
|
205
|
+
- [ ] No secrets in code or commits
|
|
206
|
+
- [ ] All secrets in gitignore
|
|
207
|
+
- [ ] Secrets encrypted at rest
|
|
208
|
+
- [ ] Access logged and auditable
|
|
209
|
+
- [ ] Rotation policy in place
|
|
210
|
+
- [ ] Secrets validated at startup
|
|
211
|
+
- [ ] Different secrets per environment
|
|
212
|
+
- [ ] Backup/recovery procedure documented
|
|
213
|
+
|
|
214
|
+
## Anti-patterns
|
|
215
|
+
|
|
216
|
+
- Committing `.env` with real secrets
|
|
217
|
+
- Using same secrets across environments
|
|
218
|
+
- Sharing secrets via Slack/email
|
|
219
|
+
- Hardcoding secrets in Dockerfiles
|
|
220
|
+
- Not rotating compromised secrets immediately
|
|
221
|
+
- Logging secrets (even accidentally)
|
|
222
|
+
- Storing secrets in plain text files on servers
|