Skip to content
Projects
In development2026 →

HuntCode

In-browser code editor

The editor and execution environment behind HunterFr's challenges. Writing code in a browser tab is the trivial part; running a stranger's code without handing them the host is the actual project.

Period
2026 →
Role
Architecture, development, sandboxing
Stack
TypeScript · Next.js · Node.js · Docker · Linux · WebSockets
SECTION / 01

Overview

HuntCode started because sending players to an external editor broke the one rule HunterFr has: the platform should never be the obstacle. Copy-pasting between a tab and a local toolchain is friction, and friction is where beginners stop.

So the editor moved into the page. That part took a week. The remaining time has gone entirely into the half that matters: arbitrary code, written by people who are on the site specifically to find holes, has to execute somewhere.

SECTION / 02

Objectives

  1. 01

    Hostile by default

    The threat model is not 'a user might make a mistake'. It is 'this user is trying to leave the container, and they read the same papers I do'.

  2. 02

    Disposable execution

    Nothing survives a run. Every execution gets a fresh filesystem and is destroyed after it, so persistence is impossible rather than merely unsupported.

  3. 03

    Fast enough to feel local

    If a run takes three seconds to start, people stop iterating and start guessing. Container startup budget is part of the UX spec.

  4. 04

    Deny egress, always

    No outbound network from an execution container. It removes exfiltration, removes the platform as a proxy, and removes an entire category of abuse.

SECTION / 03

Architecture

LayerChoiceWhy
EditorBrowser editor component, server-authoritative stateThe client is a view. What ran, what it produced and what it scored are decided server-side, because the client is under the player's control.
TransportWebSocket for output streamingOutput arrives as it is produced instead of after the process exits, so a long-running or hanging program is legible rather than a spinner.
RunnerOne short-lived container per executionRead-only root filesystem, no capabilities, non-root user, memory and CPU ceilings, pid limit, wall-clock timeout, no network namespace access outward.
SchedulingQueue with per-user concurrency limitsThe cheapest attack on a runner is not an escape, it is asking for ten thousand of them. Concurrency is capped per account before it is capped globally.
SECTION / 04

Challenges

  • Problem

    Cold container start is slow enough to be felt on every single run.

    Solution

    A small pool of pre-warmed containers is kept ready and handed out on demand, then destroyed rather than reused. Start cost moves off the critical path without any state crossing between players.

  • Problem

    A program that never terminates is indistinguishable from one that is merely slow.

    Solution

    Wall-clock timeout, output byte ceiling and pid limit are enforced by the runtime, not by the program. Exceeding any of them ends the run with a specific reason the player can read.

  • Problem

    Streaming output straight to the page is an injection sink.

    Solution

    Output is treated as bytes, never as markup, and is rendered into a text surface. The terminal is a rendering of untrusted data, so it is built like one.

SECTION / 05

Roadmap

  1. Mid 2026Editor in-page, server-authoritative runsShipped
  2. Mid 2026Streamed output over WebSocketShipped
  3. 2026Sandbox hardening and escape testingIn progress
  4. 2026Additional language runtimesPlanned
  5. 2026Public rollout across all HunterFr challengesPlanned
SECTION / 06

What it taught me

  • Every performance shortcut in a sandbox is a reuse of something, and reuse between players is the whole risk. Pre-warming is fine; recycling is not.

  • Limits enforced by the thing being limited are not limits.

  • Writing the attacker's side of your own feature is the fastest way to find out which of your assumptions were decorative.

Next project

HunterFr