aethercall
Version:
A scalable WebRTC video calling API built with Node.js and OpenVidu
1,119 lines (890 loc) • 30.4 kB
Markdown
# AetherCall
[](https://badge.fury.io/js/aethercall)
[](https://opensource.org/licenses/MIT)
[](https://github.com/RayenMiri/AetherCall/actions)
AetherCall is a production-ready, open-source video calling API built on top of OpenVidu. It provides a robust HTTP REST API for integrating real-time video communication, audio calling, screen sharing, and recording capabilities into any application.
## Features
- **Real-time Video & Audio**: High-quality WebRTC-based communication
- **Screen Sharing**: Share entire screen or specific applications with participants
- **Session Recording**: Record video sessions with configurable layouts and formats
- **Room-based Access**: Simple room code system for easy participant joining
- **JWT Authentication**: Secure token-based authentication with role management
- **HTTP REST API**: Clean, documented API endpoints for all operations
- **Database Flexibility**: Support for PostgreSQL, MongoDB, filesystem, or in-memory storage
- **Production Ready**: Built-in rate limiting, CORS, security headers, and error handling
- **Comprehensive Testing**: 45+ automated tests covering all functionality
## Architecture
```
AetherCall/
├── index.js # Main application entry point
├── src/
│ ├── core/ # Core business logic
│ │ ├── auth/ # JWT token management
│ │ ├── openvidu/ # OpenVidu API wrapper
│ │ └── storage/ # Database abstraction layer
│ │
│ ├── interfaces/
│ │ └── http/ # HTTP REST API
│ │ ├── routes/ # API route handlers
│ │ └── server.js # Express server setup
│ │
│ └── utils/ # Shared utilities
│ ├── config.js # Configuration management
│ └── logger.js # Structured logging
│
├── tests/ # Test suite (Jest)
├── docs/ # API documentation
└── package.json # Project dependencies
```
## Installation
### Prerequisites
- **Node.js** 16.x or higher
- **npm** or **yarn**
- **OpenVidu Server** (Docker recommended)
### Install via npm
```bash
npm install aethercall
```
### Install from Source
```bash
git clone https://github.com/RayenMiri/AetherCall.git
cd AetherCall
npm install
```
## Quick Start
### 1. Start OpenVidu Server
AetherCall requires an OpenVidu server. The easiest way is using Docker:
```bash
# Pull and run OpenVidu development server
docker run -p 4443:4443 --rm \
-e OPENVIDU_SECRET=MY_SECRET \
openvidu/openvidu-dev:2.29.0
```
### 2. Configure Environment
Create a `.env` file in your project root:
```env
# OpenVidu Configuration
OPENVIDU_URL=http://localhost:4443
OPENVIDU_SECRET=MY_SECRET
# Server Configuration
PORT=3000
HOST=localhost
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
# Database (optional - defaults to memory)
DB_TYPE=memory
# Logging
LOG_LEVEL=info
```
### 3. Start the API Server
```bash
# Using npm
npm start
# Using yarn
yarn start
# Development mode with auto-reload
npm run dev
# Or using the setup helper
npm run setup
```
### 4. Using as a Module
See `examples/usage-example.js` for detailed examples of how to integrate AetherCall into your existing Node.js applications:
```bash
# Run different usage examples
node examples/usage-example.js simple # Simple usage with defaults
node examples/usage-example.js advanced # Advanced configuration
node examples/usage-example.js express # Integration with Express app
node examples/usage-example.js production # Production configuration
```
The API will be available at `http://localhost:3000`
### 5. Verify Installation
Check if the API is running:
```bash
curl http://localhost:3000/health
```
Expected response:
```json
{
"status": "healthy",
"timestamp": "2025-07-27T10:00:00.000Z",
"version": "1.0.0"
}
```
## API Usage
### Base URL
```
http://localhost:3000/api
```
### Authentication
All API requests (except health check) require a JWT token:
```javascript
// Get API token
const response = await fetch('/api/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: 'your-application-id',
expiresIn: '1h' // optional
})
});
const { data } = await response.json();
const apiToken = data.accessToken;
```
### Core Endpoints
#### Sessions Management
```javascript
// Create a video session
const session = await fetch('/api/sessions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mediaMode: 'ROUTED',
recordingMode: 'MANUAL',
metadata: JSON.stringify({ roomName: 'My Meeting' })
})
});
// Get session info
const sessionInfo = await fetch(`/api/sessions/${sessionId}`, {
headers: { 'Authorization': `Bearer ${apiToken}` }
});
// Close session
await fetch(`/api/sessions/${sessionId}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${apiToken}` }
});
```
#### Connection Management
```javascript
// Create connection token for participant
const connection = await fetch('/api/connections', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
sessionId: 'session-id',
role: 'PUBLISHER', // SUBSCRIBER, PUBLISHER, MODERATOR
data: JSON.stringify({
userId: 'user123',
displayName: 'John Doe'
})
})
});
// Join room with room code (simplified API)
const roomConnection = await fetch('/api/connections/join-room', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
roomCode: 'ABC123',
userId: 'user123',
displayName: 'John Doe',
role: 'PUBLISHER'
})
});
```
#### Recording Management
```javascript
// Start recording
const recording = await fetch('/api/recordings/start', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
sessionId: 'session-id',
name: 'my-recording',
outputMode: 'COMPOSED',
recordingLayout: 'BEST_FIT'
})
});
// Stop recording
await fetch(`/api/recordings/stop/${recordingId}`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${apiToken}` }
});
// Get recording info
const recordingInfo = await fetch(`/api/recordings/${recordingId}`, {
headers: { 'Authorization': `Bearer ${apiToken}` }
});
```
#### Room Management (Simplified API)
```javascript
// Create room with simple code
const room = await fetch('/api/auth/room', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
roomName: 'Daily Standup',
maxParticipants: 10,
roomCode: 'STANDUP123' // optional, will be generated if not provided
})
});
```
## Frontend Integration
### Using with OpenVidu Browser SDK
#### React Example
```jsx
import React, { useState, useEffect, useRef } from 'react';
import { OpenVidu } from 'openvidu-browser';
function VideoRoom({ roomCode, userDisplayName }) {
const [session, setSession] = useState(null);
const [publisher, setPublisher] = useState(null);
const [subscribers, setSubscribers] = useState([]);
const publisherRef = useRef(null);
useEffect(() => {
joinRoom();
return () => leaveRoom();
}, []);
const joinRoom = async () => {
try {
// Get connection token from AetherCall API
const response = await fetch('/api/connections/join-room', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
roomCode: roomCode,
userId: `user-${Date.now()}`,
displayName: userDisplayName,
role: 'PUBLISHER'
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const { data } = await response.json();
// Initialize OpenVidu session
const OV = new OpenVidu();
const session = OV.initSession();
// Event handlers
session.on('streamCreated', (event) => {
const subscriber = session.subscribe(event.stream, undefined);
setSubscribers(prev => [...prev, subscriber]);
});
session.on('streamDestroyed', (event) => {
setSubscribers(prev =>
prev.filter(sub => sub.stream !== event.stream)
);
});
// Connect to session
await session.connect(data.token, {
clientData: data.userId
});
// Create and publish local stream
const publisher = await OV.initPublisherAsync(undefined, {
audioSource: undefined,
videoSource: undefined,
publishAudio: true,
publishVideo: true,
resolution: '640x480',
frameRate: 30,
insertMode: 'APPEND',
mirror: false
});
await session.publish(publisher);
setSession(session);
setPublisher(publisher);
} catch (error) {
console.error('Error joining room:', error);
}
};
const leaveRoom = () => {
if (session) {
session.disconnect();
}
};
const toggleVideo = () => {
if (publisher) {
publisher.publishVideo(!publisher.stream.videoActive);
}
};
const toggleAudio = () => {
if (publisher) {
publisher.publishAudio(!publisher.stream.audioActive);
}
};
return (
<div className="video-room">
<div className="controls">
<button onClick={toggleVideo}>
{publisher?.stream?.videoActive ? 'Turn Off Video' : 'Turn On Video'}
</button>
<button onClick={toggleAudio}>
{publisher?.stream?.audioActive ? 'Mute' : 'Unmute'}
</button>
<button onClick={leaveRoom}>Leave Room</button>
</div>
<div className="video-container">
<div ref={publisherRef} className="publisher-video" />
<div className="subscribers">
{subscribers.map((subscriber, index) => (
<div
key={index}
ref={(ref) => {
if (ref && subscriber.videos[0]) {
subscriber.addVideoElement(ref);
}
}}
className="subscriber-video"
/>
))}
</div>
</div>
</div>
);
}
export default VideoRoom;
```
#### Vanilla JavaScript Example
```html
<!DOCTYPE html>
<html>
<head>
<title>AetherCall Video Room</title>
<script src="https://github.com/OpenVidu/openvidu/releases/download/v2.29.0/openvidu-browser-2.29.0.min.js"></script>
</head>
<body>
<div id="video-container">
<div id="publisher"></div>
<div id="subscribers"></div>
</div>
<div id="controls">
<button onclick="toggleVideo()">Toggle Video</button>
<button onclick="toggleAudio()">Toggle Audio</button>
<button onclick="leaveRoom()">Leave Room</button>
</div>
<script>
let session;
let publisher;
async function joinRoom(roomCode, displayName) {
try {
// Get token from AetherCall
const response = await fetch('/api/connections/join-room', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
roomCode: roomCode,
userId: `user-${Date.now()}`,
displayName: displayName,
role: 'PUBLISHER'
})
});
const { data } = await response.json();
// Create OpenVidu session
const OV = new OpenVidu();
session = OV.initSession();
// Handle new streams
session.on('streamCreated', (event) => {
const subscriber = session.subscribe(event.stream, 'subscribers');
});
// Connect and publish
await session.connect(data.token);
publisher = await OV.initPublisherAsync(undefined, {
publishAudio: true,
publishVideo: true
});
await session.publish(publisher);
publisher.addVideoElement('publisher');
} catch (error) {
console.error('Error:', error);
}
}
function toggleVideo() {
if (publisher) {
publisher.publishVideo(!publisher.stream.videoActive);
}
}
function toggleAudio() {
if (publisher) {
publisher.publishAudio(!publisher.stream.audioActive);
}
}
function leaveRoom() {
if (session) {
session.disconnect();
}
}
// Join room on page load
joinRoom('DEMO123', 'Demo User');
</script>
</body>
</html>
```
## Backend Integration
### Using AetherCall as a Service
#### Express.js Integration
```javascript
const express = require('express');
const axios = require('axios');
const app = express();
const AETHERCALL_API_URL = 'http://localhost:3000/api';
// Get API token (you should cache this)
async function getAetherCallToken() {
const response = await axios.post(`${AETHERCALL_API_URL}/auth/token`, {
clientId: 'your-app-id'
});
return response.data.data.accessToken;
}
// Create meeting endpoint
app.post('/api/meetings', async (req, res) => {
try {
const token = await getAetherCallToken();
// Create room in AetherCall
const roomResponse = await axios.post(`${AETHERCALL_API_URL}/auth/room`, {
roomName: req.body.title,
maxParticipants: req.body.maxParticipants || 10
}, {
headers: { Authorization: `Bearer ${token}` }
});
const { data } = roomResponse.data;
res.json({
meetingId: data.sessionId,
roomCode: data.roomCode,
joinUrl: `${req.protocol}://${req.get('host')}/join/${data.roomCode}`
});
} catch (error) {
console.error('Error creating meeting:', error);
res.status(500).json({ error: 'Failed to create meeting' });
}
});
// Join meeting endpoint
app.post('/api/meetings/:roomCode/join', async (req, res) => {
try {
const { roomCode } = req.params;
const { userId, displayName, role = 'PUBLISHER' } = req.body;
const connectionResponse = await axios.post(`${AETHERCALL_API_URL}/connections/join-room`, {
roomCode,
userId,
displayName,
role
});
res.json(connectionResponse.data);
} catch (error) {
console.error('Error joining meeting:', error);
res.status(500).json({ error: 'Failed to join meeting' });
}
});
```
#### Recording Management
```javascript
// Start recording for a session
app.post('/api/meetings/:sessionId/recording/start', async (req, res) => {
try {
const token = await getAetherCallToken();
const { sessionId } = req.params;
const recording = await axios.post(`${AETHERCALL_API_URL}/recordings/start`, {
sessionId,
name: `meeting-${sessionId}-${Date.now()}`,
outputMode: 'COMPOSED',
recordingLayout: 'BEST_FIT'
}, {
headers: { Authorization: `Bearer ${token}` }
});
res.json({
recordingId: recording.data.data.id,
status: 'started'
});
} catch (error) {
console.error('Error starting recording:', error);
res.status(500).json({ error: 'Failed to start recording' });
}
});
// Stop recording
app.post('/api/recordings/:recordingId/stop', async (req, res) => {
try {
const token = await getAetherCallToken();
const { recordingId } = req.params;
const recording = await axios.post(`${AETHERCALL_API_URL}/recordings/stop/${recordingId}`, {}, {
headers: { Authorization: `Bearer ${token}` }
});
res.json({
recordingId,
duration: recording.data.data.duration,
downloadUrl: recording.data.data.url,
status: 'completed'
});
} catch (error) {
console.error('Error stopping recording:', error);
res.status(500).json({ error: 'Failed to stop recording' });
}
});
```
### Using AetherCall as a Library
```javascript
const AetherCall = require('aethercall');
// Initialize AetherCall instance
const aetherCall = new AetherCall({
openviduUrl: process.env.OPENVIDU_URL,
openviduSecret: process.env.OPENVIDU_SECRET,
jwtSecret: process.env.JWT_SECRET,
storage: {
type: 'postgresql',
connectionString: process.env.DATABASE_URL
}
});
// Start the server
app.listen(3000, async () => {
await aetherCall.start();
console.log('AetherCall server running on port 3000');
});
```
## Configuration
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `OPENVIDU_URL` | OpenVidu server URL | `http://localhost:4443` |
| `OPENVIDU_SECRET` | OpenVidu server secret | Required |
| `PORT` | HTTP server port | `3000` |
| `JWT_SECRET` | JWT signing secret | Required |
| `DB_TYPE` | Database type | `memory` |
| `LOG_LEVEL` | Logging level | `info` |
### Database Support
```env
# PostgreSQL
DB_TYPE=postgres
DB_CONNECTION_STRING=postgresql://user:pass@localhost:5432/aethercall
# MongoDB
DB_TYPE=mongodb
DB_CONNECTION_STRING=mongodb://localhost:27017/aethercall
# File System
DB_TYPE=filesystem
DATA_PATH=./data
# Memory (Development)
DB_TYPE=memory
```
## Deployment
### Production Deployment
#### Environment Variables for Production
```env
# OpenVidu Configuration
OPENVIDU_URL=https://your-openvidu-server.com:4443
OPENVIDU_SECRET=your-production-secret
# Server Configuration
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
JWT_SECRET=your-super-secure-jwt-secret-change-this
# Database
DB_TYPE=postgresql
DB_CONNECTION_STRING=postgresql://user:password@host:5432/aethercall
# Security
CORS_ORIGIN=https://yourdomain.com
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
# Logging
LOG_LEVEL=warn
```
#### Docker Deployment
**Dockerfile**
```dockerfile
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci --only=production
# Copy source code
COPY . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S aethercall -u 1001
USER aethercall
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"
CMD ["npm", "start"]
```
**Docker Compose with PostgreSQL**
```yaml
version: '3.8'
services:
aethercall:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- OPENVIDU_URL=http://openvidu:4443
- OPENVIDU_SECRET=MY_SECRET
- DB_TYPE=postgresql
- DB_CONNECTION_STRING=postgresql://postgres:password@postgres:5432/aethercall
- JWT_SECRET=your-jwt-secret
depends_on:
postgres:
condition: service_healthy
openvidu:
condition: service_started
restart: unless-stopped
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_DB=aethercall
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
openvidu:
image: openvidu/openvidu-dev:2.29.0
environment:
- OPENVIDU_SECRET=MY_SECRET
ports:
- "4443:4443"
restart: unless-stopped
volumes:
postgres_data:
```
#### Cloud Platform Deployment
**Railway**
```bash
# Install Railway CLI
npm install -g @railway/cli
# Login and deploy
railway login
railway init
railway up
```
**Heroku**
```bash
# Create Heroku app
heroku create your-app-name
# Set environment variables
heroku config:set NODE_ENV=production
heroku config:set OPENVIDU_URL=https://your-openvidu.herokuapp.com:4443
heroku config:set OPENVIDU_SECRET=your-secret
heroku config:set JWT_SECRET=your-jwt-secret
# Deploy
git push heroku main
```
**Render**
```yaml
# render.yaml
services:
- type: web
name: aethercall-api
env: node
plan: starter
buildCommand: npm install
startCommand: npm start
envVars:
- key: NODE_ENV
value: production
- key: OPENVIDU_URL
value: https://your-openvidu-instance.onrender.com:4443
```
### OpenVidu Server Deployment
For production, you'll need a separate OpenVidu server. Options include:
1. **OpenVidu Cloud** (Recommended for production)
2. **Self-hosted on AWS/GCP/Azure**
3. **Docker deployment on your infrastructure**
See [OpenVidu Deployment Guide](https://docs.openvidu.io/en/2.29.0/deployment/) for detailed instructions.
## Testing
AetherCall includes a comprehensive test suite with 45+ automated tests covering all functionality.
### Running Tests
```bash
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run specific test file
npm test tests/http-api.test.js
# Run tests with coverage report
npm run test:coverage
# Run tests matching a pattern
npm test -- --grep "session"
```
### Test Coverage
- **HTTP API Tests**: All REST endpoints with authentication, validation, and error handling
- **Storage Tests**: Database operations across all supported storage types
- **OpenVidu Integration Tests**: Session, connection, and recording functionality
- **Authentication Tests**: JWT token management and role-based access control
### Test Requirements
Before running tests, ensure you have:
1. **OpenVidu Server running** (see Installation section)
2. **Test environment configured**:
```env
# .env.test
OPENVIDU_URL=http://localhost:4443
OPENVIDU_SECRET=MY_SECRET
JWT_SECRET=test-secret-key
DB_TYPE=memory
LOG_LEVEL=error
```
### Continuous Integration
Tests are automatically run on:
- Pull requests
- Commits to main branch
- Release builds
Example GitHub Actions workflow:
```yaml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
openvidu:
image: openvidu/openvidu-dev:2.29.0
ports:
- 4443:4443
env:
OPENVIDU_SECRET: MY_SECRET
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '18'
- run: npm ci
- run: npm test
```
## Monitoring & Analytics
### Health Check
```bash
curl http://localhost:3000/health
```
### System Metrics
```javascript
// Built-in metrics endpoint
app.get('/metrics', (req, res) => {
res.json({
uptime: process.uptime(),
memory: process.memoryUsage(),
activeSessions: aetherCall.getActiveSessionCount(),
totalConnections: aetherCall.getTotalConnectionCount()
});
});
```
## Security
### Rate Limiting
Built-in rate limiting (100 requests per 15 minutes by default):
```env
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
```
### CORS Configuration
```env
CORS_ORIGIN=https://yourdomain.com
CORS_METHODS=GET,POST,PUT,DELETE
```
### Token Security
- JWT tokens with configurable expiration
- Role-based access control (SUBSCRIBER, PUBLISHER, MODERATOR)
- Automatic token validation middleware
## Contributing
We welcome contributions from the community! Please read our [Contributing Guidelines](CONTRIBUTING.md) before submitting PRs.
### Development Setup
1. **Fork and clone the repository**
```bash
git clone https://github.com/YOUR_USERNAME/AetherCall.git
cd AetherCall
```
2. **Install dependencies**
```bash
npm install
```
3. **Set up development environment**
```bash
cp .env.example .env
# Edit .env with your configuration
```
4. **Start OpenVidu development server**
```bash
docker run -p 4443:4443 --rm \
-e OPENVIDU_SECRET=MY_SECRET \
openvidu/openvidu-dev:2.29.0
```
5. **Run in development mode**
```bash
npm run dev
```
### Contribution Guidelines
- **Code Style**: We use ESLint and Prettier for code formatting
- **Testing**: All new features must include tests
- **Documentation**: Update README and API docs for any changes
- **Commit Messages**: Use conventional commit format
### Pull Request Process
1. Create a feature branch: `git checkout -b feature/amazing-feature`
2. Make your changes and add tests
3. Run the test suite: `npm test`
4. Update documentation if needed
5. Commit your changes: `git commit -m 'feat: add amazing feature'`
6. Push to your fork: `git push origin feature/amazing-feature`
7. Open a Pull Request with a clear description
## Security
### Reporting Security Issues
If you discover a security vulnerability, please email us at security@aethercall.dev instead of using the issue tracker.
### Security Features
- **JWT Authentication**: Secure token-based authentication with configurable expiration
- **Rate Limiting**: Built-in protection against abuse (configurable)
- **CORS Configuration**: Proper cross-origin resource sharing setup
- **Input Validation**: All API inputs are validated and sanitized
- **Security Headers**: Helmet.js for security headers
- **Role-based Access**: SUBSCRIBER, PUBLISHER, MODERATOR roles
;; ## License
;; This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Support and Documentation
### Documentation
- **[API Reference](docs/api.md)** - Complete API documentation
- **[OpenVidu Integration Guide](https://docs.openvidu.io/)** - OpenVidu documentation
;; - **[Examples Repository](https://github.com/RayenMiri/AetherCall-Examples)** - Sample implementations
;; ### Community Support
;; - **[GitHub Discussions](https://github.com/RayenMiri/AetherCall/discussions)** - Community Q&A
;; - **[Issue Tracker](https://github.com/RayenMiri/AetherCall/issues)** - Bug reports and feature requests
;; - **[Discord Server](https://discord.gg/aethercall)** - Real-time community chat
### Professional Support
- **[Email Support](mailto:rayenmiri@gmail.com)** - Technical support
;; - **[Enterprise Support](mailto:enterprise@aethercall.dev)** - Custom solutions and consulting
;; ## Roadmap
;; ### Version 2.0 (Q3 2025)
;; - [ ] **WebSocket API**: Real-time event streaming
;; - [ ] **GraphQL Interface**: Alternative to REST API
;; - [ ] **Advanced Analytics**: Detailed session and participant metrics
;; - [ ] **Load Balancing**: Multi-instance deployment support
;; ### Version 2.1 (Q4 2025)
;; - [ ] **Mobile SDKs**: React Native and Flutter wrappers
;; - [ ] **Advanced Recording**: Individual participant streams
;; - [ ] **Moderation Tools**: Participant management and content moderation
- [ ] **Custom Layouts**: Configurable recording layouts
### Future Releases
- [ ] **AI Integration**: Automatic transcription and translation
- [ ] **Cloud Storage**: Direct integration with AWS S3, Google Cloud Storage
- [ ] **Streaming**: RTMP/HLS streaming support
- [ ] **SIP Integration**: Traditional phone system integration
## License
AetherCall is released under the [MIT License](LICENSE). This means you can:
### ✅ **What You Can Do:**
- **Use commercially** - Build and sell applications using AetherCall
- **Modify freely** - Customize the code to fit your needs
- **Distribute** - Share the software with others
- **Sublicense** - Include it in projects with different licenses
- **Private use** - Use in proprietary/closed-source projects
### 📋 **Requirements:**
- **Include copyright notice** - Keep the original copyright and license notice
- **Include license text** - Include the MIT license text in distributions
### 🚫 **Limitations:**
- **No warranty** - Software is provided "as-is" without warranties
- **No liability** - Authors are not liable for damages or issues
### 💡 **Why MIT License?**
The MIT License offers maximum flexibility for developers while being business-friendly:
- **Corporate adoption** - Companies can use and modify without legal concerns
- **Open source ecosystem** - Compatible with most other open source licenses
- **Simple compliance** - Easy to understand and follow requirements
- **Innovation encouragement** - Allows derivative works and improvements
- **Community growth** - Promotes sharing and collaboration
---
**AetherCall** - Professional video calling API for modern applications
[](https://github.com/RayenMiri/AetherCall/stargazers)
[](https://github.com/RayenMiri/AetherCall/network/members)
[](https://github.com/RayenMiri/AetherCall/issues)
[](https://opensource.org/licenses/MIT)