Rewind Developer Guide
Complete guide for developers working on Rewind, covering architecture, development setup, and contribution workflows.
Table of Contents
- Architecture Overview
- Development Setup
- Project Structure
- Core Concepts
- Adding New Features
- Testing
- Deployment
- Contributing
Architecture Overview
High-Level Architecture
Rewind uses a three-tier architecture with clear separation of concerns:
┌─────────────────────────────────────────────────────────────┐
│ Frontend (SvelteKit) │
│ - Real-time Dashboard │
│ - Session Viewer │
│ - Alert Management UI │
│ - WebSocket Client │
└────────────────────┬────────────────────────────────────────┘
│ HTTP/WebSocket
▼
┌─────────────────────────────────────────────────────────────┐
│ Backend API (Bun + Elysia) │
│ - REST API Endpoints │
│ - WebSocket Server (Real-time + Terminal) │
│ - Capture Manager (Process Lifecycle) │
│ - Alert Service (Rule Evaluation) │
│ - Email Notification Service │
│ - File Watcher (Session Data) │
└────────────────────┬────────────────────────────────────────┘
│
┌────────────┴──────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ C++ Agent │ │ MongoDB │
│ - Packet Capture│ │ - Sessions │
│ - HTTP Assembly │ │ - Alerts │
│ - PII Filter │ │ - Notifications │
│ - JSON Output │ │ │
└──────────────────┘ └──────────────────┘Event-Driven Design
Rewind uses event emitters for loose coupling between services:
Alert Flow:
Session Saved → AlertService.evaluate()
↓
Match Found → emit("notification")
↓
┌──────────────┴──────────────┐
▼ ▼
EmailNotificationService WebSocketBroadcaster
↓ ↓
Send Email Push to UICapture Terminal Flow:
Frontend WebSocket ↔ Backend WebSocket ↔ C++ Agent stdin/stdoutData Flow
Capture Phase:
- C++ agent captures packets → Assembles HTTP → Writes JSON to file
- Backend file watcher detects new file → Parses JSON → Saves to MongoDB
- WebSocket broadcasts new session to connected clients
Alert Phase:
- AlertService evaluates session against active rules
- Match found → Creates notification in database
- Emits "notification" event → Email service + WebSocket broadcast
Replay Phase:
- Frontend sends replay request → Backend reconstructs HTTP request
- Backend sends request to target → Returns response to frontend
Development Setup
Prerequisites
- Bun 1.0+ or Node.js 18+ (Bun recommended for performance)
- MongoDB 7.x running locally or remotely
- C++ Build Tools (for capture agent)
- Administrator/sudo access (for packet capture)
- Git for version control
Initial Setup
Clone Repository
bashgit clone https://github.com/sreekarnv/rewind.git cd rewindBuild Capture Agent
bashcd services/capture-agent # Windows build.bat # Linux/macOS makeInstall Backend Dependencies
bashcd services/backend-api bun install cp .env.example .env # Edit .env with your configurationInstall Frontend Dependencies
bashcd services/frontend bun install # or npm installStart MongoDB
bash# macOS/Linux mongod --dbpath /path/to/data # Windows mongod --dbpath C:\data\dbStart Development Servers
Terminal 1 - Backend:
bashcd services/backend-api sudo bun run dev # sudo required for capture agentTerminal 2 - Frontend:
bashcd services/frontend npm run devAccess Application
- Frontend: http://localhost:5173
- Backend API: http://localhost:8000
- API Docs: http://localhost:8000/swagger (if enabled)
Hot Reload
- Backend: Bun's built-in watch mode (
--watchflag) automatically restarts on file changes - Frontend: Vite's HMR provides instant updates without full page reload
- Capture Agent: Must be manually rebuilt and restarted via UI
Project Structure
rewind/
├── services/
│ ├── capture-agent/ # C++ packet capture agent
│ │ ├── src/ # C++ source files
│ │ ├── include/ # Header files
│ │ ├── build/ # Build output
│ │ └── output/ # JSON session files
│ │
│ ├── backend-api/ # Bun + Elysia API server
│ │ ├── src/
│ │ │ ├── index.ts # Entry point
│ │ │ ├── models/ # MongoDB schemas
│ │ │ │ ├── Session.ts
│ │ │ │ ├── Alert.ts
│ │ │ │ └── Notification.ts
│ │ │ ├── routes/ # API route handlers
│ │ │ │ ├── sessions.ts
│ │ │ │ ├── alerts.ts
│ │ │ │ ├── notifications.ts
│ │ │ │ ├── capture.ts
│ │ │ │ └── realtime.ts
│ │ │ └── services/ # Business logic
│ │ │ ├── captureManager.ts # Process lifecycle
│ │ │ ├── fileWatcher.ts # Session file monitoring
│ │ │ ├── alertService.ts # Rule evaluation
│ │ │ └── emailNotificationService.ts
│ │ ├── scripts/ # Utility scripts
│ │ └── package.json
│ │
│ └── frontend/ # SvelteKit web UI
│ ├── src/
│ │ ├── routes/ # SvelteKit file-based routing
│ │ │ ├── (dashboard)/
│ │ │ │ ├── +page.svelte # Dashboard
│ │ │ │ ├── sessions/
│ │ │ │ │ ├── +page.svelte # Sessions list
│ │ │ │ │ └── [id]/+page.svelte # Session detail
│ │ │ │ ├── alerts/+page.svelte
│ │ │ │ ├── notifications/+page.svelte
│ │ │ │ └── capture/+page.svelte
│ │ │ └── +layout.svelte
│ │ └── lib/
│ │ ├── components/ # Reusable components
│ │ ├── stores/ # State management
│ │ ├── api.ts # API client
│ │ └── types.ts # TypeScript types
│ └── package.json
│
├── docs/ # Documentation
│ ├── USER_GUIDE.md
│ ├── DEVELOPER_GUIDE.md (this file)
│ ├── EMAIL_SETUP_GUIDE.md
│ └── screenshots/
│
├── LICENSE
├── CONTRIBUTING.md
└── README.mdCore Concepts
1. Session Data Model
A Session represents a single HTTP transaction (request + optional response).
interface Session {
_id: string; // MongoDB ObjectId
sessionId: string; // Unique identifier from capture agent
timestamp: Date; // When request was captured
// Request data
method: string; // HTTP method (GET, POST, etc.)
uri: string; // Request URI
httpVersion: string; // HTTP/1.1, HTTP/2, etc.
headers: Record<string, string>; // Request headers
body?: string; // Request body (if present)
// Response data (optional - may be pending)
statusCode?: number; // HTTP status code
statusMessage?: string; // Status text
responseHeaders?: Record<string, string>;
responseBody?: string;
// Network info
sourceIp: string; // Client IP
sourcePort: number; // Client port
destIp: string; // Server IP
destPort: number; // Server port
// Metadata
isPending: boolean; // True if no response received
createdAt: Date;
updatedAt: Date;
}Key Points:
- Sessions can exist without responses (
isPending: true) - All field names use camelCase for consistency
- Headers are stored as flat objects (not arrays)
- Large bodies may be truncated by capture agent
2. Alert System
The alert system evaluates incoming sessions against user-defined rules.
Alert Rule Structure:
interface AlertRule {
_id: string;
name: string; // User-friendly name
description?: string;
severity: 'info' | 'warning' | 'error' | 'critical';
enabled: boolean;
conditions: AlertCondition[]; // ALL must match (AND logic)
cooldownMinutes: number; // Min time between triggers
lastTriggeredAt?: Date;
createdAt: Date;
updatedAt: Date;
}
interface AlertCondition {
type: 'status_code' | 'status_range' | 'response_time' | 'method' | 'url_pattern';
operator: 'equals' | 'not_equals' | 'greater_than' | 'less_than' | 'contains' | 'regex';
value: string;
}Evaluation Logic (alertService.ts):
async evaluate(session: ISession): Promise<void> {
const rules = await Alert.find({ enabled: true });
for (const rule of rules) {
// Check cooldown
if (this.isInCooldown(rule)) continue;
// Evaluate all conditions (AND logic)
const matches = rule.conditions.every(condition =>
this.evaluateCondition(session, condition)
);
if (matches) {
await this.triggerAlert(rule, session);
}
}
}3. Email Notification Service
Event-driven service that listens for alert notifications and sends emails.
Integration Pattern:
// In index.ts
const emailService = new EmailNotificationService();
alertService.on("notification", async (notification) => {
// Non-blocking - doesn't slow down alert processing
await emailService.sendNotificationEmail(notification);
});Key Features:
- SMTP configuration from environment variables
- Retry logic with exponential backoff (3 attempts, 2s/4s delays)
- HTML email templates with color-coded severity badges
- Database tracking (emailSent, emailSentAt, emailError)
- Graceful degradation if SMTP not configured
4. WebSocket Communication
Two WebSocket endpoints serve different purposes:
Endpoint 1: /api/v1/realtime - Session Updates
// Broadcasts new sessions to all connected clients
sessionWatcher.on('new-session', (session) => {
ws.send(JSON.stringify({
type: 'new-session',
data: session
}));
});Endpoint 2: /api/v1/capture/stream - Capture Terminal
// Two-way communication with C++ agent
ws.on('message', (msg) => {
captureManager.sendInput(msg); // Forward to C++ stdin
});
captureManager.on('output', (data) => {
ws.send(data); // Forward C++ stdout to browser
});5. Capture Manager
Manages the C++ capture agent process lifecycle.
Key Responsibilities:
- Start/stop/restart the capture agent
- Forward stdin/stdout between WebSocket and process
- Monitor process health (PID, uptime, crashes)
- Handle graceful shutdown (SIGTERM with SIGKILL fallback)
class CaptureManager extends EventEmitter {
private process: ChildProcess | null = null;
async start(): Promise<void> {
this.process = spawn('./capture-agent/build/capture_agent', [], {
stdio: ['pipe', 'pipe', 'pipe']
});
this.process.stdout.on('data', (data) => {
this.emit('output', data.toString());
});
}
async stop(): Promise<void> {
if (!this.process) return;
this.process.kill('SIGTERM'); // Graceful shutdown
setTimeout(() => {
if (this.process && !this.process.killed) {
this.process.kill('SIGKILL'); // Force kill
}
}, 5000);
}
}Adding New Features
Adding a New API Endpoint
Example: Add a "Clear All Sessions" endpoint
Add route handler in
services/backend-api/src/routes/sessions.ts:typescriptimport { Elysia } from 'elysia'; import { Session } from '../models/Session'; export const sessionsRouter = new Elysia({ prefix: '/api/v1/sessions' }) .delete('/clear-all', async () => { const result = await Session.deleteMany({}); return { success: true, deletedCount: result.deletedCount }; });Register route in
services/backend-api/src/index.ts:typescriptimport { sessionsRouter } from './routes/sessions'; app.use(sessionsRouter);Add frontend API call in
services/frontend/src/lib/api.ts:typescriptexport async function clearAllSessions(): Promise<void> { const response = await fetch(`${API_BASE_URL}/sessions/clear-all`, { method: 'DELETE' }); if (!response.ok) { throw new Error('Failed to clear sessions'); } }Use in component:
svelte<script lang="ts"> import { clearAllSessions } from '$lib/api'; async function handleClearAll() { if (confirm('Delete all sessions?')) { await clearAllSessions(); // Refresh list or redirect } } </script> <button on:click={handleClearAll}>Clear All</button>
Adding a New Alert Condition Type
Example: Add "request_body_contains" condition
Update type definition in
services/backend-api/src/models/Alert.ts:typescriptexport type AlertConditionType = | 'status_code' | 'status_range' | 'response_time' | 'method' | 'url_pattern' | 'request_body_contains'; // NEWAdd evaluation logic in
services/backend-api/src/services/alertService.ts:typescriptprivate evaluateCondition(session: ISession, condition: IAlertCondition): boolean { switch (condition.type) { // ... existing cases ... case 'request_body_contains': if (!session.body) return false; const searchValue = condition.value.toLowerCase(); return session.body.toLowerCase().includes(searchValue); default: return false; } }Update frontend type in
services/frontend/src/lib/types.ts:typescriptexport type AlertConditionType = | 'status_code' | 'status_range' | 'response_time' | 'method' | 'url_pattern' | 'request_body_contains'; // NEWAdd UI option in alert creation form:
svelte<select bind:value={condition.type}> <option value="status_code">Status Code</option> <option value="status_range">Status Range</option> <option value="response_time">Response Time</option> <option value="method">HTTP Method</option> <option value="url_pattern">URL Pattern</option> <option value="request_body_contains">Request Body Contains</option> </select>
Adding a New Service
Example: Add a "Statistics Service" for analytics
Create service file
services/backend-api/src/services/statisticsService.ts:typescriptimport { EventEmitter } from 'node:events'; import { Session } from '../models/Session'; export class StatisticsService extends EventEmitter { async getOverview() { const totalSessions = await Session.countDocuments(); const errorRate = await this.calculateErrorRate(); const avgResponseTime = await this.calculateAvgResponseTime(); return { totalSessions, errorRate, avgResponseTime }; } private async calculateErrorRate(): Promise<number> { const total = await Session.countDocuments({ isPending: false }); const errors = await Session.countDocuments({ statusCode: { $gte: 500 } }); return total > 0 ? (errors / total) * 100 : 0; } private async calculateAvgResponseTime(): Promise<number> { const result = await Session.aggregate([ { $match: { responseTime: { $exists: true } } }, { $group: { _id: null, avg: { $avg: '$responseTime' } } } ]); return result[0]?.avg || 0; } }Initialize in index.ts:
typescriptimport { StatisticsService } from './services/statisticsService'; const statsService = new StatisticsService();Create route
services/backend-api/src/routes/statistics.ts:typescriptimport { Elysia } from 'elysia'; export const createStatisticsRouter = (statsService: StatisticsService) => { return new Elysia({ prefix: '/api/v1/statistics' }) .get('/overview', async () => { return await statsService.getOverview(); }); };Register route:
typescriptconst statsRouter = createStatisticsRouter(statsService); app.use(statsRouter);
Testing
Backend Testing
Unit Tests (Bun Test):
cd services/backend-api
bun testExample test file services/backend-api/src/services/alertService.test.ts:
import { describe, it, expect, beforeEach } from 'bun:test';
import { AlertService } from './alertService';
describe('AlertService', () => {
let alertService: AlertService;
beforeEach(() => {
alertService = new AlertService();
});
it('should evaluate status_code condition correctly', () => {
const session = { statusCode: 404, /* ... */ };
const condition = { type: 'status_code', operator: 'equals', value: '404' };
expect(alertService.evaluateCondition(session, condition)).toBe(true);
});
it('should respect cooldown period', async () => {
const rule = {
cooldownMinutes: 5,
lastTriggeredAt: new Date(Date.now() - 3 * 60 * 1000) // 3 min ago
};
expect(alertService.isInCooldown(rule)).toBe(true);
});
});Frontend Testing
Component Tests (Vitest + Testing Library):
cd services/frontend
npm testExample test services/frontend/src/lib/components/SessionCard.test.ts:
import { render, screen } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import SessionCard from './SessionCard.svelte';
describe('SessionCard', () => {
it('renders session method and URI', () => {
const session = {
method: 'GET',
uri: '/api/users',
statusCode: 200,
/* ... */
};
render(SessionCard, { props: { session } });
expect(screen.getByText('GET')).toBeInTheDocument();
expect(screen.getByText('/api/users')).toBeInTheDocument();
});
it('shows pending badge when no response', () => {
const session = {
method: 'POST',
uri: '/api/data',
isPending: true
};
render(SessionCard, { props: { session } });
expect(screen.getByText('Pending')).toBeInTheDocument();
});
});Manual Testing
Email Service Test:
cd services/backend-api
bun run scripts/test-email.tsCapture Agent Test:
cd services/capture-agent
./build/capture_agent
# Select interface, verify output filesIntegration Testing
Test Full Alert Flow:
- Start all services (backend, frontend, MongoDB)
- Create an alert rule via UI (e.g., status code = 500)
- Trigger the condition (make a request that returns 500)
- Verify:
- Notification appears in UI
- Email is sent (check Ethereal inbox)
- Database updated correctly
Deployment
Production Build
Backend:
cd services/backend-api
bun install --production
bun build src/index.ts --target=bun --outfile=dist/index.jsFrontend:
cd services/frontend
npm run build
# Output in build/ directoryCapture Agent:
cd services/capture-agent
make release # Optimized buildEnvironment Variables
Production .env example:
# Server
NODE_ENV=production
PORT=8000
# MongoDB
MONGODB_URI=mongodb://username:password@mongo-host:27017/rewind?authSource=admin
# Email (Production SMTP)
EMAIL_ENABLED=true
EMAIL_SMTP_HOST=smtp.sendgrid.net
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USER=apikey
EMAIL_SMTP_PASS=your-sendgrid-api-key
EMAIL_FROM=Rewind Alerts <alerts@yourdomain.com>
EMAIL_RECIPIENT=admin@yourdomain.com
FRONTEND_URL=https://rewind.yourdomain.com
# Security
CORS_ORIGIN=https://rewind.yourdomain.comDeployment Options
Option 1: Docker (Recommended)
Create docker-compose.yml:
version: '3.8'
services:
mongodb:
image: mongo:7
volumes:
- mongodb_data:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: changeme
backend:
build: ./services/backend-api
ports:
- "8000:8000"
environment:
- MONGODB_URI=mongodb://admin:changeme@mongodb:27017/rewind?authSource=admin
depends_on:
- mongodb
cap_add:
- NET_ADMIN
- NET_RAW
frontend:
build: ./services/frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
mongodb_data:Deploy:
docker-compose up -dOption 2: Traditional Server (Ubuntu)
# Install dependencies
sudo apt-get update
sudo apt-get install -y mongodb bun
# Start MongoDB
sudo systemctl start mongodb
sudo systemctl enable mongodb
# Deploy backend
cd /opt/rewind/services/backend-api
bun install --production
sudo bun run dist/index.js # Use process manager like PM2
# Deploy frontend with Nginx
cd /opt/rewind/services/frontend
npm run build
sudo cp -r build/* /var/www/html/Option 3: Cloud Platforms
- Backend: Deploy to Railway, Render, or Fly.io (supports Bun)
- Frontend: Deploy to Vercel, Netlify, or Cloudflare Pages
- MongoDB: Use MongoDB Atlas (managed)
- Capture Agent: Must run on VM/server with packet capture access
Production Checklist
- [ ] Set
NODE_ENV=production - [ ] Use production MongoDB with authentication
- [ ] Configure production SMTP (Gmail, SendGrid, etc.)
- [ ] Set up HTTPS/TLS certificates
- [ ] Configure CORS for production domain
- [ ] Set up DNS records (SPF, DKIM for email)
- [ ] Enable MongoDB authentication
- [ ] Set up backup strategy for MongoDB
- [ ] Configure log rotation
- [ ] Set up monitoring (e.g., Prometheus + Grafana)
- [ ] Test email delivery in production
- [ ] Document deployment steps
- [ ] Set up CI/CD pipeline (optional)
Contributing
Development Workflow
Fork the repository
bashgit clone https://github.com/YOUR_USERNAME/rewind.git cd rewind git remote add upstream https://github.com/sreekarnv/rewind.gitCreate feature branch
bashgit checkout -b feature/your-feature-nameMake changes
- Follow existing code style and patterns
- Add tests for new features
- Update documentation as needed
Test your changes
bash# Backend tests cd services/backend-api && bun test # Frontend tests cd services/frontend && npm test # Manual testing # Start all services and verify functionalityCommit with meaningful messages
bashgit add . git commit -m "feat: add request body search in alert conditions"Push and create PR
bashgit push origin feature/your-feature-name # Open PR on GitHub with description of changes
Code Style Guidelines
TypeScript/JavaScript:
- Use TypeScript for type safety
- Prefer
async/awaitover promises - Use camelCase for variables/functions
- Use PascalCase for classes/types
- Add JSDoc comments for complex functions
Svelte:
- Use Svelte 5 runes (
$state,$derived,$effect) - Keep components focused and small
- Use TailwindCSS for styling
- Avoid inline styles
General:
- Follow existing patterns in the codebase
- Keep functions small and focused
- Add error handling
- Log important events and errors
Pull Request Guidelines
PR Title Format:
<type>: <description>
Types: feat, fix, docs, style, refactor, test, choreExamples:
feat: add request body filtering in alert systemfix: resolve WebSocket reconnection issuedocs: update email setup guide with AWS SES
PR Description Template:
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Manual testing performed
- [ ] All existing tests pass
## Screenshots (if applicable)
[Add screenshots]
## Checklist
- [ ] Code follows project style
- [ ] Documentation updated
- [ ] No console errors
- [ ] Tested on multiple browsers (frontend changes)Debugging Tips
Backend Debugging
Enable verbose logging:
// In index.ts
if (process.env.DEBUG === 'true') {
console.log('Session received:', session);
}Debug email issues:
# Test email configuration
bun run scripts/test-email.ts
# Check logs for SMTP errors
tail -f logs/backend.log | grep "email"Frontend Debugging
Use browser DevTools:
- Network tab: Monitor API calls and WebSocket messages
- Console: Check for JavaScript errors
- Vue/Svelte DevTools: Inspect component state
Debug WebSocket:
// In frontend/src/lib/api.ts
const ws = new WebSocket('ws://localhost:8000/api/v1/realtime');
ws.onopen = () => console.log('WebSocket connected');
ws.onerror = (err) => console.error('WebSocket error:', err);
ws.onmessage = (msg) => console.log('WebSocket message:', msg.data);Capture Agent Debugging
Run manually with verbose output:
cd services/capture-agent
./build/capture_agent --verboseCheck output files:
ls -la output/
cat output/session_*.json | jqPerformance Optimization
Backend
Database Indexing: Ensure indexes on frequently queried fields
typescriptSessionSchema.index({ timestamp: -1 }); SessionSchema.index({ statusCode: 1 });Pagination: Implement pagination for large result sets
typescriptconst sessions = await Session.find() .sort({ timestamp: -1 }) .limit(50) .skip(page * 50);Caching: Use in-memory cache for frequently accessed data
typescriptconst cache = new Map<string, any>();
Frontend
Lazy Loading: Load components on demand
svelte<script> import { onMount } from 'svelte'; let SessionDetail; onMount(async () => { SessionDetail = (await import('./SessionDetail.svelte')).default; }); </script>Virtual Scrolling: For large lists (use
svelte-virtual-list)Debounce Search: Prevent excessive API calls
typescriptlet searchTimeout; function handleSearch(query: string) { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { performSearch(query); }, 300); }
Common Issues
Issue: "WebSocket connection failed"
Cause: Backend not running or CORS issues
Solution:
- Verify backend is running:
curl http://localhost:8000/api/v1/capture/status - Check CORS configuration in
index.ts - Verify WebSocket URL in frontend (
ws://notwss://for local dev)
Issue: "Email not sending"
Cause: Invalid SMTP configuration or credentials
Solution:
- Verify environment variables:
echo $EMAIL_SMTP_HOST - Test with Ethereal Email first
- Check backend logs for SMTP errors
- Run test script:
bun run scripts/test-email.ts
Issue: "Capture agent crashes immediately"
Cause: Insufficient permissions or invalid interface
Solution:
- Run backend with sudo/admin:
sudo bun run dev - Select valid network interface in UI
- Check capture agent logs in terminal
Issue: "MongoDB connection timeout"
Cause: MongoDB not running or wrong connection string
Solution:
- Start MongoDB:
mongod --dbpath /path/to/data - Verify connection:
mongosh mongodb://localhost:27017 - Check
MONGODB_URIin.env
Additional Resources
- Bun Documentation: https://bun.sh/docs
- Elysia Documentation: https://elysiajs.com
- SvelteKit Documentation: https://kit.svelte.dev
- PcapPlusPlus Documentation: https://pcapplusplus.github.io
- MongoDB Documentation: https://www.mongodb.com/docs
- Nodemailer Documentation: https://nodemailer.com
Happy Coding! 🚀
For questions or issues, please open a GitHub issue or discussion.