Skip to content

Code execution and security

Last updated:

Every line of code you run on SkillAIVibe — in a playground, an exercise or a practice problem in the Learn to Code section — executes entirely inside your own browser. Nothing is sent to a server. As of today, this site has no server-side code execution at all: there is no backend that receives, runs, or even sees the code you write. This page describes the real mechanism and its honest limits, and it is meant to be exactly, not approximately, accurate.

How your code actually runs

13 languages run here, each on a real toolchain rather than a simulation:

  • Python runs on real CPython compiled to WebAssembly (Pyodide). It is the actual language and standard library, with a few documented differences below.
  • JavaScript runs on your browser's own engine. Nothing is downloaded for it.
  • TypeScript is type-checked by the real TypeScript compiler first; a type error stops the program before a line runs, with the compiler's own message. Only then does the resulting JavaScript run.
  • Java is compiled by the real OpenJDK javac, itself running as WebAssembly, then executed as WebAssembly. It is a compiler, not a full JVM, and that has real limits, listed below.
  • C++ is compiled by a real clang toolchain (Emscripten 3.1.24) running as WebAssembly. It is large, so it downloads only when you press the button to start it — about 27 MB, kept in your browser's cache afterwards.
  • C is compiled by the same clang toolchain as C++, with the same one-time download, and runs as WebAssembly.
  • SQL runs on real SQLite (3.49.1), compiled to WebAssembly by the sql.js project. Every run gets a brand-new, empty in-memory database that is discarded when the run ends.

A language is described as running here only after real code was run in it through this sandbox and checked for printing, error reporting, reading input where the language supports it, a refused network request, and an infinite loop that is actually stopped. That was last done on and ; the date is shown on each language's page.

Whatever the language, execution happens inside the same two layers of browser-provided isolation:

  1. A dedicated Web Worker. This is where your code actually runs, and it is the boundary that matters. A Worker has no DOM, no window, no access to the page around it, and no access to browser local storage — so code running there cannot read or change this site's pages, and cannot touch your saved progress, no matter what it does.
  2. A sandboxed iframe that hosts that Worker, carrying a strict Content-Security-Policy (described below) which is what actually blocks network access.

One honest detail about that iframe, because we would rather tell you than have you find it: it is loaded with sandbox="allow-scripts allow-same-origin". We originally designed it without allow-same-origin, which would have given the frame a fully separate "opaque" origin — a stronger boundary. That turned out to be impossible rather than merely inconvenient: a document with an opaque origin physically cannot start a Web Worker from a normal script URL — browsers reject it outright — and the Worker is precisely where your code needs to run. So the frame shares this site's origin. Your browser's developer console will even warn about this combination, and that warning is accurate in general. In this specific design it changes little, because untrusted code never runs in that frame at all — only our own small bootstrap script does. Everything you write is confined to the Worker, which has none of the access the warning is about.

What is enforced, and how

  • A time limit. A run in a playground or an exercise is stopped after 8 seconds. On a practice problem, each run and each test gets 5 seconds. Nothing is ever allowed more than 10. The stop uses the browser's Worker.terminate() — a hard stop, not something your code has to cooperate with — and the page stays responsive meanwhile, because your code never runs on the page's own thread. Time spent downloading and starting a language is not counted against your code.
  • An output limit. Each run can print up to 64 KB to standard output and 64 KB to standard error. Past that, further output is dropped and you see a clear "output truncated" notice, which keeps a runaway print loop from growing without bound.
  • No network access to anywhere outside this site. The Worker your code runs in is started so that it inherits the sandbox's browser security policy (its Content-Security-Policy), and that policy only permits requests back to this site's own address. Because the browser itself enforces it, it applies however a request is attempted: fetch, a WebSocket, a dynamic import(), or another worker your code tries to start. On top of that, the usual ways of making a request are switched off before your code runs — in Python the socket, urllib.request and http.client modules, in JavaScript fetch and its relatives — and starting a new worker is refused, so most attempts fail with a clear message before the policy is even reached. Being precise about the one allowance: the policy cannot tell "code we trust" apart from "code you wrote", and the language runtimes need to download their own files from this site, so it permits requests to this site's own public files rather than none at all. Nothing private is served there — there is no login and nothing to gain. What we cannot promise is that browsers themselves are free of bugs; this rests on the browser enforcing its own security policy correctly.
  • No real filesystem access. Python's file operations inside the sandbox write to an in-memory filesystem that exists only for the lifetime of that one run — never your actual device's storage, and nothing persists after the run ends.
  • What one run can leave behind for the next, stated exactly. For speed, each language's sandbox is reused from one run to the next rather than rebuilt every time. The names your code defines at the top level — its variables, functions and classes — are cleared between runs, and a Java program is compiled completely fresh each time. Some changes to the language itself can outlast a run, though: in JavaScript and TypeScript, adding to or replacing a built-in object (such as Array.prototype or Math.max); in Python, changing a module you imported (math.pi = 3), adding to sys.modules, or adding to the built-ins; and in C++, the same kind of JavaScript change made through EM_ASM. Those last until that sandbox restarts, which happens when you reload the page, and automatically after any run that hits the time limit. Ordinary programs never do any of this. We mention it because we measured it. SQL is the exception: each run's database is created empty and thrown away when the run ends.

