Monaco is a secure, containerized code execution engine that allows you to run code in multiple programming languages through a simple REST API and WebSocket connections for real-time terminal interaction.
- Multi-language support: Run code in Python, Java, C, and C++
- Secure execution: All code runs in isolated Docker containers
- Resource limits: Memory, CPU, and file descriptor limits to prevent abuse
- Concurrent processing: Efficient job queue for handling multiple requests
- Simple REST API: Easy to integrate with any frontend
- Interactive terminal: Real-time code execution with input/output via WebSockets
- VS Code-like interface: Modern editor with syntax highlighting and file management
Monaco consists of several components:
- HTTP Handlers (
handler/handler.go
): Processes API requests and WebSocket connections - Execution Service (
service/execution.go
): Manages code execution in containers - Job Queue (
queue/queue.go
): Handles concurrent execution of code submissions - Submission Model (
model/submission.go
): Defines the data structure for code submissions
- Editor Area (
EditorArea.jsx
): Main code editor with Monaco editor integration - Terminal Panel (
Panel.jsx
): Interactive terminal for code execution and input - Sidebar (
Sidebar.jsx
): File explorer and project structure navigation - Status Bar (
StatusBar.jsx
): Information display and quick actions
- Frontend submits code to backend via REST API
- Backend assigns a unique ID and queues the execution
- Frontend connects to WebSocket endpoint with the execution ID
- Backend sends real-time execution output through WebSocket
- Frontend can send user input back through WebSocket
- Results are stored and retrievable via REST endpoints
- Backend:
- Go 1.22.3 or higher
- Docker
- Network connectivity for container image pulling
- Frontend:
- Node.js and npm/yarn
- Modern web browser
- Clone the repository:
git clone https://github.com/arnab-afk/monaco.git cd monaco/backend
2.Install Go dependencies:
go mod download
3.Build the application:
go build -o monaco
4.Run the service
./monaco
The backend service will start on port 8080 by default.
- Navigate to the Frontend directory:
cd Frontend
- Install dependencies:
npm install
- Set up environment variables: Create a
.env
or.env.local.
file with:
VITE_API_URL=http://localhost:8080
- Start the development server:
npm run dev
The frontend will be available at http://localhost:5173 by default.
POST /submit
Submits code for execution
{
"language": "python",
"code": "print('Hello, World!')",
"input": ""
}
Response:
{
"id": "6423259c-ee14-c5aa-1c90-d5e989f92aa1"
}
GET /status?id={submissionId}
Checks the status of submission:
{
"id": "6423259c-ee14-c5aa-1c90-d5e989f92aa1",
"status": "completed",
"queuedAt": "2025-03-25T14:30:00Z",
"startedAt": "2025-03-25T14:30:01Z",
"completedAt": "2025-03-25T14:30:02Z",
"executionTime": 1000
}
GET /result?id={submissionId}
Gets the execution result of a submission.
Response:
{
"id": "6423259c-ee14-c5aa-1c90-d5e989f92aa1",
"status": "completed",
"language": "python",
"output": "Hello, World!",
"queuedAt": "2025-03-25T14:30:00Z",
"startedAt": "2025-03-25T14:30:01Z",
"completedAt": "2025-03-25T14:30:02Z",
"executionTime": 1000,
"executionTimeFormatted": "1.0s",
"totalTime": 2000,
"totalTimeFormatted": "2.0s"
}
GET /queue-stats
Gets the statistics about the job queue.
Response:
{
"queue_stats": {
"queue_length": 5,
"max_workers": 3,
"running_jobs": 3
},
"submissions": 42
}
ws://localhost:8080/ws/terminal?id={submissionId}
Establishes a real-time connection for terminal interaction.
- The server sends execution output as plain text messages.
- The client can send input as plain text messages (with newline).
- Connection automatically closes when execution completes or fails.
The system supports interactive programs requiring user input:
- The frontend detects possible input prompts by looking for patterns
- When detected, it focuses the terminal and allows user input
- User input is captured in the terminal component's inputBuffer
- When the user presses Enter, the input is:
- Sent to the backend via WebSocket.
- Displayed in the terminal.
- Buffer is cleared for next input.
- The input is processed by the running program in real-time.
Troubleshooting tips:
- Ensure WebSocket connection is established before sending input
- Check for WebSocket errors in console
- Verify input reaches the backend by checking server logs
- Ensure newline characters are properly appended to input.
- Version: Python 3.9
- Input Handling: Direct stdin piping
- Limitations: No file I/O, no package imports outside standard library
- Resource Limits: 100MB memory, 10% CPU
- Version: Java 11 (Eclipse Temurin)
- Class Detection: Extracts class name from code using regex.
- Memory Settings: 64MB min heap, 256MB max heap
- Resource Limits: 400MB memory, 50% CPU C
- Version: Latest GCC
- Compilation Flags: Default GCC settings
- Resource Limits: 100MB memory, 10% CPU
- Version: Latest G++
- Standard: C++17
- Resource Limits: 100MB memory, 10% CPU
All code execution happens within isolated Docker containers with:
- No network access (
--network=none
) - Limited CPU and memory resources
- Limited file system access
- No persistent storage
- Execution time limits (10-15 seconds)
Check backend logs for execution details Use browser developer tools to debug WebSocket connections Terminal panel shows WebSocket connection status and errors Check Docker logs for container-related issues.
Contributions are welcome! Please feel free to submit a Pull Request.