ship-safe 1.0.0 → 2.0.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 +175 -19
- package/ai-defense/cost-protection.md +292 -0
- package/ai-defense/llm-security-checklist.md +324 -0
- package/ai-defense/prompt-injection-patterns.js +283 -0
- package/cli/bin/ship-safe.js +8 -1
- package/cli/commands/init.js +2 -1
- package/cli/utils/patterns.js +345 -24
- package/configs/firebase/firestore-rules.txt +215 -0
- package/configs/firebase/security-checklist.md +236 -0
- package/configs/firebase/storage-rules.txt +206 -0
- package/configs/gitignore-template +258 -0
- package/configs/supabase/rls-templates.sql +242 -0
- package/configs/supabase/secure-client.ts +225 -0
- package/configs/supabase/security-checklist.md +278 -0
- package/package.json +12 -3
- package/snippets/README.md +89 -25
- package/snippets/api-security/api-security-checklist.md +412 -0
- package/snippets/api-security/cors-config.ts +322 -0
- package/snippets/api-security/input-validation.ts +430 -0
- package/snippets/auth/jwt-checklist.md +322 -0
- package/snippets/rate-limiting/nextjs-middleware.ts +211 -0
- package/snippets/rate-limiting/upstash-ratelimit.ts +229 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ship-safe contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -6,6 +6,9 @@ You're shipping fast. You're using AI to write code. You're one `git push` away
|
|
|
6
6
|
|
|
7
7
|
**Ship Safe** is a security toolkit for indie hackers and vibe coders who want to secure their MVP in 5 minutes, not 5 days.
|
|
8
8
|
|
|
9
|
+
[](https://www.npmjs.com/package/ship-safe)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
|
|
9
12
|
---
|
|
10
13
|
|
|
11
14
|
## Quick Start
|
|
@@ -23,6 +26,18 @@ npx ship-safe init
|
|
|
23
26
|
|
|
24
27
|
That's it. Three commands to secure your MVP.
|
|
25
28
|
|
|
29
|
+
### Let AI Do It For You
|
|
30
|
+
|
|
31
|
+
Copy this prompt to your AI coding assistant:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
Run "npx ship-safe scan ." on my project and fix any secrets you find.
|
|
35
|
+
Then run "npx ship-safe init" to add security configs.
|
|
36
|
+
Explain what you're doing as you go.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
[More AI prompts for specific frameworks](./AI_SECURITY_PROMPT.md)
|
|
40
|
+
|
|
26
41
|
---
|
|
27
42
|
|
|
28
43
|
## Why This Exists
|
|
@@ -59,7 +74,14 @@ npx ship-safe scan . -v
|
|
|
59
74
|
|
|
60
75
|
**Exit codes:** Returns `1` if secrets found (useful for CI), `0` if clean.
|
|
61
76
|
|
|
62
|
-
**Detects
|
|
77
|
+
**Detects 50+ secret patterns:**
|
|
78
|
+
- **AI/ML:** OpenAI, Anthropic, Google AI, Cohere, Replicate, Hugging Face
|
|
79
|
+
- **Auth:** Clerk, Auth0, Supabase Auth
|
|
80
|
+
- **Cloud:** AWS, Google Cloud, Azure
|
|
81
|
+
- **Database:** Supabase, PlanetScale, Neon, MongoDB, PostgreSQL, MySQL
|
|
82
|
+
- **Payment:** Stripe, PayPal
|
|
83
|
+
- **Messaging:** Twilio, SendGrid, Resend
|
|
84
|
+
- **And more:** GitHub tokens, private keys, JWTs, generic secrets
|
|
63
85
|
|
|
64
86
|
---
|
|
65
87
|
|
|
@@ -111,19 +133,149 @@ npx ship-safe init -f
|
|
|
111
133
|
|
|
112
134
|
### [`/configs`](./configs)
|
|
113
135
|
**Secure defaults for popular stacks. Drop-in ready.**
|
|
114
|
-
- [Next.js Security Headers](./configs/nextjs-security-headers.js) - CSP, X-Frame-Options, and more
|
|
115
136
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
137
|
+
| Stack | Files |
|
|
138
|
+
|-------|-------|
|
|
139
|
+
| **Next.js** | [Security Headers](./configs/nextjs-security-headers.js) - CSP, X-Frame-Options, HSTS |
|
|
140
|
+
| **Supabase** | [RLS Templates](./configs/supabase/rls-templates.sql) \| [Security Checklist](./configs/supabase/security-checklist.md) \| [Secure Client](./configs/supabase/secure-client.ts) |
|
|
141
|
+
| **Firebase** | [Firestore Rules](./configs/firebase/firestore-rules.txt) \| [Storage Rules](./configs/firebase/storage-rules.txt) \| [Security Checklist](./configs/firebase/security-checklist.md) |
|
|
119
142
|
|
|
120
143
|
### [`/snippets`](./snippets)
|
|
121
144
|
**Copy-paste code blocks for common security patterns.**
|
|
122
|
-
|
|
145
|
+
|
|
146
|
+
| Category | Files |
|
|
147
|
+
|----------|-------|
|
|
148
|
+
| **Rate Limiting** | [Upstash Redis](./snippets/rate-limiting/upstash-ratelimit.ts) \| [Next.js Middleware](./snippets/rate-limiting/nextjs-middleware.ts) |
|
|
149
|
+
| **Authentication** | [JWT Security Checklist](./snippets/auth/jwt-checklist.md) |
|
|
150
|
+
| **API Security** | [CORS Config](./snippets/api-security/cors-config.ts) \| [Input Validation](./snippets/api-security/input-validation.ts) \| [API Checklist](./snippets/api-security/api-security-checklist.md) |
|
|
123
151
|
|
|
124
152
|
### [`/ai-defense`](./ai-defense)
|
|
125
|
-
**Protect your AI features from abuse.**
|
|
126
|
-
|
|
153
|
+
**Protect your AI features from abuse and cost explosions.**
|
|
154
|
+
|
|
155
|
+
| File | Description |
|
|
156
|
+
|------|-------------|
|
|
157
|
+
| [LLM Security Checklist](./ai-defense/llm-security-checklist.md) | Based on OWASP LLM Top 10 - prompt injection, data protection, scope control |
|
|
158
|
+
| [Prompt Injection Patterns](./ai-defense/prompt-injection-patterns.js) | Regex patterns to detect 25+ injection attempts |
|
|
159
|
+
| [Cost Protection Guide](./ai-defense/cost-protection.md) | Prevent $50k surprise bills - rate limits, budget caps, circuit breakers |
|
|
160
|
+
| [System Prompt Armor](./ai-defense/system-prompt-armor.md) | Template for hardened system prompts |
|
|
161
|
+
|
|
162
|
+
### [`/scripts`](./scripts)
|
|
163
|
+
**Automated scanning tools. Run them in CI or locally.**
|
|
164
|
+
- [Secret Scanner](./scripts/scan_secrets.py) - Python version of the secret scanner
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## AI/LLM Security
|
|
169
|
+
|
|
170
|
+
Building with AI? Don't let it bankrupt you or get hijacked.
|
|
171
|
+
|
|
172
|
+
### Quick Setup
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { containsInjectionAttempt } from './ai-defense/prompt-injection-patterns';
|
|
176
|
+
|
|
177
|
+
async function handleChat(userInput: string) {
|
|
178
|
+
// 1. Check for injection attempts
|
|
179
|
+
const { detected } = containsInjectionAttempt(userInput);
|
|
180
|
+
if (detected) {
|
|
181
|
+
return "I can't process that request.";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 2. Rate limit per user
|
|
185
|
+
const { success } = await ratelimit.limit(userId);
|
|
186
|
+
if (!success) {
|
|
187
|
+
return "Too many requests. Please slow down.";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 3. Check budget before calling
|
|
191
|
+
await checkUserBudget(userId, estimatedCost);
|
|
192
|
+
|
|
193
|
+
// 4. Make the API call with token limits
|
|
194
|
+
const response = await openai.chat.completions.create({
|
|
195
|
+
model: 'gpt-4',
|
|
196
|
+
messages,
|
|
197
|
+
max_tokens: 500, // Hard cap
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
return response;
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Cost Protection Layers
|
|
205
|
+
|
|
206
|
+
1. **Token limits** - Cap input/output per request
|
|
207
|
+
2. **Rate limits** - Cap requests per user (10/min)
|
|
208
|
+
3. **Budget caps** - Daily ($1) and monthly ($10) per user
|
|
209
|
+
4. **Circuit breaker** - Disable AI when global budget hit
|
|
210
|
+
5. **Provider limits** - Set hard limits in OpenAI/Anthropic dashboard
|
|
211
|
+
|
|
212
|
+
[Full cost protection guide →](./ai-defense/cost-protection.md)
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Database Security
|
|
217
|
+
|
|
218
|
+
### Supabase RLS Templates
|
|
219
|
+
|
|
220
|
+
```sql
|
|
221
|
+
-- Users can only see their own data
|
|
222
|
+
CREATE POLICY "Users own their data" ON items
|
|
223
|
+
FOR ALL USING (auth.uid() = user_id);
|
|
224
|
+
|
|
225
|
+
-- Read-only public data
|
|
226
|
+
CREATE POLICY "Public read access" ON public_items
|
|
227
|
+
FOR SELECT USING (true);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
[6 more RLS patterns →](./configs/supabase/rls-templates.sql)
|
|
231
|
+
|
|
232
|
+
### Firebase Security Rules
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
// Users can only access their own documents
|
|
236
|
+
match /users/{userId} {
|
|
237
|
+
allow read, write: if request.auth != null
|
|
238
|
+
&& request.auth.uid == userId;
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
[Full Firestore rules template →](./configs/firebase/firestore-rules.txt)
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## API Security
|
|
247
|
+
|
|
248
|
+
### CORS (Don't use `*` in production)
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const ALLOWED_ORIGINS = [
|
|
252
|
+
'https://yourapp.com',
|
|
253
|
+
'https://www.yourapp.com',
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
// Only allow specific origins
|
|
257
|
+
if (origin && ALLOWED_ORIGINS.includes(origin)) {
|
|
258
|
+
headers['Access-Control-Allow-Origin'] = origin;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
[CORS configs for Next.js, Express, Fastify, Hono →](./snippets/api-security/cors-config.ts)
|
|
263
|
+
|
|
264
|
+
### Input Validation (Zod)
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const createUserSchema = z.object({
|
|
268
|
+
email: z.string().email().max(255),
|
|
269
|
+
password: z.string().min(8).max(128),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const result = createUserSchema.safeParse(body);
|
|
273
|
+
if (!result.success) {
|
|
274
|
+
return Response.json({ error: result.error.issues }, { status: 400 });
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
[Full validation patterns →](./snippets/api-security/input-validation.ts)
|
|
127
279
|
|
|
128
280
|
---
|
|
129
281
|
|
|
@@ -152,11 +304,13 @@ The scan exits with code `1` if secrets are found, failing your build.
|
|
|
152
304
|
|
|
153
305
|
## The 5-Minute Security Checklist
|
|
154
306
|
|
|
155
|
-
1. Run `npx ship-safe scan .` on your project
|
|
156
|
-
2. Run `npx ship-safe init` to add security configs
|
|
157
|
-
3. Add security headers to your Next.js config
|
|
158
|
-
4. Run `npx ship-safe checklist` before launching
|
|
159
|
-
5. If using AI features,
|
|
307
|
+
1. ✅ Run `npx ship-safe scan .` on your project
|
|
308
|
+
2. ✅ Run `npx ship-safe init` to add security configs
|
|
309
|
+
3. ✅ Add security headers to your Next.js config
|
|
310
|
+
4. ✅ Run `npx ship-safe checklist` before launching
|
|
311
|
+
5. ✅ If using AI features, implement [cost protection](./ai-defense/cost-protection.md)
|
|
312
|
+
6. ✅ If using Supabase, check the [RLS checklist](./configs/supabase/security-checklist.md)
|
|
313
|
+
7. ✅ If using Firebase, check the [Firebase checklist](./configs/firebase/security-checklist.md)
|
|
160
314
|
|
|
161
315
|
---
|
|
162
316
|
|
|
@@ -178,15 +332,17 @@ Found a security pattern that saved your app? Share it!
|
|
|
178
332
|
3. Include educational comments explaining *why* it matters
|
|
179
333
|
4. Open a PR
|
|
180
334
|
|
|
335
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
336
|
+
|
|
181
337
|
---
|
|
182
338
|
|
|
183
|
-
##
|
|
339
|
+
## Security Standards Reference
|
|
184
340
|
|
|
185
|
-
|
|
186
|
-
- [
|
|
187
|
-
- [
|
|
188
|
-
- [
|
|
189
|
-
- [ ]
|
|
341
|
+
This toolkit is based on:
|
|
342
|
+
- [OWASP Top 10 Web 2025](https://owasp.org/Top10/)
|
|
343
|
+
- [OWASP Top 10 Mobile 2024](https://owasp.org/www-project-mobile-top-10/)
|
|
344
|
+
- [OWASP LLM Top 10 2025](https://genai.owasp.org/llm-top-10/)
|
|
345
|
+
- [OWASP API Security Top 10 2023](https://owasp.org/API-Security/)
|
|
190
346
|
|
|
191
347
|
---
|
|
192
348
|
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# AI Cost Protection Guide
|
|
2
|
+
|
|
3
|
+
**Prevent your AI features from bankrupting you.**
|
|
4
|
+
|
|
5
|
+
Real incidents: $50k+ bills from runaway AI usage, abuse, or misconfiguration.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Why Cost Protection Matters
|
|
10
|
+
|
|
11
|
+
| Scenario | Risk |
|
|
12
|
+
|----------|------|
|
|
13
|
+
| Leaked API key | Anyone can rack up charges on your account |
|
|
14
|
+
| No rate limits | Single user sends 10,000 requests |
|
|
15
|
+
| Long responses | GPT-4 response = $0.03-0.12 per request |
|
|
16
|
+
| Infinite loops | Code bug calls AI repeatedly |
|
|
17
|
+
| Viral launch | 10x traffic = 10x costs |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Layer 1: API Key Security
|
|
22
|
+
|
|
23
|
+
### Keep keys server-side only
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// BAD: Key in frontend code
|
|
27
|
+
const openai = new OpenAI({ apiKey: 'sk-...' });
|
|
28
|
+
|
|
29
|
+
// GOOD: Key in server environment variable
|
|
30
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Scan for leaked keys
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx ship-safe scan .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Rotate keys periodically
|
|
40
|
+
|
|
41
|
+
- OpenAI: Dashboard > API Keys > Create new > Delete old
|
|
42
|
+
- Anthropic: Console > API Keys > Rotate
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Layer 2: Request Limits
|
|
47
|
+
|
|
48
|
+
### Token limits per request
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// Limit input
|
|
52
|
+
const MAX_INPUT_CHARS = 2000;
|
|
53
|
+
if (userInput.length > MAX_INPUT_CHARS) {
|
|
54
|
+
return "Message too long";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Limit output
|
|
58
|
+
const response = await openai.chat.completions.create({
|
|
59
|
+
model: 'gpt-4',
|
|
60
|
+
messages: messages,
|
|
61
|
+
max_tokens: 500, // Hard cap on response length
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Rate limiting per user
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { Ratelimit } from '@upstash/ratelimit';
|
|
69
|
+
|
|
70
|
+
const aiRatelimit = new Ratelimit({
|
|
71
|
+
redis,
|
|
72
|
+
limiter: Ratelimit.slidingWindow(10, '1 m'), // 10 requests/minute
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
async function aiHandler(request, userId) {
|
|
76
|
+
const { success } = await aiRatelimit.limit(userId);
|
|
77
|
+
if (!success) {
|
|
78
|
+
return new Response('Too many requests', { status: 429 });
|
|
79
|
+
}
|
|
80
|
+
// Process request
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Global rate limiting
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
const globalRatelimit = new Ratelimit({
|
|
88
|
+
redis,
|
|
89
|
+
limiter: Ratelimit.slidingWindow(1000, '1 h'), // 1000 requests/hour total
|
|
90
|
+
prefix: 'ratelimit:global:ai',
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Layer 3: Budget Caps
|
|
97
|
+
|
|
98
|
+
### Track usage in database
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
interface AIUsageRecord {
|
|
102
|
+
userId: string;
|
|
103
|
+
model: string;
|
|
104
|
+
inputTokens: number;
|
|
105
|
+
outputTokens: number;
|
|
106
|
+
cost: number;
|
|
107
|
+
timestamp: Date;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function logUsage(usage: AIUsageRecord) {
|
|
111
|
+
await db.aiUsage.create({ data: usage });
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Calculate cost before request
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// Approximate cost calculation
|
|
119
|
+
const COSTS = {
|
|
120
|
+
'gpt-4': { input: 0.03 / 1000, output: 0.06 / 1000 },
|
|
121
|
+
'gpt-4-turbo': { input: 0.01 / 1000, output: 0.03 / 1000 },
|
|
122
|
+
'gpt-3.5-turbo': { input: 0.0005 / 1000, output: 0.0015 / 1000 },
|
|
123
|
+
'claude-3-opus': { input: 0.015 / 1000, output: 0.075 / 1000 },
|
|
124
|
+
'claude-3-sonnet': { input: 0.003 / 1000, output: 0.015 / 1000 },
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
function estimateCost(model: string, inputTokens: number, maxOutputTokens: number) {
|
|
128
|
+
const rates = COSTS[model] || COSTS['gpt-4'];
|
|
129
|
+
return (inputTokens * rates.input) + (maxOutputTokens * rates.output);
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Enforce user budget
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
async function checkUserBudget(userId: string, estimatedCost: number) {
|
|
137
|
+
const dailyLimit = 1.00; // $1/day per user
|
|
138
|
+
const monthlyLimit = 10.00; // $10/month per user
|
|
139
|
+
|
|
140
|
+
const today = new Date();
|
|
141
|
+
today.setHours(0, 0, 0, 0);
|
|
142
|
+
|
|
143
|
+
const dailyUsage = await db.aiUsage.aggregate({
|
|
144
|
+
where: { userId, timestamp: { gte: today } },
|
|
145
|
+
_sum: { cost: true },
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if ((dailyUsage._sum.cost || 0) + estimatedCost > dailyLimit) {
|
|
149
|
+
throw new Error('Daily AI budget exceeded');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Similar check for monthly
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Global budget circuit breaker
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
async function checkGlobalBudget(estimatedCost: number) {
|
|
160
|
+
const monthlyBudget = 500.00; // $500/month total
|
|
161
|
+
|
|
162
|
+
const monthStart = new Date();
|
|
163
|
+
monthStart.setDate(1);
|
|
164
|
+
monthStart.setHours(0, 0, 0, 0);
|
|
165
|
+
|
|
166
|
+
const monthlyUsage = await db.aiUsage.aggregate({
|
|
167
|
+
where: { timestamp: { gte: monthStart } },
|
|
168
|
+
_sum: { cost: true },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if ((monthlyUsage._sum.cost || 0) + estimatedCost > monthlyBudget) {
|
|
172
|
+
// CIRCUIT BREAKER: Disable AI features
|
|
173
|
+
await disableAIFeatures();
|
|
174
|
+
await alertAdmins('AI budget exceeded - features disabled');
|
|
175
|
+
throw new Error('Service temporarily unavailable');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Layer 4: Provider-Side Limits
|
|
183
|
+
|
|
184
|
+
### OpenAI usage limits
|
|
185
|
+
|
|
186
|
+
1. Go to platform.openai.com
|
|
187
|
+
2. Settings > Limits
|
|
188
|
+
3. Set monthly hard limit
|
|
189
|
+
|
|
190
|
+
### Anthropic usage limits
|
|
191
|
+
|
|
192
|
+
1. Go to console.anthropic.com
|
|
193
|
+
2. Settings > Usage Limits
|
|
194
|
+
3. Set spend limits
|
|
195
|
+
|
|
196
|
+
### Set up billing alerts
|
|
197
|
+
|
|
198
|
+
Most providers support alerts at:
|
|
199
|
+
- 50% of budget
|
|
200
|
+
- 80% of budget
|
|
201
|
+
- 100% of budget
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Layer 5: Monitoring & Alerts
|
|
206
|
+
|
|
207
|
+
### Real-time usage dashboard
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// Track key metrics
|
|
211
|
+
const metrics = {
|
|
212
|
+
requestsPerMinute: await getRequestsPerMinute(),
|
|
213
|
+
costToday: await getCostToday(),
|
|
214
|
+
costThisMonth: await getCostThisMonth(),
|
|
215
|
+
topUsers: await getTopUsersByUsage(),
|
|
216
|
+
errorRate: await getErrorRate(),
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Alert on anomalies
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
async function checkForAnomalies() {
|
|
224
|
+
// Alert if hourly cost exceeds normal
|
|
225
|
+
const hourlyCost = await getHourlyCost();
|
|
226
|
+
const avgHourlyCost = await getAvgHourlyCost();
|
|
227
|
+
|
|
228
|
+
if (hourlyCost > avgHourlyCost * 3) {
|
|
229
|
+
await sendAlert({
|
|
230
|
+
type: 'anomaly',
|
|
231
|
+
message: `Hourly AI cost spike: $${hourlyCost} (avg: $${avgHourlyCost})`,
|
|
232
|
+
severity: 'high',
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Alert if single user is abusing
|
|
237
|
+
const topUser = await getTopUserThisHour();
|
|
238
|
+
if (topUser.requests > 100) {
|
|
239
|
+
await sendAlert({
|
|
240
|
+
type: 'abuse',
|
|
241
|
+
message: `User ${topUser.id} made ${topUser.requests} AI requests this hour`,
|
|
242
|
+
severity: 'medium',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Cost Comparison: Models
|
|
251
|
+
|
|
252
|
+
| Model | Input ($/1M tokens) | Output ($/1M tokens) | Best For |
|
|
253
|
+
|-------|---------------------|----------------------|----------|
|
|
254
|
+
| GPT-4 | $30 | $60 | Complex tasks |
|
|
255
|
+
| GPT-4 Turbo | $10 | $30 | Long context |
|
|
256
|
+
| GPT-3.5 Turbo | $0.50 | $1.50 | Simple tasks |
|
|
257
|
+
| Claude 3 Opus | $15 | $75 | Highest quality |
|
|
258
|
+
| Claude 3 Sonnet | $3 | $15 | Balanced |
|
|
259
|
+
| Claude 3 Haiku | $0.25 | $1.25 | Speed/cost |
|
|
260
|
+
|
|
261
|
+
**Tip:** Use cheaper models for simple tasks, reserve expensive models for complex ones.
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
function selectModel(task: string) {
|
|
265
|
+
const simpleTasks = ['summarize', 'classify', 'extract'];
|
|
266
|
+
const complexTasks = ['code', 'analyze', 'create'];
|
|
267
|
+
|
|
268
|
+
if (simpleTasks.some(t => task.includes(t))) {
|
|
269
|
+
return 'gpt-3.5-turbo'; // Cheap and fast
|
|
270
|
+
}
|
|
271
|
+
return 'gpt-4-turbo'; // Better but pricier
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Quick Implementation Checklist
|
|
278
|
+
|
|
279
|
+
1. [ ] API keys in server-side environment variables only
|
|
280
|
+
2. [ ] Input length limits (e.g., 2000 chars)
|
|
281
|
+
3. [ ] Output token limits (e.g., 500 tokens)
|
|
282
|
+
4. [ ] Rate limiting per user (e.g., 10 requests/minute)
|
|
283
|
+
5. [ ] Daily budget per user (e.g., $1/day)
|
|
284
|
+
6. [ ] Global monthly budget with circuit breaker
|
|
285
|
+
7. [ ] Provider-side hard limits configured
|
|
286
|
+
8. [ ] Billing alerts at 50%, 80%, 100%
|
|
287
|
+
9. [ ] Usage tracking in database
|
|
288
|
+
10. [ ] Anomaly detection and alerting
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
**Remember: A $50,000 surprise bill is a real risk. Implement these layers before launch.**
|