The HTML, CSS and JavaScript preview works differently

The HTML and CSS playground does not use the Worker described above, because a web page needs a document to render into. Your HTML, CSS and JavaScript are combined into one page and shown in an iframe sandboxed with scripts allowed and nothing else. That gives the page an origin of its own, so it cannot read this site's pages, cookies or storage (measured: each attempt throws a security error), and it cannot open pop-ups or submit forms. A Content-Security-Policy written into the page blocks every network request, including fetch, XMLHttpRequest, and images or fonts from other sites.

One limit is real: that page is rendered by your browser rather than inside a Worker we can stop, so an infinite loop in its JavaScript can freeze the tab until you reload. To make that less likely, the preview reruns JavaScript only when you press Run, while HTML and CSS update as you type.

What we do not claim

  • Memory limits are best-effort, not guaranteed. No browser API lets us set a hard memory ceiling on a Web Worker. If your code tries to allocate an enormous amount of memory, it will typically throw a catchable error (which we detect and report) or, rarely, crash that one sandboxed worker — which we recover from automatically — but we cannot promise a precise memory limit the way we can promise a precise time limit.
  • Hidden test cases in practice problems are not cryptographically secret. This site has no server, so a "hidden" test case is one that is not linked from anywhere and is only fetched to your browser the moment you click Submit — it is not pre-loaded into the page, and its expected output is never shown to you. But it is fetched to your browser to be graded there, which means a technically curious learner using their browser's developer tools could see it. We think this trade-off is the right one for a project that does not want to run your code on a server. The grading still compares output rather than source text, so a solution that only fits the visible samples fails the hidden ones.
  • Pyodide behaves like real Python, with a few real differences. Threading, multiprocessing, and subprocess are not available inside WebAssembly and will raise an error if used. A small number of Python packages with compiled extensions are not available either — the standard library used in this site's lessons and problems is unaffected.
  • Java here is a compiler, not a JVM. Measured in this sandbox: there is no standard input (no Scanner); a runtime fault such as a null dereference, a division by zero or an array index out of range stops the program outright and cannot be caught, though exceptions you throw yourself work normally; e.getMessage() does not compile; threads compile and run but their bodies never execute; System.exit() and the %f and %n format specifiers do not work. The Java playground states all of this before you start.
  • C++ is frozen at Emscripten 3.1.24 (late 2022). The C++20 core language works, exceptions really work, and std::cin reads the input box, but the std::ranges algorithms are newer than this library and are not there. Starting the compiler takes about 15 to 18 seconds on our test machine even once it is cached, because the toolchain is unpacked again on every page load.
  • C here forgives some memory bugs a real computer would not. Measured: reading through a null pointer printed a garbage number instead of crashing, because WebAssembly memory begins at address zero. A C program that runs cleanly here can still crash on your own machine.
  • SQL here is SQLite's dialect. Most everyday SQL works as it does elsewhere, but some types and functions differ from PostgreSQL or MySQL, and SQLite does not enforce a column's declared type unless the table is created as STRICT.

Reporting a problem

If you notice a gap between what is written here and how the site actually behaves, please tell us. A security concern about the site itself can be sent to the same address; the terms describe how we handle reports. The open-source software the sandbox is built on, with versions and licences, is listed on the licenses page.