reflexive 1.0.0 → 1.0.2
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/dashboard/out/404/index.html +1 -1
- package/dashboard/out/404.html +1 -1
- package/dashboard/out/__next.__PAGE__.txt +2 -2
- package/dashboard/out/__next._full.txt +3 -3
- package/dashboard/out/__next._head.txt +1 -1
- package/dashboard/out/__next._index.txt +2 -2
- package/dashboard/out/__next._tree.txt +2 -2
- package/dashboard/out/_next/static/chunks/209a165917e29ad7.css +3 -0
- package/dashboard/out/_next/static/chunks/{5be66f773d014294.js → 875b2f53bcfbbafb.js} +3 -3
- package/dashboard/out/_not-found/__next._full.txt +2 -2
- package/dashboard/out/_not-found/__next._head.txt +1 -1
- package/dashboard/out/_not-found/__next._index.txt +2 -2
- package/dashboard/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dashboard/out/_not-found/__next._not-found.txt +1 -1
- package/dashboard/out/_not-found/__next._tree.txt +2 -2
- package/dashboard/out/_not-found/index.html +1 -1
- package/dashboard/out/_not-found/index.txt +2 -2
- package/dashboard/out/index.html +1 -1
- package/dashboard/out/index.txt +3 -3
- package/dist/cli.d.ts +0 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +164 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -4
- package/dist/index.js.map +1 -1
- package/dist/managers/process-manager.d.ts +22 -8
- package/dist/managers/process-manager.d.ts.map +1 -1
- package/dist/managers/process-manager.js +50 -42
- package/dist/managers/process-manager.js.map +1 -1
- package/dist/mcp/knowledge-tools.d.ts +16 -0
- package/dist/mcp/knowledge-tools.d.ts.map +1 -0
- package/dist/mcp/knowledge-tools.js +206 -0
- package/dist/mcp/knowledge-tools.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/docs/README.md +87 -0
- package/docs/api-reference.md +1422 -0
- package/docs/deployment.md +885 -0
- package/docs/developer-guide.md +899 -0
- package/docs/getting-started.md +374 -0
- package/docs/index.md +158 -0
- package/docs/user-guide.md +990 -0
- package/package.json +2 -1
- package/dashboard/out/_next/static/chunks/82bbed47af1fbb39.css +0 -3
- /package/dashboard/out/_next/static/{vYzD8icR4qocco4JtUfu3 → Y9FaARzAOsacwE8LNVv_4}/_buildManifest.js +0 -0
- /package/dashboard/out/_next/static/{vYzD8icR4qocco4JtUfu3 → Y9FaARzAOsacwE8LNVv_4}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
# Deployment Guide
|
|
2
|
+
|
|
3
|
+
Guide for deploying Reflexive in production environments, particularly hosted mode.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Deployment Options](#deployment-options)
|
|
8
|
+
- [Railway Deployment](#railway-deployment)
|
|
9
|
+
- [One-Click Deploy](#one-click-deploy)
|
|
10
|
+
- [Manual Setup](#manual-setup)
|
|
11
|
+
- [Environment Variables](#environment-variables)
|
|
12
|
+
- [Monitoring](#monitoring)
|
|
13
|
+
- [Docker Deployment](#docker-deployment)
|
|
14
|
+
- [Using Pre-built Image](#using-pre-built-image)
|
|
15
|
+
- [Building Custom Image](#building-custom-image)
|
|
16
|
+
- [Docker Compose](#docker-compose)
|
|
17
|
+
- [Kubernetes](#kubernetes)
|
|
18
|
+
- [Manual Deployment](#manual-deployment)
|
|
19
|
+
- [Build and Deploy](#build-and-deploy)
|
|
20
|
+
- [Process Management](#process-management)
|
|
21
|
+
- [Reverse Proxy](#reverse-proxy)
|
|
22
|
+
- [Security Considerations](#security-considerations)
|
|
23
|
+
- [API Authentication](#api-authentication)
|
|
24
|
+
- [Rate Limiting](#rate-limiting)
|
|
25
|
+
- [Sandbox Isolation](#sandbox-isolation)
|
|
26
|
+
- [Secrets Management](#secrets-management)
|
|
27
|
+
- [Storage Configuration](#storage-configuration)
|
|
28
|
+
- [S3 Storage](#s3-storage)
|
|
29
|
+
- [Cloudflare R2](#cloudflare-r2)
|
|
30
|
+
- [Memory Storage](#memory-storage)
|
|
31
|
+
- [Monitoring and Logging](#monitoring-and-logging)
|
|
32
|
+
- [Health Checks](#health-checks)
|
|
33
|
+
- [Metrics](#metrics)
|
|
34
|
+
- [Log Aggregation](#log-aggregation)
|
|
35
|
+
- [Scaling](#scaling)
|
|
36
|
+
- [Horizontal Scaling](#horizontal-scaling)
|
|
37
|
+
- [Resource Limits](#resource-limits)
|
|
38
|
+
- [Troubleshooting](#troubleshooting)
|
|
39
|
+
|
|
40
|
+
## Deployment Options
|
|
41
|
+
|
|
42
|
+
Reflexive can be deployed in several ways depending on your needs:
|
|
43
|
+
|
|
44
|
+
| Option | Best For | Complexity | Cost |
|
|
45
|
+
|--------|----------|------------|------|
|
|
46
|
+
| Railway | Quick deployment, managed infrastructure | Low | $ |
|
|
47
|
+
| Docker | Containerized deployment, cloud-agnostic | Medium | $$ |
|
|
48
|
+
| Manual | Full control, custom infrastructure | High | $$$ |
|
|
49
|
+
|
|
50
|
+
## Railway Deployment
|
|
51
|
+
|
|
52
|
+
Railway provides the easiest way to deploy Reflexive with minimal configuration.
|
|
53
|
+
|
|
54
|
+
### One-Click Deploy
|
|
55
|
+
|
|
56
|
+
[](https://railway.app/new/template?template=reflexive)
|
|
57
|
+
|
|
58
|
+
Click the button above or use the CLI:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Install Railway CLI
|
|
62
|
+
npm install -g @railway/cli
|
|
63
|
+
|
|
64
|
+
# Login
|
|
65
|
+
railway login
|
|
66
|
+
|
|
67
|
+
# Deploy from directory
|
|
68
|
+
cd reflexive
|
|
69
|
+
railway up
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Manual Setup
|
|
73
|
+
|
|
74
|
+
**Step 1: Create Project**
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
railway login
|
|
78
|
+
railway init
|
|
79
|
+
railway link
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Step 2: Set Environment Variables**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
railway variables set ANTHROPIC_API_KEY=sk-ant-xxxxx
|
|
86
|
+
railway variables set REFLEXIVE_API_KEY=your-secure-key
|
|
87
|
+
railway variables set NODE_ENV=production
|
|
88
|
+
railway variables set PORT=3099
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Step 3: Configure Snapshot Storage**
|
|
92
|
+
|
|
93
|
+
For S3:
|
|
94
|
+
```bash
|
|
95
|
+
railway variables set AWS_ACCESS_KEY_ID=your-access-key
|
|
96
|
+
railway variables set AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
97
|
+
railway variables set REFLEXIVE_SNAPSHOT_BUCKET=reflexive-snapshots
|
|
98
|
+
railway variables set AWS_REGION=us-east-1
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For Cloudflare R2:
|
|
102
|
+
```bash
|
|
103
|
+
railway variables set AWS_ACCESS_KEY_ID=your-r2-access-key
|
|
104
|
+
railway variables set AWS_SECRET_ACCESS_KEY=your-r2-secret-key
|
|
105
|
+
railway variables set REFLEXIVE_S3_ENDPOINT=https://account.r2.cloudflarestorage.com
|
|
106
|
+
railway variables set REFLEXIVE_SNAPSHOT_BUCKET=reflexive-snapshots
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Step 4: Deploy**
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
railway up
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Step 5: Get URL**
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
railway domain
|
|
119
|
+
# Returns: https://reflexive-production.up.railway.app
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Environment Variables
|
|
123
|
+
|
|
124
|
+
Required variables for Railway deployment:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Authentication
|
|
128
|
+
ANTHROPIC_API_KEY=sk-ant-xxxxx # Claude API key
|
|
129
|
+
REFLEXIVE_API_KEY=your-secure-key # API key for REST endpoints
|
|
130
|
+
|
|
131
|
+
# Server
|
|
132
|
+
NODE_ENV=production
|
|
133
|
+
PORT=3099 # Railway auto-sets PORT
|
|
134
|
+
|
|
135
|
+
# Snapshot Storage (choose one)
|
|
136
|
+
# Option 1: AWS S3
|
|
137
|
+
AWS_ACCESS_KEY_ID=xxx
|
|
138
|
+
AWS_SECRET_ACCESS_KEY=xxx
|
|
139
|
+
REFLEXIVE_SNAPSHOT_BUCKET=bucket-name
|
|
140
|
+
AWS_REGION=us-east-1
|
|
141
|
+
|
|
142
|
+
# Option 2: Cloudflare R2
|
|
143
|
+
AWS_ACCESS_KEY_ID=xxx
|
|
144
|
+
AWS_SECRET_ACCESS_KEY=xxx
|
|
145
|
+
REFLEXIVE_S3_ENDPOINT=https://xxx.r2.cloudflarestorage.com
|
|
146
|
+
REFLEXIVE_SNAPSHOT_BUCKET=bucket-name
|
|
147
|
+
|
|
148
|
+
# Optional: Vercel Sandbox
|
|
149
|
+
VERCEL_TOKEN=xxx # For sandbox mode
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Monitoring
|
|
153
|
+
|
|
154
|
+
Railway provides built-in monitoring:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# View logs
|
|
158
|
+
railway logs
|
|
159
|
+
|
|
160
|
+
# View metrics
|
|
161
|
+
railway status
|
|
162
|
+
|
|
163
|
+
# Open dashboard
|
|
164
|
+
railway open
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Docker Deployment
|
|
168
|
+
|
|
169
|
+
### Using Pre-built Image
|
|
170
|
+
|
|
171
|
+
Pull and run the official image:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
docker pull reflexive/reflexive:latest
|
|
175
|
+
|
|
176
|
+
docker run -d \
|
|
177
|
+
--name reflexive \
|
|
178
|
+
-p 3099:3099 \
|
|
179
|
+
-e ANTHROPIC_API_KEY=sk-ant-xxx \
|
|
180
|
+
-e REFLEXIVE_API_KEY=your-key \
|
|
181
|
+
-e AWS_ACCESS_KEY_ID=xxx \
|
|
182
|
+
-e AWS_SECRET_ACCESS_KEY=xxx \
|
|
183
|
+
-e REFLEXIVE_SNAPSHOT_BUCKET=snapshots \
|
|
184
|
+
reflexive/reflexive:latest
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Building Custom Image
|
|
188
|
+
|
|
189
|
+
**Create Dockerfile**:
|
|
190
|
+
|
|
191
|
+
```dockerfile
|
|
192
|
+
# Dockerfile
|
|
193
|
+
FROM node:18-alpine
|
|
194
|
+
|
|
195
|
+
# Install dependencies for native modules
|
|
196
|
+
RUN apk add --no-cache python3 make g++
|
|
197
|
+
|
|
198
|
+
WORKDIR /app
|
|
199
|
+
|
|
200
|
+
# Copy package files
|
|
201
|
+
COPY package*.json ./
|
|
202
|
+
|
|
203
|
+
# Install production dependencies
|
|
204
|
+
RUN npm ci --only=production
|
|
205
|
+
|
|
206
|
+
# Copy source
|
|
207
|
+
COPY . .
|
|
208
|
+
|
|
209
|
+
# Build TypeScript
|
|
210
|
+
RUN npm run build
|
|
211
|
+
|
|
212
|
+
# Remove dev dependencies
|
|
213
|
+
RUN npm prune --production
|
|
214
|
+
|
|
215
|
+
# Expose port
|
|
216
|
+
EXPOSE 3099
|
|
217
|
+
|
|
218
|
+
# Health check
|
|
219
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
220
|
+
CMD node -e "require('http').get('http://localhost:3099/api/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"
|
|
221
|
+
|
|
222
|
+
# Run
|
|
223
|
+
CMD ["node", "dist/cli.js", "--mode", "hosted"]
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Build and Run**:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Build
|
|
230
|
+
docker build -t reflexive:latest .
|
|
231
|
+
|
|
232
|
+
# Run
|
|
233
|
+
docker run -d \
|
|
234
|
+
--name reflexive \
|
|
235
|
+
-p 3099:3099 \
|
|
236
|
+
--env-file .env \
|
|
237
|
+
reflexive:latest
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Docker Compose
|
|
241
|
+
|
|
242
|
+
**docker-compose.yml**:
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
version: '3.8'
|
|
246
|
+
|
|
247
|
+
services:
|
|
248
|
+
reflexive:
|
|
249
|
+
build: .
|
|
250
|
+
ports:
|
|
251
|
+
- "3099:3099"
|
|
252
|
+
environment:
|
|
253
|
+
- NODE_ENV=production
|
|
254
|
+
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
|
255
|
+
- REFLEXIVE_API_KEY=${REFLEXIVE_API_KEY}
|
|
256
|
+
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
|
|
257
|
+
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
|
|
258
|
+
- REFLEXIVE_SNAPSHOT_BUCKET=${REFLEXIVE_SNAPSHOT_BUCKET}
|
|
259
|
+
- REFLEXIVE_S3_ENDPOINT=${REFLEXIVE_S3_ENDPOINT}
|
|
260
|
+
restart: unless-stopped
|
|
261
|
+
healthcheck:
|
|
262
|
+
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3099/api/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"]
|
|
263
|
+
interval: 30s
|
|
264
|
+
timeout: 3s
|
|
265
|
+
retries: 3
|
|
266
|
+
start_period: 5s
|
|
267
|
+
volumes:
|
|
268
|
+
- ./config:/app/config:ro
|
|
269
|
+
networks:
|
|
270
|
+
- reflexive-net
|
|
271
|
+
|
|
272
|
+
# Optional: Redis for rate limiting
|
|
273
|
+
redis:
|
|
274
|
+
image: redis:7-alpine
|
|
275
|
+
restart: unless-stopped
|
|
276
|
+
networks:
|
|
277
|
+
- reflexive-net
|
|
278
|
+
|
|
279
|
+
networks:
|
|
280
|
+
reflexive-net:
|
|
281
|
+
driver: bridge
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Start services**:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
docker-compose up -d
|
|
288
|
+
|
|
289
|
+
# View logs
|
|
290
|
+
docker-compose logs -f reflexive
|
|
291
|
+
|
|
292
|
+
# Stop
|
|
293
|
+
docker-compose down
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Kubernetes
|
|
297
|
+
|
|
298
|
+
**deployment.yaml**:
|
|
299
|
+
|
|
300
|
+
```yaml
|
|
301
|
+
apiVersion: apps/v1
|
|
302
|
+
kind: Deployment
|
|
303
|
+
metadata:
|
|
304
|
+
name: reflexive
|
|
305
|
+
spec:
|
|
306
|
+
replicas: 3
|
|
307
|
+
selector:
|
|
308
|
+
matchLabels:
|
|
309
|
+
app: reflexive
|
|
310
|
+
template:
|
|
311
|
+
metadata:
|
|
312
|
+
labels:
|
|
313
|
+
app: reflexive
|
|
314
|
+
spec:
|
|
315
|
+
containers:
|
|
316
|
+
- name: reflexive
|
|
317
|
+
image: reflexive/reflexive:latest
|
|
318
|
+
ports:
|
|
319
|
+
- containerPort: 3099
|
|
320
|
+
env:
|
|
321
|
+
- name: NODE_ENV
|
|
322
|
+
value: "production"
|
|
323
|
+
- name: ANTHROPIC_API_KEY
|
|
324
|
+
valueFrom:
|
|
325
|
+
secretKeyRef:
|
|
326
|
+
name: reflexive-secrets
|
|
327
|
+
key: anthropic-api-key
|
|
328
|
+
- name: REFLEXIVE_API_KEY
|
|
329
|
+
valueFrom:
|
|
330
|
+
secretKeyRef:
|
|
331
|
+
name: reflexive-secrets
|
|
332
|
+
key: reflexive-api-key
|
|
333
|
+
- name: AWS_ACCESS_KEY_ID
|
|
334
|
+
valueFrom:
|
|
335
|
+
secretKeyRef:
|
|
336
|
+
name: aws-secrets
|
|
337
|
+
key: access-key-id
|
|
338
|
+
- name: AWS_SECRET_ACCESS_KEY
|
|
339
|
+
valueFrom:
|
|
340
|
+
secretKeyRef:
|
|
341
|
+
name: aws-secrets
|
|
342
|
+
key: secret-access-key
|
|
343
|
+
- name: REFLEXIVE_SNAPSHOT_BUCKET
|
|
344
|
+
value: "reflexive-snapshots"
|
|
345
|
+
resources:
|
|
346
|
+
requests:
|
|
347
|
+
memory: "512Mi"
|
|
348
|
+
cpu: "500m"
|
|
349
|
+
limits:
|
|
350
|
+
memory: "2Gi"
|
|
351
|
+
cpu: "2000m"
|
|
352
|
+
livenessProbe:
|
|
353
|
+
httpGet:
|
|
354
|
+
path: /api/health
|
|
355
|
+
port: 3099
|
|
356
|
+
initialDelaySeconds: 5
|
|
357
|
+
periodSeconds: 10
|
|
358
|
+
readinessProbe:
|
|
359
|
+
httpGet:
|
|
360
|
+
path: /api/health
|
|
361
|
+
port: 3099
|
|
362
|
+
initialDelaySeconds: 5
|
|
363
|
+
periodSeconds: 5
|
|
364
|
+
---
|
|
365
|
+
apiVersion: v1
|
|
366
|
+
kind: Service
|
|
367
|
+
metadata:
|
|
368
|
+
name: reflexive
|
|
369
|
+
spec:
|
|
370
|
+
selector:
|
|
371
|
+
app: reflexive
|
|
372
|
+
ports:
|
|
373
|
+
- protocol: TCP
|
|
374
|
+
port: 80
|
|
375
|
+
targetPort: 3099
|
|
376
|
+
type: LoadBalancer
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Deploy**:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
# Create secrets
|
|
383
|
+
kubectl create secret generic reflexive-secrets \
|
|
384
|
+
--from-literal=anthropic-api-key=sk-ant-xxx \
|
|
385
|
+
--from-literal=reflexive-api-key=your-key
|
|
386
|
+
|
|
387
|
+
kubectl create secret generic aws-secrets \
|
|
388
|
+
--from-literal=access-key-id=xxx \
|
|
389
|
+
--from-literal=secret-access-key=xxx
|
|
390
|
+
|
|
391
|
+
# Apply deployment
|
|
392
|
+
kubectl apply -f deployment.yaml
|
|
393
|
+
|
|
394
|
+
# Check status
|
|
395
|
+
kubectl get pods
|
|
396
|
+
kubectl get services
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Manual Deployment
|
|
400
|
+
|
|
401
|
+
### Build and Deploy
|
|
402
|
+
|
|
403
|
+
**On your server**:
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
# Clone repository
|
|
407
|
+
git clone https://github.com/yourusername/reflexive.git
|
|
408
|
+
cd reflexive
|
|
409
|
+
|
|
410
|
+
# Install dependencies
|
|
411
|
+
npm ci
|
|
412
|
+
|
|
413
|
+
# Build
|
|
414
|
+
npm run build
|
|
415
|
+
|
|
416
|
+
# Test
|
|
417
|
+
npm test
|
|
418
|
+
|
|
419
|
+
# Start
|
|
420
|
+
NODE_ENV=production node dist/cli.js --mode hosted
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Process Management
|
|
424
|
+
|
|
425
|
+
Use PM2 for process management:
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
# Install PM2
|
|
429
|
+
npm install -g pm2
|
|
430
|
+
|
|
431
|
+
# Create ecosystem file
|
|
432
|
+
cat > ecosystem.config.js << 'EOF'
|
|
433
|
+
module.exports = {
|
|
434
|
+
apps: [{
|
|
435
|
+
name: 'reflexive',
|
|
436
|
+
script: './dist/cli.js',
|
|
437
|
+
args: '--mode hosted',
|
|
438
|
+
instances: 1,
|
|
439
|
+
autorestart: true,
|
|
440
|
+
watch: false,
|
|
441
|
+
max_memory_restart: '2G',
|
|
442
|
+
env: {
|
|
443
|
+
NODE_ENV: 'production',
|
|
444
|
+
PORT: 3099
|
|
445
|
+
}
|
|
446
|
+
}]
|
|
447
|
+
}
|
|
448
|
+
EOF
|
|
449
|
+
|
|
450
|
+
# Start with PM2
|
|
451
|
+
pm2 start ecosystem.config.js
|
|
452
|
+
|
|
453
|
+
# Setup startup script
|
|
454
|
+
pm2 startup
|
|
455
|
+
pm2 save
|
|
456
|
+
|
|
457
|
+
# Monitor
|
|
458
|
+
pm2 monit
|
|
459
|
+
pm2 logs reflexive
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Reverse Proxy
|
|
463
|
+
|
|
464
|
+
**Nginx configuration**:
|
|
465
|
+
|
|
466
|
+
```nginx
|
|
467
|
+
# /etc/nginx/sites-available/reflexive
|
|
468
|
+
upstream reflexive {
|
|
469
|
+
server localhost:3099;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
server {
|
|
473
|
+
listen 80;
|
|
474
|
+
server_name reflexive.yourdomain.com;
|
|
475
|
+
|
|
476
|
+
# Redirect to HTTPS
|
|
477
|
+
return 301 https://$server_name$request_uri;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
server {
|
|
481
|
+
listen 443 ssl http2;
|
|
482
|
+
server_name reflexive.yourdomain.com;
|
|
483
|
+
|
|
484
|
+
# SSL certificates
|
|
485
|
+
ssl_certificate /etc/letsencrypt/live/reflexive.yourdomain.com/fullchain.pem;
|
|
486
|
+
ssl_certificate_key /etc/letsencrypt/live/reflexive.yourdomain.com/privkey.pem;
|
|
487
|
+
|
|
488
|
+
# Security headers
|
|
489
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
490
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
491
|
+
add_header X-XSS-Protection "1; mode=block" always;
|
|
492
|
+
|
|
493
|
+
# Proxy settings
|
|
494
|
+
location / {
|
|
495
|
+
proxy_pass http://reflexive;
|
|
496
|
+
proxy_http_version 1.1;
|
|
497
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
498
|
+
proxy_set_header Connection "upgrade";
|
|
499
|
+
proxy_set_header Host $host;
|
|
500
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
501
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
502
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
503
|
+
|
|
504
|
+
# SSE support
|
|
505
|
+
proxy_buffering off;
|
|
506
|
+
proxy_cache off;
|
|
507
|
+
proxy_read_timeout 24h;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Enable and reload:
|
|
513
|
+
```bash
|
|
514
|
+
ln -s /etc/nginx/sites-available/reflexive /etc/nginx/sites-enabled/
|
|
515
|
+
nginx -t
|
|
516
|
+
systemctl reload nginx
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## Security Considerations
|
|
520
|
+
|
|
521
|
+
### API Authentication
|
|
522
|
+
|
|
523
|
+
**Generate strong API key**:
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
# Generate random key
|
|
527
|
+
openssl rand -base64 32
|
|
528
|
+
|
|
529
|
+
# Set as environment variable
|
|
530
|
+
export REFLEXIVE_API_KEY="generated-key-here"
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**Use in requests**:
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
curl -H "Authorization: Bearer generated-key-here" \
|
|
537
|
+
https://your-reflexive.app/api/sandboxes
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Rate Limiting
|
|
541
|
+
|
|
542
|
+
Rate limiting is built-in. Configure in `reflexive.config.js`:
|
|
543
|
+
|
|
544
|
+
```javascript
|
|
545
|
+
export default {
|
|
546
|
+
mode: 'hosted',
|
|
547
|
+
rateLimit: {
|
|
548
|
+
maxRequests: 100, // Max requests
|
|
549
|
+
windowMs: 60000, // Per minute
|
|
550
|
+
skipPublicPaths: true // Skip /api/health
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Sandbox Isolation
|
|
556
|
+
|
|
557
|
+
Sandboxes are isolated by Vercel's infrastructure:
|
|
558
|
+
- Separate filesystems
|
|
559
|
+
- Network isolation
|
|
560
|
+
- Resource limits (CPU, memory)
|
|
561
|
+
- Automatic cleanup
|
|
562
|
+
|
|
563
|
+
### Secrets Management
|
|
564
|
+
|
|
565
|
+
**Never commit secrets**. Use environment variables or secret managers.
|
|
566
|
+
|
|
567
|
+
**AWS Secrets Manager**:
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
# Store secret
|
|
571
|
+
aws secretsmanager create-secret \
|
|
572
|
+
--name reflexive/anthropic-key \
|
|
573
|
+
--secret-string "sk-ant-xxx"
|
|
574
|
+
|
|
575
|
+
# Retrieve in app
|
|
576
|
+
const secret = await secretsManager.getSecretValue({
|
|
577
|
+
SecretId: 'reflexive/anthropic-key'
|
|
578
|
+
}).promise();
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
**Kubernetes Secrets**:
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
kubectl create secret generic reflexive-secrets \
|
|
585
|
+
--from-literal=anthropic-api-key=sk-ant-xxx
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Storage Configuration
|
|
589
|
+
|
|
590
|
+
### S3 Storage
|
|
591
|
+
|
|
592
|
+
**Create S3 bucket**:
|
|
593
|
+
|
|
594
|
+
```bash
|
|
595
|
+
aws s3 mb s3://reflexive-snapshots --region us-east-1
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Set bucket policy** (private):
|
|
599
|
+
|
|
600
|
+
```json
|
|
601
|
+
{
|
|
602
|
+
"Version": "2012-10-17",
|
|
603
|
+
"Statement": [{
|
|
604
|
+
"Effect": "Deny",
|
|
605
|
+
"Principal": "*",
|
|
606
|
+
"Action": "s3:GetObject",
|
|
607
|
+
"Resource": "arn:aws:s3:::reflexive-snapshots/*",
|
|
608
|
+
"Condition": {
|
|
609
|
+
"StringNotEquals": {
|
|
610
|
+
"aws:PrincipalArn": "arn:aws:iam::ACCOUNT:user/reflexive"
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}]
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
**Configure**:
|
|
618
|
+
|
|
619
|
+
```bash
|
|
620
|
+
export AWS_ACCESS_KEY_ID=xxx
|
|
621
|
+
export AWS_SECRET_ACCESS_KEY=xxx
|
|
622
|
+
export REFLEXIVE_SNAPSHOT_BUCKET=reflexive-snapshots
|
|
623
|
+
export AWS_REGION=us-east-1
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Cloudflare R2
|
|
627
|
+
|
|
628
|
+
**Create R2 bucket**:
|
|
629
|
+
1. Go to Cloudflare dashboard
|
|
630
|
+
2. Navigate to R2
|
|
631
|
+
3. Create bucket: `reflexive-snapshots`
|
|
632
|
+
|
|
633
|
+
**Create API token**:
|
|
634
|
+
1. R2 > Manage R2 API Tokens
|
|
635
|
+
2. Create token with read/write permissions
|
|
636
|
+
|
|
637
|
+
**Configure**:
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
export AWS_ACCESS_KEY_ID=r2-access-key-id
|
|
641
|
+
export AWS_SECRET_ACCESS_KEY=r2-secret-key
|
|
642
|
+
export REFLEXIVE_S3_ENDPOINT=https://account.r2.cloudflarestorage.com
|
|
643
|
+
export REFLEXIVE_SNAPSHOT_BUCKET=reflexive-snapshots
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### Memory Storage
|
|
647
|
+
|
|
648
|
+
For development or testing (not persistent):
|
|
649
|
+
|
|
650
|
+
```javascript
|
|
651
|
+
// reflexive.config.js
|
|
652
|
+
export default {
|
|
653
|
+
mode: 'hosted',
|
|
654
|
+
hosted: {
|
|
655
|
+
snapshotStorage: {
|
|
656
|
+
provider: 'memory'
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
**Warning**: Snapshots are lost on restart.
|
|
663
|
+
|
|
664
|
+
## Monitoring and Logging
|
|
665
|
+
|
|
666
|
+
### Health Checks
|
|
667
|
+
|
|
668
|
+
**HTTP health endpoint**:
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
curl https://your-reflexive.app/api/health
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
Response:
|
|
675
|
+
```json
|
|
676
|
+
{
|
|
677
|
+
"status": "ok",
|
|
678
|
+
"sandboxes": 5,
|
|
679
|
+
"running": 3
|
|
680
|
+
}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**Use in monitoring**:
|
|
684
|
+
|
|
685
|
+
```yaml
|
|
686
|
+
# Kubernetes liveness probe
|
|
687
|
+
livenessProbe:
|
|
688
|
+
httpGet:
|
|
689
|
+
path: /api/health
|
|
690
|
+
port: 3099
|
|
691
|
+
initialDelaySeconds: 5
|
|
692
|
+
periodSeconds: 10
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### Metrics
|
|
696
|
+
|
|
697
|
+
Export metrics for monitoring:
|
|
698
|
+
|
|
699
|
+
```javascript
|
|
700
|
+
// In your app
|
|
701
|
+
import { makeReflexive } from 'reflexive';
|
|
702
|
+
|
|
703
|
+
const r = makeReflexive({
|
|
704
|
+
tools: [
|
|
705
|
+
{
|
|
706
|
+
name: 'get_metrics',
|
|
707
|
+
description: 'Get application metrics',
|
|
708
|
+
schema: { type: 'object', properties: {} },
|
|
709
|
+
handler: async () => ({
|
|
710
|
+
content: [{
|
|
711
|
+
type: 'text',
|
|
712
|
+
text: JSON.stringify({
|
|
713
|
+
uptime: process.uptime(),
|
|
714
|
+
memory: process.memoryUsage(),
|
|
715
|
+
sandboxes: manager.count()
|
|
716
|
+
})
|
|
717
|
+
}]
|
|
718
|
+
})
|
|
719
|
+
}
|
|
720
|
+
]
|
|
721
|
+
});
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### Log Aggregation
|
|
725
|
+
|
|
726
|
+
**Send logs to external service**:
|
|
727
|
+
|
|
728
|
+
```javascript
|
|
729
|
+
// Using Winston
|
|
730
|
+
import winston from 'winston';
|
|
731
|
+
|
|
732
|
+
const logger = winston.createLogger({
|
|
733
|
+
transports: [
|
|
734
|
+
new winston.transports.File({ filename: 'app.log' }),
|
|
735
|
+
new winston.transports.Http({
|
|
736
|
+
host: 'logs.example.com',
|
|
737
|
+
port: 8080,
|
|
738
|
+
path: '/logs'
|
|
739
|
+
})
|
|
740
|
+
]
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
appState.on('log', (entry) => {
|
|
744
|
+
logger.log({
|
|
745
|
+
level: entry.type,
|
|
746
|
+
message: entry.message,
|
|
747
|
+
timestamp: entry.timestamp
|
|
748
|
+
});
|
|
749
|
+
});
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
## Scaling
|
|
753
|
+
|
|
754
|
+
### Horizontal Scaling
|
|
755
|
+
|
|
756
|
+
Reflexive can be scaled horizontally with some considerations:
|
|
757
|
+
|
|
758
|
+
**Stateless design**: Each instance manages its own sandboxes
|
|
759
|
+
**Shared storage**: Use S3/R2 for snapshot persistence
|
|
760
|
+
**Load balancing**: Round-robin or least-connections
|
|
761
|
+
|
|
762
|
+
**Example with multiple instances**:
|
|
763
|
+
|
|
764
|
+
```yaml
|
|
765
|
+
# docker-compose.yml
|
|
766
|
+
services:
|
|
767
|
+
reflexive-1:
|
|
768
|
+
build: .
|
|
769
|
+
ports:
|
|
770
|
+
- "3099:3099"
|
|
771
|
+
environment:
|
|
772
|
+
- INSTANCE_ID=1
|
|
773
|
+
|
|
774
|
+
reflexive-2:
|
|
775
|
+
build: .
|
|
776
|
+
ports:
|
|
777
|
+
- "3100:3099"
|
|
778
|
+
environment:
|
|
779
|
+
- INSTANCE_ID=2
|
|
780
|
+
|
|
781
|
+
nginx:
|
|
782
|
+
image: nginx
|
|
783
|
+
ports:
|
|
784
|
+
- "80:80"
|
|
785
|
+
volumes:
|
|
786
|
+
- ./nginx.conf:/etc/nginx/nginx.conf
|
|
787
|
+
depends_on:
|
|
788
|
+
- reflexive-1
|
|
789
|
+
- reflexive-2
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
### Resource Limits
|
|
793
|
+
|
|
794
|
+
Configure per deployment:
|
|
795
|
+
|
|
796
|
+
**Docker**:
|
|
797
|
+
```bash
|
|
798
|
+
docker run \
|
|
799
|
+
--memory="2g" \
|
|
800
|
+
--cpus="2" \
|
|
801
|
+
reflexive:latest
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
**Kubernetes**:
|
|
805
|
+
```yaml
|
|
806
|
+
resources:
|
|
807
|
+
requests:
|
|
808
|
+
memory: "512Mi"
|
|
809
|
+
cpu: "500m"
|
|
810
|
+
limits:
|
|
811
|
+
memory: "2Gi"
|
|
812
|
+
cpu: "2000m"
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
**Per-sandbox limits** (in config):
|
|
816
|
+
```javascript
|
|
817
|
+
export default {
|
|
818
|
+
sandbox: {
|
|
819
|
+
vcpus: 2,
|
|
820
|
+
memory: 2048, // MB
|
|
821
|
+
timeout: '30m'
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
## Troubleshooting
|
|
827
|
+
|
|
828
|
+
### Common Issues
|
|
829
|
+
|
|
830
|
+
**Port already in use**:
|
|
831
|
+
```bash
|
|
832
|
+
# Find process
|
|
833
|
+
lsof -i :3099
|
|
834
|
+
|
|
835
|
+
# Kill process
|
|
836
|
+
kill -9 PID
|
|
837
|
+
|
|
838
|
+
# Or use different port
|
|
839
|
+
PORT=3100 node dist/cli.js
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
**Sandbox creation fails**:
|
|
843
|
+
```bash
|
|
844
|
+
# Check Vercel token
|
|
845
|
+
echo $VERCEL_TOKEN
|
|
846
|
+
|
|
847
|
+
# Test sandbox creation
|
|
848
|
+
curl -H "Authorization: Bearer $VERCEL_TOKEN" \
|
|
849
|
+
https://api.vercel.com/v1/sandbox
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**Storage errors**:
|
|
853
|
+
```bash
|
|
854
|
+
# Test S3 access
|
|
855
|
+
aws s3 ls s3://reflexive-snapshots
|
|
856
|
+
|
|
857
|
+
# Check credentials
|
|
858
|
+
aws sts get-caller-identity
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
**Memory issues**:
|
|
862
|
+
```bash
|
|
863
|
+
# Increase Node.js memory
|
|
864
|
+
NODE_OPTIONS="--max-old-space-size=4096" node dist/cli.js
|
|
865
|
+
|
|
866
|
+
# Or in PM2
|
|
867
|
+
pm2 start dist/cli.js --node-args="--max-old-space-size=4096"
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
### Debug Mode
|
|
871
|
+
|
|
872
|
+
Enable verbose logging:
|
|
873
|
+
|
|
874
|
+
```bash
|
|
875
|
+
DEBUG=reflexive:* node dist/cli.js --mode hosted
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Or set in environment:
|
|
879
|
+
```bash
|
|
880
|
+
export DEBUG=reflexive:*
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
**Next**: Check [Examples](./examples.md) for usage patterns and integrations.
|