A small experiment: bootstrapping Racket into a ChatGPT sandbox at openai

I have been working on a small experiment: can a ChatGPT AI assistant install and use a real Racket distribution inside its temporary Linux sandbox?

The story started with something much more modest. I wanted the assistant to help test a Racket module, js-maker.rkt, more seriously. But there was an immediate problem: the assistant’s sandbox did not have Racket installed.

At first we tried the obvious detour: use Scheme implementations available through npm. BiwaScheme and LIPS were useful as semantic references for some small tests, and they were certainly better than nothing. But they were not Racket. For testing a real Racket module, with Racket’s module system, macro behavior, and package ecosystem, they were simply too limited.
The next idea was to provide a real Racket environment through some kind of web-accessible sandbox. That sounded promising, but it ran into another practical constraint: the assistant could not just SSH into a machine, run arbitrary web-session logic, or use the shell environment as a normal network client.

So the current setup emerged as a kind of small composition for constrained instruments: HTML pages, ordinary links, text/plain, base64, PHP, and a little bit of conducting.

The answer turns out to be: yes — a ChatGPT AI assistant can bootstrap and use Racket — provided the protocol is carefully arranged and the assistant is given a score it can actually follow. At that point the human only has to provide the initial bootstrap link; from there the assistant can follow the score by itself, link by link, until Racket is installed and the requested package test has run.

The important distinction is that there are really two worlds involved. The assistant does have a web-fetching facility: it can read web pages from the internet and follow ordinary links it finds there. But inside the actual Linux sandbox where commands run, network access is absent or very restricted. In practice, the shell cannot simply do:

curl ...
wget ...
python -c 'import urllib.request; ...'

DNS may not work there, or outbound access may be blocked. So the assistant can read the score through its web fetcher, but the orchestra pit where the shell commands run has no direct line to the outside world. No romantic repertoire; everything has to be carefully arranged.

There are a few other constraints as well. The assistant is not really a general-purpose browser. It cannot conveniently submit arbitrary POST requests, maintain an interactive web session, or behave like a full HTTP client from inside the sandbox. The web-fetching side also seems, understandably, cautious about anything that looks too much like a binary download. If the Content-Type says “zip”, or even if the URL looks like it points to .zip or .bin, the fetch may be refused.

bootstrapping racket

So the orchestration is deliberately simple:

  • HTML pages are used for navigation;
  • every next step is represented as an ordinary link;
  • payloads are always text/plain;
  • binary data is transported as base64;
  • payload URLs avoid binary-looking filenames.
    The first endpoint is a Racket bootstrap page:
https://rktsndbx.dijkewijk.nl/bootstrap-racket?next=<a unique token>

On the server there is a racket.zip. This archive is not produced by the assistant. I prepared it beforehand by running the Racket 9.1 Linux shell installer in my own sandbox and installing Racket directly into /tmp/racket, without adding extra symlinks or relying on system paths. I then zipped that /tmp/racket directory. That makes the archive self-contained for the assistant’s Linux sandbox: once it is unpacked into /tmp, the expected tools are available as:

/tmp/racket/bin/racket
/tmp/racket/bin/raco

A small PHP script splits this zip into manageable parts and generates a plain HTML page with links to those parts. Each part link returns only text/plain containing base64 for one binary chunk. The assistant follows the links with its web fetcher, stores the results in the sandbox, decodes them locally, concatenates the binary chunks, reconstructs racket.zip, tests it, and unpacks it with:

(cd /tmp && unzip /tmp/racket.zip)

From that point on, the assistant is instructed to use those exact paths — no wandering off into whatever racket or raco might mean in $PATH.

Package installation

The next movement is package installation. The bootstrap page links to an HTML package index generated from the Racket package catalog. Each package name is itself a link back to my server, for example:

/package?name=crc32&next=...

The package endpoint resolves the package source, fetches or reuses a cached zip of the current repository HEAD, checks whether the repository SHA has changed, splits the zip into base64 text/plain parts, and produces another HTML page with links to those parts. The assistant follows the links, reconstructs the package zip, and installs it with:

/tmp/racket/bin/raco pkg install --auto --no-setup ./crc32.zip

For a first real test, I installed the crc32 package this way and ran:

(require crc32)
(displayln (crc32-bytes #"123456789"))

The output was:

3421780262

which is the expected CRC32 value #xcbf43926.

The full chain

So the current chain works end to end:

  1. bootstrap Racket into /tmp/racket;
  2. follow an HTML package index;
  3. fetch a package as base64 text parts;
  4. reconstruct the package zip in the sandbox;
  5. install it with /tmp/racket/bin/raco;
  6. run an actual Racket test with /tmp/racket/bin/racket.

This is still experimental — more rehearsal than finished performance. But it is already useful as a small Racket test rig for an AI assistant in a sandbox where the assistant can inspect the web, but the shell environment itself cannot simply reach the network.

The main lesson so far: when the environment is constrained, the arrangement matters. Give the assistant a clear score to follow, keep the conducting simple, and avoid asking any single instrument to do too much. HTML for navigation, text/plain base64 for payloads, and no suspicious-looking binary URLs.

It is simple, but the piece plays :smiling_face_with_sunglasses:.

The site has a login and prompt management. If you want to try it out yourself, just drop me a private note.

2 Likes