Hi everyone! My name is Maksim Fedorov, and I'm an indie developer. I'm about to launch an interactive code player (like YouTube lessons, but you always have access to the code at any moment) in the browser. One of the key features of my platform is the ability to execute written code. Since users can create lessons or record their experience in any language, I urgently needed a universal playground that supports any language, preferably with the ability to run code from multiple files.
Moreover, I didn't want to pay for requests to existing platforms. I also needed a solution that allows me to quickly manage (add/modify) new technologies without incurring extra costs. And, finally, I wanted to release something useful on my GitHub and add a solid project to my resume.
What's available on the market?
There aren't that many solutions. The main provider of such services is Judge0. This sandbox is great overall:
- Supports dozens of languages
- Uses isolate for sandboxing, restricting processes via Linux cgroups/permissions
However, I encountered several issues:
- It's outdated and rarely updatedāmany language versions are obsolete.
- Running code from multiple files is cumbersome. While Judge0 technically supports it, the workflow is very inconvenient.
- Limited configuration options for each language. It simply includes a huge list of binaries without considering language-specific features.
- For example, TypeScript runs without a tsconfig.json, so you can't use Foo[] or Array.
- Judge0 is architecturally complex, relying on a Ruby application and a database for storing states.
- I donāt know Ruby, I donāt need state storage, and I prefer a stateless execution model.
- It requires payments for API access or extensive self-hosting and modifications.
There are also single-language playgrounds, such as:
- The open-source Golang playground (great, but limited to one language).
- JetBrains' Kotlin playground (also solid, but restricted to Kotlin).
š Building My Own
I really liked Googleās approach to sandboxing for Go. Their system works as follows:
- A playground web service compiles the Go code.
- The compiled binary is sent over a private network to another machine (sandbox).
- This machine runs a Docker container with privileged access to the host Docker and spawns sandbox containers, executing the binary via stdin.
- For isolation, Google uses gVisor.
Based on this approach, I decided to build my own universal sandbox supporting multiple languages. Letās go! š
The Architecture
Since the code can be written in any language, the first Playground service is no longer responsible for compilation (that's exactly how it's implemented in Google Playground). Instead, this responsibility is moved to a separate Sandbox service, which runs the necessary commands inside a container.
Each sandbox is no longer Go-specific. Instead, we provide Docker images for different languages. A Docker service receives the request.
Each sandbox image consists of:
- A Dockerfile with the necessary language setup.
- A JSON config defining the execution commands.
- An alias/code identifier for routing requests to the correct container.
Additionally, Iāve integrated hooks via go-plugin, which allow:
- Delegating execution of some languages to external services.
- Implementing authentication and storage mechanisms (currently, only PreRequest hooks are functional).
š¤Ø Challenges
- Compilation requires a network (if need dependencies) and containers run in a gVisor private runtime with --network=none, preventing package installation.I'm open to suggestions on how to resolve this.
- Sandbox state cannot be reused
- Right now, thereās no support for persistent sessions.If anyone has ideas, Iād love your input!
About code
š The project is still in MVP stage (no tests and linter yet) and lacks many features, but you can already run and experiment with it. Iād love to hear your feedback!
š GitHub: https://github.com/codiewio/codenire
Top comments (0)